From a08970461a61a7da7c1228c1f643ba75b004523b Mon Sep 17 00:00:00 2001 From: gospider <2216403312@qq.com> Date: Tue, 29 Jul 2025 16:04:47 +0800 Subject: [PATCH] sync --- README.md | 14 +++++----- compressConn.go | 4 +-- conn.go | 53 +++++++++++++++++++++++-------------- option.go | 11 ++++---- requests.go | 5 ---- roundTripper.go | 51 +++++++++++++++++++++++------------ test/request/stream_test.go | 15 +++++++---- 7 files changed, 90 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 40ad8a9..91799b9 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,11 @@

Requests is a fully featured HTTP client library for Golang. Network requests can be completed with just a few lines of code. Unified support for http1, http2, http3, websocket, sse, utls, uquic

## Innovative Features -| **gospider007/requests** | **Other Request Libraries** | -|---------------------------|----------------------------| -| Unlimited chained proxy | Not supported | -| HTTP/3 fingerprint spoofing protection | Not supported | -| Arbitrary closure of underlying connections | Not supported | -| Genuine request-level proxy settings | Not supported | -| Unique transport layer management mechanism, fully unifying HTTP/1, HTTP/2, HTTP/3, WebSocket, SSE, UTLS, QUIC protocol handling | Not supported | +* Unlimited chained proxy +* HTTP/3 fingerprint spoofing protection +* Arbitrary closure of underlying connections +* Genuine request-level proxy settings +* Unique transport layer management mechanism, fully unifying HTTP/1, HTTP/2, HTTP/3, WebSocket, SSE, UTLS, QUIC protocol handling ## Features * [Simple for settings and Request](https://github.com/gospider007/requests#quickly-send-requests) @@ -69,7 +67,7 @@ * [Well tested client library](https://github.com/gospider007/requests/tree/master/test) ## Supported Go Versions -Recommended to use `go1.23.0` and above. +Recommended to use `go1.24.0` and above. Initially Requests started supporting `go modules` ## Installation diff --git a/compressConn.go b/compressConn.go index 764cbeb..1639a42 100644 --- a/compressConn.go +++ b/compressConn.go @@ -220,11 +220,11 @@ func NewReaderCompression(conn io.Reader, arch Compression) (*ReaderCompression, func NewCompressionConn(conn net.Conn, arch Compression) (net.Conn, error) { w, err := NewWriterCompression(conn, arch) if err != nil { - return nil, err + return conn, err } r, err := NewReaderCompression(conn, arch) if err != nil { - return nil, err + return conn, err } ccon := &CompressionConn{ conn: conn, diff --git a/conn.go b/conn.go index bd6ff00..40ec846 100644 --- a/conn.go +++ b/conn.go @@ -62,29 +62,42 @@ func taskMain(conn http1.Conn, task *reqTask) (err error) { defer close(done) response, derr = conn.DoRequest(task.reqCtx.request, &http1.Option{OrderHeaders: task.reqCtx.option.orderHeaders.Data()}) }() - select { - case <-conn.Context().Done(): //force conn close - err = tools.WrapError(context.Cause(conn.Context()), "taskMain delete ctx error: ") - case <-time.After(task.reqCtx.option.ResponseHeaderTimeout): - err = errors.New("ResponseHeaderTimeout error: ") - case <-task.ctx.Done(): - err = context.Cause(task.ctx) - case <-done: - if derr != nil { - err = tools.WrapError(derr, "roundTrip error") - } else { - task.reqCtx.response = response - task.reqCtx.response.Request = task.reqCtx.request + if task.reqCtx.option.ResponseHeaderTimeout > 0 { + select { + case <-conn.Context().Done(): //force conn close + err = tools.WrapError(context.Cause(conn.Context()), "taskMain delete ctx error: ") + case <-time.After(task.reqCtx.option.ResponseHeaderTimeout): + err = errors.New("ResponseHeaderTimeout error: ") + case <-task.ctx.Done(): + err = context.Cause(task.ctx) + case <-done: } - if task.reqCtx.option.Logger != nil { - task.reqCtx.option.Logger(Log{ - Id: task.reqCtx.requestId, - Time: time.Now(), - Type: LogType_ResponseHeader, - Msg: "response header", - }) + } else { + select { + case <-conn.Context().Done(): //force conn close + err = tools.WrapError(context.Cause(conn.Context()), "taskMain delete ctx error: ") + case <-task.ctx.Done(): + err = context.Cause(task.ctx) + case <-done: } } + + if err != nil { + err = tools.WrapError(err, "roundTrip error") + } else if derr != nil { + err = tools.WrapError(derr, "roundTrip error") + } else { + task.reqCtx.response = response + task.reqCtx.response.Request = task.reqCtx.request + } + if task.reqCtx.option.Logger != nil { + task.reqCtx.option.Logger(Log{ + Id: task.reqCtx.requestId, + Time: time.Now(), + Type: LogType_ResponseHeader, + Msg: "response header", + }) + } return } diff --git a/option.go b/option.go index becf6ab..f82fb78 100644 --- a/option.go +++ b/option.go @@ -54,8 +54,8 @@ type ClientOption struct { MaxRetries int //try num MaxRedirect int //redirect num ,<0 no redirect,==0 no limit Timeout time.Duration //request timeout - ResponseHeaderTimeout time.Duration //ResponseHeaderTimeout ,default:300 - TlsHandshakeTimeout time.Duration //tls timeout,default:15 + ResponseHeaderTimeout time.Duration //ResponseHeaderTimeout + TlsHandshakeTimeout time.Duration //tls timeout ForceHttp3 bool //force use http3 send requests ForceHttp1 bool //force use http1 send requests DisCookie bool //disable cookies @@ -85,8 +85,8 @@ type RequestOption struct { MaxRetries int //try num MaxRedirect int //redirect num ,<0 no redirect,==0 no limit Timeout time.Duration //request timeout - ResponseHeaderTimeout time.Duration //ResponseHeaderTimeout ,default:300 - TlsHandshakeTimeout time.Duration //tls timeout,default:15 + ResponseHeaderTimeout time.Duration //ResponseHeaderTimeout + TlsHandshakeTimeout time.Duration //tls timeout ForceHttp3 bool //force use http3 send requests ForceHttp1 bool //force use http1 send requests DisCookie bool //disable cookies @@ -201,10 +201,9 @@ func merge(option *RequestOption, clientOption *ClientOption) { option.Bar = clientOption.Bar } } + func (obj *Client) newRequestOption(option RequestOption) (RequestOption, error) { - // err := tools.Merge(&option, obj.ClientOption) merge(&option, obj.ClientOption) - //end if option.MaxRetries < 0 { option.MaxRetries = 0 } diff --git a/requests.go b/requests.go index 3bfd6f4..7b4a145 100644 --- a/requests.go +++ b/requests.go @@ -6,7 +6,6 @@ import ( "io" "os" "strings" - "time" "net/url" @@ -239,10 +238,6 @@ func (obj *Client) request(ctx *Response) (err error) { return } } - //init tls timeout - if ctx.option.TlsHandshakeTimeout == 0 { - ctx.option.TlsHandshakeTimeout = time.Second * 15 - } //init proxy if ctx.option.Proxy != nil { ctx.proxys, err = parseProxy(ctx.option.Proxy) diff --git a/roundTripper.go b/roundTripper.go index 7e5c876..f7e26ac 100644 --- a/roundTripper.go +++ b/roundTripper.go @@ -115,6 +115,11 @@ func (obj *roundTripper) ghttp3Dial(ctx *Response, remoteAddress Address, proxyA if err != nil { return nil, err } + defer func() { + if err != nil { + udpConn.Close() + } + }() tlsConfig := ctx.option.TlsConfig.Clone() tlsConfig.NextProtos = []string{http3.NextProtoH3} tlsConfig.ServerName = remoteAddress.Host @@ -133,7 +138,6 @@ func (obj *roundTripper) ghttp3Dial(ctx *Response, remoteAddress Address, proxyA return nil, err } cctx, ccnl := context.WithCancelCause(obj.ctx) - // conn = obj.newConnecotr() conn = http3.NewClient(cctx, netConn, udpConn, func() { ccnl(errors.New("http3 client close")) }) @@ -156,6 +160,11 @@ func (obj *roundTripper) uhttp3Dial(ctx *Response, remoteAddress Address, proxyA if err != nil { return nil, err } + defer func() { + if err != nil { + udpConn.Close() + } + }() tlsConfig := ctx.option.UtlsConfig.Clone() tlsConfig.NextProtos = []string{http3.NextProtoH3} tlsConfig.ServerName = remoteAddress.Host @@ -227,18 +236,22 @@ func (obj *roundTripper) dial(ctx *Response) (conn http1.Conn, err error) { } rawNetConn, err = obj.dialer.DialContext(ctx, "tcp", remoteAddress) } - defer func() { - if err != nil && rawNetConn != nil { + if err != nil { + if rawNetConn != nil { rawNetConn.Close() } - }() - if err != nil { return nil, err } var h2 bool var rawConn net.Conn if ctx.request.URL.Scheme == "https" { - rawConn, h2, err = obj.dialAddTls(ctx.option, ctx.request, rawNetConn) + if ctx.option.TlsHandshakeTimeout > 0 { + tlsCtx, tlsCnl := context.WithTimeout(ctx.Context(), ctx.option.TlsHandshakeTimeout) + rawConn, h2, err = obj.dialAddTls(tlsCtx, ctx.option, ctx.request, rawNetConn) + tlsCnl() + } else { + rawConn, h2, err = obj.dialAddTls(ctx.Context(), ctx.option, ctx.request, rawNetConn) + } if ctx.option.Logger != nil { ctx.option.Logger(Log{ Id: ctx.requestId, @@ -247,17 +260,23 @@ func (obj *roundTripper) dial(ctx *Response) (conn http1.Conn, err error) { Msg: fmt.Sprintf("host:%s, h2:%t", getHost(ctx.request), h2), }) } - if err != nil { - return nil, err - } } else { rawConn = rawNetConn } + if err != nil { + if rawConn != nil { + rawConn.Close() + } + return nil, err + } if arch != nil { rawConn, err = NewCompressionConn(rawConn, arch) - if err != nil { - return nil, err + } + if err != nil { + if rawConn != nil { + rawConn.Close() } + return nil, err } return obj.dialConnecotr(ctx, rawConn, h2) } @@ -278,9 +297,7 @@ func (obj *roundTripper) dialConnecotr(ctx *Response, rawCon net.Conn, h2 bool) } return } -func (obj *roundTripper) dialAddTls(option *RequestOption, req *http.Request, netConn net.Conn) (net.Conn, bool, error) { - ctx, cnl := context.WithTimeout(req.Context(), option.TlsHandshakeTimeout) - defer cnl() +func (obj *roundTripper) dialAddTls(ctx context.Context, option *RequestOption, req *http.Request, netConn net.Conn) (net.Conn, bool, error) { if option.gospiderSpec != nil && option.gospiderSpec.TLSSpec != nil { if tlsConn, err := obj.dialer.addJa3Tls(ctx, netConn, getHost(req), option.gospiderSpec.TLSSpec, option.UtlsConfig.Clone(), option.ForceHttp1); err != nil { return tlsConn, false, tools.WrapError(err, "add ja3 tls error") @@ -345,6 +362,9 @@ func (obj *roundTripper) newRoundTrip(task *reqTask) error { task.reqCtx.isNewConn = true conn, err := obj.dial(task.reqCtx) if err != nil { + if conn != nil { + conn.CloseWithError(err) + } err = tools.WrapError(err, "newRoudTrip dial error") if task.reqCtx.option.ErrCallBack != nil { task.reqCtx.err = err @@ -362,9 +382,6 @@ func (obj *roundTripper) newRoundTrip(task *reqTask) error { } func (obj *roundTripper) newReqTask(ctx *Response) (*reqTask, error) { - if ctx.option.ResponseHeaderTimeout == 0 { - ctx.option.ResponseHeaderTimeout = time.Second * 300 - } task := new(reqTask) task.reqCtx = ctx task.reqCtx.response = nil diff --git a/test/request/stream_test.go b/test/request/stream_test.go index 9171905..2ed86df 100644 --- a/test/request/stream_test.go +++ b/test/request/stream_test.go @@ -27,8 +27,9 @@ func TestStream(t *testing.T) { } } func TestStreamWithConn(t *testing.T) { + session, _ := requests.NewClient(nil) for i := 0; i < 2; i++ { - resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) + resp, err := session.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) if err != nil { t.Fatal(err) } @@ -52,8 +53,9 @@ func TestStreamWithConn(t *testing.T) { } func TestStreamWithConn2(t *testing.T) { + session, _ := requests.NewClient(nil) for i := 0; i < 2; i++ { - resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) + resp, err := session.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) if err != nil { t.Fatal(err) } @@ -77,8 +79,9 @@ func TestStreamWithConn2(t *testing.T) { } func TestStreamWithConn3(t *testing.T) { + session, _ := requests.NewClient(nil) for i := 0; i < 2; i++ { - resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) + resp, err := session.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) if err != nil { t.Fatal(err) } @@ -102,8 +105,9 @@ func TestStreamWithConn3(t *testing.T) { } func TestStreamWithConn4(t *testing.T) { + session, _ := requests.NewClient(nil) for i := 0; i < 2; i++ { - resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) + resp, err := session.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) if err != nil { t.Fatal(err) } @@ -129,8 +133,9 @@ func TestStreamWithConn4(t *testing.T) { } } func TestStreamWithConn5(t *testing.T) { + session, _ := requests.NewClient(nil) for i := 0; i < 2; i++ { - resp, err := requests.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) + resp, err := session.Get(nil, "https://httpbin.org/anything", requests.RequestOption{Stream: true}) if err != nil { t.Fatal(err) }