http.RoundTripper应在何时关闭其连接?

http.RoundTripper应在何时关闭其连接?,http,go,Http,Go,我正在使用我自己的一个实现,它使用作为传输。我的往返方法大致如下所示: 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()

我正在使用我自己的一个实现,它使用作为传输。我的往返方法大致如下所示:

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
}
请注意注释掉的defer ch.Close()。最初我天真地关闭了这里的连接,但响应主体有时会是空的,因为HTTP代理读取主体和关闭SSH通道之间存在竞争


假设现在我不想保持活动状态,我什么时候可以关闭ssh.Channel?如果我不这样做,每个请求都会启动一个新的goroutine(因为go-ssh.DiscardRequests(req)),因此我会在每个HTTP请求上泄漏一个goroutine,直到底层ssh连接关闭。

An
HTTP.RoundTripper
应该在响应主体完全消耗之后或在服务器请求时关闭连接

最简单的选择是完全缓冲响应并立即关闭连接。在某些情况下,如果通信量主要由小的、独立的请求组成,那么这实际上可能是最有效的

下一个选项是钩住响应主体的关闭以关闭通道

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
}

最后,为了最有效地利用ssh通道,您可以使用现有的
传输
net.Dialer
来进行ssh连接,并将通道包装在
net.Conn
接口中

我还没想清楚,但是回应。身体只是一个io.ReadCloser。也许可以包装默认的Close()方法。然后您将知道调用方何时完成响应,您可以关闭连接。是的,由客户端使用并关闭
响应.Body
,但关闭它不会直接关闭连接,它只会将其从Body中释放出来。我认为,如果您想在这一层而不是在传输层处理连接,则需要缓冲整个响应并在往返中关闭连接。有没有理由不将ssh仅仅用作网络传输工具?@Peter Good idea;实施和工作。也许值得回答一下?@JimB你介意再解释一下吗?我不太清楚您所说的“传输层”或“仅将ssh用作网络传输”是什么意思。@TrevorDixon:RoundTripper(几乎总是
传输
)用于处理
往返
之外的连接寿命。连接在主体被消耗后的某个时间点关闭,通常由服务器启动,这可能在往返返回后很长时间内关闭。http请求(也称为“往返”)和连接处理发生在不同的概念层。ssh连接充当网络代理,因此要正常工作,它需要在该级别挂接,就像使用http或socks代理一样。