(rw http.ResponseWriter, r *http.Request)
| 50 | } |
| 51 | |
| 52 | func (h *binHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { |
| 53 | if !strings.HasPrefix(r.URL.Path, "/bin/") { |
| 54 | rw.WriteHeader(http.StatusNotFound) |
| 55 | _, _ = rw.Write([]byte("not found")) |
| 56 | return |
| 57 | } |
| 58 | r.URL.Path = strings.TrimPrefix(r.URL.Path, "/bin") |
| 59 | // Convert underscores in the filename to hyphens. We eventually want to |
| 60 | // change our hyphen-based filenames to underscores, but we need to |
| 61 | // support both for now. |
| 62 | r.URL.Path = strings.ReplaceAll(r.URL.Path, "_", "-") |
| 63 | |
| 64 | // Set ETag header to the SHA1 hash of the file contents. |
| 65 | name := filePath(r.URL.Path) |
| 66 | if name == "" || name == "/" { |
| 67 | // Serve the directory listing. This intentionally allows directory listings to |
| 68 | // be served. This file system should not contain anything sensitive. |
| 69 | h.handler.ServeHTTP(rw, r) |
| 70 | return |
| 71 | } |
| 72 | if strings.Contains(name, "/") { |
| 73 | // We only serve files from the root of this directory, so avoid any |
| 74 | // shenanigans by blocking slashes in the URL path. |
| 75 | http.NotFound(rw, r) |
| 76 | return |
| 77 | } |
| 78 | |
| 79 | metadata, err := h.metadataCache.getMetadata(name) |
| 80 | if xerrors.Is(err, os.ErrNotExist) { |
| 81 | http.NotFound(rw, r) |
| 82 | return |
| 83 | } |
| 84 | if err != nil { |
| 85 | http.Error(rw, err.Error(), http.StatusInternalServerError) |
| 86 | return |
| 87 | } |
| 88 | |
| 89 | // http.FileServer will not set Content-Length when performing chunked |
| 90 | // transport encoding, which is used for large files like our binaries |
| 91 | // so stream compression can be used. |
| 92 | // |
| 93 | // Clients like IDE extensions and the desktop apps can compare the |
| 94 | // value of this header with the amount of bytes written to disk after |
| 95 | // decompression to show progress. Without this, they cannot show |
| 96 | // progress without disabling compression. |
| 97 | // |
| 98 | // There isn't really a spec for a length header for the "inner" content |
| 99 | // size, but some nginx modules use this header. |
| 100 | rw.Header().Set("X-Original-Content-Length", fmt.Sprintf("%d", metadata.sizeBytes)) |
| 101 | |
| 102 | // Get and set ETag header. Must be quoted. |
| 103 | rw.Header().Set("ETag", fmt.Sprintf(`%q`, metadata.sha1Hash)) |
| 104 | |
| 105 | // http.FileServer will see the ETag header and automatically handle |
| 106 | // If-Match and If-None-Match headers on the request properly. |
| 107 | h.handler.ServeHTTP(rw, r) |
| 108 | } |
| 109 |
nothing calls this directly
no test coverage detected