mirror of
https://github.com/gospider007/requests.git
synced 2025-12-24 13:57:52 +08:00
sync
This commit is contained in:
2
conn.go
2
conn.go
@@ -116,7 +116,7 @@ func (obj *connecotr) taskMain(task *reqTask) {
|
||||
if task.reqCtx.response == nil {
|
||||
task.err = context.Cause(task.ctx)
|
||||
if task.err == nil {
|
||||
task.err = errors.New("response is nil")
|
||||
task.err = errors.New("body done response is nil")
|
||||
}
|
||||
}
|
||||
if task.reqCtx.option.Logger != nil {
|
||||
|
||||
6
go.mod
6
go.mod
@@ -40,7 +40,7 @@ require (
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/gobwas/ws v1.4.0 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/google/pprof v0.0.0-20250315033105-103756e64e1d // indirect
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
||||
github.com/gospider007/blog v0.0.0-20250302134054-8afc12c2a9a7 // indirect
|
||||
github.com/gospider007/kinds v0.0.0-20250217075226-10f199f7215d // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
@@ -54,11 +54,11 @@ require (
|
||||
github.com/libdns/libdns v0.2.3 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.0 // indirect
|
||||
github.com/mholt/archives v0.1.0 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/miekg/dns v1.1.64 // 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.1.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.23.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.23.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||
|
||||
12
go.sum
12
go.sum
@@ -96,8 +96,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-20250315033105-103756e64e1d h1:tx51Lf+wdE+aavqH8TcPJoCjTf4cE8hrMzROghCely0=
|
||||
github.com/google/pprof v0.0.0-20250315033105-103756e64e1d/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
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=
|
||||
@@ -163,8 +163,8 @@ github.com/mholt/acmez/v3 v3.1.0 h1:RlOx2SSZ8dIAM5GfkMe8TdaxjjkiHTGorlMUt8GeMzg=
|
||||
github.com/mholt/acmez/v3 v3.1.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q=
|
||||
github.com/mholt/archives v0.1.0/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ=
|
||||
github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -172,8 +172,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nwaples/rardecode/v2 v2.1.1 h1:OJaYalXdliBUXPmC8CZGQ7oZDxzX1/5mQmgn0/GASew=
|
||||
github.com/nwaples/rardecode/v2 v2.1.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ=
|
||||
github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/ginkgo/v2 v2.23.1 h1:Ox0cOPv/t8RzKJUfDo9ZKtRvBOJY369sFJnl00CjqwY=
|
||||
github.com/onsi/ginkgo/v2 v2.23.1/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
|
||||
313
http.go
313
http.go
@@ -9,7 +9,6 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gospider007/tools"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
@@ -24,9 +23,6 @@ type httpTask struct {
|
||||
}
|
||||
err error
|
||||
|
||||
ctx context.Context
|
||||
cnl context.CancelFunc
|
||||
|
||||
writeCtx context.Context
|
||||
writeCnl context.CancelFunc
|
||||
|
||||
@@ -35,7 +31,7 @@ type httpTask struct {
|
||||
}
|
||||
type clientConn struct {
|
||||
err error
|
||||
tasks chan *httpTask
|
||||
task *httpTask
|
||||
conn net.Conn
|
||||
r *bufio.Reader
|
||||
w *bufio.Writer
|
||||
@@ -44,15 +40,6 @@ type clientConn struct {
|
||||
|
||||
closeCtx context.Context
|
||||
closeCnl context.CancelCauseFunc
|
||||
|
||||
keepMsgNotice chan struct{}
|
||||
keepEnableNotice chan struct{}
|
||||
keepDisNotice chan struct{}
|
||||
|
||||
keepCloseCtx context.Context
|
||||
keepCloseCnl context.CancelFunc
|
||||
|
||||
readCtx context.Context
|
||||
}
|
||||
type httpBody struct {
|
||||
r *io.PipeReader
|
||||
@@ -67,215 +54,71 @@ func (obj *httpBody) Close() error {
|
||||
|
||||
func newClientConn(ctx context.Context, con net.Conn, closeFunc func(error)) *clientConn {
|
||||
closeCtx, closeCnl := context.WithCancelCause(ctx)
|
||||
keepCloseCtx, keepCloseCnl := context.WithCancel(closeCtx)
|
||||
reader, writer := io.Pipe()
|
||||
c := &clientConn{
|
||||
closeCtx: closeCtx,
|
||||
closeCnl: closeCnl,
|
||||
ctx: ctx,
|
||||
conn: con,
|
||||
closeFunc: closeFunc,
|
||||
r: bufio.NewReader(con),
|
||||
w: bufio.NewWriter(con),
|
||||
tasks: make(chan *httpTask),
|
||||
keepMsgNotice: make(chan struct{}),
|
||||
keepEnableNotice: make(chan struct{}),
|
||||
keepDisNotice: make(chan struct{}),
|
||||
|
||||
keepCloseCtx: keepCloseCtx,
|
||||
keepCloseCnl: keepCloseCnl,
|
||||
closeCtx: closeCtx,
|
||||
closeCnl: closeCnl,
|
||||
ctx: ctx,
|
||||
conn: con,
|
||||
closeFunc: closeFunc,
|
||||
r: bufio.NewReader(reader),
|
||||
w: bufio.NewWriter(con),
|
||||
}
|
||||
go c.run()
|
||||
go c.CheckTCPAliveSafe()
|
||||
go func() {
|
||||
_, err := io.Copy(writer, con)
|
||||
writer.CloseWithError(err)
|
||||
c.CloseWithError(err)
|
||||
}()
|
||||
return c
|
||||
}
|
||||
|
||||
func (obj *clientConn) CheckTCPAliveSafe() {
|
||||
for {
|
||||
select {
|
||||
case <-obj.ctx.Done():
|
||||
return
|
||||
case <-obj.closeCtx.Done():
|
||||
return
|
||||
case <-obj.keepDisNotice:
|
||||
obj.keepSendMsg()
|
||||
case <-obj.keepCloseCtx.Done():
|
||||
return
|
||||
case <-obj.keepEnableNotice:
|
||||
obj.keepSendMsg()
|
||||
if obj.CheckTCPAliveSafeEnable() {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func (obj *clientConn) CheckTCPAliveSafeEnable() (closed bool) {
|
||||
select {
|
||||
case <-obj.ctx.Done():
|
||||
return true
|
||||
case <-obj.keepCloseCtx.Done():
|
||||
return true
|
||||
case <-obj.keepDisNotice:
|
||||
obj.keepSendMsg()
|
||||
return false
|
||||
case <-obj.readCtx.Done():
|
||||
}
|
||||
totalPeek := 1
|
||||
for {
|
||||
select {
|
||||
case <-obj.ctx.Done():
|
||||
return true
|
||||
case <-obj.closeCtx.Done():
|
||||
return true
|
||||
case <-obj.keepCloseCtx.Done():
|
||||
return true
|
||||
case <-obj.keepDisNotice:
|
||||
obj.keepSendMsg()
|
||||
return false
|
||||
case <-time.After(time.Second * 30):
|
||||
err := obj.conn.SetReadDeadline(time.Now().Add(2 * time.Millisecond))
|
||||
if err != nil {
|
||||
obj.CloseWithError(err)
|
||||
return true
|
||||
}
|
||||
if _, err = obj.r.Peek(totalPeek); err != nil {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
err = nil
|
||||
} else {
|
||||
obj.CloseWithError(err)
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
totalPeek++
|
||||
}
|
||||
if err = obj.conn.SetReadDeadline(time.Time{}); err != nil {
|
||||
obj.CloseWithError(err)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (obj *clientConn) keepSendMsg() {
|
||||
select {
|
||||
case obj.keepMsgNotice <- struct{}{}:
|
||||
case <-obj.ctx.Done():
|
||||
return
|
||||
case <-obj.closeCtx.Done():
|
||||
return
|
||||
case <-obj.keepCloseCtx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
func (obj *clientConn) keepRecvMsg() {
|
||||
select {
|
||||
case <-obj.keepMsgNotice:
|
||||
case <-obj.ctx.Done():
|
||||
return
|
||||
case <-obj.closeCtx.Done():
|
||||
return
|
||||
case <-obj.keepCloseCtx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
func (obj *clientConn) keepSendEnable(task *httpTask) {
|
||||
obj.readCtx = task.readCtx
|
||||
select {
|
||||
case obj.keepEnableNotice <- struct{}{}:
|
||||
case <-obj.ctx.Done():
|
||||
return
|
||||
case <-obj.closeCtx.Done():
|
||||
return
|
||||
case <-obj.keepCloseCtx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
obj.keepRecvMsg()
|
||||
}
|
||||
func (obj *clientConn) keepSendDisable() {
|
||||
select {
|
||||
case obj.keepDisNotice <- struct{}{}:
|
||||
case <-obj.ctx.Done():
|
||||
return
|
||||
case <-obj.closeCtx.Done():
|
||||
return
|
||||
case <-obj.keepCloseCtx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
obj.keepRecvMsg()
|
||||
}
|
||||
func (obj *clientConn) keepSendClose() {
|
||||
obj.keepCloseCnl()
|
||||
}
|
||||
|
||||
var errLastTaskRuning = errors.New("last task is running")
|
||||
|
||||
func (obj *clientConn) run() (err error) {
|
||||
defer func() {
|
||||
obj.CloseWithError(err)
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-obj.ctx.Done():
|
||||
return
|
||||
case <-obj.closeCtx.Done():
|
||||
return
|
||||
case task := <-obj.tasks:
|
||||
obj.keepSendDisable()
|
||||
go obj.httpWrite(task, task.req.Header.Clone())
|
||||
task.res, task.err = http.ReadResponse(obj.r, nil)
|
||||
if task.res != nil {
|
||||
task.res.Request = task.req
|
||||
}
|
||||
if task.res != nil && task.res.Body != nil && task.err == nil {
|
||||
rawBody := task.res.Body
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
var readErr error
|
||||
defer func() {
|
||||
task.readCnl(readErr)
|
||||
}()
|
||||
_, readErr = io.Copy(pw, rawBody)
|
||||
pw.CloseWithError(readErr)
|
||||
if readErr != nil && readErr != io.EOF && readErr != io.ErrUnexpectedEOF {
|
||||
task.err = tools.WrapError(readErr, "failed to read response body")
|
||||
} else {
|
||||
readErr = nil
|
||||
func (obj *clientConn) send() {
|
||||
go obj.httpWrite(obj.task, obj.task.req.Header.Clone())
|
||||
obj.task.res, obj.task.err = http.ReadResponse(obj.r, obj.task.req)
|
||||
if obj.task.res == nil || obj.task.err != nil || obj.task.res.Body == nil {
|
||||
obj.task.readCnl(nil)
|
||||
return
|
||||
}
|
||||
rawBody := obj.task.res.Body
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
var readErr error
|
||||
defer func() {
|
||||
obj.task.readCnl(readErr)
|
||||
}()
|
||||
_, readErr = io.Copy(pw, rawBody)
|
||||
pw.CloseWithError(readErr)
|
||||
if readErr != nil && readErr != io.EOF && readErr != io.ErrUnexpectedEOF {
|
||||
obj.task.err = tools.WrapError(readErr, "failed to read response body")
|
||||
} else {
|
||||
readErr = nil
|
||||
}
|
||||
if readErr != nil {
|
||||
obj.CloseWithError(readErr)
|
||||
} else {
|
||||
select {
|
||||
case <-obj.task.writeCtx.Done():
|
||||
if obj.task.res.StatusCode == 101 || strings.Contains(obj.task.res.Header.Get("Content-Type"), "text/event-stream") {
|
||||
select {
|
||||
case <-obj.ctx.Done():
|
||||
return
|
||||
case <-obj.closeCtx.Done():
|
||||
return
|
||||
}
|
||||
if readErr != nil {
|
||||
obj.CloseWithError(readErr)
|
||||
} else {
|
||||
select {
|
||||
case <-task.writeCtx.Done():
|
||||
if task.res.StatusCode == 101 || strings.Contains(task.res.Header.Get("Content-Type"), "text/event-stream") {
|
||||
obj.keepSendClose()
|
||||
select {
|
||||
case <-obj.ctx.Done():
|
||||
return
|
||||
case <-obj.closeCtx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
readErr = tools.WrapError(errLastTaskRuning, errors.New("last task not write done with read done"))
|
||||
task.err = readErr
|
||||
obj.CloseWithError(readErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
task.res.Body = &httpBody{r: pr}
|
||||
} else {
|
||||
task.readCnl(nil)
|
||||
}
|
||||
task.cnl()
|
||||
if task.res == nil || task.err != nil {
|
||||
}
|
||||
default:
|
||||
readErr = tools.WrapError(errLastTaskRuning, errors.New("last task not write done with read done"))
|
||||
obj.task.err = readErr
|
||||
obj.CloseWithError(readErr)
|
||||
return
|
||||
}
|
||||
obj.keepSendEnable(task)
|
||||
}
|
||||
}
|
||||
}()
|
||||
obj.task.res.Body = &httpBody{r: pr}
|
||||
}
|
||||
|
||||
func (obj *clientConn) CloseCtx() context.Context {
|
||||
return obj.closeCtx
|
||||
}
|
||||
@@ -299,10 +142,28 @@ func (obj *clientConn) DoRequest(req *http.Request, orderHeaders []interface {
|
||||
Key() string
|
||||
Val() any
|
||||
}) (*http.Response, context.Context, error) {
|
||||
select {
|
||||
case <-obj.ctx.Done():
|
||||
return nil, obj.task.readCtx, obj.ctx.Err()
|
||||
case <-obj.closeCtx.Done():
|
||||
return nil, obj.task.readCtx, obj.closeCtx.Err()
|
||||
default:
|
||||
}
|
||||
if obj.task != nil {
|
||||
select {
|
||||
case <-obj.task.writeCtx.Done():
|
||||
default:
|
||||
return nil, obj.task.readCtx, errLastTaskRuning
|
||||
}
|
||||
select {
|
||||
case <-obj.task.readCtx.Done():
|
||||
default:
|
||||
return nil, obj.task.readCtx, errLastTaskRuning
|
||||
}
|
||||
}
|
||||
readCtx, readCnl := context.WithCancelCause(obj.closeCtx)
|
||||
writeCtx, writeCnl := context.WithCancel(obj.closeCtx)
|
||||
ctx, cnl := context.WithCancel(req.Context())
|
||||
task := &httpTask{
|
||||
obj.task = &httpTask{
|
||||
readCtx: readCtx,
|
||||
readCnl: readCnl,
|
||||
|
||||
@@ -311,29 +172,12 @@ func (obj *clientConn) DoRequest(req *http.Request, orderHeaders []interface {
|
||||
|
||||
req: req,
|
||||
orderHeaders: orderHeaders,
|
||||
ctx: ctx,
|
||||
cnl: cnl,
|
||||
}
|
||||
select {
|
||||
case <-obj.ctx.Done():
|
||||
return nil, task.readCtx, obj.ctx.Err()
|
||||
case <-obj.closeCtx.Done():
|
||||
return nil, task.readCtx, obj.closeCtx.Err()
|
||||
case obj.tasks <- task:
|
||||
default:
|
||||
return nil, nil, errLastTaskRuning
|
||||
obj.send()
|
||||
if obj.task.err != nil {
|
||||
obj.CloseWithError(obj.task.err)
|
||||
}
|
||||
select {
|
||||
case <-obj.ctx.Done():
|
||||
return nil, task.readCtx, obj.ctx.Err()
|
||||
case <-obj.closeCtx.Done():
|
||||
return nil, task.readCtx, obj.closeCtx.Err()
|
||||
case <-task.ctx.Done():
|
||||
}
|
||||
if task.err != nil {
|
||||
obj.CloseWithError(task.err)
|
||||
}
|
||||
return task.res, task.readCtx, task.err
|
||||
return obj.task.res, obj.task.readCtx, obj.task.err
|
||||
}
|
||||
|
||||
type websocketConn struct {
|
||||
@@ -357,7 +201,6 @@ func (obj *websocketConn) Close() error {
|
||||
}
|
||||
|
||||
func (obj *clientConn) Stream() io.ReadWriteCloser {
|
||||
obj.keepSendClose()
|
||||
return &websocketConn{
|
||||
cnl: obj.closeCnl,
|
||||
r: obj.r,
|
||||
|
||||
@@ -330,7 +330,7 @@ func (obj *Client) request(ctx *Response) (err error) {
|
||||
return
|
||||
}
|
||||
if ctx.response == nil {
|
||||
err = errors.New("response is nil")
|
||||
err = errors.New("send req response is nil")
|
||||
return
|
||||
}
|
||||
if ctx.Body() != nil {
|
||||
|
||||
Reference in New Issue
Block a user