ServeBlobRef serves a blob.
(rw http.ResponseWriter, req *http.Request, blobRef blob.Ref, fetcher blob.Fetcher)
| 61 | |
| 62 | // ServeBlobRef serves a blob. |
| 63 | func ServeBlobRef(rw http.ResponseWriter, req *http.Request, blobRef blob.Ref, fetcher blob.Fetcher) { |
| 64 | ctx := req.Context() |
| 65 | if fetcher == nil { |
| 66 | log.Printf("gethandler: no fetcher configured for %s (ref=%v)", req.URL.Path, blobRef) |
| 67 | rw.WriteHeader(http.StatusNotFound) |
| 68 | io.WriteString(rw, "no fetcher configured") |
| 69 | return |
| 70 | } |
| 71 | rc, size, err := fetcher.Fetch(ctx, blobRef) |
| 72 | switch err { |
| 73 | case nil: |
| 74 | break |
| 75 | case os.ErrNotExist: |
| 76 | rw.WriteHeader(http.StatusNotFound) |
| 77 | fmt.Fprintf(rw, "Blob %q not found", blobRef) |
| 78 | return |
| 79 | default: |
| 80 | httputil.ServeError(rw, req, err) |
| 81 | return |
| 82 | } |
| 83 | defer rc.Close() |
| 84 | rw.Header().Set("Content-Type", "application/octet-stream") |
| 85 | rw.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d, immutable", int(HTTP_CACHE_DURATION.Seconds()))) |
| 86 | |
| 87 | var content = readerutil.NewFakeSeeker(rc, int64(size)) |
| 88 | rangeHeader := req.Header.Get("Range") != "" |
| 89 | const small = 32 << 10 |
| 90 | var b *blob.Blob |
| 91 | if rangeHeader || size < small { |
| 92 | // Slurp to memory, so we can actually seek on it (for Range support), |
| 93 | // or if we're going to be showing it in the browser (below). |
| 94 | b, err = blob.FromReader(ctx, blobRef, rc, size) |
| 95 | if err != nil { |
| 96 | httputil.ServeError(rw, req, err) |
| 97 | return |
| 98 | } |
| 99 | content, err = b.ReadAll(ctx) |
| 100 | if err != nil { |
| 101 | httputil.ServeError(rw, req, err) |
| 102 | return |
| 103 | } |
| 104 | } |
| 105 | if !rangeHeader && size < small { |
| 106 | // If it's small and all UTF-8, assume it's text and |
| 107 | // just render it in the browser. This is more for |
| 108 | // demos/debuggability than anything else. It isn't |
| 109 | // part of the spec. |
| 110 | isUTF8, err := b.IsUTF8(ctx) |
| 111 | if err != nil { |
| 112 | httputil.ServeError(rw, req, err) |
| 113 | return |
| 114 | } |
| 115 | if isUTF8 { |
| 116 | rw.Header().Set("Content-Type", "text/plain; charset=utf-8") |
| 117 | } |
| 118 | } |
| 119 | http.ServeContent(rw, req, "", dummyModTime, content) |
| 120 | } |