func (dh *DownloadHandler) fileInfo(req *http.Request, file blob.Ref) (fi fileInfo, packed bool, err error) { // Fast path for blobpacked. fi, ok := fileInfoPacked(dh.Search, dh.Fetcher, req, file) if debugPack { log.Printf("download.go: fileInfoPacked: ok=%v, %+v", ok, fi) } if ok { return fi, true, nil } fr, err := schema.NewFileReader(dh.blobSource(), file) if err != nil { return } mime := dh.ForceMIME if mime == "" { mime = magic.MIMETypeFromReaderAt(fr) } if mime == "" { mime = "application/octet-stream" } return fileInfo{ mime: mime, name: fr.FileName(), size: fr.Size(), rs: fr, close: fr.Close, }, false, nil }
func (dh *DownloadHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request, file blob.Ref) { if req.Method != "GET" && req.Method != "HEAD" { http.Error(rw, "Invalid download method", 400) return } if req.Header.Get("If-Modified-Since") != "" { // Immutable, so any copy's a good copy. rw.WriteHeader(http.StatusNotModified) return } fr, err := schema.NewFileReader(dh.blobSource(), file) if err != nil { http.Error(rw, "Can't serve file: "+err.Error(), 500) return } defer fr.Close() schema := fr.FileSchema() h := rw.Header() h.Set("Content-Length", fmt.Sprintf("%d", schema.SumPartsSize())) h.Set("Expires", time.Now().Add(oneYear).Format(http.TimeFormat)) mimeType := magic.MIMETypeFromReaderAt(fr) if dh.ForceMime != "" { mimeType = dh.ForceMime } if mimeType == "" { mimeType = "application/octet-stream" } h.Set("Content-Type", mimeType) if mimeType == "application/octet-stream" { // Chrome seems to silently do nothing on // application/octet-stream unless this is set. // Maybe it's confused by lack of URL it recognizes // along with lack of mime type? rw.Header().Set("Content-Disposition", "attachment; filename=file-"+file.String()+".dat") } if req.Method == "HEAD" && req.FormValue("verifycontents") != "" { vbr, ok := blob.Parse(req.FormValue("verifycontents")) if !ok { return } hash := vbr.Hash() if hash == nil { return } io.Copy(hash, fr) // ignore errors, caught later if vbr.HashMatches(hash) { rw.Header().Set("X-Camli-Contents", vbr.String()) } return } http.ServeContent(rw, req, "", time.Now(), fr) }