package deconnector import ( "bufio" "context" "fmt" "io" "net" "net/http" ) //nolint:funlen func (d *Deconnector) handleTunnel(ctx context.Context, clientConn net.Conn, host string) { d.app.Logger().WithField("host", host).Info("Handling CONNECT tunnel") upstreamConn, err := d.dialer.Dial(ctx) if err != nil { d.app.Logger().WithError(err).Error("upstream dial failed") fmt.Fprintf(clientConn, "HTTP/1.1 502 Bad Gateway\r\n\r\n") return } defer upstreamConn.Close() connectLine := fmt.Sprintf( "CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n", host, host, ) if authHeader, ok := d.dialer.Auth(); ok { connectLine += fmt.Sprintf("Proxy-Authorization: Basic %s\r\n", authHeader) } fmt.Fprint(upstreamConn, connectLine) resp, err := http.ReadResponse(bufio.NewReader(upstreamConn), nil) if err != nil || resp.StatusCode > 499 { d.app.Logger().WithError(err).Error("upstream CONNECT failed") fmt.Fprintf(clientConn, "HTTP/1.1 502 Bad Gateway\r\n\r\n") return } defer resp.Body.Close() fmt.Fprintf(clientConn, "HTTP/1.1 200 Connection established\r\n\r\n") done := make(chan struct{}, 2) go func() { defer func() { _ = upstreamConn.Close() _ = clientConn.Close() done <- struct{}{} }() if _, err := io.Copy(upstreamConn, clientConn); err != nil { d.app.Logger(). WithError(err). Debug("client -> upstream copy stopped") } }() go func() { defer func() { _ = upstreamConn.Close() _ = clientConn.Close() done <- struct{}{} }() if _, err := io.Copy(clientConn, upstreamConn); err != nil { d.app.Logger(). WithError(err). Debug("upstream -> client copy stopped") } }() <-done }