When should an http.RoundTripper close its connection?

Trevor Dixon :

I'm using httputil.ReverseProxy with an http.RoundTripper of my own implementation that uses an ssh.Channel as a transport. My RoundTrip method looks approximately like this:

func (c SSHConnection) RoundTrip(req *http.Request) (*http.Response, error) {
    ch, err := c.GetChannel()
    if err != nil {
        return nil, errors.New("couldn't open forwarded-tcpip channel: " + err.Error())
    }
    // defer ch.Close()
    err = req.Write(ch)
    if err != nil {
        return nil, errors.New("couldn't send request: " + err.Error())
    }

    return http.ReadResponse(bufio.NewReader(ch), req)
}

func (c SSHConnection) GetChannel() (ssh.Channel, error) {
    ch, req, err := c.Conn.OpenChannel("forwarded-tcpip", msg)
    if err != nil {
        return nil, err
    }
    go ssh.DiscardRequests(req)
    return ch, nil
}

Notice the commented-out defer ch.Close(). Initially I naively closed the connection here, but the response body would sometimes be empty, due to a race between the HTTP proxy's reading of the body and this closing of the SSH channel.

Assuming, for now, that I don't care to do keep-alive, when can I close the ssh.Channel? If I don't, every request starts a new goroutine (because of go ssh.DiscardRequests(req)), so I leak a goroutine on every HTTP requests until the underlying SSH connection is closed.

JimB :

An http.RoundTripper should not close the connection until after the response body has been fully consumed, or at the request of the server.

The simplest option is to fully buffer the response and close the connection immediately. In some cases this may actually be the most efficient, if the traffic mostly consists of small, independent requests.

The next option is to hook the closing of the response body to close the channel.

type Body struct {
    io.ReadCloser
    channel ssh.Channel
}

func (b *Body) Close() error {
    b.channel.Close()
    return b.ReadCloser.Close()
}

func (c SSHConnection) RoundTrip(req *http.Request) (*http.Response, error) {
    ch, err := c.GetChannel()
    if err != nil {
        return nil, errors.New("couldn't open forwarded-tcpip channel: " + err.Error())
    }

    err = req.Write(ch)
    if err != nil {
        return nil, errors.New("couldn't send request: " + err.Error())
    }

    resp, err := http.ReadResponse(bufio.NewReader(ch), req)
    if err != nil {
        ch.Close()
        return nil, err
    }

    resp.Body = &Body{
        ReadCloser: resp.Body,
        channel:    ch,
    }
    return resp, err
}

Lastly, to make the most efficient use of the ssh channels, you could use an existing Transport with a net.Dialer which makes the ssh connection, and wraps the channel in a net.Conn interface.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

When should we close database connection

C socket HTTP 1.1 persistent connection when should server close the socket?

Where should I store a DBI connection and when should I close it?

When should I close the database connection in this simple web app?

How to close an HTTP connection?

Why Akka HTTP close user connection, when multiple messages is produced?

When does a http2 TCP connection close?

Python3 http request close connection when nothing is sent

when to close connection in mongodb?

Must HTTP clients close their connection

Error with HTTP Connection: close header

Forcefully close http connection in golang

How to close HTTP connection with okhttp?

Should an android app close its service when exit if there's nothing to do

EF: Should I explicitly close database connection when calling OpenConnection manually

When should I open/close a connection to the database ? (MariaDB / node.js)

When should I close the socket, if the request contains Connection: keep-alive?

Which should I close first, the PreparedStatement or the Connection?

Should I close a connection that came from a datasource?

Should i close mysql connection in websocket

Should I close connection in each method with JDBC?

Why should we always close a connection to a database?

When does sshpass close a connection

flask - When to close mysql connection?

When should one close a SolrSearcher?

When should we close the EntityManagerFactory?

How do I know WHEN to close an HTTP 1.1 Keep-Alive Connection?

How to close a HTTP connection from the HttpServlet

Python-Requests close http connection