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

oxyno-zeta / s3-proxy / 10999982596

23 Sep 2024 06:00PM UTC coverage: 86.012% (-0.2%) from 86.185%
10999982596

push

github

oxyno-zeta
feat: Add HEAD request support

Close #474

252 of 299 new or added lines in 7 files covered. (84.28%)

27 existing lines in 2 files now uncovered.

5325 of 6191 relevant lines covered (86.01%)

44.89 hits per line

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

96.55
/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
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/config"
13
        utils "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/utils/generalutils"
14
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/utils/templateutils"
15
)
16

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

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

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

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

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

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

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

76
        // Return
77
        return res, nil
127✔
78
}
79

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

88
        // Set status code
89
        h.res.WriteHeader(status)
116✔
90

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

101
        return nil
116✔
102
}
103

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

44✔
117
        httpStatus := determineHTTPStatus(obj)
44✔
118
        w.WriteHeader(httpStatus)
44✔
119
}
44✔
120

121
func determineHTTPStatus(obj *StreamInput) int {
44✔
122
        contentRangeIsGiven := len(obj.ContentRange) > 0
44✔
123
        // Check if content will be partial
44✔
124
        if contentRangeIsGiven {
46✔
125
                if !totalFileSizeEqualToContentRange(obj) {
3✔
126
                        // Return partial content
1✔
127
                        return http.StatusPartialContent
1✔
128
                }
1✔
129
        }
130

131
        // Return ok
132
        return http.StatusOK
43✔
133
}
134

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

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

160
func setStrHeader(w http.ResponseWriter, key string, value string) {
396✔
161
        if len(value) > 0 {
525✔
162
                w.Header().Add(key, value)
129✔
163
        }
129✔
164
}
165

166
func setIntHeader(w http.ResponseWriter, key string, value int64) {
44✔
167
        if value > 0 {
87✔
168
                w.Header().Add(key, strconv.FormatInt(value, 10))
43✔
169
        }
43✔
170
}
171

172
func setTimeHeader(w http.ResponseWriter, key string, value time.Time) {
44✔
173
        if !reflect.DeepEqual(value, time.Time{}) {
87✔
174
                w.Header().Add(key, value.UTC().Format(http.TimeFormat))
43✔
175
        }
43✔
176
}
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