MCPcopy
hub / github.com/perkeep/perkeep / ServeHTTP

Method ServeHTTP

app/publisher/zip.go:227–290  ·  view source on GitHub ↗

ServeHTTP streams a zip archive of all the files "under" zh.root. That is, all the files pointed by file permanodes, which are directly members of zh.root or recursively down directory permanodes and permanodes members. To build the fullpath of a file in a collection, it uses the collection title if

(rw http.ResponseWriter, req *http.Request)

Source from the content-addressed store, hash-verified

225// the collection title if present, its blobRef otherwise, as
226// a directory name.
227func (zh *zipHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
228 // TODO: use http.ServeContent, so Range requests work and downloads can be resumed.
229 // Will require calculating the zip length once first (ideally as cheaply as possible,
230 // with dummy counting writer and dummy all-zero-byte-files of a fixed size),
231 // and then making a dummy ReadSeeker for ServeContent that can seek to the end,
232 // and then seek back to the beginning, but then seeks forward make it remember
233 // to skip that many bytes from the archive/zip writer when answering Reads.
234 if !httputil.IsGet(req) {
235 http.Error(rw, "Invalid method", http.StatusMethodNotAllowed)
236 return
237 }
238 bf, err := zh.blobList("", zh.root)
239 if err != nil {
240 log.Printf("Could not serve zip for %v: %v", zh.root, err)
241 http.Error(rw, "Server error", http.StatusInternalServerError)
242 return
243 }
244 blobFiles := renameDuplicates(bf)
245
246 h := rw.Header()
247 h.Set("Content-Type", "application/zip")
248 filename := zh.filename
249 if filename == "" {
250 filename = "download.zip"
251 }
252 h.Set("Content-Disposition", mime.FormatMediaType("attachment", map[string]string{"filename": filename}))
253 zw := zip.NewWriter(rw)
254 etag := sha1.New()
255 for _, file := range blobFiles {
256 etag.Write([]byte(file.blobRef.String()))
257 }
258 h.Set("Etag", fmt.Sprintf(`"%x"`, etag.Sum(nil)))
259
260 for _, file := range blobFiles {
261 fr, err := schema.NewFileReader(context.TODO(), zh.fetcher, file.blobRef)
262 if err != nil {
263 log.Printf("Can not add %v in zip, not a file: %v", file.blobRef, err)
264 http.Error(rw, "Server error", http.StatusInternalServerError)
265 return
266 }
267 zh := zip.FileHeader{
268 Name: file.path,
269 Method: zip.Store,
270 Modified: fr.ModTime().UTC(),
271 }
272 f, err := zw.CreateHeader(&zh)
273 if err != nil {
274 log.Printf("Could not create %q in zip: %v", file.path, err)
275 http.Error(rw, "Server error", http.StatusInternalServerError)
276 return
277 }
278 _, err = io.Copy(f, fr)
279 fr.Close()
280 if err != nil {
281 log.Printf("Could not zip %q: %v", file.path, err)
282 return
283 }
284 }

Callers 1

serveZipMethod · 0.95

Calls 13

blobListMethod · 0.95
ModTimeMethod · 0.95
CloseMethod · 0.95
IsGetFunction · 0.92
NewFileReaderFunction · 0.92
renameDuplicatesFunction · 0.85
PrintfMethod · 0.80
SumMethod · 0.80
SetMethod · 0.65
CloseMethod · 0.65
ErrorMethod · 0.45
WriteMethod · 0.45

Tested by

no test coverage detected