optimize code

This commit is contained in:
bxd
2023-11-28 15:00:02 +08:00
parent ca7549534f
commit f8cebb55ea
12 changed files with 197 additions and 284 deletions

26
body.go
View File

@@ -43,9 +43,6 @@ func (obj *orderMap) Del(key string) {
obj.order = tools.DelSliceVals(obj.order, key)
}
func (obj *orderMap) parseHeaders() (map[string][]string, []string) {
if len(obj.order) == 0 || len(obj.data) == 0 {
return nil, nil
}
head := make(http.Header)
for kk, vv := range obj.data {
if vvs, ok := vv.([]string); ok {
@@ -186,10 +183,7 @@ func paramsWrite(buf *bytes.Buffer, key string, val any) {
buf.WriteByte('=')
buf.WriteString(url.QueryEscape(fmt.Sprint(val)))
}
func (obj *orderMap) parseParams() string {
if len(obj.order) == 0 || len(obj.data) == 0 {
return ""
}
func (obj *orderMap) parseParams() *bytes.Buffer {
buf := bytes.NewBuffer(nil)
for _, k := range obj.order {
if vals, ok := obj.data[k].([]any); ok {
@@ -200,25 +194,15 @@ func (obj *orderMap) parseParams() string {
paramsWrite(buf, k, obj.data[k])
}
}
return buf.String()
return buf
}
func (obj *orderMap) parseData() *bytes.Reader {
if len(obj.order) == 0 || len(obj.data) == 0 {
val := obj.parseParams().Bytes()
if val == nil {
return nil
}
tempVal := url.Values{}
for kk, vv := range obj.data {
if vvs, ok := vv.([]any); ok {
for _, vv := range vvs {
tempVal.Add(kk, fmt.Sprint(vv))
}
} else {
tempVal.Add(kk, fmt.Sprint(vv))
}
}
return bytes.NewReader(tools.StringToBytes(tempVal.Encode()))
return bytes.NewReader(val)
}
func (obj *orderMap) MarshalJSON() ([]byte, error) {
buf := bytes.NewBuffer(nil)
err := buf.WriteByte('{')

121
client.go
View File

@@ -3,136 +3,65 @@ package requests
import (
"context"
"net/url"
"time"
"net/http"
"github.com/gospider007/gtls"
"github.com/gospider007/ja3"
)
// Connection Management
type Client struct {
forceHttp1 bool
orderHeaders []string
jar *Jar
maxRedirect int
disDecode bool
disUnZip bool
disAlive bool
maxRetries int
requestCallBack func(context.Context, *http.Request, *http.Response) error
optionCallBack func(context.Context, *Client, *RequestOption) error
resultCallBack func(ctx context.Context, client *Client, response *Response) error
errCallBack func(context.Context, *Client, *Response, error) error
timeout time.Duration
responseHeaderTimeout time.Duration
tlsHandshakeTimeout time.Duration
headers any
bar bool
disCookie bool
option ClientOption
client *http.Client
proxy *url.URL
ctx context.Context
cnl context.CancelFunc
transport *RoundTripper
ja3Spec ja3.Ja3Spec
h2Ja3Spec ja3.H2Ja3Spec
addrType gtls.AddrType
transport *roundTripper
}
var defaultClient, _ = NewClient(nil)
func checkRedirect(req *http.Request, via []*http.Request) error {
ctxData := GetReqCtxData(req.Context())
if ctxData.maxRedirect == 0 || ctxData.maxRedirect >= len(via) {
return nil
}
return http.ErrUseLastResponse
}
// New Connection Management
func NewClient(preCtx context.Context, options ...ClientOption) (*Client, error) {
if preCtx == nil {
preCtx = context.TODO()
}
ctx, cnl := context.WithCancel(preCtx)
var option ClientOption
if len(options) > 0 {
option = options[0]
}
transport := newRoundTripper(ctx, roundTripperOption{
DialTimeout: option.DialTimeout,
KeepAlive: option.KeepAlive,
LocalAddr: option.LocalAddr,
AddrType: option.AddrType,
GetAddrType: option.GetAddrType,
Dns: option.Dns,
GetProxy: option.GetProxy,
})
client := &http.Client{
Transport: transport,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
ctxData := GetReqCtxData(req.Context())
if ctxData.maxRedirect == 0 || ctxData.maxRedirect >= len(via) {
return nil
}
return http.ErrUseLastResponse
},
}
result := &Client{
ctx: ctx,
cnl: cnl,
client: client,
transport: transport,
forceHttp1: option.ForceHttp1,
requestCallBack: option.RequestCallBack,
orderHeaders: option.OrderHeaders,
disCookie: option.DisCookie,
maxRedirect: option.MaxRedirect,
disDecode: option.DisDecode,
disUnZip: option.DisUnZip,
disAlive: option.DisAlive,
maxRetries: option.MaxRetries,
optionCallBack: option.OptionCallBack,
resultCallBack: option.ResultCallBack,
errCallBack: option.ErrCallBack,
timeout: option.Timeout,
responseHeaderTimeout: option.ResponseHeaderTimeout,
tlsHandshakeTimeout: option.TlsHandshakeTimeout,
headers: option.Headers,
bar: option.Bar,
addrType: option.AddrType,
}
result := new(Client)
result.ctx, result.cnl = context.WithCancel(preCtx)
result.transport = newRoundTripper(result.ctx, option)
result.client = &http.Client{Transport: result.transport, CheckRedirect: checkRedirect}
result.option = option
//cookiesjar
if !option.DisCookie {
if option.Jar != nil {
result.jar = option.Jar
} else {
result.jar = NewJar()
if !result.option.DisCookie {
if result.option.Jar == nil {
result.option.Jar = NewJar()
}
result.client.Jar = result.jar.jar
result.client.Jar = result.option.Jar.jar
}
var err error
if option.Proxy != "" {
result.proxy, err = gtls.VerifyProxy(option.Proxy)
if result.option.Proxy != "" {
_, err = gtls.VerifyProxy(result.option.Proxy)
}
if option.Ja3Spec.IsSet() {
result.ja3Spec = option.Ja3Spec
} else if option.Ja3 {
result.ja3Spec = ja3.DefaultJa3Spec()
}
result.h2Ja3Spec = option.H2Ja3Spec
return result, err
}
// Modifying the client's proxy
func (obj *Client) SetProxy(proxyUrl string) (err error) {
obj.proxy, err = gtls.VerifyProxy(proxyUrl)
_, err = gtls.VerifyProxy(proxyUrl)
if err == nil {
obj.option.Proxy = proxyUrl
}
return
}

48
conn.go
View File

@@ -17,33 +17,30 @@ import (
type connecotr struct {
key connKey
err error
deleteCtx context.Context //force close
deleteCnl context.CancelFunc
deleteCnl context.CancelCauseFunc
closeCtx context.Context //safe close
closeCnl context.CancelFunc
closeCnl context.CancelCauseFunc
bodyCtx context.Context //body close
bodyCnl context.CancelFunc
bodyCnl context.CancelCauseFunc
rawConn net.Conn
h2 bool
r *bufio.Reader
w *bufio.Writer
h2RawConn *http2.ClientConn
rc chan []byte
rn chan int
pr *pipCon
isPool bool
}
func (obj *connecotr) withCancel(deleteCtx context.Context, closeCtx context.Context) {
obj.deleteCtx, obj.deleteCnl = context.WithCancel(deleteCtx)
obj.closeCtx, obj.closeCnl = context.WithCancel(closeCtx)
obj.deleteCtx, obj.deleteCnl = context.WithCancelCause(deleteCtx)
obj.closeCtx, obj.closeCnl = context.WithCancelCause(closeCtx)
}
func (obj *connecotr) Close() error {
obj.deleteCnl()
obj.deleteCnl(errors.New("connecotr close"))
if obj.h2RawConn != nil {
obj.h2RawConn.Close()
}
@@ -56,11 +53,13 @@ func (obj *connecotr) read() (err error) {
var pw *pipCon
obj.pr, pw = pipe(obj.deleteCtx)
defer func() {
obj.pr.cnl(err)
pw.cnl(err)
obj.pr.cnl(err)
obj.Close()
}()
_, err = io.Copy(pw, obj.rawConn)
if _, err = io.Copy(pw, obj.rawConn); err == nil {
err = io.EOF
}
return
}
func (obj *connecotr) Read(b []byte) (i int, err error) {
@@ -72,21 +71,6 @@ func (obj *connecotr) Read(b []byte) (i int, err error) {
func (obj *connecotr) Write(b []byte) (int, error) {
return obj.rawConn.Write(b)
}
func (obj *connecotr) LocalAddr() net.Addr {
return obj.rawConn.LocalAddr()
}
func (obj *connecotr) RemoteAddr() net.Addr {
return obj.rawConn.RemoteAddr()
}
func (obj *connecotr) SetDeadline(t time.Time) error {
return obj.rawConn.SetDeadline(t)
}
func (obj *connecotr) SetReadDeadline(t time.Time) error {
return obj.rawConn.SetReadDeadline(t)
}
func (obj *connecotr) SetWriteDeadline(t time.Time) error {
return obj.rawConn.SetWriteDeadline(t)
}
func (obj *connecotr) h2Closed() bool {
state := obj.h2RawConn.State()
@@ -94,7 +78,7 @@ func (obj *connecotr) h2Closed() bool {
}
func (obj *connecotr) wrapBody(task *reqTask) {
body := new(readWriteCloser)
obj.bodyCtx, obj.bodyCnl = context.WithCancel(obj.deleteCtx)
obj.bodyCtx, obj.bodyCnl = context.WithCancelCause(obj.deleteCtx)
body.body = task.res.Body
body.conn = obj
task.res.Body = body
@@ -191,13 +175,13 @@ func (obj *connecotr) taskMain(task *reqTask, afterTime *time.Timer) (*http.Resp
type connPool struct {
deleteCtx context.Context
deleteCnl context.CancelFunc
deleteCnl context.CancelCauseFunc
closeCtx context.Context
closeCnl context.CancelFunc
closeCnl context.CancelCauseFunc
key connKey
total atomic.Int64
tasks chan *reqTask
rt *RoundTripper
rt *roundTripper
lock sync.Mutex
}
@@ -252,10 +236,10 @@ func (obj *connPool) rwMain(conn *connecotr) {
}
}
func (obj *connPool) forceClose() {
obj.deleteCnl()
obj.deleteCnl(errors.New("connPool forceClose"))
obj.close()
}
func (obj *connPool) close() {
obj.closeCnl()
obj.closeCnl(errors.New("connPool close"))
obj.rt.delConnPool(obj.key)
}

12
go.mod
View File

@@ -12,10 +12,10 @@ require (
github.com/gospider007/net v0.0.0-20231028084010-313c148cf0a1
github.com/gospider007/re v0.0.0-20231024115818-adfd03636256
github.com/gospider007/tools v0.0.0-20231122021245-1cafbac3ef46
github.com/gospider007/websocket v0.0.0-20231124124758-1491f1b57db1
github.com/gospider007/websocket v0.0.0-20231128065110-6296f87425c4
github.com/refraction-networking/utls v1.5.4
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/net v0.18.0
golang.org/x/exp v0.0.0-20231127185646-65229373498e
golang.org/x/net v0.19.0
)
require (
@@ -42,11 +42,11 @@ require (
go.mongodb.org/mongo-driver v1.13.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/image v0.14.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.15.0 // indirect
golang.org/x/tools v0.16.0 // indirect
nhooyr.io/websocket v1.8.10 // indirect
)

36
go.sum
View File

@@ -25,16 +25,10 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ=
github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/gospider007/bar v0.0.0-20231024075629-3f50832a4cbf h1:ow0/zY4+dwhS6xMDEc/m0woLJ0XDO5Q5cttcprJ4hRs=
github.com/gospider007/bar v0.0.0-20231024075629-3f50832a4cbf/go.mod h1:KC7Tt189vS0u4KB6ThC5ShzTRvDsU54KM6wvmJBvqfg=
github.com/gospider007/bar v0.0.0-20231121084140-33c7b6797626 h1:zDK4PcXQmAX37JdGUp45gFDMolnBGzWVXgemm5ekG1Y=
github.com/gospider007/bar v0.0.0-20231121084140-33c7b6797626/go.mod h1:aYPgmG9340i9x9VQZhf34/XtIj7PHDTq0wSO+7zU/8s=
github.com/gospider007/blog v0.0.0-20231024075658-5da1a801a2c8 h1:rcbzO343eHoZ1yJygef05WYxCjPgqo03OMwG4Ql6ckE=
github.com/gospider007/blog v0.0.0-20231024075658-5da1a801a2c8/go.mod h1:CCJ+hvQ0kxL+qB/Wfr1xt7xspsG4XiczhnAPVxG2m3M=
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-20231119141556-162d71807dcc h1:cYnsCLDMRfuWV3AoK4d2R1CTXdmQpOYm42ms8PhcBFI=
github.com/gospider007/bs4 v0.0.0-20231119141556-162d71807dcc/go.mod h1:oFH47TbYECc3Mbd+6W3UmJWZoOvj/8ZwXimE8iIedRI=
github.com/gospider007/bs4 v0.0.0-20231123090151-001db0b91941 h1:Aik3aBqnpujF5LA+JyIm3LNxivobnqAOPr+VVlTbqds=
github.com/gospider007/bs4 v0.0.0-20231123090151-001db0b91941/go.mod h1:bbSbFlcbgzxvmjVGp8rZq/BX975acPgGbuICISi1usI=
github.com/gospider007/gson v0.0.0-20231119141525-66095080057d h1:K9IbMHY2dZfi1ddJWL7a+KkZe6apIYaXN1cD8lDRH5I=
@@ -49,16 +43,10 @@ github.com/gospider007/net v0.0.0-20231028084010-313c148cf0a1 h1:tYOQEvELrV+USjK
github.com/gospider007/net v0.0.0-20231028084010-313c148cf0a1/go.mod h1:3ggAwYdh0NB0OvtiX0l5AfHdBjgsIt9MGsXCQ3iCzQc=
github.com/gospider007/re v0.0.0-20231024115818-adfd03636256 h1:Z6kHRANoWB+/4rDzq51vBts0rIXilDrF8pdRNmbMJi4=
github.com/gospider007/re v0.0.0-20231024115818-adfd03636256/go.mod h1:X58uk0/F3mVskuQOZng0ZKJiAt3ETn0wxuLN//rVZrE=
github.com/gospider007/tools v0.0.0-20231120122411-d631cc2fc371 h1:wF5pm7gilDuDKg4rmhmz4S+/Y3EEVgvZdwgywnhbyKA=
github.com/gospider007/tools v0.0.0-20231120122411-d631cc2fc371/go.mod h1:myK4kDqDx4TlplDVnfYMI7Xi5VUbFZ3fxwAh2Cwm7ks=
github.com/gospider007/tools v0.0.0-20231122021245-1cafbac3ef46 h1:vskdS8WLAveNSDHsAAdwiD+LBLMHq3AND1nGnVydwfM=
github.com/gospider007/tools v0.0.0-20231122021245-1cafbac3ef46/go.mod h1:myK4kDqDx4TlplDVnfYMI7Xi5VUbFZ3fxwAh2Cwm7ks=
github.com/gospider007/websocket v0.0.0-20231114095858-b8bc9b2033d3 h1:HpiNfOZ9Tjo4hhP1+jmlgqykngykles3ypXa2BUuxRc=
github.com/gospider007/websocket v0.0.0-20231114095858-b8bc9b2033d3/go.mod h1:jINjCM6qIRiqn2Di1bat4Ie5gY66ae7LYT8YK4fAejY=
github.com/gospider007/websocket v0.0.0-20231124122326-78d52f163d6c h1:AVquutD7Mbb9gcq7/ciRC/Vt2StSiBuUagJviG8+vkg=
github.com/gospider007/websocket v0.0.0-20231124122326-78d52f163d6c/go.mod h1:TquIvV/QrLmSufnwdc+54DAbUd39HsNgpFcoQYthVU8=
github.com/gospider007/websocket v0.0.0-20231124124758-1491f1b57db1 h1:/7C93+nXTXAsg87hUH5n4trT99uaTUPglQ8wMxKA5gQ=
github.com/gospider007/websocket v0.0.0-20231124124758-1491f1b57db1/go.mod h1:TquIvV/QrLmSufnwdc+54DAbUd39HsNgpFcoQYthVU8=
github.com/gospider007/websocket v0.0.0-20231128065110-6296f87425c4 h1:h+74nkhhTDN2tiaDjHwR4CjqBTHgh+t1pqE2IAWHN3k=
github.com/gospider007/websocket v0.0.0-20231128065110-6296f87425c4/go.mod h1:OncvZIlq9TzwD/tQS/BYY/RKBqbW4+gGY3Ere1K7s24=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
@@ -122,10 +110,10 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No=
golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -140,8 +128,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -155,8 +143,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -174,8 +162,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

8
jar.go
View File

@@ -24,19 +24,19 @@ func NewJar() *Jar {
// get cookies
func (obj *Client) GetCookies(href string) (Cookies, error) {
return obj.jar.GetCookies(href)
return obj.option.Jar.GetCookies(href)
}
// set cookies
func (obj *Client) SetCookies(href string, cookies ...any) error {
return obj.jar.SetCookies(href, cookies...)
return obj.option.Jar.SetCookies(href, cookies...)
}
// clear cookies
func (obj *Client) ClearCookies() {
if obj.client.Jar != nil {
obj.jar.ClearCookies()
obj.client.Jar = obj.jar.jar
obj.option.Jar.ClearCookies()
obj.client.Jar = obj.option.Jar.jar
}
}

View File

@@ -158,92 +158,95 @@ func (obj *RequestOption) initParams() (string, error) {
if err != nil {
return obj.Url.String(), err
}
query := dataMap.parseParams()
query := dataMap.parseParams().String()
if query == "" {
return obj.Url.String(), nil
}
pu := cloneUrl(obj.Url)
puValues := pu.Query()
pu.RawQuery = puValues.Encode() + "&" + query
pquery := pu.Query().Encode()
if pquery == "" {
pu.RawQuery = query
} else {
pu.RawQuery = pquery + "&" + query
}
return pu.String(), nil
}
func (obj *Client) newRequestOption(option RequestOption) RequestOption {
if !option.DisProxy {
if option.Proxy == "" && obj.proxy != nil {
option.Proxy = obj.proxy.String()
if option.Proxy == "" && obj.option.Proxy != "" {
option.Proxy = obj.option.Proxy
}
} else {
option.Proxy = ""
}
if option.MaxRetries < 0 {
option.MaxRetries = 0
} else if option.MaxRetries == 0 {
option.MaxRetries = obj.maxRetries
option.MaxRetries = obj.option.MaxRetries
}
if option.Headers == nil {
option.Headers = obj.headers
option.Headers = obj.option.Headers
}
if !option.Bar {
option.Bar = obj.bar
option.Bar = obj.option.Bar
}
if option.MaxRedirect == 0 {
option.MaxRedirect = obj.maxRedirect
option.MaxRedirect = obj.option.MaxRedirect
}
if option.Timeout == 0 {
option.Timeout = obj.timeout
option.Timeout = obj.option.Timeout
}
if option.ResponseHeaderTimeout == 0 {
option.ResponseHeaderTimeout = obj.responseHeaderTimeout
option.ResponseHeaderTimeout = obj.option.ResponseHeaderTimeout
}
if option.AddrType == 0 {
option.AddrType = obj.addrType
option.AddrType = obj.option.AddrType
}
if option.TlsHandshakeTimeout == 0 {
option.TlsHandshakeTimeout = obj.tlsHandshakeTimeout
option.TlsHandshakeTimeout = obj.option.TlsHandshakeTimeout
}
if !option.DisCookie {
option.DisCookie = obj.disCookie
option.DisCookie = obj.option.DisCookie
}
if !option.DisDecode {
option.DisDecode = obj.disDecode
option.DisDecode = obj.option.DisDecode
}
if !option.DisUnZip {
option.DisUnZip = obj.disUnZip
option.DisUnZip = obj.option.DisUnZip
}
if !option.ForceHttp1 {
option.ForceHttp1 = obj.forceHttp1
option.ForceHttp1 = obj.option.ForceHttp1
}
if !option.DisAlive {
option.DisAlive = obj.disAlive
option.DisAlive = obj.option.DisAlive
}
if option.OrderHeaders == nil {
option.OrderHeaders = obj.orderHeaders
option.OrderHeaders = obj.option.OrderHeaders
}
if !option.Ja3Spec.IsSet() {
if obj.ja3Spec.IsSet() {
option.Ja3Spec = obj.ja3Spec
} else if option.Ja3 {
if obj.option.Ja3Spec.IsSet() {
option.Ja3Spec = obj.option.Ja3Spec
} else if option.Ja3 || obj.option.Ja3 {
option.Ja3Spec = ja3.DefaultJa3Spec()
}
}
if !option.H2Ja3Spec.IsSet() {
option.H2Ja3Spec = obj.h2Ja3Spec
option.H2Ja3Spec = obj.option.H2Ja3Spec
}
if option.OptionCallBack == nil {
option.OptionCallBack = obj.optionCallBack
option.OptionCallBack = obj.option.OptionCallBack
}
if option.ResultCallBack == nil {
option.ResultCallBack = obj.resultCallBack
option.ResultCallBack = obj.option.ResultCallBack
}
if option.ErrCallBack == nil {
option.ErrCallBack = obj.errCallBack
option.ErrCallBack = obj.option.ErrCallBack
}
if option.RequestCallBack == nil {
option.RequestCallBack = obj.requestCallBack
option.RequestCallBack = obj.option.RequestCallBack
}
return option
}

View File

@@ -432,7 +432,7 @@ func (obj *Client) request(ctx context.Context, option *RequestOption) (response
response.disUnzip = response.response.Uncompressed
}
if response.response.StatusCode == 101 {
response.webSocket, err = websocket.NewClientConn(response.response, response.ForceCloseConn)
response.webSocket, err = websocket.NewClientConn(response.rawConn.Conn(), response.response.Header, response.ForceCloseConn)
if ctxData.debug {
debugPrint(ctxData.requestId, "new websocket client, err: ", err)
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/gospider007/bar"
"github.com/gospider007/bs4"
"github.com/gospider007/gson"
"github.com/gospider007/re"
"github.com/gospider007/tools"
"github.com/gospider007/websocket"
)
@@ -61,21 +62,35 @@ func (obj *Sse) Recv() (Event, error) {
if err != nil || readStr == "\n" {
return event, err
}
if strings.HasPrefix(readStr, "data: ") {
event.Data += readStr[6 : len(readStr)-1]
} else if strings.HasPrefix(readStr, "event: ") {
event.Event = readStr[7 : len(readStr)-1]
} else if strings.HasPrefix(readStr, "id: ") {
event.Id = readStr[4 : len(readStr)-1]
} else if strings.HasPrefix(readStr, "retry: ") {
if event.Retry, err = strconv.Atoi(readStr[7 : len(readStr)-1]); err != nil {
reResult := re.Search(`data:\s?(.*)`, readStr)
if reResult != nil {
event.Data += reResult.Group(1)
continue
}
reResult = re.Search(`event:\s?(.*)`, readStr)
if reResult != nil {
event.Event = reResult.Group(1)
continue
}
reResult = re.Search(`id:\s?(.*)`, readStr)
if reResult != nil {
event.Id = reResult.Group(1)
continue
}
reResult = re.Search(`retry:\s?(.*)`, readStr)
if reResult != nil {
if event.Retry, err = strconv.Atoi(reResult.Group(1)); err != nil {
return event, err
}
} else if strings.HasPrefix(readStr, ": ") {
event.Comment = readStr[2 : len(readStr)-1]
} else {
return event, errors.New("content parse error:" + readStr)
continue
}
reResult = re.Search(`:\s?(.*)`, readStr)
if reResult != nil {
event.Comment = reResult.Group(1)
continue
}
return event, errors.New("content parse error:" + readStr)
}
}
@@ -341,22 +356,6 @@ func (obj *Response) InPool() bool {
return false
}
// conn ja3
func (obj *Response) Ja3() string {
if obj.rawConn != nil {
return obj.rawConn.Ja3()
}
return ""
}
// conn h2ja3
func (obj *Response) H2Ja3() string {
if obj.rawConn != nil {
return obj.rawConn.H2Ja3()
}
return ""
}
// safe close conn
func (obj *Response) CloseConn() {
if obj.rawConn != nil {

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"context"
"crypto/tls"
"errors"
"net"
"net/url"
"sync"
@@ -50,8 +51,6 @@ func (obj *reqTask) inPool() bool {
type connKey struct {
proxy string
addr string
ja3 string
h2Ja3 string
}
func getKey(ctxData *reqCtxData, req *http.Request) connKey {
@@ -60,14 +59,12 @@ func getKey(ctxData *reqCtxData, req *http.Request) connKey {
proxy = ctxData.proxy.String()
}
return connKey{
h2Ja3: ctxData.h2Ja3Spec.Fp(),
ja3: ctxData.ja3Spec.String(),
proxy: proxy,
addr: getAddr(req.URL),
}
}
type RoundTripper struct {
type roundTripper struct {
ctx context.Context
cnl context.CancelFunc
connPools sync.Map
@@ -87,7 +84,7 @@ type roundTripperOption struct {
GetProxy func(ctx context.Context, url *url.URL) (string, error)
}
func newRoundTripper(preCtx context.Context, option roundTripperOption) *RoundTripper {
func newRoundTripper(preCtx context.Context, option ClientOption) *roundTripper {
if preCtx == nil {
preCtx = context.TODO()
}
@@ -113,7 +110,7 @@ func newRoundTripper(preCtx context.Context, option roundTripperOption) *RoundTr
OmitEmptyPsk: true,
PreferSkipResumptionOnNilExtension: true,
}
return &RoundTripper{
return &roundTripper{
tlsConfig: tlsConfig,
utlsConfig: utlsConfig,
ctx: ctx,
@@ -122,28 +119,28 @@ func newRoundTripper(preCtx context.Context, option roundTripperOption) *RoundTr
proxy: option.GetProxy,
}
}
func (obj *RoundTripper) newConnPool(conn *connecotr, key connKey) *connPool {
func (obj *roundTripper) newConnPool(conn *connecotr, key connKey) *connPool {
pool := new(connPool)
pool.key = key
pool.deleteCtx, pool.deleteCnl = context.WithCancel(obj.ctx)
pool.closeCtx, pool.closeCnl = context.WithCancel(pool.deleteCtx)
pool.deleteCtx, pool.deleteCnl = context.WithCancelCause(obj.ctx)
pool.closeCtx, pool.closeCnl = context.WithCancelCause(pool.deleteCtx)
pool.tasks = make(chan *reqTask)
pool.rt = obj
pool.total.Add(1)
go pool.rwMain(conn)
return pool
}
func (obj *RoundTripper) getConnPool(key connKey) *connPool {
func (obj *roundTripper) getConnPool(key connKey) *connPool {
val, ok := obj.connPools.Load(key)
if !ok {
return nil
}
return val.(*connPool)
}
func (obj *RoundTripper) delConnPool(key connKey) {
func (obj *roundTripper) delConnPool(key connKey) {
obj.connPools.Delete(key)
}
func (obj *RoundTripper) putConnPool(key connKey, conn *connecotr) {
func (obj *roundTripper) putConnPool(key connKey, conn *connecotr) {
conn.isPool = true
if !conn.h2 {
go conn.read()
@@ -162,13 +159,13 @@ func (obj *RoundTripper) putConnPool(key connKey, conn *connecotr) {
obj.connPools.Store(key, obj.newConnPool(conn, key))
}
}
func (obj *RoundTripper) tlsConfigClone() *tls.Config {
func (obj *roundTripper) tlsConfigClone() *tls.Config {
return obj.tlsConfig.Clone()
}
func (obj *RoundTripper) utlsConfigClone() *utls.Config {
func (obj *roundTripper) utlsConfigClone() *utls.Config {
return obj.utlsConfig.Clone()
}
func (obj *RoundTripper) dial(ctxData *reqCtxData, key *connKey, req *http.Request) (conn *connecotr, err error) {
func (obj *roundTripper) dial(ctxData *reqCtxData, key *connKey, req *http.Request) (conn *connecotr, err error) {
proxy := ctxData.proxy
if !ctxData.disProxy && proxy == nil {
if proxy, err = obj.getProxy(req.Context(), req.URL); err != nil {
@@ -189,8 +186,6 @@ func (obj *RoundTripper) dial(ctxData *reqCtxData, key *connKey, req *http.Reque
return conn, err
}
conne := new(connecotr)
conne.rn = make(chan int)
conne.rc = make(chan []byte)
conne.withCancel(obj.ctx, obj.ctx)
if req.URL.Scheme == "https" {
ctx, cnl := context.WithTimeout(req.Context(), ctxData.tlsHandshakeTimeout)
@@ -218,7 +213,7 @@ func (obj *RoundTripper) dial(ctxData *reqCtxData, key *connKey, req *http.Reque
conne.rawConn = netConn
if conne.h2 {
if conne.h2RawConn, err = http2.NewClientConn(func() {
conne.closeCnl()
conne.closeCnl(errors.New("http2 client close"))
}, netConn, ctxData.h2Ja3Spec); err != nil {
return conne, err
}
@@ -228,10 +223,10 @@ func (obj *RoundTripper) dial(ctxData *reqCtxData, key *connKey, req *http.Reque
}
return conne, err
}
func (obj *RoundTripper) setGetProxy(getProxy func(ctx context.Context, url *url.URL) (string, error)) {
func (obj *roundTripper) setGetProxy(getProxy func(ctx context.Context, url *url.URL) (string, error)) {
obj.proxy = getProxy
}
func (obj *RoundTripper) getProxy(ctx context.Context, proxyUrl *url.URL) (*url.URL, error) {
func (obj *roundTripper) getProxy(ctx context.Context, proxyUrl *url.URL) (*url.URL, error) {
if obj.proxy == nil {
return nil, nil
}
@@ -242,7 +237,7 @@ func (obj *RoundTripper) getProxy(ctx context.Context, proxyUrl *url.URL) (*url.
return gtls.VerifyProxy(proxy)
}
func (obj *RoundTripper) poolRoundTrip(task *reqTask, key connKey) (bool, error) {
func (obj *roundTripper) poolRoundTrip(task *reqTask, key connKey) (bool, error) {
if task.debug {
debugPrint(task.requestId, "poolRoundTrip start")
}
@@ -290,7 +285,7 @@ func (obj *RoundTripper) poolRoundTrip(task *reqTask, key connKey) (bool, error)
return false, nil
}
func (obj *RoundTripper) closeConns() {
func (obj *roundTripper) closeConns() {
obj.connPools.Range(func(key, value any) bool {
pool := value.(*connPool)
pool.close()
@@ -299,7 +294,7 @@ func (obj *RoundTripper) closeConns() {
})
}
func (obj *RoundTripper) forceCloseConns() {
func (obj *roundTripper) forceCloseConns() {
obj.connPools.Range(func(key, value any) bool {
pool := value.(*connPool)
pool.forceClose()
@@ -307,7 +302,7 @@ func (obj *RoundTripper) forceCloseConns() {
return true
})
}
func (obj *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
func (obj *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
ctxData := GetReqCtxData(req.Context())
if ctxData.requestCallBack != nil {
if err := ctxData.requestCallBack(req.Context(), req, nil); err != nil {

14
rw.go
View File

@@ -1,8 +1,8 @@
package requests
import (
"errors"
"io"
"net"
)
type readWriteCloser struct {
@@ -10,7 +10,7 @@ type readWriteCloser struct {
conn *connecotr
}
func (obj *readWriteCloser) Conn() net.Conn {
func (obj *readWriteCloser) Conn() *connecotr {
return obj.conn
}
func (obj *readWriteCloser) Read(p []byte) (n int, err error) {
@@ -21,7 +21,7 @@ func (obj *readWriteCloser) Close() (err error) {
if !obj.InPool() {
obj.ForceCloseConn()
} else {
obj.conn.bodyCnl()
obj.conn.bodyCnl(errors.New("readWriteCloser close"))
}
return
}
@@ -31,16 +31,10 @@ func (obj *readWriteCloser) InPool() bool {
func (obj *readWriteCloser) Proxy() string {
return obj.conn.key.proxy
}
func (obj *readWriteCloser) Ja3() string {
return obj.conn.key.ja3
}
func (obj *readWriteCloser) H2Ja3() string {
return obj.conn.key.h2Ja3
}
// safe close conn
func (obj *readWriteCloser) CloseConn() {
obj.conn.closeCnl()
obj.conn.closeCnl(errors.New("readWriterCloser close conn"))
}
// force close conn

View File

@@ -1,14 +1,52 @@
package main
import (
"io"
"log"
"net/http"
"testing"
"time"
"github.com/gospider007/gson"
"github.com/gospider007/requests"
)
func TestSse(t *testing.T) {
response, err := requests.Get(nil, "https://sse.dev/test") // Send WebSocket request
// Start the server
go func() {
err := http.ListenAndServe(":3333", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*")
// SSE event format
event := "message"
data := "testing"
// Start SSE loop
for i := 0; i < 3; i++ {
// Send SSE event
_, err := w.Write([]byte("event: " + event + "\n"))
if err != nil {
log.Println("Error writing SSE event:", err)
return
}
_, err = w.Write([]byte("data: " + data + "\n\n"))
if err != nil {
log.Println("Error writing SSE data:", err)
return
}
// Flush the response writer
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
// Delay before sending the next event
time.Sleep(1 * time.Second)
}
}))
if err != nil {
t.Error(err)
}
}()
response, err := requests.Get(nil, "http://127.0.0.1:3333/events") // Send WebSocket request
if err != nil {
t.Error(err)
}
@@ -17,16 +55,15 @@ func TestSse(t *testing.T) {
if sseCli == nil {
t.Error("not is sseCli")
}
for maxNum := 0; maxNum < 3; maxNum++ {
for {
data, err := sseCli.Recv()
if err != nil {
t.Error(err)
if err != io.EOF {
t.Error(err)
}
break
}
jsonData, err := gson.Decode(data.Data)
if err != nil {
t.Error(err)
}
if !jsonData.Get("testing").Bool() {
if data.Data != "testing" {
t.Error("testing")
}
}