Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 1b9c1ec

Browse files
committed
mime/multipart: add field Reader.MaxMIMEHeaderSize
If the field is set, it is used instead of maxMIMEHeaderSize constant, allowing to further constraint memory usage when parsing multipart streams. Fixes #68889
1 parent 656b5b3 commit 1b9c1ec

File tree

3 files changed

+56
-3
lines changed

3 files changed

+56
-3
lines changed

‎src/mime/multipart/multipart.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,11 @@ func (p *Part) Close() error {
331331
// Reader's underlying parser consumes its input as needed. Seeking
332332
// isn't supported.
333333
type Reader struct {
334+
// MaxMIMEHeaderSize is the maximum size of a MIME header we will parse,
335+
// including header keys, values, and map overhead. If not set, then the
336+
// default value of 10 << 20 is used.
337+
MaxMIMEHeaderSize int64
338+
334339
bufReader *bufio.Reader
335340
tempDir string // used in tests
336341

@@ -362,14 +367,25 @@ func maxMIMEHeaders() int64 {
362367
return 10000
363368
}
364369

370+
// maxMIMEHeaderSize returns the maximum size of a MIME header we will parse.
371+
// It uses the value of Reader.MaxMIMEHeaderSize if it is not 0, otherwise the
372+
// value of maxMIMEHeaderSize constant is used.
373+
func (r *Reader) maxMIMEHeaderSize() int64 {
374+
if r.MaxMIMEHeaderSize != 0 {
375+
return r.MaxMIMEHeaderSize
376+
} else {
377+
return maxMIMEHeaderSize
378+
}
379+
}
380+
365381
// NextPart returns the next part in the multipart or an error.
366382
// When there are no more parts, the error [io.EOF] is returned.
367383
//
368384
// As a special case, if the "Content-Transfer-Encoding" header
369385
// has a value of "quoted-printable", that header is instead
370386
// hidden and the body is transparently decoded during Read calls.
371387
func (r *Reader) NextPart() (*Part, error) {
372-
return r.nextPart(false, maxMIMEHeaderSize, maxMIMEHeaders())
388+
return r.nextPart(false, r.maxMIMEHeaderSize(), maxMIMEHeaders())
373389
}
374390

375391
// NextRawPart returns the next part in the multipart or an error.
@@ -378,7 +394,7 @@ func (r *Reader) NextPart() (*Part, error) {
378394
// Unlike [Reader.NextPart], it does not have special handling for
379395
// "Content-Transfer-Encoding: quoted-printable".
380396
func (r *Reader) NextRawPart() (*Part, error) {
381-
return r.nextPart(true, maxMIMEHeaderSize, maxMIMEHeaders())
397+
return r.nextPart(true, r.maxMIMEHeaderSize(), maxMIMEHeaders())
382398
}
383399

384400
func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) {

‎src/mime/multipart/multipart_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,41 @@ Cases:
931931
}
932932
}
933933

934+
func TestParseMaxMIMEHeaderSize(t *testing.T) {
935+
sep := "MyBoundary"
936+
in := `This is a multi-part message. This line is ignored.
937+
--MyBoundary
938+
Header1: value1
939+
Header2: value2
940+
Header3: value3
941+
Header4: value4
942+
Header5: value5
943+
Header6: value6
944+
Header7: value7
945+
Header8: value8
946+
947+
My value
948+
The end.
949+
--MyBoundary--`
950+
951+
in = strings.Replace(in, "\n", "\r\n", -1)
952+
953+
// Control.
954+
r := NewReader(strings.NewReader(in), sep)
955+
_, err := r.NextPart()
956+
if err != nil {
957+
t.Fatalf("control failed: %v", err)
958+
}
959+
960+
// Test MaxMIMEHeaderSize.
961+
r = NewReader(strings.NewReader(in), sep)
962+
r.MaxMIMEHeaderSize = 100
963+
_, err = r.NextPart()
964+
if err != ErrMessageTooLarge {
965+
t.Fatalf("test failed: %v", err)
966+
}
967+
}
968+
934969
func partsFromReader(r *Reader) ([]headerBody, error) {
935970
got := []headerBody{}
936971
for {

‎src/net/http/request.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,8 @@ var multipartByReader = &multipart.Form{
493493
// MultipartReader returns a MIME multipart reader if this is a
494494
// multipart/form-data or a multipart/mixed POST request, else returns nil and an error.
495495
// Use this function instead of [Request.ParseMultipartForm] to
496-
// process the request body as a stream.
496+
// process the request body as a stream or to limit maximum MIME header size
497+
// using reader's field MaxMIMEHeaderSize.
497498
func (r *Request) MultipartReader() (*multipart.Reader, error) {
498499
if r.MultipartForm == multipartByReader {
499500
return nil, errors.New("http: MultipartReader called twice")
@@ -1367,6 +1368,7 @@ func (r *Request) ParseForm() error {
13671368
// If ParseForm returns an error, ParseMultipartForm returns it but also
13681369
// continues parsing the request body.
13691370
// After one call to ParseMultipartForm, subsequent calls have no effect.
1371+
// If you want to process the request body as a stream, use [MultipartReader].
13701372
func (r *Request) ParseMultipartForm(maxMemory int64) error {
13711373
if r.MultipartForm == multipartByReader {
13721374
return errors.New("http: multipart handled by MultipartReader")

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /