// 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 httputil import ( "bufio" "errors" "io" "net" "net/http" "net/textproto" "sync" ) var ( // Deprecated: No longer used. ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"} // Deprecated: No longer used. ErrClosed = &http.ProtocolError{ErrorString: "connection closed by user"} // Deprecated: No longer used. ErrPipeline = &http.ProtocolError{ErrorString: "pipeline error"} ) // This is an API usage error - the local side is closed. // ErrPersistEOF (above) reports that the remote side is closed. var errClosed = errors.New("i/o operation on closed connection") // ServerConn is an artifact of Go's early HTTP implementation. // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // // Deprecated: Use the Server in package [net/http] instead. type ServerConn struct { mu sync.Mutex // read-write protects the following fields c net.Conn r *bufio.Reader re, we error // read/write errors lastbody io.ReadCloser nread, nwritten int pipereq map[*http.Request]uint pipe textproto.Pipeline } // NewServerConn is an artifact of Go's early HTTP implementation. // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // // Deprecated: Use the Server in package [net/http] instead. func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn { if r == nil { r = bufio.NewReader(c) } return &ServerConn{c: c, r: r, pipereq: make(map[*http.Request]uint)} } // Hijack detaches the [ServerConn] and returns the underlying connection as well // as the read-side bufio which may have some left over data. Hijack may be // called before Read has signaled the end of the keep-alive logic. The user // should not call Hijack while [ServerConn.Read] or [ServerConn.Write] is in progress. func (sc *ServerConn) Hijack() (net.Conn, *bufio.Reader) { sc.mu.Lock() defer sc.mu.Unlock() c := sc.c r := sc.r sc.c = nil sc.r = nil return c, r } // Close calls [ServerConn.Hijack] and then also closes the underlying connection. func (sc *ServerConn) Close() error { c, _ := sc.Hijack() if c != nil { return c.Close() } return nil } // Read returns the next request on the wire. An [ErrPersistEOF] is returned if // it is gracefully determined that there are no more requests (e.g. after the // first request on an HTTP/1.0 connection, or after a Connection:close on a // HTTP/1.1 connection). func (sc *ServerConn) Read() (*http.Request, error) { var req *http.Request var err error // Ensure ordered execution of Reads and Writes id := sc.pipe.Next() sc.pipe.StartRequest(id) defer func() { sc.pipe.EndRequest(id) if req == nil { sc.pipe.StartResponse(id) sc.pipe.EndResponse(id) } else { // Remember the pipeline id of this request sc.mu.Lock() sc.pipereq[req] = id sc.mu.Unlock() } }() sc.mu.Lock() if sc.we != nil { // no point receiving if write-side broken or closed defer sc.mu.Unlock() return nil, sc.we } if sc.re != nil { defer sc.mu.Unlock() return nil, sc.re } if sc.r == nil { // connection closed by user in the meantime defer sc.mu.Unlock() return nil, errClosed } r := sc.r lastbody := sc.lastbody sc.lastbody = nil sc.mu.Unlock() // Make sure body is fully consumed, even if user does not call body.Close if lastbody != nil { // body.Close is assumed to be idempotent and multiple calls to // it should return the error that its first invocation // returned. err = lastbody.Close() if err != nil { sc.mu.Lock() defer sc.mu.Unlock() sc.re = err return nil, err } } req, err = http.ReadRequest(r) sc.mu.Lock() defer sc.mu.Unlock() if err != nil { if err == io.ErrUnexpectedEOF { // A close from the opposing client is treated as a // graceful close, even if there was some unparse-able // data before the close. sc.re = ErrPersistEOF return nil, sc.re } else { sc.re = err return req, err } } sc.lastbody = req.Body sc.nread++ if req.Close { sc.re = ErrPersistEOF return req, sc.re } return req, err } // Pending returns the number of unanswered requests // that have been received on the connection. func (sc *ServerConn) Pending() int { sc.mu.Lock() defer sc.mu.Unlock() return sc.nread - sc.nwritten } // Write writes resp in response to req. To close the connection gracefully, set the // Response.Close field to true. Write should be considered operational until // it returns an error, regardless of any errors returned on the [ServerConn.Read] side. func (sc *ServerConn) Write(req *http.Request, resp *http.Response) error { // Retrieve the pipeline ID of this request/response pair sc.mu.Lock() id, ok := sc.pipereq[req] delete(sc.pipereq, req) if !ok { sc.mu.Unlock() return ErrPipeline } sc.mu.Unlock() // Ensure pipeline order sc.pipe.StartResponse(id) defer sc.pipe.EndResponse(id) sc.mu.Lock() if sc.we != nil { defer sc.mu.Unlock() return sc.we } if sc.c == nil { // connection closed by user in the meantime defer sc.mu.Unlock() return ErrClosed } c := sc.c if sc.nread <= sc.nwritten { defer sc.mu.Unlock() return errors.New("persist server pipe count") } if resp.Close { // After signaling a keep-alive close, any pipelined unread // requests will be lost. It is up to the user to drain them // before signaling. sc.re = ErrPersistEOF } sc.mu.Unlock() err := resp.Write(c) sc.mu.Lock() defer sc.mu.Unlock() if err != nil { sc.we = err return err } sc.nwritten++ return nil } // ClientConn is an artifact of Go's early HTTP implementation. // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // // Deprecated: Use Client or Transport in package [net/http] instead. type ClientConn struct { mu sync.Mutex // read-write protects the following fields c net.Conn r *bufio.Reader re, we error // read/write errors lastbody io.ReadCloser nread, nwritten int pipereq map[*http.Request]uint pipe textproto.Pipeline writeReq func(*http.Request, io.Writer) error } // NewClientConn is an artifact of Go's early HTTP implementation. // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // // Deprecated: Use the Client or Transport in package [net/http] instead. func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn { if r == nil { r = bufio.NewReader(c) } return &ClientConn{ c: c, r: r, pipereq: make(map[*http.Request]uint), writeReq: (*http.Request).Write, } } // NewProxyClientConn is an artifact of Go's early HTTP implementation. // It is low-level, old, and unused by Go's current HTTP stack. // We should have deleted it before Go 1. // // Deprecated: Use the Client or Transport in package [net/http] instead. func NewProxyClientConn(c net.Conn, r *bufio.Reader) *ClientConn { cc := NewClientConn(c, r) cc.writeReq = (*http.Request).WriteProxy return cc } // Hijack detaches the [ClientConn] and returns the underlying connection as well // as the read-side bufio which may have some left over data. Hijack may be // called before the user or Read have signaled the end of the keep-alive // logic. The user should not call Hijack while [ClientConn.Read] or ClientConn.Write is in progress. func (cc *ClientConn) Hijack() (c net.Conn, r *bufio.Reader) { cc.mu.Lock() defer cc.mu.Unlock() c = cc.c r = cc.r cc.c = nil cc.r = nil return } // Close calls [ClientConn.Hijack] and then also closes the underlying connection. func (cc *ClientConn) Close() error { c, _ := cc.Hijack() if c != nil { return c.Close() } return nil } // Write writes a request. An [ErrPersistEOF] error is returned if the connection // has been closed in an HTTP keep-alive sense. If req.Close equals true, the // keep-alive connection is logically closed after this request and the opposing // server is informed. An ErrUnexpectedEOF indicates the remote closed the // underlying TCP connection, which is usually considered as graceful close. func (cc *ClientConn) Write(req *http.Request) error { var err error // Ensure ordered execution of Writes id := cc.pipe.Next() cc.pipe.StartRequest(id) defer func() { cc.pipe.EndRequest(id) if err != nil { cc.pipe.StartResponse(id) cc.pipe.EndResponse(id) } else { // Remember the pipeline id of this request cc.mu.Lock() cc.pipereq[req] = id cc.mu.Unlock() } }() cc.mu.Lock() if cc.re != nil { // no point sending if read-side closed or broken defer cc.mu.Unlock() return cc.re } if cc.we != nil { defer cc.mu.Unlock() return cc.we } if cc.c == nil { // connection closed by user in the meantime defer cc.mu.Unlock() return errClosed } c := cc.c if req.Close { // We write the EOF to the write-side error, because there // still might be some pipelined reads cc.we = ErrPersistEOF } cc.mu.Unlock() err = cc.writeReq(req, c) cc.mu.Lock() defer cc.mu.Unlock() if err != nil { cc.we = err return err } cc.nwritten++ return nil } // Pending returns the number of unanswered requests // that have been sent on the connection. func (cc *ClientConn) Pending() int { cc.mu.Lock() defer cc.mu.Unlock() return cc.nwritten - cc.nread } // Read reads the next response from the wire. A valid response might be // returned together with an [ErrPersistEOF], which means that the remote // requested that this be the last request serviced. Read can be called // concurrently with [ClientConn.Write], but not with another Read. func (cc *ClientConn) Read(req *http.Request) (resp *http.Response, err error) { // Retrieve the pipeline ID of this request/response pair cc.mu.Lock() id, ok := cc.pipereq[req] delete(cc.pipereq, req) if !ok { cc.mu.Unlock() return nil, ErrPipeline } cc.mu.Unlock() // Ensure pipeline order cc.pipe.StartResponse(id) defer cc.pipe.EndResponse(id) cc.mu.Lock() if cc.re != nil { defer cc.mu.Unlock() return nil, cc.re } if cc.r == nil { // connection closed by user in the meantime defer cc.mu.Unlock() return nil, errClosed } r := cc.r lastbody := cc.lastbody cc.lastbody = nil cc.mu.Unlock() // Make sure body is fully consumed, even if user does not call body.Close if lastbody != nil { // body.Close is assumed to be idempotent and multiple calls to // it should return the error that its first invocation // returned. err = lastbody.Close() if err != nil { cc.mu.Lock() defer cc.mu.Unlock() cc.re = err return nil, err } } resp, err = http.ReadResponse(r, req) cc.mu.Lock() defer cc.mu.Unlock() if err != nil { cc.re = err return resp, err } cc.lastbody = resp.Body cc.nread++ if resp.Close { cc.re = ErrPersistEOF // don't send any more requests return resp, cc.re } return resp, err } // Do is convenience method that writes a request and reads a response. func (cc *ClientConn) Do(req *http.Request) (*http.Response, error) { err := cc.Write(req) if err != nil { return nil, err } return cc.Read(req) }