// ServeBlobRef serves a blob. func ServeBlobRef(rw http.ResponseWriter, req *http.Request, blobRef blob.Ref, fetcher blob.Fetcher) { rc, size, err := fetcher.Fetch(blobRef) switch err { case nil: break case os.ErrNotExist: rw.WriteHeader(http.StatusNotFound) fmt.Fprintf(rw, "Blob %q not found", blobRef) return default: httputil.ServeError(rw, req, err) return } defer rc.Close() rw.Header().Set("Content-Type", "application/octet-stream") var content io.ReadSeeker = readerutil.NewFakeSeeker(rc, int64(size)) rangeHeader := req.Header.Get("Range") != "" const small = 32 << 10 var b *blob.Blob if rangeHeader || size < small { // Slurp to memory, so we can actually seek on it (for Range support), // or if we're going to be showing it in the browser (below). b, err = blob.FromReader(blobRef, rc, size) if err != nil { httputil.ServeError(rw, req, err) return } content = b.Open() } if !rangeHeader && size < small { // If it's small and all UTF-8, assume it's text and // just render it in the browser. This is more for // demos/debuggability than anything else. It isn't // part of the spec. if b.IsUTF8() { rw.Header().Set("Content-Type", "text/plain; charset=utf-8") } } http.ServeContent(rw, req, "", dummyModTime, content) }
func verifySizeAndHash(t *testing.T, blob *blob.Blob) { hash := sha1.New() r := blob.Open() n, err := io.Copy(hash, r) if err != nil { t.Fatal(err) } r.Close() if uint32(n) != blob.Size() { t.Fatalf("read %d bytes from blob %v; want %v", n, blob.Ref(), blob.Size()) } if !blob.SizedRef().HashMatches(hash) { t.Fatalf("read wrong bytes from blobref %v (digest mismatch)", blob.Ref()) } }