2017-03-05 07:56:08 +00:00
|
|
|
package multipart
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
"mime/multipart"
|
|
|
|
"net/textproto"
|
|
|
|
)
|
|
|
|
|
2017-03-12 08:46:59 +00:00
|
|
|
type (
|
|
|
|
// Reader is an iterator over parts in a multipart log stream.
|
|
|
|
Reader interface {
|
|
|
|
// NextPart returns the next part in the multipart or
|
|
|
|
// an error. When there are no more parts, the error
|
|
|
|
// io.EOF is returned.
|
|
|
|
NextPart() (Part, error)
|
|
|
|
}
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2017-03-12 08:46:59 +00:00
|
|
|
// A Part represents a single part in a multipart body.
|
|
|
|
Part interface {
|
|
|
|
io.Reader
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2017-03-12 08:46:59 +00:00
|
|
|
// Header returns the headers of the body with the
|
|
|
|
// keys canonicalized.
|
|
|
|
Header() textproto.MIMEHeader
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2017-03-12 08:46:59 +00:00
|
|
|
// FileName returns the filename parameter of the
|
|
|
|
// Content-Disposition header.
|
|
|
|
FileName() string
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2017-03-12 08:46:59 +00:00
|
|
|
// FormName returns the name parameter if p has a
|
|
|
|
// Content-Disposition of type form-data.
|
|
|
|
FormName() string
|
|
|
|
}
|
|
|
|
)
|
2017-03-05 07:56:08 +00:00
|
|
|
|
|
|
|
// New returns a new multipart Reader.
|
|
|
|
func New(r io.Reader) Reader {
|
|
|
|
buf := bufio.NewReader(r)
|
2017-03-13 15:24:11 +00:00
|
|
|
out, _ := buf.Peek(8)
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2017-03-13 15:24:11 +00:00
|
|
|
if bytes.Equal(out, []byte("PIPELINE")) {
|
2017-03-05 07:56:08 +00:00
|
|
|
return &multipartReader{
|
|
|
|
reader: multipart.NewReader(buf, "boundary"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &textReader{
|
2017-03-12 09:00:55 +00:00
|
|
|
reader: buf,
|
2017-03-05 07:56:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2017-03-12 08:46:59 +00:00
|
|
|
// wraps the stdlib multi-part reader
|
2017-03-05 07:56:08 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
type multipartReader struct {
|
|
|
|
reader *multipart.Reader
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *multipartReader) NextPart() (Part, error) {
|
|
|
|
next, err := r.reader.NextPart()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
part := new(part)
|
|
|
|
part.Reader = next
|
|
|
|
part.filename = next.FileName()
|
|
|
|
part.formname = next.FormName()
|
|
|
|
part.header = next.Header
|
|
|
|
return part, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2017-03-12 08:46:59 +00:00
|
|
|
// wraps a simple io.Reader to satisfy the multi-part interface
|
2017-03-05 07:56:08 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
type textReader struct {
|
|
|
|
reader io.Reader
|
|
|
|
done bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *textReader) NextPart() (Part, error) {
|
|
|
|
if r.done {
|
|
|
|
return nil, io.EOF
|
|
|
|
}
|
|
|
|
r.done = true
|
|
|
|
p := new(part)
|
|
|
|
p.Reader = r.reader
|
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type part struct {
|
|
|
|
io.Reader
|
|
|
|
|
|
|
|
filename string
|
|
|
|
formname string
|
|
|
|
header textproto.MIMEHeader
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *part) Header() textproto.MIMEHeader { return p.header }
|
|
|
|
func (p *part) FileName() string { return p.filename }
|
|
|
|
func (p *part) FormName() string { return p.filename }
|