+31
-1
knotserver/git/git.go
+31
-1
knotserver/git/git.go
···
37
37
}
38
38
39
39
var (
40
-
ErrBinaryFile = fmt.Errorf("binary file")
40
+
ErrBinaryFile = fmt.Errorf("binary file")
41
+
ErrNotBinaryFile = fmt.Errorf("not binary file")
41
42
)
42
43
43
44
type GitRepo struct {
···
191
192
} else {
192
193
return "", ErrBinaryFile
193
194
}
195
+
}
196
+
197
+
func (g *GitRepo) BinContent(path string) ([]byte, error) {
198
+
c, err := g.r.CommitObject(g.h)
199
+
if err != nil {
200
+
return nil, fmt.Errorf("commit object: %w", err)
201
+
}
202
+
203
+
tree, err := c.Tree()
204
+
if err != nil {
205
+
return nil, fmt.Errorf("file tree: %w", err)
206
+
}
207
+
208
+
file, err := tree.File(path)
209
+
if err != nil {
210
+
return nil, err
211
+
}
212
+
213
+
isbin, _ := file.IsBinary()
214
+
if isbin {
215
+
reader, err := file.Reader()
216
+
if err != nil {
217
+
return nil, fmt.Errorf("opening file reader: %w", err)
218
+
}
219
+
defer reader.Close()
220
+
221
+
return io.ReadAll(reader)
222
+
}
223
+
return nil, ErrNotBinaryFile
194
224
}
195
225
196
226
func (g *GitRepo) Tags() ([]*TagReference, error) {
+1
knotserver/handler.go
+1
knotserver/handler.go
+36
-1
knotserver/routes.go
+36
-1
knotserver/routes.go
···
194
194
return
195
195
}
196
196
197
+
func (h *Handle) BlobRaw(w http.ResponseWriter, r *http.Request) {
198
+
treePath := chi.URLParam(r, "*")
199
+
ref := chi.URLParam(r, "ref")
200
+
ref, _ = url.PathUnescape(ref)
201
+
202
+
l := h.l.With("handler", "BlobRaw", "ref", ref, "treePath", treePath)
203
+
204
+
path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
205
+
gr, err := git.Open(path, ref)
206
+
if err != nil {
207
+
notFound(w)
208
+
return
209
+
}
210
+
211
+
contents, err := gr.BinContent(treePath)
212
+
if err != nil {
213
+
writeError(w, err.Error(), http.StatusBadRequest)
214
+
l.Error("file content", "error", err.Error())
215
+
return
216
+
}
217
+
218
+
mimeType := http.DetectContentType(contents)
219
+
220
+
if !strings.HasPrefix(mimeType, "image/") && !strings.HasPrefix(mimeType, "video/") {
221
+
l.Error("attempted to serve non-image/video file", "mimetype", mimeType)
222
+
writeError(w, "only image and video files can be accessed directly", http.StatusForbidden)
223
+
return
224
+
}
225
+
226
+
w.Header().Set("Cache-Control", "public, max-age=86400") // cache for 24 hours
227
+
w.Header().Set("ETag", fmt.Sprintf("%x", sha256.Sum256(contents)))
228
+
w.Header().Set("Content-Type", mimeType)
229
+
w.Write(contents)
230
+
}
231
+
197
232
func (h *Handle) Blob(w http.ResponseWriter, r *http.Request) {
198
233
treePath := chi.URLParam(r, "*")
199
234
ref := chi.URLParam(r, "ref")
200
235
ref, _ = url.PathUnescape(ref)
201
236
202
-
l := h.l.With("handler", "FileContent", "ref", ref, "treePath", treePath)
237
+
l := h.l.With("handler", "Blob", "ref", ref, "treePath", treePath)
203
238
204
239
path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
205
240
gr, err := git.Open(path, ref)