From de93fceb41042a7d87a3cb03af0f5c2895e5bb4b Mon Sep 17 00:00:00 2001 From: vincentezw <59287019+vincentezw@users.noreply.github.com> Date: Thu, 12 Jan 2023 19:04:23 +0000 Subject: [PATCH] supportsuccess_action_status form field for HTML form uploads (#1042) * check for success_action_status form field * return xml on 201, PR feedback * struct instead of interface --- fakestorage/upload.go | 19 ++++++++++++++++++- fakestorage/upload_test.go | 14 +++++++++++--- fakestorage/xml_response.go | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/fakestorage/upload.go b/fakestorage/upload.go index 0499864b2d..1fbb1ef849 100644 --- a/fakestorage/upload.go +++ b/fakestorage/upload.go @@ -123,6 +123,17 @@ func (s *Server) insertFormObject(r *http.Request) xmlResponse { if contentTypes, ok := r.MultipartForm.Value["Content-Type"]; ok { contentType = contentTypes[0] } + successActionStatus := http.StatusNoContent + if successActionStatuses, ok := r.MultipartForm.Value["success_action_status"]; ok { + successInt, err := strconv.Atoi(successActionStatuses[0]) + if err != nil { + return xmlResponse{errorMessage: err.Error(), status: http.StatusBadRequest} + } + if successInt != http.StatusOK && successInt != http.StatusCreated && successInt != http.StatusNoContent { + return xmlResponse{errorMessage: "invalid success action status", status: http.StatusBadRequest} + } + successActionStatus = successInt + } metaData := make(map[string]string) for key := range r.MultipartForm.Value { lowerKey := strings.ToLower(key) @@ -159,7 +170,13 @@ func (s *Server) insertFormObject(r *http.Request) xmlResponse { return xmlResponse{errorMessage: err.Error()} } defer obj.Close() - return xmlResponse{status: http.StatusNoContent} + + if successActionStatus == 201 { + objectURI := fmt.Sprintf("%s/%s%s", s.URL(), bucketName, name) + xmlBody := createXmlResponseBody(bucketName, obj.Etag, strings.TrimPrefix(name, "/"), objectURI) + return xmlResponse{status: successActionStatus, data: xmlBody} + } + return xmlResponse{status: successActionStatus} } func (s *Server) wrapUploadPreconditions(r *http.Request, bucketName string, objectName string) (generationCondition, error) { diff --git a/fakestorage/upload_test.go b/fakestorage/upload_test.go index 38e0636d5f..1d27e4db5f 100644 --- a/fakestorage/upload_test.go +++ b/fakestorage/upload_test.go @@ -16,6 +16,7 @@ import ( "mime/multipart" "net/http" "reflect" + "strconv" "strings" "testing" "time" @@ -922,6 +923,7 @@ func TestFormDataUpload(t *testing.T) { var buf bytes.Buffer const content = "some weird content" const contentType = "text/plain" + successActionStatus := http.StatusNoContent writer := multipart.NewWriter(&buf) var fieldWriter io.Writer @@ -939,6 +941,13 @@ func TestFormDataUpload(t *testing.T) { t.Fatal(err) } + if fieldWriter, err = writer.CreateFormField("success_action_status"); err != nil { + t.Fatal(err) + } + if _, err := fieldWriter.Write([]byte(strconv.Itoa(successActionStatus))); err != nil { + t.Fatal(err) + } + if fieldWriter, err = writer.CreateFormField("x-goog-meta-key"); err != nil { t.Fatal(err) } @@ -974,9 +983,8 @@ func TestFormDataUpload(t *testing.T) { t.Fatal(err) } defer resp.Body.Close() - expectedStatus := http.StatusNoContent - if resp.StatusCode != expectedStatus { - t.Errorf("wrong status code\nwant %d\ngot %d", expectedStatus, resp.StatusCode) + if resp.StatusCode != successActionStatus { + t.Errorf("wrong status code\nwant %d\ngot %d", successActionStatus, resp.StatusCode) } obj, err := server.GetObject("other-bucket", "object.txt") diff --git a/fakestorage/xml_response.go b/fakestorage/xml_response.go index d6ee1324c6..0014ff7101 100644 --- a/fakestorage/xml_response.go +++ b/fakestorage/xml_response.go @@ -12,6 +12,16 @@ type xmlResponse struct { errorMessage string } +type xmlResponseBody struct { + XMLName xml.Name `xml:"PostResponse"` + Bucket string + Etag struct { + Value string `xml:",innerxml"` + } + Key string + Location string +} + type xmlHandler = func(r *http.Request) xmlResponse func xmlToHTTPHandler(h xmlHandler) http.HandlerFunc { @@ -33,10 +43,32 @@ func xmlToHTTPHandler(h xmlHandler) http.HandlerFunc { } w.WriteHeader(status) - xml.NewEncoder(w).Encode(data) + if status == 201 { + dataBytes, _ := data.([]byte) + w.Write(dataBytes) + } else { + xml.NewEncoder(w).Encode(data) + } } } +func createXmlResponseBody(bucketName, etag, key, location string) []byte { + responseBody := xmlResponseBody{ + Bucket: bucketName, + Etag: struct { + Value string `xml:",innerxml"` + }{etag}, + Location: location, + Key: key, + } + x, err := xml.Marshal(responseBody) + if err != nil { + return nil + } + + return []byte(xml.Header + string(x)) +} + func (r *xmlResponse) getStatus() int { if r.status > 0 { return r.status