Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate fixes from Stripe fork #585

Merged
merged 5 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion counterecryptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type CounterEncryptorRand struct {
ix int
}

func NewCounterEncryptorRandFromKey(key interface{}, seed []byte) (r CounterEncryptorRand, err error) {
func NewCounterEncryptorRandFromKey(key any, seed []byte) (r CounterEncryptorRand, err error) {
var keyBytes []byte
switch key := key.(type) {
case *rsa.PrivateKey:
Expand Down
10 changes: 5 additions & 5 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type ProxyCtx struct {
Error error
// A handle for the user to keep data in the context, from the call of ReqHandler to the
// call of RespHandler
UserData interface{}
UserData any
// Will connect a request to a response
Session int64
certStore CertStorage
Expand Down Expand Up @@ -46,8 +46,8 @@ func (ctx *ProxyCtx) RoundTrip(req *http.Request) (*http.Response, error) {
return ctx.Proxy.Tr.RoundTrip(req)
}

func (ctx *ProxyCtx) printf(msg string, argv ...interface{}) {
ctx.Proxy.Logger.Printf("[%03d] "+msg+"\n", append([]interface{}{ctx.Session & 0xFF}, argv...)...)
func (ctx *ProxyCtx) printf(msg string, argv ...any) {
ctx.Proxy.Logger.Printf("[%03d] "+msg+"\n", append([]any{ctx.Session & 0xFFFF}, argv...)...)
}

// Logf prints a message to the proxy's log. Should be used in a ProxyHttpServer's filter
Expand All @@ -58,7 +58,7 @@ func (ctx *ProxyCtx) printf(msg string, argv ...interface{}) {
// ctx.Printf("So far %d requests",nr)
// return r, nil
// })
func (ctx *ProxyCtx) Logf(msg string, argv ...interface{}) {
func (ctx *ProxyCtx) Logf(msg string, argv ...any) {
if ctx.Proxy.Verbose {
ctx.printf("INFO: "+msg, argv...)
}
Expand All @@ -75,7 +75,7 @@ func (ctx *ProxyCtx) Logf(msg string, argv ...interface{}) {
// }
// return r, nil
// })
func (ctx *ProxyCtx) Warnf(msg string, argv ...interface{}) {
func (ctx *ProxyCtx) Warnf(msg string, argv ...any) {
ctx.printf("WARN: "+msg, argv...)
}

Expand Down
2 changes: 1 addition & 1 deletion examples/goproxy-httpdump/httpdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type Meta struct {
from string
}

func fprintf(nr *int64, err *error, w io.Writer, pat string, a ...interface{}) {
func fprintf(nr *int64, err *error, w io.Writer, pat string, a ...any) {
if *err != nil {
return
}
Expand Down
69 changes: 50 additions & 19 deletions https.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"os"
"strconv"
"strings"
"sync"
"sync/atomic"
)

Expand Down Expand Up @@ -137,17 +136,35 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request
targetTCP, targetOK := targetSiteCon.(halfClosable)
proxyClientTCP, clientOK := proxyClient.(halfClosable)
if targetOK && clientOK {
go copyAndClose(targetTCP, proxyClientTCP)
go copyAndClose(proxyClientTCP, targetTCP)
go copyAndClose(ctx, targetTCP, proxyClientTCP)
go copyAndClose(ctx, proxyClientTCP, targetTCP)
} else {
// There is a race with the runtime here. In the case where the
// connection to the target site times out, we cannot control which
// io.Copy loop will receive the timeout signal first. This means
// that in some cases the error passed to the ConnErrorHandler will
// be the timeout error, and in other cases it will be an error raised
// by the use of a closed network connection.
//
// 2020/05/28 23:42:17 [001] WARN: Error copying to client: read tcp 127.0.0.1:33742->127.0.0.1:34763: i/o timeout
// 2020/05/28 23:42:17 [001] WARN: Error copying to client: read tcp 127.0.0.1:45145->127.0.0.1:60494: use of closed network connection
//
// It's also not possible to synchronize these connection closures due to
// TCP connections which are half-closed. When this happens, only the one
// side of the connection breaks out of its io.Copy loop. The other side
// of the connection remains open until it either times out or is reset by
// the client.
go func() {
var wg sync.WaitGroup
wg.Add(2)
go copyOrWarn(targetSiteCon, proxyClient, &wg)
go copyOrWarn(proxyClient, targetSiteCon, &wg)
wg.Wait()
proxyClient.Close()
targetSiteCon.Close()
err := copyOrWarn(ctx, targetSiteCon, proxyClient)
if err != nil && proxy.ConnectionErrHandler != nil {
proxy.ConnectionErrHandler(w, ctx, err)
}
_ = targetSiteCon.Close()
}()

go func() {
_ = copyOrWarn(ctx, proxyClient, targetSiteCon)
_ = proxyClient.Close()
}()
}

Expand Down Expand Up @@ -226,7 +243,7 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request
clientTlsReader := bufio.NewReader(rawClientTls)
for !isEof(clientTlsReader) {
req, err := http.ReadRequest(clientTlsReader)
var ctx = &ProxyCtx{Req: req, Session: atomic.AddInt64(&proxy.sess, 1), Proxy: proxy, UserData: ctx.UserData}
var ctx = &ProxyCtx{Req: req, Session: atomic.AddInt64(&proxy.sess, 1), Proxy: proxy, UserData: ctx.UserData, RoundTripper: ctx.RoundTripper}
if err != nil && err != io.EOF {
return
}
Expand Down Expand Up @@ -380,22 +397,36 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request
}

func httpError(w io.WriteCloser, ctx *ProxyCtx, err error) {
errStr := fmt.Sprintf("HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s", len(err.Error()), err.Error())
if _, err := io.WriteString(w, errStr); err != nil {
ctx.Warnf("Error responding to client: %s", err)
if ctx.Proxy.ConnectionErrHandler != nil {
ctx.Proxy.ConnectionErrHandler(w, ctx, err)
} else {
errStr := fmt.Sprintf("HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n%s", len(err.Error()), err.Error())
if _, err := io.WriteString(w, errStr); err != nil {
ctx.Warnf("Error responding to client: %s", err)
}
}
if err := w.Close(); err != nil {
ctx.Warnf("Error closing client connection: %s", err)
}
}

func copyOrWarn(dst io.Writer, src io.Reader, wg *sync.WaitGroup) {
_, _ = io.Copy(dst, src)
wg.Done()
func copyOrWarn(ctx *ProxyCtx, dst io.Writer, src io.Reader) error {
_, err := io.Copy(dst, src)
if err != nil && errors.Is(err, net.ErrClosed) {
// Discard closed connection errors
err = nil
} else if err != nil {
ctx.Warnf("Error copying to client: %s", err)
}
return err
}

func copyAndClose(dst, src halfClosable) {
_, _ = io.Copy(dst, src)
func copyAndClose(ctx *ProxyCtx, dst, src halfClosable) {
_, err := io.Copy(dst, src)
if err != nil && !errors.Is(err, net.ErrClosed) {
ctx.Warnf("Error copying to client: %s", err.Error())
}

dst.CloseWrite()
src.CloseRead()
}
Expand Down
2 changes: 1 addition & 1 deletion logger.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package goproxy

type Logger interface {
Printf(format string, v ...interface{})
Printf(format string, v ...any)
}
23 changes: 19 additions & 4 deletions proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,27 @@ type ProxyHttpServer struct {
respHandlers []RespHandler
httpsHandlers []HttpsHandler
Tr *http.Transport
// ConnectionErrHandler will be invoked to return a custom response
// to clients (written using conn parameter), when goproxy fails to connect
// to a target proxy.
// The error is passed as function parameter and not inside the proxy
// context, to avoid race conditions.
ConnectionErrHandler func(conn io.Writer, ctx *ProxyCtx, err error)
// ConnectDial will be used to create TCP connections for CONNECT requests
// if nil Tr.Dial will be used
ConnectDial func(network string, addr string) (net.Conn, error)
ConnectDialWithReq func(req *http.Request, network string, addr string) (net.Conn, error)
CertStore CertStorage
KeepHeader bool
AllowHTTP2 bool
// KeepAcceptEncoding, if true, prevents the proxy from dropping
// Accept-Encoding headers from the client.
//
// Note that the outbound http.Transport may still choose to add
// Accept-Encoding: gzip if the client did not explicitly send an
// Accept-Encoding header. To disable this behavior, set
// Tr.DisableCompression to true.
KeepAcceptEncoding bool
}

var hasPort = regexp.MustCompile(`:\d+$`)
Expand Down Expand Up @@ -83,9 +97,11 @@ func (proxy *ProxyHttpServer) filterResponse(respOrig *http.Response, ctx *Proxy
func RemoveProxyHeaders(ctx *ProxyCtx, r *http.Request) {
r.RequestURI = "" // this must be reset when serving a request with the client
ctx.Logf("Sending request %v %v", r.Method, r.URL.String())
// If no Accept-Encoding header exists, Transport will add the headers it can accept
// and would wrap the response body with the relevant reader.
r.Header.Del("Accept-Encoding")
if !ctx.Proxy.KeepAcceptEncoding {
// If no Accept-Encoding header exists, Transport will add the headers it can accept
// and would wrap the response body with the relevant reader.
r.Header.Del("Accept-Encoding")
}
// curl can add that, see
// https://jdebp.eu./FGA/web-proxy-connection-header.html
r.Header.Del("Proxy-Connection")
Expand Down Expand Up @@ -153,7 +169,6 @@ func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request)
if err != nil {
ctx.Error = err
resp = proxy.filterResponse(nil, ctx)

}
if resp != nil {
ctx.Logf("Received response %v", resp.Status)
Expand Down
Loading