This commit is contained in:
gospider
2024-11-24 09:27:36 +08:00
parent 80622b41ef
commit d65b427c3b
12 changed files with 257 additions and 89 deletions

View File

@@ -50,6 +50,7 @@ Requests is a fully featured HTTP client library for Golang. Network requests ca
* [IPv4, IPv6 Address Control Parsing](https://github.com/gospider007/requests/blob/master/test/addType_test.go)
* [DNS Settings](https://github.com/gospider007/requests/blob/master/test/dns_test.go)
* [Proxy](https://github.com/gospider007/requests/blob/master/test/proxy_test.go)
* [Logger](https://github.com/gospider007/requests/blob/master/test/logger/logger_test.go)
* [Well tested client library](https://github.com/gospider007/requests/tree/master/test)
## [Benchmark](https://github.com/gospider007/benchmark)
[gospider007/requests](https://github.com/gospider007/requests) > [imroc/req](github.com/imroc/req) > [go-resty](github.com/go-resty/resty) > [wangluozhe/requests](github.com/wangluozhe/requests) > [curl_cffi](https://github.com/yifeikong/curl_cffi) > [httpx](https://github.com/encode/httpx) > [psf/requests](https://github.com/psf/requests)

25
conn.go
View File

@@ -12,8 +12,8 @@ import (
"sync/atomic"
"time"
"github.com/gospider007/http2"
"github.com/gospider007/http3"
"github.com/gospider007/net/http2"
"github.com/gospider007/tools"
)
@@ -28,7 +28,7 @@ type connecotr struct {
bodyCnl context.CancelCauseFunc
rawConn net.Conn
h2RawConn *http2.ClientConn
h2RawConn *http2.Http2ClientConn
h3RawConn http3.RoundTripper
proxy *url.URL
r *bufio.Reader
@@ -101,13 +101,6 @@ func (obj *connecotr) SetWriteDeadline(t time.Time) error {
return obj.rawConn.SetWriteDeadline(t)
}
func (obj *connecotr) h2Closed() bool {
if obj.h2RawConn == nil {
return false
}
state := obj.h2RawConn.State()
return state.Closed || state.Closing
}
func (obj *connecotr) wrapBody(task *reqTask) {
body := new(readWriteCloser)
obj.bodyCtx, obj.bodyCnl = context.WithCancelCause(task.req.Context())
@@ -130,7 +123,7 @@ func (obj *connecotr) http1Req(task *reqTask, done chan struct{}) {
}
func (obj *connecotr) http2Req(task *reqTask, done chan struct{}) {
if task.res, task.err = obj.h2RawConn.RoundTripWithOrderHeaders(task.req, task.orderHeaders2); task.res != nil && task.err == nil {
if task.res, task.err = obj.h2RawConn.DoRequest(task.req, task.orderHeaders2); task.res != nil && task.err == nil {
obj.wrapBody(task)
} else if task.err != nil {
task.err = tools.WrapError(task.err, "http2 roundTrip error")
@@ -160,7 +153,6 @@ func (obj *connecotr) waitBodyClose() error {
func (obj *connecotr) taskMain(task *reqTask, waitBody bool) (retry bool) {
defer func() {
// log.Print("taskMain", retry, task.err)
if retry {
task.err = nil
obj.closeWithError(errors.New("taskMain retry close"))
@@ -175,9 +167,6 @@ func (obj *connecotr) taskMain(task *reqTask, waitBody bool) (retry bool) {
}
}
}()
if obj.h2Closed() {
return true
}
select {
case <-obj.safeCtx.Done():
return true
@@ -208,6 +197,14 @@ func (obj *connecotr) taskMain(task *reqTask, waitBody bool) (retry bool) {
}
return task.suppertRetry()
}
if task.ctxData.logger != nil {
task.ctxData.logger(Log{
Id: task.ctxData.requestId,
Time: time.Now(),
Type: LogType_ResponseHeader,
Msg: "response header",
})
}
return false
case <-obj.forceCtx.Done(): //force conn close
err := context.Cause(obj.forceCtx)

77
dial.go
View File

@@ -82,7 +82,7 @@ func NewDail(option DialOption) *DialClient {
getAddrType: option.GetAddrType,
}
}
func (obj *DialClient) DialContext(ctx context.Context, ctxData *reqCtxData, network string, addr string) (net.Conn, error) {
func (obj *DialClient) dialContext(ctx context.Context, ctxData *reqCtxData, network string, addr string, isProxy bool) (net.Conn, error) {
if ctxData == nil {
ctxData = &reqCtxData{}
}
@@ -108,13 +108,54 @@ func (obj *DialClient) DialContext(ctx context.Context, ctxData *reqCtxData, net
if host, err = obj.addrToIp(host, ips, addrType); err != nil {
return nil, err
}
if ctxData.logger != nil {
if isProxy {
ctxData.logger(Log{
Id: ctxData.requestId,
Time: time.Now(),
Type: LogType_ProxyDNSLookup,
Msg: host,
})
} else {
ctxData.logger(Log{
Id: ctxData.requestId,
Time: time.Now(),
Type: LogType_DNSLookup,
Msg: host,
})
}
}
addr = net.JoinHostPort(host, port)
}
}
if dialer == nil {
dialer = obj.getDialer(ctxData, false)
}
return dialer.DialContext(ctx, network, addr)
con, err := dialer.DialContext(ctx, network, addr)
if ctxData.logger != nil {
if isProxy {
ctxData.logger(Log{
Id: ctxData.requestId,
Time: time.Now(),
Type: LogType_ProxyTCPConnect,
Msg: addr,
})
} else {
ctxData.logger(Log{
Id: ctxData.requestId,
Time: time.Now(),
Type: LogType_TCPConnect,
Msg: addr,
})
}
}
return con, err
}
func (obj *DialClient) DialContext(ctx context.Context, ctxData *reqCtxData, network string, addr string) (net.Conn, error) {
return obj.dialContext(ctx, ctxData, network, addr, false)
}
func (obj *DialClient) ProxyDialContext(ctx context.Context, ctxData *reqCtxData, network string, addr string) (net.Conn, error) {
return obj.dialContext(ctx, ctxData, network, addr, true)
}
func (obj *DialClient) DialContextWithProxy(ctx context.Context, ctxData *reqCtxData, network string, scheme string, addr string, host string, proxyUrl *url.URL, tlsConfig *tls.Config) (net.Conn, error) {
if ctxData == nil {
@@ -132,17 +173,43 @@ func (obj *DialClient) DialContextWithProxy(ctx context.Context, ctxData *reqCtx
}
switch proxyUrl.Scheme {
case "http", "https":
conn, err := obj.DialContext(ctx, ctxData, network, net.JoinHostPort(proxyUrl.Hostname(), proxyUrl.Port()))
conn, err := obj.ProxyDialContext(ctx, ctxData, network, net.JoinHostPort(proxyUrl.Hostname(), proxyUrl.Port()))
if err != nil {
return conn, err
} else if proxyUrl.Scheme == "https" {
if conn, err = obj.addTls(ctx, conn, proxyUrl.Host, true, tlsConfig); err != nil {
return conn, err
}
if ctxData.logger != nil {
ctxData.logger(Log{
Id: ctxData.requestId,
Time: time.Now(),
Type: LogType_ProxyTLSHandshake,
Msg: addr,
})
}
}
return conn, obj.clientVerifyHttps(ctx, scheme, proxyUrl, addr, host, conn)
err = obj.clientVerifyHttps(ctx, scheme, proxyUrl, addr, host, conn)
if ctxData.logger != nil {
ctxData.logger(Log{
Id: ctxData.requestId,
Time: time.Now(),
Type: LogType_ProxyConnectRemote,
Msg: addr,
})
}
return conn, err
case "socks5":
return obj.Socks5Proxy(ctx, ctxData, network, addr, proxyUrl)
conn, err := obj.Socks5Proxy(ctx, ctxData, network, addr, proxyUrl)
if ctxData.logger != nil {
ctxData.logger(Log{
Id: ctxData.requestId,
Time: time.Now(),
Type: LogType_ProxyTCPConnect,
Msg: addr,
})
}
return conn, err
default:
return nil, errors.New("proxyUrl Scheme error")
}

12
go.mod
View File

@@ -4,12 +4,12 @@ go 1.23.0
require (
github.com/gospider007/bar v0.0.0-20231215084215-956cfa59ce61
github.com/gospider007/bs4 v0.0.0-20240531060354-fe6c0582dfd9
github.com/gospider007/bs4 v0.0.0-20241120020109-42eda1e01a18
github.com/gospider007/gson v0.0.0-20240912023741-2238f9748e4a
github.com/gospider007/gtls v0.0.0-20240527084326-e580531eb89e
github.com/gospider007/http2 v0.0.0-20241124010135-caaf1749745b
github.com/gospider007/http3 v0.0.0-20240925091819-2e960e23b259
github.com/gospider007/ja3 v0.0.0-20240620005139-f0602f169903
github.com/gospider007/net v0.0.0-20240620005014-93bab3eb6b6c
github.com/gospider007/re v0.0.0-20240227100911-e27255e48eff
github.com/gospider007/tools v0.0.0-20241120013952-ff42051bfc9f
github.com/gospider007/websocket v0.0.0-20240331132617-8217ca7a8439
@@ -32,7 +32,7 @@ require (
github.com/cloudflare/circl v1.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect
github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b // indirect
github.com/gospider007/blog v0.0.0-20231121084103-59a004dafccf // indirect
github.com/gospider007/kinds v0.0.0-20240929092451-8f867acde255 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
@@ -44,12 +44,12 @@ require (
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/libdns/libdns v0.2.2 // indirect
github.com/mholt/acmez/v2 v2.0.3 // indirect
github.com/mholt/archives v0.0.0-20241119192853-c1ebc8406725 // indirect
github.com/mholt/archives v0.0.0-20241123153623-b85dd98f4298 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 // indirect
github.com/onsi/ginkgo/v2 v2.21.0 // indirect
github.com/nwaples/rardecode/v2 v2.0.0 // indirect
github.com/onsi/ginkgo/v2 v2.22.0 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/sorairolake/lzip-go v0.3.5 // indirect

24
go.sum
View File

@@ -82,8 +82,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs=
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b h1:SXO0REt4iu865upYCk8aKBBJQ4BqoE0ReP23ClMu60s=
github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
@@ -93,20 +93,20 @@ github.com/gospider007/bar v0.0.0-20231215084215-956cfa59ce61 h1:XS7tOVjv207CKZT
github.com/gospider007/bar v0.0.0-20231215084215-956cfa59ce61/go.mod h1:aYPgmG9340i9x9VQZhf34/XtIj7PHDTq0wSO+7zU/8s=
github.com/gospider007/blog v0.0.0-20231121084103-59a004dafccf h1:1laTsuH/wl5pZ5QlHzacX09QzvwQw0DFENoRMpGBK8Y=
github.com/gospider007/blog v0.0.0-20231121084103-59a004dafccf/go.mod h1:CCJ+hvQ0kxL+qB/Wfr1xt7xspsG4XiczhnAPVxG2m3M=
github.com/gospider007/bs4 v0.0.0-20240531060354-fe6c0582dfd9 h1:SiePZGEq9yUj97mSzNAGO9wuKnGrAui9VI4jgUUZEts=
github.com/gospider007/bs4 v0.0.0-20240531060354-fe6c0582dfd9/go.mod h1:kFlkn2u1ulhiybnhC9I840oS98tRSLJVwoBI5qNfZ2U=
github.com/gospider007/bs4 v0.0.0-20241120020109-42eda1e01a18 h1:X0VOdziaqSlQzJLYrbPoYAZW4I1ub3rWTtQYzHIc6xE=
github.com/gospider007/bs4 v0.0.0-20241120020109-42eda1e01a18/go.mod h1:4f04luriR/6gaBqfyUFC7jgSlrlu6YJj6/ya9llb5M0=
github.com/gospider007/gson v0.0.0-20240912023741-2238f9748e4a h1:Gu0mPyLzS27Ij6Chr/HDFUs36yTegGUt1kNsqbXligY=
github.com/gospider007/gson v0.0.0-20240912023741-2238f9748e4a/go.mod h1:nz5cp3n/fquuXqhHdpamPnOJz/yBEKuOGsFfezpr3f8=
github.com/gospider007/gtls v0.0.0-20240527084326-e580531eb89e h1:3bTAtZx+JstbWbTVRd4DivK7QDf6cJwBpWUJpVAgyjY=
github.com/gospider007/gtls v0.0.0-20240527084326-e580531eb89e/go.mod h1:pUD3WKesQAdqD1W8O3v03qRYyq760iPNE8IPEILf52Y=
github.com/gospider007/http2 v0.0.0-20241124010135-caaf1749745b h1:qRpUCl+MzzjPxkZQ99wt8UY60bSckJ624j0GN8wq/QI=
github.com/gospider007/http2 v0.0.0-20241124010135-caaf1749745b/go.mod h1:pyzs3hnbrKDH/IcQN+kVxqEXJHNzEWs6Londu7MXGoE=
github.com/gospider007/http3 v0.0.0-20240925091819-2e960e23b259 h1:2UBbFJLn+B6sjHXnQxSwjOwvLQr40x45tCHdOegS0JM=
github.com/gospider007/http3 v0.0.0-20240925091819-2e960e23b259/go.mod h1:ypu2W38mKOS757LzsBHbs/qqaBFnEdOUwqK30rtPCgE=
github.com/gospider007/ja3 v0.0.0-20240620005139-f0602f169903 h1:elWGt/rRpoPLc7dsnC8axzXnzAAyNdrtOzOgJ3OxisY=
github.com/gospider007/ja3 v0.0.0-20240620005139-f0602f169903/go.mod h1:coutudbhlLqA/xpGB+IFKUGKkzivbYO/Ghl4e0N6hmI=
github.com/gospider007/kinds v0.0.0-20240929092451-8f867acde255 h1:X+AM/mgmh/EfyQUjKZp1VFc9TSlrhkwS0eSYeo5fMs4=
github.com/gospider007/kinds v0.0.0-20240929092451-8f867acde255/go.mod h1:yZx7Zfp1I4P6CO3TcDyDY5SuXQYr0bZjzT9zG0XrJAI=
github.com/gospider007/net v0.0.0-20240620005014-93bab3eb6b6c h1:5g2/mRpUxNcGRB4DDUepbeo9qBYOqdPCdMFgDADiwr4=
github.com/gospider007/net v0.0.0-20240620005014-93bab3eb6b6c/go.mod h1:cM4DcAwxPcg4TaBkCoksZ2TZGTA7YCmvzzYOdDbjS3c=
github.com/gospider007/re v0.0.0-20240227100911-e27255e48eff h1:tiTX2nKAnHZ45JacB/cOf1p4pv5+gw4s1/iGX0ikF0E=
github.com/gospider007/re v0.0.0-20240227100911-e27255e48eff/go.mod h1:X58uk0/F3mVskuQOZng0ZKJiAt3ETn0wxuLN//rVZrE=
github.com/gospider007/tools v0.0.0-20241120013952-ff42051bfc9f h1:nIUWpSjqtNcDu75oGA2E6NtIIHNOAMhY0qg+U9NJDrE=
@@ -143,8 +143,8 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
github.com/mholt/archives v0.0.0-20241119192853-c1ebc8406725 h1:1RnJdkUevC/SFdFMKbSiZnlyZb36s02bmJnmAfL2SE4=
github.com/mholt/archives v0.0.0-20241119192853-c1ebc8406725/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I=
github.com/mholt/archives v0.0.0-20241123153623-b85dd98f4298 h1:KyCSnt0fdY6Bi4vxM0On3zlonJwnDtTYSpMg7Y1ZNhs=
github.com/mholt/archives v0.0.0-20241123153623-b85dd98f4298/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -152,10 +152,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 h1:MYzLheyVx1tJVDqfu3YnN4jtnyALNzLvwl+f58TcvQY=
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/nwaples/rardecode/v2 v2.0.0 h1:C+58s2TPUdvgVFioQLpUNmIjTchtegHGS2xTNiKBYXA=
github.com/nwaples/rardecode/v2 v2.0.0/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=

View File

@@ -15,8 +15,38 @@ import (
utls "github.com/refraction-networking/utls"
)
type LogType string
const (
LogType_DNSLookup LogType = "DNSLookup"
LogType_TCPConnect LogType = "TCPConnect"
LogType_TLSHandshake LogType = "TLSHandshake"
LogType_ProxyDNSLookup LogType = "ProxyDNSLookup"
LogType_ProxyTCPConnect LogType = "ProxyTCPConnect"
LogType_ProxyTLSHandshake LogType = "ProxyTLSHandshake"
LogType_ProxyConnectRemote LogType = "ProxyConnectRemote"
LogType_ResponseHeader LogType = "ResponseHeader"
LogType_ResponseBody LogType = "ResponseBody"
)
type Log struct {
Id string `json:"id"`
Type LogType `json:"type"`
Time time.Time
Msg any `json:"msg"`
}
// Connection Management Options
type ClientOption struct {
Logger func(Log) //debuggable
H3 bool //开启http3
OrderHeaders []string //order headers
Ja3Spec ja3.Ja3Spec //custom ja3Spec,use ja3.CreateSpecWithStr or ja3.CreateSpecWithId create
@@ -57,6 +87,7 @@ type ClientOption struct {
// Options for sending requests
type RequestOption struct {
Logger func(Log) //debuggable
H3 bool //开启http3
OrderHeaders []string //order headers
Ja3Spec ja3.Ja3Spec //custom ja3Spec,use ja3.CreateSpecWithStr or ja3.CreateSpecWithId create
@@ -255,6 +286,30 @@ func (obj *Client) newRequestOption(option RequestOption) RequestOption {
if !option.H3 {
option.H3 = obj.option.H3
}
if option.Logger == nil {
option.Logger = obj.option.Logger
}
if option.Headers == nil {
option.Headers = obj.option.Headers
}
if option.Timeout == 0 {
option.Timeout = obj.option.Timeout
}
if option.ResponseHeaderTimeout == 0 {
option.ResponseHeaderTimeout = obj.option.ResponseHeaderTimeout
}
if option.TlsHandshakeTimeout == 0 {
option.TlsHandshakeTimeout = obj.option.TlsHandshakeTimeout
}
if option.DialTimeout == 0 {
option.DialTimeout = obj.option.DialTimeout
}
if option.KeepAlive == 0 {
option.KeepAlive = obj.option.KeepAlive
}
if option.LocalAddr == nil {
option.LocalAddr = obj.option.LocalAddr
}
if option.TlsConfig == nil {
option.TlsConfig = obj.option.TlsConfig
}

View File

@@ -54,11 +54,15 @@ type reqCtxData struct {
addrType gtls.AddrType //first ip type
dns *net.UDPAddr
isNewConn bool
logger func(Log)
requestId string
}
func NewReqCtxData(ctx context.Context, option *RequestOption) (*reqCtxData, error) {
//init ctxData
ctxData := new(reqCtxData)
ctxData.requestId = tools.NaoId()
ctxData.logger = option.Logger
ctxData.h3 = option.H3
ctxData.tlsConfig = option.TlsConfig
ctxData.utlsConfig = option.UtlsConfig
@@ -261,11 +265,7 @@ func (obj *Client) request(ctx context.Context, option *RequestOption) (response
return
}
}
response.bar = option.Bar
response.disUnzip = option.DisUnZip
response.disDecode = option.DisDecode
response.stream = option.Stream
response.requestOption = option
//init headers and orderheaders,befor init ctxData
headers, orderHeaders, err := option.initHeaders()
if err != nil {
@@ -284,7 +284,7 @@ func (obj *Client) request(ctx context.Context, option *RequestOption) (response
}
}
//init ctxData
ctxData, err := NewReqCtxData(ctx, option)
response.reqCtxData, err = NewReqCtxData(ctx, option)
if err != nil {
return response, tools.WrapError(err, " reqCtxData init error")
}
@@ -293,15 +293,15 @@ func (obj *Client) request(ctx context.Context, option *RequestOption) (response
}
//设置 h1 请求头顺序
if orderHeaders != nil {
ctxData.orderHeaders = orderHeaders
response.reqCtxData.orderHeaders = orderHeaders
} else {
ctxData.orderHeaders = ja3.DefaultOrderHeaders()
response.reqCtxData.orderHeaders = ja3.DefaultOrderHeaders()
}
//init ctx,cnl
if option.Timeout > 0 { //超时
response.ctx, response.cnl = context.WithTimeout(CreateReqCtx(ctx, ctxData), option.Timeout)
response.ctx, response.cnl = context.WithTimeout(CreateReqCtx(ctx, response.reqCtxData), option.Timeout)
} else {
response.ctx, response.cnl = context.WithCancel(CreateReqCtx(ctx, ctxData))
response.ctx, response.cnl = context.WithCancel(CreateReqCtx(ctx, response.reqCtxData))
}
//init url
href, err := option.initParams()
@@ -338,7 +338,7 @@ func (obj *Client) request(ctx context.Context, option *RequestOption) (response
}
//init ws
if ctxData.isWs {
if response.reqCtxData.isWs {
websocket.SetClientHeadersWithOption(reqs.Header, option.WsOption)
}
@@ -369,7 +369,6 @@ func (obj *Client) request(ctx context.Context, option *RequestOption) (response
}
//send req
response.response, err = obj.do(reqs, option)
response.isNewConn = ctxData.isNewConn
if err != nil {
err = tools.WrapError(err, "client do error")
return
@@ -381,14 +380,14 @@ func (obj *Client) request(ctx context.Context, option *RequestOption) (response
if response.Body() != nil {
response.rawConn = response.Body().(*readWriteCloser)
}
if !response.disUnzip {
response.disUnzip = response.response.Uncompressed
if !response.requestOption.DisUnZip {
response.requestOption.DisUnZip = response.response.Uncompressed
}
if response.response.StatusCode == 101 {
response.webSocket = websocket.NewClientConn(response.rawConn.Conn(), websocket.GetResponseHeaderOption(response.response.Header))
} else if strings.Contains(response.response.Header.Get("Content-Type"), "text/event-stream") {
response.sse = newSse(response.Body())
} else if !response.disUnzip {
} else if !response.requestOption.DisUnZip {
var unCompressionBody io.ReadCloser
unCompressionBody, err = tools.CompressionDecode(response.Body(), response.ContentEncoding())
if err != nil {

View File

@@ -9,6 +9,7 @@ import (
"net/url"
"strconv"
"strings"
"time"
"net/http"
@@ -21,21 +22,24 @@ import (
)
type Response struct {
rawConn *readWriteCloser
response *http.Response
webSocket *websocket.Conn
sse *Sse
ctx context.Context
cnl context.CancelFunc
content []byte
encoding string
stream bool
disDecode bool
disUnzip bool
filePath string
bar bool
isNewConn bool
readBody bool
rawConn *readWriteCloser
response *http.Response
webSocket *websocket.Conn
sse *Sse
ctx context.Context
cnl context.CancelFunc
reqCtxData *reqCtxData
requestOption *RequestOption
content []byte
encoding string
// stream bool
// disDecode bool
// disUnzip bool
filePath string
// bar bool
// isNewConn bool
readBody bool
}
type Sse struct {
@@ -273,7 +277,7 @@ func (obj *Response) Body() io.ReadCloser {
// return true if response is stream
func (obj *Response) IsStream() bool {
return obj.stream
return obj.requestOption.Stream
}
// return true if response is other stream
@@ -294,7 +298,7 @@ func (obj *Response) ReadBody() (err error) {
}
obj.readBody = true
bBody := bytes.NewBuffer(nil)
if obj.bar && obj.ContentLength() > 0 {
if obj.requestOption.Bar && obj.ContentLength() > 0 {
_, err = io.Copy(&barBody{
bar: bar.NewClient(obj.response.ContentLength),
body: bBody,
@@ -302,11 +306,19 @@ func (obj *Response) ReadBody() (err error) {
} else {
_, err = io.Copy(bBody, obj.Body())
}
if obj.reqCtxData.logger != nil {
obj.reqCtxData.logger(Log{
Id: obj.reqCtxData.requestId,
Time: time.Now(),
Type: LogType_ResponseBody,
Msg: "response body",
})
}
if err != nil {
obj.ForceCloseConn()
return errors.New("response read content error: " + err.Error())
}
if !obj.disDecode && obj.defaultDecode() {
if !obj.requestOption.DisDecode && obj.defaultDecode() {
obj.content, obj.encoding, _ = tools.Charset(bBody.Bytes(), obj.ContentType())
} else {
obj.content = bBody.Bytes()
@@ -317,7 +329,7 @@ func (obj *Response) ReadBody() (err error) {
// conn is new conn
func (obj *Response) IsNewConn() bool {
return obj.isNewConn
return obj.reqCtxData.isNewConn
}
// conn proxy

View File

@@ -15,14 +15,15 @@ import (
"net/http"
"github.com/gospider007/gtls"
"github.com/gospider007/http2"
"github.com/gospider007/http3"
"github.com/gospider007/net/http2"
"github.com/gospider007/tools"
"github.com/quic-go/quic-go"
utls "github.com/refraction-networking/utls"
)
type reqTask struct {
ctxData *reqCtxData
ctx context.Context
cnl context.CancelFunc
req *http.Request
@@ -106,6 +107,7 @@ func (obj *roundTripper) newConnPool(conn *connecotr, key string) *connPool {
pool.forceCtx, pool.forceCnl = context.WithCancelCause(obj.ctx)
pool.safeCtx, pool.safeCnl = context.WithCancelCause(pool.forceCtx)
pool.tasks = make(chan *reqTask)
pool.connPools = obj.connPools
pool.total.Add(1)
go pool.rwMain(conn)
@@ -172,7 +174,8 @@ func (obj *roundTripper) dial(ctxData *reqCtxData, req *http.Request) (conn *con
}
}
}
netConn, err := obj.dialer.DialContextWithProxy(req.Context(), ctxData, "tcp", req.URL.Scheme, getAddr(req.URL), getHost(req), proxy, obj.tlsConfigClone(ctxData))
host := getHost(req)
netConn, err := obj.dialer.DialContextWithProxy(req.Context(), ctxData, "tcp", req.URL.Scheme, getAddr(req.URL), host, proxy, obj.tlsConfigClone(ctxData))
if err != nil {
return conn, err
}
@@ -180,24 +183,29 @@ func (obj *roundTripper) dial(ctxData *reqCtxData, req *http.Request) (conn *con
if req.URL.Scheme == "https" {
ctx, cnl := context.WithTimeout(req.Context(), ctxData.tlsHandshakeTimeout)
defer cnl()
disHttp2 := ctxData.isWs || ctxData.forceHttp1
if ctxData.ja3Spec.IsSet() {
tlsConfig := obj.utlsConfigClone(ctxData)
if ctxData.forceHttp1 {
tlsConfig.NextProtos = []string{"http/1.1"}
}
tlsConn, err := obj.dialer.addJa3Tls(ctx, netConn, getHost(req), ctxData.isWs || ctxData.forceHttp1, ctxData.ja3Spec, tlsConfig)
if err != nil {
if tlsConn, err := obj.dialer.addJa3Tls(ctx, netConn, host, disHttp2, ctxData.ja3Spec, obj.utlsConfigClone(ctxData)); err != nil {
return conn, tools.WrapError(err, "add ja3 tls error")
} else {
h2 = tlsConn.ConnectionState().NegotiatedProtocol == "h2"
netConn = tlsConn
}
h2 = tlsConn.ConnectionState().NegotiatedProtocol == "h2"
netConn = tlsConn
} else {
tlsConn, err := obj.dialer.addTls(ctx, netConn, getHost(req), ctxData.isWs || ctxData.forceHttp1, obj.tlsConfigClone(ctxData))
if err != nil {
if tlsConn, err := obj.dialer.addTls(ctx, netConn, host, disHttp2, obj.tlsConfigClone(ctxData)); err != nil {
return conn, tools.WrapError(err, "add tls error")
} else {
h2 = tlsConn.ConnectionState().NegotiatedProtocol == "h2"
netConn = tlsConn
}
h2 = tlsConn.ConnectionState().NegotiatedProtocol == "h2"
netConn = tlsConn
}
if ctxData.logger != nil {
ctxData.logger(Log{
Id: ctxData.requestId,
Time: time.Now(),
Type: LogType_TLSHandshake,
Msg: host,
})
}
}
conne := obj.newConnecotr(netConn)
@@ -206,7 +214,7 @@ func (obj *roundTripper) dial(ctxData *reqCtxData, req *http.Request) (conn *con
}
if h2 {
if conne.h2RawConn, err = http2.NewClientConn(func() {
conne.safeCnl(errors.New("http2 client close"))
conne.forceCnl(errors.New("http2 client close"))
}, netConn, ctxData.h2Ja3Spec); err != nil {
return conne, err
}
@@ -283,6 +291,7 @@ func (obj *roundTripper) newReqTask(req *http.Request, ctxData *reqCtxData) *req
}
task := new(reqTask)
task.req = req
task.ctxData = ctxData
task.emptyPool = make(chan struct{})
task.orderHeaders = ctxData.orderHeaders
if ctxData.h2Ja3Spec.OrderHeaders != nil {

8
rw.go
View File

@@ -21,8 +21,10 @@ func (obj *readWriteCloser) InPool() bool {
return obj.conn.inPool
}
func (obj *readWriteCloser) Proxy() *url.URL {
p := *obj.conn.proxy
return &p
if obj.conn.proxy == nil {
return nil
}
return cloneUrl(obj.conn.proxy)
}
var ErrgospiderBodyClose = errors.New("gospider body close error")
@@ -31,8 +33,8 @@ func (obj *readWriteCloser) Close() (err error) {
if !obj.InPool() {
obj.ForceCloseConn()
} else {
err = obj.body.Close() //reuse conn
obj.conn.bodyCnl(ErrgospiderBodyClose)
err = obj.body.Close() //reuse conn
}
return
}

View File

@@ -44,6 +44,7 @@ func TestJa3Psk(t *testing.T) {
if err != nil {
t.Fatal(err)
}
log.Print(ja3Spec.String())
session, _ := requests.NewClient(nil)
for i := 0; i < 2; i++ {
resp, err := session.Get(nil, "https://tools.scrapfly.io/api/fp/anything", requests.RequestOption{

View File

@@ -0,0 +1,25 @@
package main
import (
"log"
"testing"
"github.com/gospider007/requests"
)
func TestLogger(t *testing.T) {
for i := 0; i < 2; i++ {
response, err := requests.Get(nil, "https://www.httpbin.org", requests.RequestOption{
Ja3: true,
Logger: func(l requests.Log) {
log.Print(l)
},
})
if err != nil {
log.Panic(err)
}
log.Print(len(response.Text()))
log.Print(response.Status())
log.Print(response.Proto())
}
}