This commit is contained in:
gospider
2025-03-03 14:43:50 +08:00
parent cf53e11a0b
commit 10d2c70ae2
5 changed files with 70 additions and 118 deletions

View File

@@ -210,9 +210,6 @@ func (obj *RequestOption) newBody(val any) (io.Reader, *OrderData, bool, error)
switch value := val.(type) {
case *OrderData:
return nil, value, true, nil
case io.ReadCloser:
obj.once = true
return value, nil, true, nil
case io.Reader:
obj.once = true
return value, nil, true, nil

View File

@@ -3,9 +3,6 @@ package requests
import (
"context"
"crypto/tls"
"fmt"
"io"
"net/http"
utls "github.com/refraction-networking/utls"
)
@@ -72,62 +69,6 @@ func (obj *Client) Close() {
obj.CloseConns()
obj.cnl()
}
func (obj *Client) do(ctx *Response) (err error) {
var redirectNum int
for {
redirectNum++
err = obj.send(ctx)
if ctx.Request().Body != nil {
ctx.Request().Body.Close()
}
if err != nil {
return
}
if ctx.Option().MaxRedirect < 0 { //dis redirect
return
}
if ctx.Option().MaxRedirect > 0 && redirectNum > ctx.Option().MaxRedirect {
return
}
loc := ctx.response.Header.Get("Location")
if loc == "" {
return nil
}
u, err := ctx.Request().URL.Parse(loc)
if err != nil {
return fmt.Errorf("failed to parse Location header %q: %v", loc, err)
}
ctx.request, err = NewRequestWithContext(ctx.Context(), http.MethodGet, u, nil)
if err != nil {
return err
}
var shouldRedirect bool
ctx.request.Method, shouldRedirect, _ = redirectBehavior(ctx.Request().Method, ctx.response, ctx.request)
if !shouldRedirect {
return nil
}
ctx.request.Response = ctx.response
ctx.request.Header = make(http.Header)
ctx.request.Header.Set("Referer", ctx.Request().URL.String())
for key := range ctx.request.Header {
if val := ctx.Request().Header.Get(key); val != "" {
ctx.request.Header.Set(key, val)
}
}
if getDomain(u) == getDomain(ctx.Request().URL) {
if Authorization := ctx.Request().Header.Get("Authorization"); Authorization != "" {
ctx.request.Header.Set("Authorization", Authorization)
}
cookies := Cookies(ctx.Request().Cookies()).String()
if cookies != "" {
ctx.request.Header.Set("Cookie", cookies)
}
addCookie(ctx.request, ctx.response.Cookies())
}
io.Copy(io.Discard, ctx.response.Body)
ctx.response.Body.Close()
}
}
func (obj *Client) send(ctx *Response) (err error) {
if ctx.Option().Jar != nil {
addCookie(ctx.Request(), ctx.Option().Jar.GetCookies(ctx.Request().URL))

View File

@@ -93,6 +93,7 @@ type RequestOption struct {
once bool
orderHeaders *OrderData //order headers
gospiderSpec *GospiderSpec
disBody bool
}
// Upload files with form-data,
@@ -242,3 +243,10 @@ func (obj *Client) newRequestOption(option RequestOption) (RequestOption, error)
}
return option, err
}
func (obj *Client) newResponse(ctx context.Context, option RequestOption, uhref *url.URL, requestId string) *Response {
option.Url = cloneUrl(uhref)
response := NewResponse(ctx, option)
response.client = obj
response.requestId = requestId
return response
}

View File

@@ -116,6 +116,54 @@ func (obj *Client) Trace(ctx context.Context, href string, options ...RequestOpt
return obj.Request(ctx, http.MethodTrace, href, options...)
}
// Define a function named Request that takes in four parameters:
func (obj *Client) retryRequest(ctx context.Context, option RequestOption, uhref *url.URL, requestId string) (response *Response, err error) {
defer func() {
if errors.Is(err, errFatal) || response.Option().once {
response.Option().MaxRetries = -1
}
}()
var redirectNum int
var loc *url.URL
response = obj.newResponse(ctx, option, uhref, requestId)
for {
redirectNum++
select {
case <-ctx.Done():
err = ctx.Err()
return
default:
}
err = obj.request(response)
if err != nil || response.Option().MaxRedirect < 0 || (response.Option().MaxRedirect > 0 && redirectNum > response.Option().MaxRedirect) {
return
}
loc, err = response.Location()
if err != nil || loc == nil {
return
}
response.Close()
switch response.StatusCode() {
case 307, 308:
if response.Option().once {
return
}
response = obj.newResponse(ctx, option, loc, requestId)
default:
option.Method = http.MethodGet
option.disBody = true
option.Headers = nil
option.Referer = response.Url().String()
if getDomain(loc) == getDomain(response.Url()) {
if Authorization := response.Request().Header.Get("Authorization"); Authorization != "" {
option.Headers = map[string]any{"Authorization": Authorization}
}
}
response = obj.newResponse(ctx, option, loc, requestId)
}
}
}
// Define a function named Request that takes in four parameters:
func (obj *Client) Request(ctx context.Context, method string, href string, options ...RequestOption) (response *Response, err error) {
if obj.closed {
@@ -144,19 +192,9 @@ func (obj *Client) Request(ctx context.Context, method string, href string, opti
}
}
for ; optionBak.MaxRetries >= 0; optionBak.MaxRetries-- {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
option := optionBak
option.Url = cloneUrl(uhref)
response = NewResponse(ctx, option)
response.client = obj
response.requestId = requestId
err = obj.request(response)
if err == nil || errors.Is(err, errFatal) || response.Option().once {
return
response, err = obj.retryRequest(ctx, optionBak, uhref, requestId)
if err == nil {
break
}
optionBak.MaxRetries = response.Option().MaxRetries
}
@@ -182,6 +220,9 @@ func (obj *Client) request(ctx *Response) (err error) {
}
}
}
if ctx.Request().Body != nil {
ctx.Request().Body.Close()
}
}()
if ctx.option.OptionCallBack != nil {
if err = ctx.option.OptionCallBack(ctx); err != nil {
@@ -232,9 +273,13 @@ func (obj *Client) request(ctx *Response) (err error) {
}
//init body
body, err := ctx.option.initBody(ctx.ctx)
if err != nil {
return tools.WrapError(err, errors.New("tempRequest init body error"), err)
var body io.Reader
if ctx.option.disBody {
body = nil
} else {
if body, err = ctx.option.initBody(ctx.ctx); err != nil {
return tools.WrapError(err, errors.New("tempRequest init body error"), err)
}
}
//create request
reqs, err := NewRequestWithContext(ctx.Context(), ctx.option.Method, href, body)
@@ -279,7 +324,7 @@ func (obj *Client) request(ctx *Response) (err error) {
//init spec
//send req
err = obj.do(ctx)
err = obj.send(ctx)
if err != nil && err != ErrUseLastResponse {
err = tools.WrapError(err, "client do error")
return

View File

@@ -129,45 +129,6 @@ func removeZone(host string) string {
return host[:j] + host[i:]
}
func outgoingLength(r *http.Request) int64 {
if r.Body == nil || r.Body == http.NoBody {
return 0
}
if r.ContentLength != 0 {
return r.ContentLength
}
return -1
}
func redirectBehavior(reqMethod string, resp *http.Response, ireq *http.Request) (redirectMethod string, shouldRedirect, includeBody bool) {
switch resp.StatusCode {
case 301, 302, 303:
redirectMethod = reqMethod
shouldRedirect = true
includeBody = false
// RFC 2616 allowed automatic redirection only with GET and
// HEAD requests. RFC 7231 lifts this restriction, but we still
// restrict other methods to GET to maintain compatibility.
// See Issue 18570.
if reqMethod != "GET" && reqMethod != "HEAD" {
redirectMethod = "GET"
}
case 307, 308:
redirectMethod = reqMethod
shouldRedirect = true
includeBody = true
if ireq.GetBody == nil && outgoingLength(ireq) != 0 {
// We had a request body, and 307/308 require
// re-sending it, but GetBody is not defined. So just
// return this response to the user instead of an
// error, like we did in Go 1.7 and earlier.
shouldRedirect = false
}
}
return redirectMethod, shouldRedirect, includeBody
}
type requestBody struct {
r io.Reader
}