我正在使用 httputil.ReverseProxy用 http.RoundTripper我自己的实现使用 ssh.Channel作为交通工具。我的 RoundTrip 方法大致如下所示:
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 channel 之间的竞争。
假设,现在我不想保持事件状态,我什么时候可以关闭 ssh.Channel?如果我不这样做,每个请求都会启动一个新的 goroutine(因为 go ssh.DiscardRequests(req)),所以我会在每个 HTTP 请求上泄漏一个 goroutine,直到底层 SSH 连接关闭。
最佳答案
http.RoundTripper
不应该关闭连接,直到响应主体被完全消耗,或者在服务器请求时。
最简单的选择是完全缓冲响应并立即关闭连接。在某些情况下,如果流量主要由小型独立请求组成,这实际上可能是最有效的。
下一个选项是 Hook 响应主体的关闭以关闭 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
}
最后,为了最有效地使用 ssh channel ,您可以使用现有的 Transport
和 net.Dialer
来建立 ssh 连接,并包装net.Conn
接口(interface)中的 channel 。
关于http - http.RoundTripper 应该什么时候关闭它的连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46834710/