// Copyright 2009 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 httpimport ()// ErrLineTooLong is returned when reading request or response bodies// with malformed chunked encoding.varErrLineTooLong = internal.ErrLineTooLongtype errorReader struct { err error}func ( errorReader) ( []byte) ( int, error) {return0, .err}type byteReader struct { b byte done bool}func ( *byteReader) ( []byte) ( int, error) {if .done {return0, io.EOF }iflen() == 0 {return0, nil } .done = true [0] = .breturn1, io.EOF}// transferWriter inspects the fields of a user-supplied Request or Response,// sanitizes them without changing the user object and provides methods for// writing the respective header, body and trailer in wire format.type transferWriter struct { Method string Body io.Reader BodyCloser io.Closer ResponseToHEAD bool ContentLength int64// -1 means unknown, 0 means exactly none Close bool TransferEncoding []string Header Header Trailer Header IsResponse bool bodyReadError error// any non-EOF error from reading Body FlushHeaders bool// flush headers to network before body ByteReadCh chanreadResult// non-nil if probeRequestBody called}func newTransferWriter( any) ( *transferWriter, error) { = &transferWriter{}// Extract relevant fields := falseswitch rr := .(type) {case *Request:if .ContentLength != 0 && .Body == nil {returnnil, fmt.Errorf("http: Request.ContentLength=%d with nil Body", .ContentLength) } .Method = valueOrDefault(.Method, "GET") .Close = .Close .TransferEncoding = .TransferEncoding .Header = .Header .Trailer = .Trailer .Body = .Body .BodyCloser = .Body .ContentLength = .outgoingLength()if .ContentLength < 0 && len(.TransferEncoding) == 0 && .shouldSendChunkedRequestBody() { .TransferEncoding = []string{"chunked"} }// If there's a body, conservatively flush the headers // to any bufio.Writer we're writing to, just in case // the server needs the headers early, before we copy // the body and possibly block. We make an exception // for the common standard library in-memory types, // though, to avoid unnecessary TCP packets on the // wire. (Issue 22088.)if .ContentLength != 0 && !isKnownInMemoryReader(.Body) { .FlushHeaders = true } = true// Transport requests are always 1.1 or 2.0case *Response: .IsResponse = trueif .Request != nil { .Method = .Request.Method } .Body = .Body .BodyCloser = .Body .ContentLength = .ContentLength .Close = .Close .TransferEncoding = .TransferEncoding .Header = .Header .Trailer = .Trailer = .ProtoAtLeast(1, 1) .ResponseToHEAD = noResponseBodyExpected(.Method) }// Sanitize Body,ContentLength,TransferEncodingif .ResponseToHEAD { .Body = nilifchunked(.TransferEncoding) { .ContentLength = -1 } } else {if ! || .Body == nil { .TransferEncoding = nil }ifchunked(.TransferEncoding) { .ContentLength = -1 } elseif .Body == nil { // no chunking, no body .ContentLength = 0 } }// Sanitize Trailerif !chunked(.TransferEncoding) { .Trailer = nil }return , nil}// shouldSendChunkedRequestBody reports whether we should try to send a// chunked request body to the server. In particular, the case we really// want to prevent is sending a GET or other typically-bodyless request to a// server with a chunked body when the body has zero bytes, since GETs with// bodies (while acceptable according to specs), even zero-byte chunked// bodies, are approximately never seen in the wild and confuse most// servers. See Issue 18257, as one example.//// The only reason we'd send such a request is if the user set the Body to a// non-nil value (say, io.NopCloser(bytes.NewReader(nil))) and didn't// set ContentLength, or NewRequest set it to -1 (unknown), so then we assume// there's bytes to send.//// This code tries to read a byte from the Request.Body in such cases to see// whether the body actually has content (super rare) or is actually just// a non-nil content-less ReadCloser (the more common case). In that more// common case, we act as if their Body were nil instead, and don't send// a body.func ( *transferWriter) () bool {// Note that t.ContentLength is the corrected content length // from rr.outgoingLength, so 0 actually means zero, not unknown.if .ContentLength >= 0 || .Body == nil { // redundant checks; caller did themreturnfalse }if .Method == "CONNECT" {returnfalse }ifrequestMethodUsuallyLacksBody(.Method) {// Only probe the Request.Body for GET/HEAD/DELETE/etc // requests, because it's only those types of requests // that confuse servers. .probeRequestBody() // adjusts t.Body, t.ContentLengthreturn .Body != nil }// For all other request types (PUT, POST, PATCH, or anything // made-up we've never heard of), assume it's normal and the server // can deal with a chunked request body. Maybe we'll adjust this // later.returntrue}// probeRequestBody reads a byte from t.Body to see whether it's empty// (returns io.EOF right away).//// But because we've had problems with this blocking users in the past// (issue 17480) when the body is a pipe (perhaps waiting on the response// headers before the pipe is fed data), we need to be careful and bound how// long we wait for it. This delay will only affect users if all the following// are true:// - the request body blocks// - the content length is not set (or set to -1)// - the method doesn't usually have a body (GET, HEAD, DELETE, ...)// - there is no transfer-encoding=chunked already set.//// In other words, this delay will not normally affect anybody, and there// are workarounds if it does.func ( *transferWriter) () { .ByteReadCh = make(chanreadResult, 1)gofunc( io.Reader) {var [1]bytevarreadResult .n, .err = .Read([:])if .n == 1 { .b = [0] } .ByteReadCh <- close(.ByteReadCh) }(.Body) := time.NewTimer(200 * time.Millisecond)select {case := <-.ByteReadCh: .Stop()if .n == 0 && .err == io.EOF {// It was empty. .Body = nil .ContentLength = 0 } elseif .n == 1 {if .err != nil { .Body = io.MultiReader(&byteReader{b: .b}, errorReader{.err}) } else { .Body = io.MultiReader(&byteReader{b: .b}, .Body) } } elseif .err != nil { .Body = errorReader{.err} }case<-.C:// Too slow. Don't wait. Read it later, and keep // assuming that this is ContentLength == -1 // (unknown), which means we'll send a // "Transfer-Encoding: chunked" header. .Body = io.MultiReader(finishAsyncByteRead{}, .Body)// Request that Request.Write flush the headers to the // network before writing the body, since our body may not // become readable until it's seen the response headers. .FlushHeaders = true }}func noResponseBodyExpected( string) bool {return == "HEAD"}func ( *transferWriter) () bool {ifchunked(.TransferEncoding) {returnfalse }if .ContentLength > 0 {returntrue }if .ContentLength < 0 {returnfalse }// Many servers expect a Content-Length for these methodsif .Method == "POST" || .Method == "PUT" || .Method == "PATCH" {returntrue }if .ContentLength == 0 && isIdentity(.TransferEncoding) {if .Method == "GET" || .Method == "HEAD" {returnfalse }returntrue }returnfalse}func ( *transferWriter) ( io.Writer, *httptrace.ClientTrace) error {if .Close && !hasToken(.Header.get("Connection"), "close") {if , := io.WriteString(, "Connection: close\r\n"); != nil {return }if != nil && .WroteHeaderField != nil { .WroteHeaderField("Connection", []string{"close"}) } }// Write Content-Length and/or Transfer-Encoding whose values are a // function of the sanitized field triple (Body, ContentLength, // TransferEncoding)if .shouldSendContentLength() {if , := io.WriteString(, "Content-Length: "); != nil {return }if , := io.WriteString(, strconv.FormatInt(.ContentLength, 10)+"\r\n"); != nil {return }if != nil && .WroteHeaderField != nil { .WroteHeaderField("Content-Length", []string{strconv.FormatInt(.ContentLength, 10)}) } } elseifchunked(.TransferEncoding) {if , := io.WriteString(, "Transfer-Encoding: chunked\r\n"); != nil {return }if != nil && .WroteHeaderField != nil { .WroteHeaderField("Transfer-Encoding", []string{"chunked"}) } }// Write Trailer headerif .Trailer != nil { := make([]string, 0, len(.Trailer))for := range .Trailer { = CanonicalHeaderKey()switch {case"Transfer-Encoding", "Trailer", "Content-Length":returnbadStringError("invalid Trailer key", ) } = append(, ) }iflen() > 0 {slices.Sort()// TODO: could do better allocation-wise here, but trailers are rare, // so being lazy for now.if , := io.WriteString(, "Trailer: "+strings.Join(, ",")+"\r\n"); != nil {return }if != nil && .WroteHeaderField != nil { .WroteHeaderField("Trailer", ) } } }returnnil}// always closes t.BodyCloserfunc ( *transferWriter) ( io.Writer) ( error) {varint64 := falsedeferfunc() {if || .BodyCloser == nil {return }if := .BodyCloser.Close(); != nil && == nil { = } }()// Write body. We "unwrap" the body first if it was wrapped in a // nopCloser or readTrackingBody. This is to ensure that we can take advantage of // OS-level optimizations in the event that the body is an // *os.File.if !.ResponseToHEAD && .Body != nil {var = .unwrapBody()ifchunked(.TransferEncoding) {if , := .(*bufio.Writer); && !.IsResponse { = &internal.FlushAfterChunkWriter{Writer: } } := internal.NewChunkedWriter() _, = .doBodyCopy(, )if == nil { = .Close() } } elseif .ContentLength == -1 { := if .Method == "CONNECT" { = bufioFlushWriter{} } , = .doBodyCopy(, ) } else { , = .doBodyCopy(, io.LimitReader(, .ContentLength))if != nil {return }varint64 , = .doBodyCopy(io.Discard, ) += }if != nil {return } }if .BodyCloser != nil { = trueif := .BodyCloser.Close(); != nil {return } }if !.ResponseToHEAD && .ContentLength != -1 && .ContentLength != {returnfmt.Errorf("http: ContentLength=%d with Body length %d", .ContentLength, ) }if !.ResponseToHEAD && chunked(.TransferEncoding) {// Write Trailer headerif .Trailer != nil {if := .Trailer.Write(); != nil {return } }// Last chunk, empty trailer _, = io.WriteString(, "\r\n") }return}// doBodyCopy wraps a copy operation, with any resulting error also// being saved in bodyReadError.//// This function is only intended for use in writeBody.func ( *transferWriter) ( io.Writer, io.Reader) ( int64, error) { := getCopyBuf()deferputCopyBuf() , = io.CopyBuffer(, , )if != nil && != io.EOF { .bodyReadError = }return}// unwrapBody unwraps the body's inner reader if it's a// nopCloser. This is to ensure that body writes sourced from local// files (*os.File types) are properly optimized.//// This function is only intended for use in writeBody.func ( *transferWriter) () io.Reader {if , := unwrapNopCloser(.Body); {return }if , := .Body.(*readTrackingBody); { .didRead = truereturn .ReadCloser }return .Body}type transferReader struct {// Input Header Header StatusCode int RequestMethod string ProtoMajor int ProtoMinor int// Output Body io.ReadCloser ContentLength int64 Chunked bool Close bool Trailer Header}func ( *transferReader) (, int) bool {return .ProtoMajor > || (.ProtoMajor == && .ProtoMinor >= )}// bodyAllowedForStatus reports whether a given response status code// permits a body. See RFC 7230, section 3.3.func bodyAllowedForStatus( int) bool {switch {case >= 100 && <= 199:returnfalsecase == 204:returnfalsecase == 304:returnfalse }returntrue}var ( suppressedHeaders304 = []string{"Content-Type", "Content-Length", "Transfer-Encoding"} suppressedHeadersNoBody = []string{"Content-Length", "Transfer-Encoding"} excludedHeadersNoBody = map[string]bool{"Content-Length": true, "Transfer-Encoding": true})func suppressedHeaders( int) []string {switch {case == 304:// RFC 7232 section 4.1returnsuppressedHeaders304case !bodyAllowedForStatus():returnsuppressedHeadersNoBody }returnnil}// msg is *Request or *Response.func readTransfer( any, *bufio.Reader) ( error) { := &transferReader{RequestMethod: "GET"}// Unify input := falseswitch rr := .(type) {case *Response: .Header = .Header .StatusCode = .StatusCode .ProtoMajor = .ProtoMajor .ProtoMinor = .ProtoMinor .Close = shouldClose(.ProtoMajor, .ProtoMinor, .Header, true) = trueif .Request != nil { .RequestMethod = .Request.Method }case *Request: .Header = .Header .RequestMethod = .Method .ProtoMajor = .ProtoMajor .ProtoMinor = .ProtoMinor// Transfer semantics for Requests are exactly like those for // Responses with status code 200, responding to a GET method .StatusCode = 200 .Close = .Closedefault:panic("unexpected type") }// Default to HTTP/1.1if .ProtoMajor == 0 && .ProtoMinor == 0 { .ProtoMajor, .ProtoMinor = 1, 1 }// Transfer-Encoding: chunked, and overriding Content-Length.if := .parseTransferEncoding(); != nil {return } , := fixLength(, .StatusCode, .RequestMethod, .Header, .Chunked)if != nil {return }if && .RequestMethod == "HEAD" {if , := parseContentLength(.Header["Content-Length"]); != nil {return } else { .ContentLength = } } else { .ContentLength = }// Trailer .Trailer, = fixTrailer(.Header, .Chunked)if != nil {return }// If there is no Content-Length or chunked Transfer-Encoding on a *Response // and the status is not 1xx, 204 or 304, then the body is unbounded. // See RFC 7230, section 3.3.switch .(type) {case *Response:if == -1 && !.Chunked && bodyAllowedForStatus(.StatusCode) {// Unbounded body. .Close = true } }// Prepare body reader. ContentLength < 0 means chunked encoding // or close connection when finished, since multipart is not supported yetswitch {case .Chunked:if && (noResponseBodyExpected(.RequestMethod) || !bodyAllowedForStatus(.StatusCode)) { .Body = NoBody } else { .Body = &body{src: internal.NewChunkedReader(), hdr: , r: , closing: .Close} }case == 0: .Body = NoBodycase > 0: .Body = &body{src: io.LimitReader(, ), closing: .Close}default:// realLength < 0, i.e. "Content-Length" not mentioned in headerif .Close {// Close semantics (i.e. HTTP/1.0) .Body = &body{src: , closing: .Close} } else {// Persistent connection (i.e. HTTP/1.1) .Body = NoBody } }// Unify outputswitch rr := .(type) {case *Request: .Body = .Body .ContentLength = .ContentLengthif .Chunked { .TransferEncoding = []string{"chunked"} } .Close = .Close .Trailer = .Trailercase *Response: .Body = .Body .ContentLength = .ContentLengthif .Chunked { .TransferEncoding = []string{"chunked"} } .Close = .Close .Trailer = .Trailer }returnnil}// Checks whether chunked is part of the encodings stack.func chunked( []string) bool { returnlen() > 0 && [0] == "chunked" }// Checks whether the encoding is explicitly "identity".func isIdentity( []string) bool { returnlen() == 1 && [0] == "identity" }// unsupportedTEError reports unsupported transfer-encodings.type unsupportedTEError struct { err string}func ( *unsupportedTEError) () string {return .err}// isUnsupportedTEError checks if the error is of type// unsupportedTEError. It is usually invoked with a non-nil err.func isUnsupportedTEError( error) bool { , := .(*unsupportedTEError)return}// parseTransferEncoding sets t.Chunked based on the Transfer-Encoding header.func ( *transferReader) () error { , := .Header["Transfer-Encoding"]if ! {returnnil }delete(.Header, "Transfer-Encoding")// Issue 12785; ignore Transfer-Encoding on HTTP/1.0 requests.if !.protoAtLeast(1, 1) {returnnil }// Like nginx, we only support a single Transfer-Encoding header field, and // only if set to "chunked". This is one of the most security sensitive // surfaces in HTTP/1.1 due to the risk of request smuggling, so we keep it // strict and simple.iflen() != 1 {return &unsupportedTEError{fmt.Sprintf("too many transfer encodings: %q", )} }if !ascii.EqualFold([0], "chunked") {return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", [0])} } .Chunked = truereturnnil}// Determine the expected body length, using RFC 7230 Section 3.3. This// function is not a method, because ultimately it should be shared by// ReadResponse and ReadRequest.func fixLength( bool, int, string, Header, bool) ( int64, error) { := ! := ["Content-Length"]// Hardening against HTTP request smugglingiflen() > 1 {// Per RFC 7230 Section 3.3.2, prevent multiple // Content-Length headers if they differ in value. // If there are dups of the value, remove the dups. // See Issue 16490. := textproto.TrimString([0])for , := range [1:] {if != textproto.TrimString() {return0, fmt.Errorf("http: message cannot contain multiple Content-Length headers; got %q", ) } }// deduplicate Content-Length .Del("Content-Length") .Add("Content-Length", ) = ["Content-Length"] }// Reject requests with invalid Content-Length headers.iflen() > 0 { , = parseContentLength()if != nil {return -1, } }// Logic based on response type or statusif && noResponseBodyExpected() {return0, nil }if /100 == 1 {return0, nil }switch {case204, 304:return0, nil }// According to RFC 9112, "If a message is received with both a // Transfer-Encoding and a Content-Length header field, the Transfer-Encoding // overrides the Content-Length. Such a message might indicate an attempt to // perform request smuggling (Section 11.2) or response splitting (Section 11.1) // and ought to be handled as an error. An intermediary that chooses to forward // the message MUST first remove the received Content-Length field and process // the Transfer-Encoding (as described below) prior to forwarding the message downstream." // // Chunked-encoding requests with either valid Content-Length // headers or no Content-Length headers are accepted after removing // the Content-Length field from header. // // Logic based on Transfer-Encodingif { .Del("Content-Length")return -1, nil }// Logic based on Content-Lengthiflen() > 0 {return , nil } .Del("Content-Length")if {// RFC 7230 neither explicitly permits nor forbids an // entity-body on a GET request so we permit one if // declared, but we default to 0 here (not -1 below) // if there's no mention of a body. // Likewise, all other request methods are assumed to have // no body if neither Transfer-Encoding chunked nor a // Content-Length are set.return0, nil }// Body-EOF logic based on other methods (like closing, or chunked coding)return -1, nil}// Determine whether to hang up after sending a request and body, or// receiving a response and body// 'header' is the request headers.func shouldClose(, int, Header, bool) bool {if < 1 {returntrue } := ["Connection"] := httpguts.HeaderValuesContainsToken(, "close")if == 1 && == 0 {return || !httpguts.HeaderValuesContainsToken(, "keep-alive") }if && { .Del("Connection") }return}// Parse the trailer header.func fixTrailer( Header, bool) (Header, error) { , := ["Trailer"]if ! {returnnil, nil }if ! {// Trailer and no chunking: // this is an invalid use case for trailer header. // Nevertheless, no error will be returned and we // let users decide if this is a valid HTTP message. // The Trailer header will be kept in Response.Header // but not populate Response.Trailer. // See issue #27197.returnnil, nil } .Del("Trailer") := make(Header)varerrorfor , := range {foreachHeaderElement(, func( string) { = CanonicalHeaderKey()switch {case"Transfer-Encoding", "Trailer", "Content-Length":if == nil { = badStringError("bad trailer key", )return } } [] = nil }) }if != nil {returnnil, }iflen() == 0 {returnnil, nil }return , nil}// body turns a Reader into a ReadCloser.// Close ensures that the body has been fully read// and then reads the trailer if necessary.type body struct { src io.Reader hdr any// non-nil (Response or Request) value means read trailer r *bufio.Reader// underlying wire-format reader for the trailer closing bool// is the connection to be closed after reading body? doEarlyClose bool// whether Close should stop early mu sync.Mutex// guards following, and calls to Read and Close sawEOF bool closed bool earlyClose bool// Close called and we didn't read to the end of src onHitEOF func() // if non-nil, func to call when EOF is Read}// ErrBodyReadAfterClose is returned when reading a [Request] or [Response]// Body after the body has been closed. This typically happens when the body is// read after an HTTP [Handler] calls WriteHeader or Write on its// [ResponseWriter].varErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body")func ( *body) ( []byte) ( int, error) { .mu.Lock()defer .mu.Unlock()if .closed {return0, ErrBodyReadAfterClose }return .readLocked()}// Must hold b.mu.func ( *body) ( []byte) ( int, error) {if .sawEOF {return0, io.EOF } , = .src.Read()if == io.EOF { .sawEOF = true// Chunked case. Read the trailer.if .hdr != nil {if := .readTrailer(); != nil { = // Something went wrong in the trailer, we must not allow any // further reads of any kind to succeed from body, nor any // subsequent requests on the server connection. See // golang.org/issue/12027 .sawEOF = false .closed = true } .hdr = nil } else {// If the server declared the Content-Length, our body is a LimitedReader // and we need to check whether this EOF arrived early.if , := .src.(*io.LimitedReader); && .N > 0 { = io.ErrUnexpectedEOF } } }// If we can return an EOF here along with the read data, do // so. This is optional per the io.Reader contract, but doing // so helps the HTTP transport code recycle its connection // earlier (since it will see this EOF itself), even if the // client doesn't do future reads or Close.if == nil && > 0 {if , := .src.(*io.LimitedReader); && .N == 0 { = io.EOF .sawEOF = true } }if .sawEOF && .onHitEOF != nil { .onHitEOF() }return , }var ( singleCRLF = []byte("\r\n") doubleCRLF = []byte("\r\n\r\n"))func seeUpcomingDoubleCRLF( *bufio.Reader) bool {for := 4; ; ++ {// This loop stops when Peek returns an error, // which it does when r's buffer has been filled. , := .Peek()ifbytes.HasSuffix(, doubleCRLF) {returntrue }if != nil {break } }returnfalse}var errTrailerEOF = errors.New("http: unexpected EOF reading trailer")func ( *body) () error {// The common case, since nobody uses trailers. , := .r.Peek(2)ifbytes.Equal(, singleCRLF) { .r.Discard(2)returnnil }iflen() < 2 {returnerrTrailerEOF }if != nil {return }// Make sure there's a header terminator coming up, to prevent // a DoS with an unbounded size Trailer. It's not easy to // slip in a LimitReader here, as textproto.NewReader requires // a concrete *bufio.Reader. Also, we can't get all the way // back up to our conn's LimitedReader that *might* be backing // this bufio.Reader. Instead, a hack: we iteratively Peek up // to the bufio.Reader's max size, looking for a double CRLF. // This limits the trailer to the underlying buffer size, typically 4kB.if !seeUpcomingDoubleCRLF(.r) {returnerrors.New("http: suspiciously long trailer after chunked body") } , := textproto.NewReader(.r).ReadMIMEHeader()if != nil {if == io.EOF {returnerrTrailerEOF }return }switch rr := .hdr.(type) {case *Request:mergeSetHeader(&.Trailer, Header())case *Response:mergeSetHeader(&.Trailer, Header()) }returnnil}func mergeSetHeader( *Header, Header) {if * == nil { * = return }maps.Copy(*, )}// unreadDataSizeLocked returns the number of bytes of unread input.// It returns -1 if unknown.// b.mu must be held.func ( *body) () int64 {if , := .src.(*io.LimitedReader); {return .N }return -1}func ( *body) () error { .mu.Lock()defer .mu.Unlock()if .closed {returnnil }varerrorswitch {case .sawEOF:// Already saw EOF, so no need going to look for it.case .hdr == nil && .closing:// no trailer and closing the connection next. // no point in reading to EOF.case .doEarlyClose:// Read up to maxPostHandlerReadBytes bytes of the body, looking // for EOF (and trailers), so we can re-use this connection.if , := .src.(*io.LimitedReader); && .N > maxPostHandlerReadBytes {// There was a declared Content-Length, and we have more bytes remaining // than our maxPostHandlerReadBytes tolerance. So, give up. .earlyClose = true } else {varint64// Consume the body, or, which will also lead to us reading // the trailer headers after the body, if present. , = io.CopyN(io.Discard, bodyLocked{}, maxPostHandlerReadBytes)if == io.EOF { = nil }if == maxPostHandlerReadBytes { .earlyClose = true } }default:// Fully consume the body, which will also lead to us reading // the trailer headers after the body, if present. _, = io.Copy(io.Discard, bodyLocked{}) } .closed = truereturn}func ( *body) () bool { .mu.Lock()defer .mu.Unlock()return .earlyClose}// bodyRemains reports whether future Read calls might// yield data.func ( *body) () bool { .mu.Lock()defer .mu.Unlock()return !.sawEOF}func ( *body) ( func()) { .mu.Lock()defer .mu.Unlock() .onHitEOF = }// bodyLocked is an io.Reader reading from a *body when its mutex is// already held.type bodyLocked struct { b *body}func ( bodyLocked) ( []byte) ( int, error) {if .b.closed {return0, ErrBodyReadAfterClose }return .b.readLocked()}var httplaxcontentlength = godebug.New("httplaxcontentlength")// parseContentLength checks that the header is valid and then trims// whitespace. It returns -1 if no value is set otherwise the value// if it's >= 0.func parseContentLength( []string) (int64, error) {iflen() == 0 {return -1, nil } := textproto.TrimString([0])// The Content-Length must be a valid numeric value. // See: https://datatracker.ietf.org/doc/html/rfc2616/#section-14.13if == "" {ifhttplaxcontentlength.Value() == "1" {httplaxcontentlength.IncNonDefault()return -1, nil }return0, badStringError("invalid empty Content-Length", ) } , := strconv.ParseUint(, 10, 63)if != nil {return0, badStringError("bad Content-Length", ) }returnint64(), nil}// finishAsyncByteRead finishes reading the 1-byte sniff// from the ContentLength==0, Body!=nil case.type finishAsyncByteRead struct { tw *transferWriter}func ( finishAsyncByteRead) ( []byte) ( int, error) {iflen() == 0 {return } := <-.tw.ByteReadCh , = .n, .errif == 1 { [0] = .b }if == nil { = io.EOF }return}var nopCloserType = reflect.TypeOf(io.NopCloser(nil))var nopCloserWriterToType = reflect.TypeOf(io.NopCloser(struct {io.Readerio.WriterTo}{}))// unwrapNopCloser return the underlying reader and true if r is a NopCloser// else it return false.func unwrapNopCloser( io.Reader) ( io.Reader, bool) {switchreflect.TypeOf() {casenopCloserType, nopCloserWriterToType:returnreflect.ValueOf().Field(0).Interface().(io.Reader), truedefault:returnnil, false }}// isKnownInMemoryReader reports whether r is a type known to not// block on Read. Its caller uses this as an optional optimization to// send fewer TCP packets.func isKnownInMemoryReader( io.Reader) bool {switch .(type) {case *bytes.Reader, *bytes.Buffer, *strings.Reader:returntrue }if , := unwrapNopCloser(); {return () }if , := .(*readTrackingBody); {return (.ReadCloser) }returnfalse}// bufioFlushWriter is an io.Writer wrapper that flushes all writes// on its wrapped writer if it's a *bufio.Writer.type bufioFlushWriter struct{ w io.Writer }func ( bufioFlushWriter) ( []byte) ( int, error) { , = .w.Write()if , := .w.(*bufio.Writer); > 0 && { := .Flush()if != nil && == nil { = } }return}
The pages are generated with Goldsv0.7.3-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.