mirror of
https://github.com/gospider007/requests.git
synced 2025-12-24 13:57:52 +08:00
sync
This commit is contained in:
14
README.md
14
README.md
@@ -20,13 +20,11 @@
|
||||
<h2 align="center">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</h2>
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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,
|
||||
|
||||
53
conn.go
53
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
|
||||
}
|
||||
|
||||
|
||||
11
option.go
11
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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user