From 91f6899d7599bf3de2495f8ebcf6823752a9d33b Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Tue, 8 Aug 2023 13:14:17 +0100 Subject: [PATCH] Signal content length to upstream components when present When the HTTP request contains `Content-Length` header, include it in the body reader passed down to upstream system components. This is to enable further optimisations during processing when the total size of a blob is known beforehand. Examples include piece selection in order to guarantee that a blob will be fully contained within a piece. With changes introduced, an upstream component may type-check for `interface { Size() int64}` and use it to then gain access to the content length. Note that based on HTTP standards, `application/octet-stream` content type is not strictly required to specify `Content-Length` header. Hence the opportunistic signalling via type-checkers. --- api/server/handler.go | 10 ++++++++-- api/server/sizer_reader.go | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 api/server/sizer_reader.go diff --git a/api/server/handler.go b/api/server/handler.go index 4b3cf28..7bf82bc 100644 --- a/api/server/handler.go +++ b/api/server/handler.go @@ -27,6 +27,7 @@ func (m *HttpServer) handlePostBlob(w http.ResponseWriter, r *http.Request) { respondWithJson(w, errResponseNotStreamContentType, http.StatusBadRequest) return } + body := r.Body var contentLength uint64 if value := r.Header.Get("Content-Length"); value != "" { var err error @@ -38,9 +39,14 @@ func (m *HttpServer) handlePostBlob(w http.ResponseWriter, r *http.Request) { respondWithJson(w, errResponseContentLengthTooLarge(m.maxBlobLength), http.StatusBadRequest) return } + // Wrap body reader to signal content length to upstream components. + body = sizerReadCloser{ + ReadCloser: r.Body, + size: int64(contentLength), + } } - defer r.Body.Close() - desc, err := m.store.Put(r.Context(), r.Body) + defer body.Close() + desc, err := m.store.Put(r.Context(), body) switch err { case nil: case blob.ErrBlobTooLarge: diff --git a/api/server/sizer_reader.go b/api/server/sizer_reader.go new file mode 100644 index 0000000..d6d27b0 --- /dev/null +++ b/api/server/sizer_reader.go @@ -0,0 +1,14 @@ +package server + +import "io" + +var _ interface{ Size() int64 } = (*sizerReadCloser)(nil) + +type sizerReadCloser struct { + io.ReadCloser + size int64 +} + +func (s sizerReadCloser) Size() int64 { + return s.size +}