Skip to content

Commit

Permalink
Merge pull request #401 from tdakkota/feat/multipart-headers
Browse files Browse the repository at this point in the history
feat(http): pass multipart headers
  • Loading branch information
ernado authored Jun 16, 2022
2 parents efd42d5 + ad3595c commit 5c5c427
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 13 deletions.
6 changes: 6 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ issues:
linters: [ gosec ]
text: G103

# We are using quoting algorithm from mime/multipart package. False-positive.
- path: http(\/|\\)file\.go
linters: [ gocritic ]
text: sprintfQuotedString

# Intended design.
- path: http
source: Set
Expand All @@ -134,3 +139,4 @@ issues:

- linters: [ revive ]
text: "if-return: redundant if ...; err != nil check, just return error instead."

12 changes: 11 additions & 1 deletion form_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/textproto"
"net/url"
"strings"
"testing"
Expand All @@ -16,6 +17,7 @@ import (

ht "github.com/ogen-go/ogen/http"
api "github.com/ogen-go/ogen/internal/sample_api"
"github.com/ogen-go/ogen/validate"
)

func testForm() api.TestForm {
Expand Down Expand Up @@ -62,8 +64,13 @@ func (s testFormServer) TestMultipart(ctx context.Context, req api.TestForm) (r
}

func (s testFormServer) TestMultipartUpload(ctx context.Context, req api.TestMultipartUploadReq) (r string, _ error) {
f := req.FileName
if val := f.Header.Get("Content-Type"); val != "image/jpeg" {
return "", validate.InvalidContentType(val)
}

var b strings.Builder
_, err := io.Copy(&b, req.FileName.File)
_, err := io.Copy(&b, f.File)
return b.String(), err
}

Expand Down Expand Up @@ -143,6 +150,9 @@ func TestMultipartUploadE2E(t *testing.T) {
FileName: ht.MultipartFile{
Name: "pablo.jpg",
File: strings.NewReader(data),
Header: textproto.MIMEHeader{
"Content-Type": []string{"image/jpeg"},
},
},
})
a.NoError(err)
Expand Down
9 changes: 5 additions & 4 deletions gen/_template/request_decode.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,18 @@ func (s *Server) decode{{ $op.Name }}Request(r *http.Request, span trace.Span) (
if !ok || len(files) < 1 {
return errors.New("file is not set")
}
header := files[0]
fh := files[0]

f, err := header.Open()
f, err := fh.Open()
if err != nil {
return errors.Wrap(err, "open")
}
closers = append(closers, f)

{{ printf "req.%s" $param.Name }} = ht.MultipartFile{
Name: header.Filename,
File: f,
Name: fh.Filename,
File: f,
Header: fh.Header,
}
return nil
}(); err != nil {
Expand Down
33 changes: 29 additions & 4 deletions http/file.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
package http

import (
"fmt"
"io"
"mime/multipart"
"net/textproto"
"strings"
)

// MultipartFile is multipart form file.
type MultipartFile struct {
Name string
File io.Reader
Name string
File io.Reader
Header textproto.MIMEHeader
}

var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")

func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}

func (m MultipartFile) headers(fieldName string) (h textproto.MIMEHeader) {
h = make(textproto.MIMEHeader, len(m.Header)+2)
for k, v := range m.Header {
h[k] = v
}

disposition := fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
escapeQuotes(fieldName), escapeQuotes(m.Name))
h.Set("Content-Disposition", disposition)
if _, ok := h["Content-Type"]; !ok {
h.Set("Content-Type", "application/octet-stream")
}
return h
}

// WriteMultipart writes data from reader to given multipart.Writer as a form file.
func (m MultipartFile) WriteMultipart(fieldName string, w *multipart.Writer) error {
fw, err := w.CreateFormFile(fieldName, m.Name)
p, err := w.CreatePart(m.headers(fieldName))
if err != nil {
return err
}
_, err = io.Copy(fw, m.File)
_, err = io.Copy(p, m.File)
return err
}
9 changes: 5 additions & 4 deletions internal/sample_api/oas_request_decoders_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5c5c427

Please sign in to comment.