Skip to content

Commit

Permalink
supportsuccess_action_status form field for HTML form uploads (#1042)
Browse files Browse the repository at this point in the history
* check for success_action_status form field

* return xml on 201, PR feedback

* struct instead of interface
  • Loading branch information
vincentezw committed Jan 12, 2023
1 parent c82b595 commit de93fce
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 5 deletions.
19 changes: 18 additions & 1 deletion fakestorage/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down
14 changes: 11 additions & 3 deletions fakestorage/upload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"mime/multipart"
"net/http"
"reflect"
"strconv"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
Expand Down Expand Up @@ -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")
Expand Down
34 changes: 33 additions & 1 deletion fakestorage/xml_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand Down

0 comments on commit de93fce

Please sign in to comment.