URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [mime/] [multipart/] [multipart.go] - Rev 747
Compare with Previous | Blame | View Log
// Copyright 2010 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.///*Package multipart implements MIME multipart parsing, as defined in RFC2046.The implementation is sufficient for HTTP (RFC 2388) and the multipartbodies generated by popular browsers.*/package multipartimport ("bufio""bytes""fmt""io""io/ioutil""mime""net/textproto")// TODO(bradfitz): inline these once the compiler can inline them in// read-only situation (such as bytes.HasSuffix)var lf = []byte("\n")var crlf = []byte("\r\n")var emptyParams = make(map[string]string)// A Part represents a single part in a multipart body.type Part struct {// The headers of the body, if any, with the keys canonicalized// in the same fashion that the Go http.Request headers are.// i.e. "foo-bar" changes case to "Foo-Bar"Header textproto.MIMEHeaderbuffer *bytes.Buffermr *Readerdisposition stringdispositionParams map[string]string}// FormName returns the name parameter if p has a Content-Disposition// of type "form-data". Otherwise it returns the empty 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 {p.parseContentDisposition()}if p.disposition != "form-data" {return ""}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")var err errorp.disposition, p.dispositionParams, err = mime.ParseMediaType(v)if err != 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 {b := []byte("\r\n--" + boundary + "--")return &Reader{bufReader: bufio.NewReader(reader),nl: b[:2],nlDashBoundary: b[:len(b)-2],dashBoundaryDash: b[2:],dashBoundary: b[2 : len(b)-2],}}func newPart(mr *Reader) (*Part, error) {bp := &Part{Header: make(map[string][]string),mr: mr,buffer: new(bytes.Buffer),}if err := bp.populateHeaders(); err != nil {return nil, err}return bp, nil}func (bp *Part) populateHeaders() error {r := textproto.NewReader(bp.mr.bufReader)header, err := r.ReadMIMEHeader()if err == nil {bp.Header = header}return err}// Read reads the body of a part, after its headers and before the// next part (if any) begins.func (p *Part) Read(d []byte) (n int, err error) {if p.buffer.Len() >= len(d) {// Internal buffer of unconsumed data is large enough for// the read request. No need to parse more at the moment.return p.buffer.Read(d)}peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessorunexpectedEof := err == io.EOFif err != nil && !unexpectedEof {return 0, fmt.Errorf("multipart: Part Read: %v", err)}if peek == nil {panic("nil peek buf")}// Search the peek buffer for "\r\n--boundary". If found,// consume everything up to the boundary. If not, consume only// as much of the peek buffer as cannot hold the boundary// string.nCopy := 0foundBoundary := falseif idx := bytes.Index(peek, p.mr.nlDashBoundary); idx != -1 {nCopy = idxfoundBoundary = true} else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {nCopy = safeCount} else if unexpectedEof {// If we've run out of peek buffer and the boundary// wasn't found (and can't possibly fit), we must have// hit the end of the file unexpectedly.return 0, io.ErrUnexpectedEOF}if nCopy > 0 {if _, err := io.CopyN(p.buffer, p.mr.bufReader, int64(nCopy)); err != nil {return 0, err}}n, err = p.buffer.Read(d)if err == io.EOF && !foundBoundary {// If the boundary hasn't been reached there's more to// read, so don't pass through an EOF from the buffererr = nil}return}func (p *Part) Close() error {io.Copy(ioutil.Discard, p)return nil}// Reader is an iterator over parts in a MIME multipart body.// Reader's underlying parser consumes its input as needed. Seeking// isn't supported.type Reader struct {bufReader *bufio.ReadercurrentPart *PartpartsRead intnl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte}// NextPart returns the next part in the multipart or an error.// When there are no more parts, the error io.EOF is returned.func (r *Reader) NextPart() (*Part, error) {if r.currentPart != nil {r.currentPart.Close()}expectNewPart := falsefor {line, err := r.bufReader.ReadSlice('\n')if err != nil {return nil, fmt.Errorf("multipart: NextPart: %v", err)}if r.isBoundaryDelimiterLine(line) {r.partsRead++bp, err := newPart(r)if err != nil {return nil, err}r.currentPart = bpreturn bp, nil}if hasPrefixThenNewline(line, r.dashBoundaryDash) {// Expected EOFreturn nil, io.EOF}if expectNewPart {return nil, fmt.Errorf("multipart: expecting a new Part; got line %q", string(line))}if r.partsRead == 0 {// skip linecontinue}// Consume the "\n" or "\r\n" separator between the// body of the previous part and the boundary line we// now expect will follow. (either a new part or the// end boundary)if bytes.Equal(line, r.nl) {expectNewPart = truecontinue}return nil, fmt.Errorf("multipart: unexpected line in Next(): %q", line)}panic("unreachable")}func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {// http://tools.ietf.org/html/rfc2046#section-5.1// The boundary delimiter line is then defined as a line// consisting entirely of two hyphen characters ("-",// decimal value 45) followed by the boundary parameter// value from the Content-Type header field, optional linear// whitespace, and a terminating CRLF.if !bytes.HasPrefix(line, mr.dashBoundary) {return false}if bytes.HasSuffix(line, mr.nl) {return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])}// Violate the spec and also support newlines without the// carriage return...if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {mr.nl = mr.nl[1:]mr.nlDashBoundary = mr.nlDashBoundary[1:]return true}}return false}func onlyHorizontalWhitespace(s []byte) bool {for _, b := range s {if b != ' ' && b != '\t' {return false}}return true}func hasPrefixThenNewline(s, prefix []byte) bool {return bytes.HasPrefix(s, prefix) &&(len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))}
