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()
return nil, errors.New("couldn't open forwarded-tcpip channel: " + err.Error())
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)
go ssh.DiscardRequests(req)
Notice the commented-out defer ch.Close(). Evidently it's wrong to close the connection here, because if I do, the response body is sometimes empty, apparently due to a race condition 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 SSH connection is closed.