...
Run Format

Source file src/net/http/transfer.go

     1	// Copyright 2009 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package http
     6	
     7	import (
     8		"bufio"
     9		"bytes"
    10		"errors"
    11		"fmt"
    12		"io"
    13		"io/ioutil"
    14		"net/http/internal"
    15		"net/textproto"
    16		"sort"
    17		"strconv"
    18		"strings"
    19		"sync"
    20	)
    21	
    22	// ErrLineTooLong is returned when reading request or response bodies
    23	// with malformed chunked encoding.
    24	var ErrLineTooLong = internal.ErrLineTooLong
    25	
    26	type errorReader struct {
    27		err error
    28	}
    29	
    30	func (r *errorReader) Read(p []byte) (n int, err error) {
    31		return 0, r.err
    32	}
    33	
    34	// transferWriter inspects the fields of a user-supplied Request or Response,
    35	// sanitizes them without changing the user object and provides methods for
    36	// writing the respective header, body and trailer in wire format.
    37	type transferWriter struct {
    38		Method           string
    39		Body             io.Reader
    40		BodyCloser       io.Closer
    41		ResponseToHEAD   bool
    42		ContentLength    int64 // -1 means unknown, 0 means exactly none
    43		Close            bool
    44		TransferEncoding []string
    45		Trailer          Header
    46	}
    47	
    48	func newTransferWriter(r interface{}) (t *transferWriter, err error) {
    49		t = &transferWriter{}
    50	
    51		// Extract relevant fields
    52		atLeastHTTP11 := false
    53		switch rr := r.(type) {
    54		case *Request:
    55			if rr.ContentLength != 0 && rr.Body == nil {
    56				return nil, fmt.Errorf("http: Request.ContentLength=%d with nil Body", rr.ContentLength)
    57			}
    58			t.Method = rr.Method
    59			t.Body = rr.Body
    60			t.BodyCloser = rr.Body
    61			t.ContentLength = rr.ContentLength
    62			t.Close = rr.Close
    63			t.TransferEncoding = rr.TransferEncoding
    64			t.Trailer = rr.Trailer
    65			atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
    66			if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 {
    67				if t.ContentLength == 0 {
    68					// Test to see if it's actually zero or just unset.
    69					var buf [1]byte
    70					n, rerr := io.ReadFull(t.Body, buf[:])
    71					if rerr != nil && rerr != io.EOF {
    72						t.ContentLength = -1
    73						t.Body = &errorReader{rerr}
    74					} else if n == 1 {
    75						// Oh, guess there is data in this Body Reader after all.
    76						// The ContentLength field just wasn't set.
    77						// Stich the Body back together again, re-attaching our
    78						// consumed byte.
    79						t.ContentLength = -1
    80						t.Body = io.MultiReader(bytes.NewReader(buf[:]), t.Body)
    81					} else {
    82						// Body is actually empty.
    83						t.Body = nil
    84						t.BodyCloser = nil
    85					}
    86				}
    87				if t.ContentLength < 0 {
    88					t.TransferEncoding = []string{"chunked"}
    89				}
    90			}
    91		case *Response:
    92			if rr.Request != nil {
    93				t.Method = rr.Request.Method
    94			}
    95			t.Body = rr.Body
    96			t.BodyCloser = rr.Body
    97			t.ContentLength = rr.ContentLength
    98			t.Close = rr.Close
    99			t.TransferEncoding = rr.TransferEncoding
   100			t.Trailer = rr.Trailer
   101			atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
   102			t.ResponseToHEAD = noBodyExpected(t.Method)
   103		}
   104	
   105		// Sanitize Body,ContentLength,TransferEncoding
   106		if t.ResponseToHEAD {
   107			t.Body = nil
   108			if chunked(t.TransferEncoding) {
   109				t.ContentLength = -1
   110			}
   111		} else {
   112			if !atLeastHTTP11 || t.Body == nil {
   113				t.TransferEncoding = nil
   114			}
   115			if chunked(t.TransferEncoding) {
   116				t.ContentLength = -1
   117			} else if t.Body == nil { // no chunking, no body
   118				t.ContentLength = 0
   119			}
   120		}
   121	
   122		// Sanitize Trailer
   123		if !chunked(t.TransferEncoding) {
   124			t.Trailer = nil
   125		}
   126	
   127		return t, nil
   128	}
   129	
   130	func noBodyExpected(requestMethod string) bool {
   131		return requestMethod == "HEAD"
   132	}
   133	
   134	func (t *transferWriter) shouldSendContentLength() bool {
   135		if chunked(t.TransferEncoding) {
   136			return false
   137		}
   138		if t.ContentLength > 0 {
   139			return true
   140		}
   141		// Many servers expect a Content-Length for these methods
   142		if t.Method == "POST" || t.Method == "PUT" {
   143			return true
   144		}
   145		if t.ContentLength == 0 && isIdentity(t.TransferEncoding) {
   146			return true
   147		}
   148	
   149		return false
   150	}
   151	
   152	func (t *transferWriter) WriteHeader(w io.Writer) error {
   153		if t.Close {
   154			if _, err := io.WriteString(w, "Connection: close\r\n"); err != nil {
   155				return err
   156			}
   157		}
   158	
   159		// Write Content-Length and/or Transfer-Encoding whose values are a
   160		// function of the sanitized field triple (Body, ContentLength,
   161		// TransferEncoding)
   162		if t.shouldSendContentLength() {
   163			if _, err := io.WriteString(w, "Content-Length: "); err != nil {
   164				return err
   165			}
   166			if _, err := io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n"); err != nil {
   167				return err
   168			}
   169		} else if chunked(t.TransferEncoding) {
   170			if _, err := io.WriteString(w, "Transfer-Encoding: chunked\r\n"); err != nil {
   171				return err
   172			}
   173		}
   174	
   175		// Write Trailer header
   176		if t.Trailer != nil {
   177			keys := make([]string, 0, len(t.Trailer))
   178			for k := range t.Trailer {
   179				k = CanonicalHeaderKey(k)
   180				switch k {
   181				case "Transfer-Encoding", "Trailer", "Content-Length":
   182					return &badStringError{"invalid Trailer key", k}
   183				}
   184				keys = append(keys, k)
   185			}
   186			if len(keys) > 0 {
   187				sort.Strings(keys)
   188				// TODO: could do better allocation-wise here, but trailers are rare,
   189				// so being lazy for now.
   190				if _, err := io.WriteString(w, "Trailer: "+strings.Join(keys, ",")+"\r\n"); err != nil {
   191					return err
   192				}
   193			}
   194		}
   195	
   196		return nil
   197	}
   198	
   199	func (t *transferWriter) WriteBody(w io.Writer) error {
   200		var err error
   201		var ncopy int64
   202	
   203		// Write body
   204		if t.Body != nil {
   205			if chunked(t.TransferEncoding) {
   206				cw := internal.NewChunkedWriter(w)
   207				_, err = io.Copy(cw, t.Body)
   208				if err == nil {
   209					err = cw.Close()
   210				}
   211			} else if t.ContentLength == -1 {
   212				ncopy, err = io.Copy(w, t.Body)
   213			} else {
   214				ncopy, err = io.Copy(w, io.LimitReader(t.Body, t.ContentLength))
   215				if err != nil {
   216					return err
   217				}
   218				var nextra int64
   219				nextra, err = io.Copy(ioutil.Discard, t.Body)
   220				ncopy += nextra
   221			}
   222			if err != nil {
   223				return err
   224			}
   225			if err = t.BodyCloser.Close(); err != nil {
   226				return err
   227			}
   228		}
   229	
   230		if !t.ResponseToHEAD && t.ContentLength != -1 && t.ContentLength != ncopy {
   231			return fmt.Errorf("http: ContentLength=%d with Body length %d",
   232				t.ContentLength, ncopy)
   233		}
   234	
   235		// TODO(petar): Place trailer writer code here.
   236		if chunked(t.TransferEncoding) {
   237			// Write Trailer header
   238			if t.Trailer != nil {
   239				if err := t.Trailer.Write(w); err != nil {
   240					return err
   241				}
   242			}
   243			// Last chunk, empty trailer
   244			_, err = io.WriteString(w, "\r\n")
   245		}
   246		return err
   247	}
   248	
   249	type transferReader struct {
   250		// Input
   251		Header        Header
   252		StatusCode    int
   253		RequestMethod string
   254		ProtoMajor    int
   255		ProtoMinor    int
   256		// Output
   257		Body             io.ReadCloser
   258		ContentLength    int64
   259		TransferEncoding []string
   260		Close            bool
   261		Trailer          Header
   262	}
   263	
   264	// bodyAllowedForStatus reports whether a given response status code
   265	// permits a body.  See RFC2616, section 4.4.
   266	func bodyAllowedForStatus(status int) bool {
   267		switch {
   268		case status >= 100 && status <= 199:
   269			return false
   270		case status == 204:
   271			return false
   272		case status == 304:
   273			return false
   274		}
   275		return true
   276	}
   277	
   278	var (
   279		suppressedHeaders304    = []string{"Content-Type", "Content-Length", "Transfer-Encoding"}
   280		suppressedHeadersNoBody = []string{"Content-Length", "Transfer-Encoding"}
   281	)
   282	
   283	func suppressedHeaders(status int) []string {
   284		switch {
   285		case status == 304:
   286			// RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
   287			return suppressedHeaders304
   288		case !bodyAllowedForStatus(status):
   289			return suppressedHeadersNoBody
   290		}
   291		return nil
   292	}
   293	
   294	// msg is *Request or *Response.
   295	func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
   296		t := &transferReader{RequestMethod: "GET"}
   297	
   298		// Unify input
   299		isResponse := false
   300		switch rr := msg.(type) {
   301		case *Response:
   302			t.Header = rr.Header
   303			t.StatusCode = rr.StatusCode
   304			t.ProtoMajor = rr.ProtoMajor
   305			t.ProtoMinor = rr.ProtoMinor
   306			t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header, true)
   307			isResponse = true
   308			if rr.Request != nil {
   309				t.RequestMethod = rr.Request.Method
   310			}
   311		case *Request:
   312			t.Header = rr.Header
   313			t.ProtoMajor = rr.ProtoMajor
   314			t.ProtoMinor = rr.ProtoMinor
   315			// Transfer semantics for Requests are exactly like those for
   316			// Responses with status code 200, responding to a GET method
   317			t.StatusCode = 200
   318		default:
   319			panic("unexpected type")
   320		}
   321	
   322		// Default to HTTP/1.1
   323		if t.ProtoMajor == 0 && t.ProtoMinor == 0 {
   324			t.ProtoMajor, t.ProtoMinor = 1, 1
   325		}
   326	
   327		// Transfer encoding, content length
   328		t.TransferEncoding, err = fixTransferEncoding(t.RequestMethod, t.Header)
   329		if err != nil {
   330			return err
   331		}
   332	
   333		realLength, err := fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
   334		if err != nil {
   335			return err
   336		}
   337		if isResponse && t.RequestMethod == "HEAD" {
   338			if n, err := parseContentLength(t.Header.get("Content-Length")); err != nil {
   339				return err
   340			} else {
   341				t.ContentLength = n
   342			}
   343		} else {
   344			t.ContentLength = realLength
   345		}
   346	
   347		// Trailer
   348		t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding)
   349		if err != nil {
   350			return err
   351		}
   352	
   353		// If there is no Content-Length or chunked Transfer-Encoding on a *Response
   354		// and the status is not 1xx, 204 or 304, then the body is unbounded.
   355		// See RFC2616, section 4.4.
   356		switch msg.(type) {
   357		case *Response:
   358			if realLength == -1 &&
   359				!chunked(t.TransferEncoding) &&
   360				bodyAllowedForStatus(t.StatusCode) {
   361				// Unbounded body.
   362				t.Close = true
   363			}
   364		}
   365	
   366		// Prepare body reader.  ContentLength < 0 means chunked encoding
   367		// or close connection when finished, since multipart is not supported yet
   368		switch {
   369		case chunked(t.TransferEncoding):
   370			if noBodyExpected(t.RequestMethod) {
   371				t.Body = eofReader
   372			} else {
   373				t.Body = &body{src: internal.NewChunkedReader(r), hdr: msg, r: r, closing: t.Close}
   374			}
   375		case realLength == 0:
   376			t.Body = eofReader
   377		case realLength > 0:
   378			t.Body = &body{src: io.LimitReader(r, realLength), closing: t.Close}
   379		default:
   380			// realLength < 0, i.e. "Content-Length" not mentioned in header
   381			if t.Close {
   382				// Close semantics (i.e. HTTP/1.0)
   383				t.Body = &body{src: r, closing: t.Close}
   384			} else {
   385				// Persistent connection (i.e. HTTP/1.1)
   386				t.Body = eofReader
   387			}
   388		}
   389	
   390		// Unify output
   391		switch rr := msg.(type) {
   392		case *Request:
   393			rr.Body = t.Body
   394			rr.ContentLength = t.ContentLength
   395			rr.TransferEncoding = t.TransferEncoding
   396			rr.Close = t.Close
   397			rr.Trailer = t.Trailer
   398		case *Response:
   399			rr.Body = t.Body
   400			rr.ContentLength = t.ContentLength
   401			rr.TransferEncoding = t.TransferEncoding
   402			rr.Close = t.Close
   403			rr.Trailer = t.Trailer
   404		}
   405	
   406		return nil
   407	}
   408	
   409	// Checks whether chunked is part of the encodings stack
   410	func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" }
   411	
   412	// Checks whether the encoding is explicitly "identity".
   413	func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" }
   414	
   415	// Sanitize transfer encoding
   416	func fixTransferEncoding(requestMethod string, header Header) ([]string, error) {
   417		raw, present := header["Transfer-Encoding"]
   418		if !present {
   419			return nil, nil
   420		}
   421	
   422		delete(header, "Transfer-Encoding")
   423	
   424		encodings := strings.Split(raw[0], ",")
   425		te := make([]string, 0, len(encodings))
   426		// TODO: Even though we only support "identity" and "chunked"
   427		// encodings, the loop below is designed with foresight. One
   428		// invariant that must be maintained is that, if present,
   429		// chunked encoding must always come first.
   430		for _, encoding := range encodings {
   431			encoding = strings.ToLower(strings.TrimSpace(encoding))
   432			// "identity" encoding is not recorded
   433			if encoding == "identity" {
   434				break
   435			}
   436			if encoding != "chunked" {
   437				return nil, &badStringError{"unsupported transfer encoding", encoding}
   438			}
   439			te = te[0 : len(te)+1]
   440			te[len(te)-1] = encoding
   441		}
   442		if len(te) > 1 {
   443			return nil, &badStringError{"too many transfer encodings", strings.Join(te, ",")}
   444		}
   445		if len(te) > 0 {
   446			// Chunked encoding trumps Content-Length. See RFC 2616
   447			// Section 4.4. Currently len(te) > 0 implies chunked
   448			// encoding.
   449			delete(header, "Content-Length")
   450			return te, nil
   451		}
   452	
   453		return nil, nil
   454	}
   455	
   456	// Determine the expected body length, using RFC 2616 Section 4.4. This
   457	// function is not a method, because ultimately it should be shared by
   458	// ReadResponse and ReadRequest.
   459	func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, error) {
   460	
   461		// Logic based on response type or status
   462		if noBodyExpected(requestMethod) {
   463			return 0, nil
   464		}
   465		if status/100 == 1 {
   466			return 0, nil
   467		}
   468		switch status {
   469		case 204, 304:
   470			return 0, nil
   471		}
   472	
   473		// Logic based on Transfer-Encoding
   474		if chunked(te) {
   475			return -1, nil
   476		}
   477	
   478		// Logic based on Content-Length
   479		cl := strings.TrimSpace(header.get("Content-Length"))
   480		if cl != "" {
   481			n, err := parseContentLength(cl)
   482			if err != nil {
   483				return -1, err
   484			}
   485			return n, nil
   486		} else {
   487			header.Del("Content-Length")
   488		}
   489	
   490		if !isResponse && requestMethod == "GET" {
   491			// RFC 2616 doesn't explicitly permit nor forbid an
   492			// entity-body on a GET request so we permit one if
   493			// declared, but we default to 0 here (not -1 below)
   494			// if there's no mention of a body.
   495			return 0, nil
   496		}
   497	
   498		// Body-EOF logic based on other methods (like closing, or chunked coding)
   499		return -1, nil
   500	}
   501	
   502	// Determine whether to hang up after sending a request and body, or
   503	// receiving a response and body
   504	// 'header' is the request headers
   505	func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool {
   506		if major < 1 {
   507			return true
   508		} else if major == 1 && minor == 0 {
   509			if !strings.Contains(strings.ToLower(header.get("Connection")), "keep-alive") {
   510				return true
   511			}
   512			return false
   513		} else {
   514			// TODO: Should split on commas, toss surrounding white space,
   515			// and check each field.
   516			if strings.ToLower(header.get("Connection")) == "close" {
   517				if removeCloseHeader {
   518					header.Del("Connection")
   519				}
   520				return true
   521			}
   522		}
   523		return false
   524	}
   525	
   526	// Parse the trailer header
   527	func fixTrailer(header Header, te []string) (Header, error) {
   528		raw := header.get("Trailer")
   529		if raw == "" {
   530			return nil, nil
   531		}
   532	
   533		header.Del("Trailer")
   534		trailer := make(Header)
   535		keys := strings.Split(raw, ",")
   536		for _, key := range keys {
   537			key = CanonicalHeaderKey(strings.TrimSpace(key))
   538			switch key {
   539			case "Transfer-Encoding", "Trailer", "Content-Length":
   540				return nil, &badStringError{"bad trailer key", key}
   541			}
   542			trailer[key] = nil
   543		}
   544		if len(trailer) == 0 {
   545			return nil, nil
   546		}
   547		if !chunked(te) {
   548			// Trailer and no chunking
   549			return nil, ErrUnexpectedTrailer
   550		}
   551		return trailer, nil
   552	}
   553	
   554	// body turns a Reader into a ReadCloser.
   555	// Close ensures that the body has been fully read
   556	// and then reads the trailer if necessary.
   557	type body struct {
   558		src     io.Reader
   559		hdr     interface{}   // non-nil (Response or Request) value means read trailer
   560		r       *bufio.Reader // underlying wire-format reader for the trailer
   561		closing bool          // is the connection to be closed after reading body?
   562	
   563		mu     sync.Mutex // guards closed, and calls to Read and Close
   564		closed bool
   565	}
   566	
   567	// ErrBodyReadAfterClose is returned when reading a Request or Response
   568	// Body after the body has been closed. This typically happens when the body is
   569	// read after an HTTP Handler calls WriteHeader or Write on its
   570	// ResponseWriter.
   571	var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body")
   572	
   573	func (b *body) Read(p []byte) (n int, err error) {
   574		b.mu.Lock()
   575		defer b.mu.Unlock()
   576		if b.closed {
   577			return 0, ErrBodyReadAfterClose
   578		}
   579		return b.readLocked(p)
   580	}
   581	
   582	// Must hold b.mu.
   583	func (b *body) readLocked(p []byte) (n int, err error) {
   584		n, err = b.src.Read(p)
   585	
   586		if err == io.EOF {
   587			// Chunked case. Read the trailer.
   588			if b.hdr != nil {
   589				if e := b.readTrailer(); e != nil {
   590					err = e
   591				}
   592				b.hdr = nil
   593			} else {
   594				// If the server declared the Content-Length, our body is a LimitedReader
   595				// and we need to check whether this EOF arrived early.
   596				if lr, ok := b.src.(*io.LimitedReader); ok && lr.N > 0 {
   597					err = io.ErrUnexpectedEOF
   598				}
   599			}
   600		}
   601	
   602		// If we can return an EOF here along with the read data, do
   603		// so. This is optional per the io.Reader contract, but doing
   604		// so helps the HTTP transport code recycle its connection
   605		// earlier (since it will see this EOF itself), even if the
   606		// client doesn't do future reads or Close.
   607		if err == nil && n > 0 {
   608			if lr, ok := b.src.(*io.LimitedReader); ok && lr.N == 0 {
   609				err = io.EOF
   610			}
   611		}
   612	
   613		return n, err
   614	}
   615	
   616	var (
   617		singleCRLF = []byte("\r\n")
   618		doubleCRLF = []byte("\r\n\r\n")
   619	)
   620	
   621	func seeUpcomingDoubleCRLF(r *bufio.Reader) bool {
   622		for peekSize := 4; ; peekSize++ {
   623			// This loop stops when Peek returns an error,
   624			// which it does when r's buffer has been filled.
   625			buf, err := r.Peek(peekSize)
   626			if bytes.HasSuffix(buf, doubleCRLF) {
   627				return true
   628			}
   629			if err != nil {
   630				break
   631			}
   632		}
   633		return false
   634	}
   635	
   636	var errTrailerEOF = errors.New("http: unexpected EOF reading trailer")
   637	
   638	func (b *body) readTrailer() error {
   639		// The common case, since nobody uses trailers.
   640		buf, err := b.r.Peek(2)
   641		if bytes.Equal(buf, singleCRLF) {
   642			b.r.ReadByte()
   643			b.r.ReadByte()
   644			return nil
   645		}
   646		if len(buf) < 2 {
   647			return errTrailerEOF
   648		}
   649		if err != nil {
   650			return err
   651		}
   652	
   653		// Make sure there's a header terminator coming up, to prevent
   654		// a DoS with an unbounded size Trailer.  It's not easy to
   655		// slip in a LimitReader here, as textproto.NewReader requires
   656		// a concrete *bufio.Reader.  Also, we can't get all the way
   657		// back up to our conn's LimitedReader that *might* be backing
   658		// this bufio.Reader.  Instead, a hack: we iteratively Peek up
   659		// to the bufio.Reader's max size, looking for a double CRLF.
   660		// This limits the trailer to the underlying buffer size, typically 4kB.
   661		if !seeUpcomingDoubleCRLF(b.r) {
   662			return errors.New("http: suspiciously long trailer after chunked body")
   663		}
   664	
   665		hdr, err := textproto.NewReader(b.r).ReadMIMEHeader()
   666		if err != nil {
   667			if err == io.EOF {
   668				return errTrailerEOF
   669			}
   670			return err
   671		}
   672		switch rr := b.hdr.(type) {
   673		case *Request:
   674			mergeSetHeader(&rr.Trailer, Header(hdr))
   675		case *Response:
   676			mergeSetHeader(&rr.Trailer, Header(hdr))
   677		}
   678		return nil
   679	}
   680	
   681	func mergeSetHeader(dst *Header, src Header) {
   682		if *dst == nil {
   683			*dst = src
   684			return
   685		}
   686		for k, vv := range src {
   687			(*dst)[k] = vv
   688		}
   689	}
   690	
   691	func (b *body) Close() error {
   692		b.mu.Lock()
   693		defer b.mu.Unlock()
   694		if b.closed {
   695			return nil
   696		}
   697		var err error
   698		switch {
   699		case b.hdr == nil && b.closing:
   700			// no trailer and closing the connection next.
   701			// no point in reading to EOF.
   702		default:
   703			// Fully consume the body, which will also lead to us reading
   704			// the trailer headers after the body, if present.
   705			_, err = io.Copy(ioutil.Discard, bodyLocked{b})
   706		}
   707		b.closed = true
   708		return err
   709	}
   710	
   711	// bodyLocked is a io.Reader reading from a *body when its mutex is
   712	// already held.
   713	type bodyLocked struct {
   714		b *body
   715	}
   716	
   717	func (bl bodyLocked) Read(p []byte) (n int, err error) {
   718		if bl.b.closed {
   719			return 0, ErrBodyReadAfterClose
   720		}
   721		return bl.b.readLocked(p)
   722	}
   723	
   724	// parseContentLength trims whitespace from s and returns -1 if no value
   725	// is set, or the value if it's >= 0.
   726	func parseContentLength(cl string) (int64, error) {
   727		cl = strings.TrimSpace(cl)
   728		if cl == "" {
   729			return -1, nil
   730		}
   731		n, err := strconv.ParseInt(cl, 10, 64)
   732		if err != nil || n < 0 {
   733			return 0, &badStringError{"bad Content-Length", cl}
   734		}
   735		return n, nil
   736	
   737	}
   738	

View as plain text