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

cinode / go / 3761031085

22 Dec 2022 08:43PM UTC coverage: 79.498%. Remained the same
3761031085

push

github

Bartek
Add codecov support

919 of 1156 relevant lines covered (79.5%)

0.87 hits per line

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

94.29
/datastore/webinterface.go
1
package datastore
2

3
import (
4
        "encoding/json"
5
        "errors"
6
        "io"
7
        "mime/multipart"
8
        "net/http"
9

10
        "github.com/cinode/go/common"
11
)
12

13
var (
14
        errNoData = errors.New("no upload data")
15
)
16

17
// WebInterface provides simple web interface for given Datastore
18
type webInterface struct {
19
        ds DS
20
}
21

22
// WebInterface returns http handler representing web interface to given
23
// Datastore instance
24
func WebInterface(ds DS) http.Handler {
1✔
25
        return &webInterface{
1✔
26
                ds: ds,
1✔
27
        }
1✔
28
}
1✔
29

30
func (i *webInterface) ServeHTTP(w http.ResponseWriter, r *http.Request) {
1✔
31
        switch r.Method {
1✔
32
        case http.MethodGet:
1✔
33
                i.serveGet(w, r)
1✔
34
        case http.MethodPut:
1✔
35
                i.servePut(w, r)
1✔
36
        case http.MethodDelete:
1✔
37
                i.serveDelete(w, r)
1✔
38
        case http.MethodHead:
1✔
39
                i.serveHead(w, r)
1✔
40
        default:
1✔
41
                http.Error(w, "Unsupported method", http.StatusMethodNotAllowed)
1✔
42
        }
43
}
44

45
func (i *webInterface) getName(w http.ResponseWriter, r *http.Request) (common.BlobName, error) {
1✔
46
        // Don't allow url queries and require path to start with '/'
1✔
47
        if r.URL.Path[0] != '/' || r.URL.RawQuery != "" {
2✔
48
                return nil, common.ErrInvalidBlobName
1✔
49
        }
1✔
50

51
        bn, err := common.BlobNameFromString(r.URL.Path[1:])
1✔
52
        if err != nil {
1✔
53
                return nil, err
×
54
        }
×
55

56
        return bn, nil
1✔
57
}
58

59
func (i *webInterface) sendName(name common.BlobName, w http.ResponseWriter, r *http.Request) {
1✔
60
        w.Header().Set("Content-type", "application/json")
1✔
61
        json.NewEncoder(w).Encode(&webNameResponse{
1✔
62
                Name: name.String(),
1✔
63
        })
1✔
64
}
1✔
65

66
func (i *webInterface) sendError(w http.ResponseWriter, httpCode int, code string, message string) {
1✔
67
        w.Header().Set("Content-type", "application/json")
1✔
68
        w.WriteHeader(httpCode)
1✔
69
        json.NewEncoder(w).Encode(&webErrResponse{
1✔
70
                Code:    code,
1✔
71
                Message: message,
1✔
72
        })
1✔
73
}
1✔
74
func (i *webInterface) checkErr(err error, w http.ResponseWriter, r *http.Request) bool {
1✔
75
        if err == nil {
2✔
76
                return true
1✔
77
        }
1✔
78

79
        if errors.Is(err, ErrNotFound) {
2✔
80
                http.NotFound(w, r)
1✔
81
                return false
1✔
82
        }
1✔
83

84
        code := webErrToCode(err)
1✔
85
        if code != "" {
2✔
86
                i.sendError(w, http.StatusBadRequest, code, err.Error())
1✔
87
                return false
1✔
88
        }
1✔
89

90
        http.Error(w, "Internal server error", http.StatusInternalServerError)
1✔
91
        return false
1✔
92
}
93

94
func (i *webInterface) serveGet(w http.ResponseWriter, r *http.Request) {
1✔
95
        name, err := i.getName(w, r)
1✔
96
        if !i.checkErr(err, w, r) {
2✔
97
                return
1✔
98
        }
1✔
99

100
        err = i.ds.Read(r.Context(), name, w)
1✔
101
        if !i.checkErr(err, w, r) {
2✔
102
                return
1✔
103
        }
1✔
104
}
105

106
type partReader struct {
107
        p *multipart.Part
108
        b io.Closer
109
}
110

111
func (r *partReader) Read(b []byte) (int, error) {
1✔
112
        return r.p.Read(b)
1✔
113
}
1✔
114

115
func (r *partReader) Close() error {
1✔
116
        err1 := r.p.Close()
1✔
117
        err2 := r.b.Close()
1✔
118
        if err1 != nil {
1✔
119
                return err1
×
120
        }
×
121
        return err2
1✔
122
}
123

124
func (i *webInterface) getUploadReader(r *http.Request) (io.ReadCloser, error) {
1✔
125

1✔
126
        mpr, err := r.MultipartReader()
1✔
127
        if err == http.ErrNotMultipart {
2✔
128
                // Not multipart, read raw body data
1✔
129
                return r.Body, nil
1✔
130
        }
1✔
131
        if err != nil {
1✔
132
                return nil, err
×
133
        }
×
134

135
        for {
2✔
136
                // Get next part of the upload
1✔
137
                part, err := mpr.NextPart()
1✔
138
                if err == io.EOF {
2✔
139
                        return nil, errNoData
1✔
140
                }
1✔
141
                if err != nil {
1✔
142
                        return nil, err
×
143
                }
×
144

145
                // Search for first file input
146
                fn := part.FileName()
1✔
147
                if fn != "" {
2✔
148
                        return &partReader{
1✔
149
                                p: part,
1✔
150
                                b: r.Body,
1✔
151
                        }, nil
1✔
152
                }
1✔
153
        }
154
}
155

156
func (i *webInterface) servePut(w http.ResponseWriter, r *http.Request) {
1✔
157
        name, err := i.getName(w, r)
1✔
158
        if !i.checkErr(err, w, r) {
2✔
159
                return
1✔
160
        }
1✔
161

162
        reader, err := i.getUploadReader(r)
1✔
163
        if !i.checkErr(err, w, r) {
2✔
164
                return
1✔
165
        }
1✔
166
        defer reader.Close()
1✔
167

1✔
168
        err = i.ds.Update(r.Context(), name, reader)
1✔
169
        if !i.checkErr(err, w, r) {
2✔
170
                return
1✔
171
        }
1✔
172

173
        i.sendName(name, w, r)
1✔
174
}
175

176
func (i *webInterface) serveDelete(w http.ResponseWriter, r *http.Request) {
1✔
177

1✔
178
        name, err := i.getName(w, r)
1✔
179
        if !i.checkErr(err, w, r) {
2✔
180
                return
1✔
181
        }
1✔
182

183
        err = i.ds.Delete(r.Context(), name)
1✔
184
        if !i.checkErr(err, w, r) {
2✔
185
                return
1✔
186
        }
1✔
187

188
        i.sendName(name, w, r)
1✔
189
}
190

191
func (i *webInterface) serveHead(w http.ResponseWriter, r *http.Request) {
1✔
192
        name, err := i.getName(w, r)
1✔
193
        if !i.checkErr(err, w, r) {
2✔
194
                return
1✔
195
        }
1✔
196

197
        exists, err := i.ds.Exists(r.Context(), name)
1✔
198
        if !i.checkErr(err, w, r) {
2✔
199
                return
1✔
200
        }
1✔
201

202
        if !exists {
2✔
203
                http.NotFound(w, r)
1✔
204
        }
1✔
205
}
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

© 2025 Coveralls, Inc