| // 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) | |
| } | |