Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(14)

Delta Between Two Patch Sets: src/pkg/mime/multipart/multipart_test.go

Issue 6212046: code review 6212046: mime/multipart: fix handling of empty parts without CRL... (Closed)
Left Patch Set: Created 12 years, 10 months ago
Right Patch Set: diff -r 55fd2dba69aa https://go.googlecode.com/hg/ Created 12 years, 10 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « src/pkg/mime/multipart/multipart.go ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 // Copyright 2010 The Go Authors. All rights reserved. 1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package multipart 5 package multipart
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "encoding/json" 9 "encoding/json"
10 "fmt" 10 "fmt"
11 "io" 11 "io"
12 "io/ioutil" 12 "io/ioutil"
13 "net/textproto"
13 "os" 14 "os"
15 "reflect"
14 "strings" 16 "strings"
15 "testing" 17 "testing"
16 ) 18 )
17
18 func TestHorizontalWhitespace(t *testing.T) {
19 if !onlyHorizontalWhitespace([]byte(" \t")) {
20 t.Error("expected pass")
21 }
22 if onlyHorizontalWhitespace([]byte("foo bar")) {
23 t.Error("expected failure")
24 }
25 }
26 19
27 func TestBoundaryLine(t *testing.T) { 20 func TestBoundaryLine(t *testing.T) {
28 mr := NewReader(strings.NewReader(""), "myBoundary") 21 mr := NewReader(strings.NewReader(""), "myBoundary")
29 if !mr.isBoundaryDelimiterLine([]byte("--myBoundary\r\n")) { 22 if !mr.isBoundaryDelimiterLine([]byte("--myBoundary\r\n")) {
30 t.Error("expected") 23 t.Error("expected")
31 } 24 }
32 if !mr.isBoundaryDelimiterLine([]byte("--myBoundary \r\n")) { 25 if !mr.isBoundaryDelimiterLine([]byte("--myBoundary \r\n")) {
33 t.Error("expected") 26 t.Error("expected")
34 } 27 }
35 if !mr.isBoundaryDelimiterLine([]byte("--myBoundary \n")) { 28 if !mr.isBoundaryDelimiterLine([]byte("--myBoundary \n")) {
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after
312 part, err := r.NextPart() 305 part, err := r.NextPart()
313 if err != nil { 306 if err != nil {
314 t.Fatalf("didn't get a part") 307 t.Fatalf("didn't get a part")
315 } 308 }
316 _, err = io.Copy(ioutil.Discard, part) 309 _, err = io.Copy(ioutil.Discard, part)
317 if err != io.ErrUnexpectedEOF { 310 if err != io.ErrUnexpectedEOF {
318 t.Fatalf("expected error io.ErrUnexpectedEOF; got %v", err) 311 t.Fatalf("expected error io.ErrUnexpectedEOF; got %v", err)
319 } 312 }
320 } 313 }
321 314
322 func TestZeroLengthBody(t *testing.T) {
323 testBody := strings.Replace(`
324 This is a multi-part message. This line is ignored.
325 --MyBoundary
326 foo: bar
327
328
329 --MyBoundary--
330 `, "\n", "\r\n", -1)
331 r := NewReader(strings.NewReader(testBody), "MyBoundary")
332 part, err := r.NextPart()
333 if err != nil {
334 t.Fatalf("didn't get a part")
335 }
336 n, err := io.Copy(ioutil.Discard, part)
337 if err != nil {
338 t.Errorf("error reading part: %v", err)
339 }
340 if n != 0 {
341 t.Errorf("read %d bytes; expected 0", n)
342 }
343 }
344
345 type slowReader struct { 315 type slowReader struct {
346 r io.Reader 316 r io.Reader
347 } 317 }
348 318
349 func (s *slowReader) Read(p []byte) (int, error) { 319 func (s *slowReader) Read(p []byte) (int, error) {
350 if len(p) == 0 { 320 if len(p) == 0 {
351 return s.r.Read(p) 321 return s.r.Read(p)
352 } 322 }
353 return s.r.Read(p[:1]) 323 return s.r.Read(p[:1])
354 } 324 }
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
420 _, err = mr.NextPart() 390 _, err = mr.NextPart()
421 if err != nil { 391 if err != nil {
422 t.Fatalf("error reading the image attachment at the end: %v", er r) 392 t.Fatalf("error reading the image attachment at the end: %v", er r)
423 } 393 }
424 394
425 _, err = mr.NextPart() 395 _, err = mr.NextPart()
426 if err != io.EOF { 396 if err != io.EOF {
427 t.Fatalf("final outer NextPart = %v; want io.EOF", err) 397 t.Fatalf("final outer NextPart = %v; want io.EOF", err)
428 } 398 }
429 } 399 }
400
401 type headerBody struct {
402 header textproto.MIMEHeader
403 body string
404 }
405
406 func formData(key, value string) headerBody {
407 return headerBody{
408 textproto.MIMEHeader{
409 "Content-Type": {"text/plain; charset=ISO-8859-1" },
410 "Content-Disposition": {"form-data; name=" + key},
411 },
412 value,
413 }
414 }
415
416 type parseTest struct {
417 name string
418 in, sep string
419 want []headerBody
420 }
421
422 var parseTests = []parseTest{
423 // Actual body from App Engine on a blob upload. The final part (the
424 // Content-Type: message/external-body) is what App Engine replaces
425 // the uploaded file with. The other form fields (prefixed with
426 // "other" in their form-data name) are unchanged. A bug was
427 // reported with blob uploads failing when the other fields were
428 // empty. This was the MIME POST body that previously failed.
429 {
430 name: "App Engine post",
431 sep: "00151757727e9583fd04bfbca4c6",
432 in: "--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plai n; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty1\r\n\r\ n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\ r\nContent-Disposition: form-data; name=otherFoo1\r\n\r\nfoo\r\n--00151757727e95 83fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Dispos ition: form-data; name=otherFoo2\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\ nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty2\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/pl ain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r \n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset =ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r\n\r\nfoo\r\ n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\ r\nContent-Disposition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e95 83fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Dispos ition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e9583fd04bfbca4c6\r\ nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: me ssage/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS 3Mbbkb0RjjBoM_Kc1UqEN2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7 IgbvZytyGMpL7xO1tlIvgwcM47JNfv_tGhy1XwyEUO8oldjPqg5Q\r\nContent-Disposition: for m-data; name=file; filename=\"fall.png\"\r\n\r\nContent-Type: image/png\r\nConte nt-Length: 232303\r\nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\n Content-MD5: MzRjODU1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition : form-data; name=file; filename=\"fall.png\"\r\n\r\n\r\n--00151757727e9583fd04b fbca4c6--",
433 want: []headerBody{
434 formData("otherEmpty1", ""),
435 formData("otherFoo1", "foo"),
436 formData("otherFoo2", "foo"),
437 formData("otherEmpty2", ""),
438 formData("otherRepeatFoo", "foo"),
439 formData("otherRepeatFoo", "foo"),
440 formData("otherRepeatEmpty", ""),
441 formData("otherRepeatEmpty", ""),
442 formData("submit", "Submit"),
443 {textproto.MIMEHeader{
444 "Content-Type": {"message/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS3Mbbkb0RjjBoM_Kc1UqEN 2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7IgbvZytyGMpL7xO1tlIvg wcM47JNfv_tGhy1XwyEUO8oldjPqg5Q"},
445 "Content-Disposition": {"form-data; name=file; f ilename=\"fall.png\""},
446 }, "Content-Type: image/png\r\nContent-Length: 232303\r\ nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\nContent-MD5: MzRjODU 1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition: form-data; name=fi le; filename=\"fall.png\"\r\n\r\n"},
447 },
448 },
449
450 // Single empty part, ended with --boundary immediately after headers.
451 {
452 name: "single empty part, --boundary",
453 sep: "abc",
454 in: "--abc\r\nFoo: bar\r\n\r\n--abc--",
455 want: []headerBody{
456 {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
457 },
458 },
459
460 // Single empty part, ended with \r\n--boundary immediately after header s.
461 {
462 name: "single empty part, \r\n--boundary",
463 sep: "abc",
464 in: "--abc\r\nFoo: bar\r\n\r\n\r\n--abc--",
465 want: []headerBody{
466 {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
467 },
468 },
469
470 // Final part empty.
471 {
472 name: "final part empty",
473 sep: "abc",
474 in: "--abc\r\nFoo: bar\r\n\r\n--abc\r\nFoo2: bar2\r\n\r\n--abc --",
475 want: []headerBody{
476 {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
477 {textproto.MIMEHeader{"Foo2": {"bar2"}}, ""},
478 },
479 },
480
481 // Final part empty with newlines after final separator.
482 {
483 name: "final part empty then crlf",
484 sep: "abc",
485 in: "--abc\r\nFoo: bar\r\n\r\n--abc--\r\n",
486 want: []headerBody{
487 {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
488 },
489 },
490
491 // Final part empty with lwsp-chars after final separator.
492 {
493 name: "final part empty then lwsp",
494 sep: "abc",
495 in: "--abc\r\nFoo: bar\r\n\r\n--abc-- \t",
496 want: []headerBody{
497 {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
498 },
499 },
500
501 // No parts (empty form as submitted by Chrome)
502 {
503 name: "no parts",
504 sep: "----WebKitFormBoundaryQfEAfzFOiSemeHfA",
505 in: "------WebKitFormBoundaryQfEAfzFOiSemeHfA--\r\n",
506 want: []headerBody{},
507 },
508
509 // Part containing data starting with the boundary, but with additional suffix.
510 {
511 name: "fake separator as data",
512 sep: "sep",
513 in: "--sep\r\nFoo: bar\r\n\r\n--sepFAKE\r\n--sep--",
514 want: []headerBody{
515 {textproto.MIMEHeader{"Foo": {"bar"}}, "--sepFAKE"},
516 },
517 },
518
519 // Part containing a boundary with whitespace following it.
520 {
521 name: "boundary with whitespace",
522 sep: "sep",
523 in: "--sep \r\nFoo: bar\r\n\r\ntext\r\n--sep--",
524 want: []headerBody{
525 {textproto.MIMEHeader{"Foo": {"bar"}}, "text"},
526 },
527 },
528
529 // With ignored leading line.
530 {
531 name: "leading line",
532 sep: "MyBoundary",
533 in: strings.Replace(`This is a multi-part message. This line is ignored.
534 --MyBoundary
535 foo: bar
536
537
538 --MyBoundary--`, "\n", "\r\n", -1),
539 want: []headerBody{
540 {textproto.MIMEHeader{"Foo": {"bar"}}, ""},
541 },
542 },
543
544 roundTripParseTest(),
545 }
546
547 func TestParse(t *testing.T) {
548 Cases:
549 for _, tt := range parseTests {
550 r := NewReader(strings.NewReader(tt.in), tt.sep)
551 got := []headerBody{}
552 for {
553 p, err := r.NextPart()
554 if err == io.EOF {
555 break
556 }
557 if err != nil {
558 t.Errorf("in test %q, NextPart: %v", tt.name, er r)
559 continue Cases
560 }
561 pbody, err := ioutil.ReadAll(p)
562 if err != nil {
563 t.Errorf("in test %q, error reading part: %v", t t.name, err)
564 continue Cases
565 }
566 got = append(got, headerBody{p.Header, string(pbody)})
567 }
568 if !reflect.DeepEqual(tt.want, got) {
569 t.Errorf("test %q:\n got: %v\nwant: %v", tt.name, got, t t.want)
570 if len(tt.want) != len(got) {
571 t.Errorf("test %q: got %d parts, want %d", tt.na me, len(got), len(tt.want))
572 } else if len(got) > 1 {
573 for pi, wantPart := range tt.want {
574 if !reflect.DeepEqual(wantPart, got[pi]) {
575 t.Errorf("test %q, part %d:\n go t: %v\nwant: %v", tt.name, pi, got[pi], wantPart)
576 }
577 }
578 }
579 }
580 }
581 }
582
583 func roundTripParseTest() parseTest {
584 t := parseTest{
585 name: "round trip",
586 want: []headerBody{
587 formData("empty", ""),
588 formData("lf", "\n"),
589 formData("cr", "\r"),
590 formData("crlf", "\r\n"),
591 formData("foo", "bar"),
592 },
593 }
594 var buf bytes.Buffer
595 w := NewWriter(&buf)
596 for _, p := range t.want {
597 pw, err := w.CreatePart(p.header)
598 if err != nil {
599 panic(err)
600 }
601 _, err = pw.Write([]byte(p.body))
602 if err != nil {
603 panic(err)
604 }
605 }
606 w.Close()
607 t.in = buf.String()
608 t.sep = w.Boundary()
609 return t
610 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b