var headerRegexp *regexp.Regexp = regexp.MustCompile("^([a-zA-Z0-9\\-]+): *([^\r\n]+)")
+var emptyParams = make(map[string]string)
+
// Reader is an iterator over parts in a MIME multipart body.
// Reader's underlying parser consumes its input as needed. Seeking
// isn't supported.
buffer *bytes.Buffer
mr *multiReader
+ disposition string
dispositionParams map[string]string
}
func (p *Part) FormName() string {
// See http://tools.ietf.org/html/rfc2183 section 2 for EBNF
// of Content-Disposition value format.
- if p.dispositionParams != nil {
- return p.dispositionParams["name"]
- }
- v := p.Header.Get("Content-Disposition")
- if v == "" {
- return ""
+ if p.dispositionParams == nil {
+ p.parseContentDisposition()
}
- if d, params := mime.ParseMediaType(v); d != "form-data" {
+ if p.disposition != "form-data" {
return ""
- } else {
- p.dispositionParams = params
}
return p.dispositionParams["name"]
}
+
+// FileName returns the filename parameter of the Part's
+// Content-Disposition header.
+func (p *Part) FileName() string {
+ if p.dispositionParams == nil {
+ p.parseContentDisposition()
+ }
+ return p.dispositionParams["filename"]
+}
+
+func (p *Part) parseContentDisposition() {
+ v := p.Header.Get("Content-Disposition")
+ p.disposition, p.dispositionParams = mime.ParseMediaType(v)
+ if p.dispositionParams == nil {
+ p.dispositionParams = emptyParams
+ }
+}
+
// NewReader creates a new multipart Reader reading from r using the
// given MIME boundary.
func NewReader(reader io.Reader, boundary string) Reader {
what, escapeString(actual), len(actual), escapeString(expected), len(expected))
}
-func TestFormName(t *testing.T) {
- p := new(Part)
- p.Header = make(map[string][]string)
- tests := [...][2]string{
- {`form-data; name="foo"`, "foo"},
- {` form-data ; name=foo`, "foo"},
- {`FORM-DATA;name="foo"`, "foo"},
- {` FORM-DATA ; name="foo"`, "foo"},
- {` FORM-DATA ; name="foo"`, "foo"},
- {` FORM-DATA ; name=foo`, "foo"},
- {` FORM-DATA ; filename="foo.txt"; name=foo; baz=quux`, "foo"},
- }
- for _, test := range tests {
+func TestNameAccessors(t *testing.T) {
+ tests := [...][3]string{
+ {`form-data; name="foo"`, "foo", ""},
+ {` form-data ; name=foo`, "foo", ""},
+ {`FORM-DATA;name="foo"`, "foo", ""},
+ {` FORM-DATA ; name="foo"`, "foo", ""},
+ {` FORM-DATA ; name="foo"`, "foo", ""},
+ {` FORM-DATA ; name=foo`, "foo", ""},
+ {` FORM-DATA ; filename="foo.txt"; name=foo; baz=quux`, "foo", "foo.txt"},
+ {` not-form-data ; filename="bar.txt"; name=foo; baz=quux`, "", "bar.txt"},
+ }
+ for i, test := range tests {
+ p := &Part{Header: make(map[string][]string)}
p.Header.Set("Content-Disposition", test[0])
- expected := test[1]
- actual := p.FormName()
- if actual != expected {
- t.Errorf("expected \"%s\"; got: \"%s\"", expected, actual)
+ if g, e := p.FormName(), test[1]; g != e {
+ t.Errorf("test %d: FormName() = %q; want %q", i, g, e)
+ }
+ if g, e := p.FileName(), test[2]; g != e {
+ t.Errorf("test %d: FileName() = %q; want %q", i, g, e)
}
}
}