mirror of
https://github.com/soypat/lneto.git
synced 2026-08-21 23:19:03 +00:00
add Exchange.ReadMultiparts reimagining of clanker slop
This commit is contained in:
@@ -39,8 +39,8 @@ type Multipart struct {
|
||||
// parameters that identify it. All fields alias the buffer they were parsed
|
||||
// from and stay valid as long as it does.
|
||||
type MultipartHeader struct {
|
||||
// Part is the part's raw header block, ending in its final CRLF.
|
||||
Part []byte
|
||||
// PartView is the part's raw header block, ending in its final CRLF.
|
||||
PartView []byte
|
||||
// Name is the name parameter of a part's Content-Disposition field,
|
||||
// i.e: "photo" for `form-data; name="photo"; filename="beach.png"`.
|
||||
Name []byte
|
||||
@@ -96,13 +96,63 @@ func (m *Multipart) NextHeader(dst *MultipartHeader, data []byte) (rest []byte,
|
||||
if end < 0 {
|
||||
return nil, ErrNeedMoreData
|
||||
}
|
||||
dst.Part = data[after : after+end+2]
|
||||
disposition := partField(dst.Part)
|
||||
dst.Name = ContentParam(disposition, "name")
|
||||
dst.Filename = ContentParam(disposition, "filename")
|
||||
dst.PartView = data[after : after+end+2]
|
||||
disposition := partField(dst.PartView)
|
||||
dst.Name = append(dst.Name[:0], ContentParam(disposition, "name")...)
|
||||
dst.Filename = append(dst.Filename[:0], ContentParam(disposition, "filename")...)
|
||||
return data[after+end+4:], nil
|
||||
}
|
||||
|
||||
func (m *Multipart) NextHeaderInt(dst *MultipartHeader, data []byte) (parsedLen int, err error) {
|
||||
*dst = MultipartHeader{}
|
||||
if len(m.Boundary) == 0 {
|
||||
return 0, errNoBoundary
|
||||
}
|
||||
idx := m.indexDelimiter(data)
|
||||
if idx < 0 {
|
||||
return 0, nil
|
||||
}
|
||||
after := idx + len("--") + len(m.Boundary)
|
||||
if after+2 > len(data) {
|
||||
return 0, nil // Cannot tell a closing delimiter yet.
|
||||
} else if data[after] == '-' && data[after+1] == '-' {
|
||||
return 0, io.EOF
|
||||
}
|
||||
// Delimiter is followed by CRLF, then the part's header block.
|
||||
if data[after] == '\r' {
|
||||
after++
|
||||
}
|
||||
if after >= len(data) {
|
||||
return 0, nil
|
||||
} else if data[after] != '\n' {
|
||||
return 0, errBadDelimiter
|
||||
}
|
||||
after++
|
||||
end := bytes.Index(data[after:], []byte("\r\n\r\n"))
|
||||
if end < 0 {
|
||||
return 0, nil
|
||||
}
|
||||
dst.PartView = data[after : after+end+2]
|
||||
disposition := partField(dst.PartView)
|
||||
dst.Name = append(dst.Name[:0], ContentParam(disposition, "name")...)
|
||||
dst.Filename = append(dst.Filename[:0], ContentParam(disposition, "filename")...)
|
||||
return after + end + 4, nil
|
||||
}
|
||||
|
||||
func (m *Multipart) NextBodyInt(data []byte) (bodyLen int, done bool) {
|
||||
idx := m.indexPartEnd(data)
|
||||
if idx >= 0 {
|
||||
return idx, true
|
||||
// return data[:idx], data[idx+len("\r\n"):], true
|
||||
}
|
||||
// Longest prefix of "\r\n--"+boundary that could still be completed.
|
||||
hold := len("\r\n--") + len(m.Boundary) - 1
|
||||
if hold > len(data) {
|
||||
hold = len(data)
|
||||
}
|
||||
return len(data) - hold, false
|
||||
}
|
||||
|
||||
// NextBody returns the part bytes available in data, holding back any tail
|
||||
// that could be the start of a delimiter. done reports the part ended, in which
|
||||
// case rest begins the next part's delimiter; otherwise rest is the held back
|
||||
|
||||
@@ -106,8 +106,8 @@ func TestNextPartHeader(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const wantHdr = "Content-Disposition: form-data; name=\"caption\"\r\n"
|
||||
if string(hdr.Part) != wantHdr {
|
||||
t.Errorf("want header %q, got %q", wantHdr, hdr.Part)
|
||||
if string(hdr.PartView) != wantHdr {
|
||||
t.Errorf("want header %q, got %q", wantHdr, hdr.PartView)
|
||||
}
|
||||
if string(hdr.Name) != "caption" {
|
||||
t.Errorf("want name %q, got %q", "caption", hdr.Name)
|
||||
@@ -239,8 +239,8 @@ func TestNextHeaderFilePart(t *testing.T) {
|
||||
if got := string(hdr.Filename); got != "beach.png" {
|
||||
t.Errorf("want filename %q, got %q", "beach.png", got)
|
||||
}
|
||||
if !strings.Contains(string(hdr.Part), "Content-Type: image/png") {
|
||||
t.Errorf("want the raw block to hold every field, got %q", hdr.Part)
|
||||
if !strings.Contains(string(hdr.PartView), "Content-Type: image/png") {
|
||||
t.Errorf("want the raw block to hold every field, got %q", hdr.PartView)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ func TestNextHeaderZeroesOnError(t *testing.T) {
|
||||
if _, err := m.NextHeader(&hdr, []byte("------abc123--\r\n")); err != io.EOF {
|
||||
t.Fatalf("want io.EOF, got %v", err)
|
||||
}
|
||||
if hdr.Part != nil || hdr.Name != nil || hdr.Filename != nil {
|
||||
if hdr.PartView != nil || hdr.Name != nil || hdr.Filename != nil {
|
||||
t.Errorf("want zeroed header on error, got %+v", hdr)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user