From 6d38db062346e35582aae01c5b4fe0b6b7175bfd Mon Sep 17 00:00:00 2001 From: Jarek Kowalski Date: Thu, 16 Dec 2021 15:20:07 -0800 Subject: [PATCH] b2: fixed regression when uploading zero-length blobs introduced by #1589 (#1593) Turns out B2 library will treat seekable readers in a special way and will pass those directly to http.NewRequest() for upload. Other readers are copied to a temporary buffer first. The #1589 made the reader io.ReadSeeker so it was passed to http.NewRequest(), which for payloads of length zero uses heuristics to determine if the Content-Length should be passed as zero or not passed at all. https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/net/http/request.go;l=890;drc=e6dda19888180c5159460486d30c0412e4980748 Since reader was not one of the magic types, Content-Length was not passed at all, causing B2 server to choke on zero-length files. --- repo/blob/b2/b2_storage.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/repo/blob/b2/b2_storage.go b/repo/blob/b2/b2_storage.go index 1c7cfbacd..e3ca533b5 100644 --- a/repo/blob/b2/b2_storage.go +++ b/repo/blob/b2/b2_storage.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "io" "net/http" "strings" "time" @@ -139,7 +140,16 @@ func translateError(err error) error { func (s *b2Storage) PutBlob(ctx context.Context, id blob.ID, data blob.Bytes, opts blob.PutOptions) error { fileName := s.getObjectNameString(id) - _, err := s.bucket.UploadFile(fileName, nil, data.Reader()) + + // Backblaze always expects Content-Length to be set, even in http.Request ContentLength==0 + // can mean "unknown" or "zero". http.Request used by B2 client requires http.NoBody to + // reliably pass zero length to the server as opposed to not passing it at all. + var r io.Reader = data.Reader() + if data.Length() == 0 { + r = http.NoBody + } + + _, err := s.bucket.UploadFile(fileName, nil, r) return translateError(err) }