• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

oxyno-zeta / s3-proxy / 20213714844

14 Dec 2025 08:29PM UTC coverage: 86.02% (-0.07%) from 86.089%
20213714844

push

github

oxyno-zeta
chore(deps): update gomock monorepo to v0.6.0

5427 of 6309 relevant lines covered (86.02%)

46.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.58
/pkg/s3-proxy/response-handler/utils.go
1
package responsehandler
2

3
import (
4
        "io"
5
        "net/http"
6
        "reflect"
7
        "strconv"
8
        "strings"
9
        "time"
10

11
        "emperror.dev/errors"
12

13
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/config"
14
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/response-handler/models"
15
        utils "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/utils/generalutils"
16
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/utils/templateutils"
17
)
18

19
func (*handler) manageStatus(
20
        helpersContent string,
21
        tplConfigItem *config.TargetTemplateConfigItem,
22
        defaultTpl string,
23
        data any,
24
) (int, error) {
98✔
25
        // Create main status content
98✔
26
        statusContent := helpersContent
98✔
27

98✔
28
        // Check if per target template is declared
98✔
29
        if tplConfigItem != nil && tplConfigItem.Status != "" {
104✔
30
                // Concat
6✔
31
                statusContent = statusContent + "\n" + tplConfigItem.Status
6✔
32
        } else {
98✔
33
                // Concat
92✔
34
                statusContent = statusContent + "\n" + defaultTpl
92✔
35
        }
92✔
36

37
        // Execute status main template
38
        buf, err := templateutils.ExecuteTemplate(statusContent, data)
98✔
39
        // Check error
98✔
40
        if err != nil {
98✔
41
                return 0, err
×
42
        }
×
43

44
        // Get string from buffer
45
        str := buf.String()
98✔
46
        // Remove all new lines
98✔
47
        str = utils.NewLineMatcherRegex.ReplaceAllString(str, "")
98✔
48

98✔
49
        // Try to parse int from string
98✔
50
        return strconv.Atoi(str)
98✔
51
}
52

53
func (*handler) manageHeaders(helpersContent string, headersTpl map[string]string, hData any) (map[string]string, error) {
131✔
54
        // Store result
131✔
55
        res := map[string]string{}
131✔
56

131✔
57
        // Loop over all headers asked
131✔
58
        for k, htpl := range headersTpl {
261✔
59
                // Concat helpers to header template
130✔
60
                tpl := helpersContent + "\n" + htpl
130✔
61
                // Execute template
130✔
62
                buf, err := templateutils.ExecuteTemplate(tpl, hData)
130✔
63
                // Check error
130✔
64
                if err != nil {
134✔
65
                        return nil, err
4✔
66
                }
4✔
67
                // Get string from buffer
68
                str := buf.String()
126✔
69
                // Remove all new lines
126✔
70
                str = utils.NewLineMatcherRegex.ReplaceAllString(str, "")
126✔
71
                // Save data only if the header isn't empty
126✔
72
                if str != "" {
251✔
73
                        // Save
125✔
74
                        res[k] = str
125✔
75
                }
125✔
76
        }
77

78
        // Return
79
        return res, nil
127✔
80
}
81

82
// send will send the response.
83
func (h *handler) send(bodyBuf io.WriterTo, headers map[string]string, status int) error {
116✔
84
        // Loop over headers
116✔
85
        for k, v := range headers {
228✔
86
                // Set header
112✔
87
                h.res.Header().Set(k, v)
112✔
88
        }
112✔
89

90
        // Set status code
91
        h.res.WriteHeader(status)
116✔
92

116✔
93
        // Check if we aren't in head answer
116✔
94
        if !h.headAnswerMode {
215✔
95
                // Write to response
99✔
96
                _, err := bodyBuf.WriteTo(h.res)
99✔
97
                // Check if error exists
99✔
98
                if err != nil {
99✔
99
                        return errors.WithStack(err)
×
100
                }
×
101
        }
102

103
        return nil
116✔
104
}
105

106
func setHeadersFromObjectOutput(w http.ResponseWriter, obj *models.StreamInput) {
45✔
107
        setStrHeader(w, "Cache-Control", obj.CacheControl)
45✔
108
        setStrHeader(w, "Expires", obj.Expires)
45✔
109
        setStrHeader(w, "Content-Digest", obj.ContentDigest)
45✔
110
        setStrHeader(w, "Content-Disposition", obj.ContentDisposition)
45✔
111
        setStrHeader(w, "Content-Encoding", obj.ContentEncoding)
45✔
112
        setStrHeader(w, "Content-Language", obj.ContentLanguage)
45✔
113
        setIntHeader(w, "Content-Length", obj.ContentLength)
45✔
114
        setStrHeader(w, "Content-Range", obj.ContentRange)
45✔
115
        setStrHeader(w, "Content-Type", obj.ContentType)
45✔
116
        setStrHeader(w, "ETag", obj.ETag)
45✔
117
        setStrHeader(w, "Accept-Ranges", "bytes")
45✔
118
        setTimeHeader(w, "Last-Modified", obj.LastModified)
45✔
119

45✔
120
        httpStatus := determineHTTPStatus(obj)
45✔
121
        w.WriteHeader(httpStatus)
45✔
122
}
45✔
123

124
func determineHTTPStatus(obj *models.StreamInput) int {
45✔
125
        contentRangeIsGiven := len(obj.ContentRange) > 0
45✔
126
        // Check if content will be partial
45✔
127
        if contentRangeIsGiven {
48✔
128
                if !totalFileSizeEqualToContentRange(obj) {
4✔
129
                        // Return partial content
1✔
130
                        return http.StatusPartialContent
1✔
131
                }
1✔
132
        }
133

134
        // Return ok
135
        return http.StatusOK
43✔
136
}
137

138
func totalFileSizeEqualToContentRange(obj *models.StreamInput) bool {
3✔
139
        totalSizeIsEqualToContentRange := false
3✔
140
        // Calculate total file size
3✔
141
        totalSize, err := strconv.ParseInt(getFileSizeAsString(obj), 10, 64)
3✔
142
        if err == nil {
6✔
143
                if totalSize == (obj.ContentLength) {
5✔
144
                        totalSizeIsEqualToContentRange = true
2✔
145
                }
2✔
146
        }
147
        // Return result
148
        return totalSizeIsEqualToContentRange
3✔
149
}
150

151
/*
152
*
153
See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range
154
*/
155
func getFileSizeAsString(obj *models.StreamInput) string {
3✔
156
        s := strings.Split(obj.ContentRange, "/")
3✔
157
        totalSizeString := s[1]
3✔
158
        totalSizeString = strings.TrimSpace(totalSizeString)
3✔
159
        // Return result
3✔
160
        return totalSizeString
3✔
161
}
3✔
162

163
func setStrHeader(w http.ResponseWriter, key, value string) {
447✔
164
        if len(value) > 0 {
584✔
165
                w.Header().Add(key, value)
137✔
166
        }
137✔
167
}
168

169
func setIntHeader(w http.ResponseWriter, key string, value int64) {
45✔
170
        if value > 0 {
89✔
171
                w.Header().Add(key, strconv.FormatInt(value, 10))
44✔
172
        }
44✔
173
}
174

175
func setTimeHeader(w http.ResponseWriter, key string, value time.Time) {
45✔
176
        if !reflect.DeepEqual(value, time.Time{}) {
89✔
177
                w.Header().Add(key, value.UTC().Format(http.TimeFormat))
44✔
178
        }
44✔
179
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc