Repository: fsouza/fake-gcs-server
Pull request #497 (2021-05-18 16:31:10)

Fix nodeJS file upload (issue #346)
As https://github.com/fsouza/fake-gcs-server/issues/346 states, there is a bug when uploading files with the nodeJS client lib.

I debugged it and the problem is, that nodeJS sends `Content-Range: bytes 0-*/*` (see https://github.com/googleapis/gcs-resumable-upload/blob/master/src/index.ts#L456).

I could not really find a good documentation on the exact semantics but from observing the client lib I guess it means "start and end upload stream with all the data I provide you". I adjusted the code accordingly and added a test case. I also checked that the following node example works fine:

```javascript
async function saveFile() {
  // [START storage_list_buckets]
  // Imports the Google Cloud client library
  const { Storage } = require("@google-cloud/storage");

  // Creates a client
  const storage = new Storage({
    apiEndpoint: "http://localhost:8080",
    projectId: "test",
  });

  const bucket = await storage.createBucket("testbucket");
  const file = new File(bucket, "test.txt");
  await file.save("abc", { contentType: "text/plain" });
  // [END storage_list_buckets]
}

saveFile().catch((err) => {
  console.error(err);
  process.exit(1);
});
```


Patch:
diff --git a/fakestorage/upload.go b/fakestorage/upload.go
index 5416a34..223733e 100644
--- a/fakestorage/upload.go
+++ b/fakestorage/upload.go
@@ -414,6 +414,7 @@ func (s *Server) uploadFileContent(r *http.Request) jsonResponse {
 //   bytes 0-1023/4096 (first 1024 bytes of a 4096-byte document)
 //   bytes 1024-2047/* (second 1024 bytes of a streaming document)
 //   bytes */4096      (The end of 4096 byte streaming document)
+//   bytes 0-*/*       (start and end of a streaming document as sent by nodeJS client lib)
 func parseContentRange(r string) (parsed contentRange, err error) {
 	invalidErr := fmt.Errorf("invalid Content-Range: %v", r)
 
@@ -438,22 +439,28 @@ func parseContentRange(r string) (parsed contentRange, err error) {
 		if len(rangeParts) != 2 {
 			return parsed, invalidErr
 		}
-		parsed.KnownRange = true
+
 		parsed.Start, err = strconv.Atoi(rangeParts[0])
 		if err != nil {
 			return parsed, invalidErr
 		}
-		parsed.End, err = strconv.Atoi(rangeParts[1])
-		if err != nil {
-			return parsed, invalidErr
+
+		if rangeParts[1] == "*" {
+			parsed.End = -1
+		} else {
+			parsed.KnownRange = true
+			parsed.End, err = strconv.Atoi(rangeParts[1])
+			if err != nil {
+				return parsed, invalidErr
+			}
 		}
 	}
 
 	// Process total length
 	if parts[1] == "*" {
 		parsed.Total = -1
-		if !parsed.KnownRange {
-			// Must know either range or total
+
+		if parsed.Start < 0 {
 			return parsed, invalidErr
 		}
 	} else {


Test patch:
diff --git a/fakestorage/upload_test.go b/fakestorage/upload_test.go
index 6970b71..94a0cb0 100644
--- a/fakestorage/upload_test.go
+++ b/fakestorage/upload_test.go
@@ -487,6 +487,10 @@ func TestParseContentRange(t *testing.T) {
 			"bytes */1024", // End of a streaming request, total is now known
 			contentRange{KnownTotal: true, Start: -1, End: -1, Total: 1024},
 		},
+		{
+			"bytes 0-*/*", // Start and end of a streaming request as done by nodeJS client lib
+			contentRange{KnownTotal: false, Start: 0, End: -1, Total: -1},
+		},
 		{
 			"bytes 1024-2047/4096", // Range with a known total
 			contentRange{KnownRange: true, KnownTotal: true, Start: 1024, End: 2047, Total: 4096},