bug fix:nil pointer
This commit is contained in:
parent
deed4207ea
commit
0d847462b3
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.idea
|
189
curl.go
189
curl.go
@ -14,6 +14,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -108,6 +109,185 @@ type Request struct {
|
|||||||
RequestOpts
|
RequestOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Request) Clone() *Request {
|
||||||
|
clonedRequest := &Request{
|
||||||
|
ctx: r.ctx,
|
||||||
|
uri: r.uri,
|
||||||
|
method: r.method,
|
||||||
|
errInfo: r.errInfo,
|
||||||
|
RequestOpts: RequestOpts{
|
||||||
|
headers: CloneHeader(r.headers),
|
||||||
|
cookies: CloneCookies(r.cookies),
|
||||||
|
bodyFormData: CloneStringMapSlice(r.bodyFormData),
|
||||||
|
bodyFileData: CloneFiles(r.bodyFileData),
|
||||||
|
queries: CloneStringMapSlice(r.queries),
|
||||||
|
bodyDataBytes: CloneByteSlice(r.bodyDataBytes),
|
||||||
|
proxy: r.proxy,
|
||||||
|
timeout: r.timeout,
|
||||||
|
dialTimeout: r.dialTimeout,
|
||||||
|
alreadyApply: r.alreadyApply,
|
||||||
|
disableRedirect: r.disableRedirect,
|
||||||
|
doRawRequest: r.doRawRequest,
|
||||||
|
doRawClient: r.doRawClient,
|
||||||
|
doRawTransport: r.doRawTransport,
|
||||||
|
skipTLSVerify: r.skipTLSVerify,
|
||||||
|
autoFetchRespBody: r.autoFetchRespBody,
|
||||||
|
customIP: CloneStringSlice(r.customIP),
|
||||||
|
alreadySetLookUpIPfn: r.alreadySetLookUpIPfn,
|
||||||
|
lookUpIPfn: r.lookUpIPfn,
|
||||||
|
customDNS: CloneStringSlice(r.customDNS),
|
||||||
|
basicAuth: r.basicAuth,
|
||||||
|
autoCalcContentLength: r.autoCalcContentLength,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动深拷贝嵌套引用类型
|
||||||
|
if r.bodyDataReader != nil {
|
||||||
|
clonedRequest.bodyDataReader = r.bodyDataReader
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.FileUploadRecallFn != nil {
|
||||||
|
clonedRequest.FileUploadRecallFn = r.FileUploadRecallFn
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对于 tlsConfig 类型,需要手动复制
|
||||||
|
if r.tlsConfig != nil {
|
||||||
|
clonedRequest.tlsConfig = CloneTLSConfig(r.tlsConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对于 http.Transport,需要进行手动复制
|
||||||
|
if r.transport != nil {
|
||||||
|
clonedRequest.transport = CloneTransport(r.transport)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clonedRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneHeader 复制 http.Header
|
||||||
|
func CloneHeader(original http.Header) http.Header {
|
||||||
|
newHeader := make(http.Header)
|
||||||
|
for key, values := range original {
|
||||||
|
copiedValues := make([]string, len(values))
|
||||||
|
copy(copiedValues, values)
|
||||||
|
newHeader[key] = copiedValues
|
||||||
|
}
|
||||||
|
return newHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneCookies 复制 []*http.Cookie
|
||||||
|
func CloneCookies(original []*http.Cookie) []*http.Cookie {
|
||||||
|
cloned := make([]*http.Cookie, len(original))
|
||||||
|
for i, cookie := range original {
|
||||||
|
cloned[i] = &http.Cookie{
|
||||||
|
Name: cookie.Name,
|
||||||
|
Value: cookie.Value,
|
||||||
|
Path: cookie.Path,
|
||||||
|
Domain: cookie.Domain,
|
||||||
|
Expires: cookie.Expires,
|
||||||
|
RawExpires: cookie.RawExpires,
|
||||||
|
MaxAge: cookie.MaxAge,
|
||||||
|
Secure: cookie.Secure,
|
||||||
|
HttpOnly: cookie.HttpOnly,
|
||||||
|
SameSite: cookie.SameSite,
|
||||||
|
Raw: cookie.Raw,
|
||||||
|
Unparsed: append([]string(nil), cookie.Unparsed...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cloned
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneStringMapSlice 复制 map[string][]string
|
||||||
|
func CloneStringMapSlice(original map[string][]string) map[string][]string {
|
||||||
|
newMap := make(map[string][]string)
|
||||||
|
for key, values := range original {
|
||||||
|
copiedValues := make([]string, len(values))
|
||||||
|
copy(copiedValues, values)
|
||||||
|
newMap[key] = copiedValues
|
||||||
|
}
|
||||||
|
return newMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneFiles 复制 []RequestFile
|
||||||
|
func CloneFiles(original []RequestFile) []RequestFile {
|
||||||
|
newFiles := make([]RequestFile, len(original))
|
||||||
|
copy(newFiles, original)
|
||||||
|
return newFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneByteSlice 复制 []byte
|
||||||
|
func CloneByteSlice(original []byte) []byte {
|
||||||
|
if original == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
newSlice := make([]byte, len(original))
|
||||||
|
copy(newSlice, original)
|
||||||
|
return newSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneStringSlice 复制 []string
|
||||||
|
func CloneStringSlice(original []string) []string {
|
||||||
|
newSlice := make([]string, len(original))
|
||||||
|
copy(newSlice, original)
|
||||||
|
return newSlice
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneTLSConfig 复制 tls.Config
|
||||||
|
func CloneTLSConfig(original *tls.Config) *tls.Config {
|
||||||
|
newConfig := &tls.Config{
|
||||||
|
Rand: original.Rand,
|
||||||
|
Time: original.Time,
|
||||||
|
Certificates: append([]tls.Certificate(nil), original.Certificates...),
|
||||||
|
NameToCertificate: original.NameToCertificate,
|
||||||
|
GetCertificate: original.GetCertificate,
|
||||||
|
GetClientCertificate: original.GetClientCertificate,
|
||||||
|
GetConfigForClient: original.GetConfigForClient,
|
||||||
|
VerifyPeerCertificate: original.VerifyPeerCertificate,
|
||||||
|
VerifyConnection: original.VerifyConnection,
|
||||||
|
RootCAs: original.RootCAs,
|
||||||
|
NextProtos: append([]string(nil), original.NextProtos...),
|
||||||
|
ServerName: original.ServerName,
|
||||||
|
ClientAuth: original.ClientAuth,
|
||||||
|
ClientCAs: original.ClientCAs,
|
||||||
|
InsecureSkipVerify: original.InsecureSkipVerify,
|
||||||
|
CipherSuites: append([]uint16(nil), original.CipherSuites...),
|
||||||
|
PreferServerCipherSuites: original.PreferServerCipherSuites,
|
||||||
|
SessionTicketsDisabled: original.SessionTicketsDisabled,
|
||||||
|
SessionTicketKey: original.SessionTicketKey,
|
||||||
|
ClientSessionCache: original.ClientSessionCache,
|
||||||
|
MinVersion: original.MinVersion,
|
||||||
|
MaxVersion: original.MaxVersion,
|
||||||
|
CurvePreferences: append([]tls.CurveID(nil), original.CurvePreferences...),
|
||||||
|
DynamicRecordSizingDisabled: original.DynamicRecordSizingDisabled,
|
||||||
|
Renegotiation: original.Renegotiation,
|
||||||
|
KeyLogWriter: original.KeyLogWriter,
|
||||||
|
}
|
||||||
|
return newConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneTransport 复制 http.Transport
|
||||||
|
func CloneTransport(original *http.Transport) *http.Transport {
|
||||||
|
newTransport := &http.Transport{
|
||||||
|
Proxy: original.Proxy,
|
||||||
|
DialContext: original.DialContext,
|
||||||
|
Dial: original.Dial,
|
||||||
|
DialTLS: original.DialTLS,
|
||||||
|
TLSClientConfig: original.TLSClientConfig,
|
||||||
|
TLSHandshakeTimeout: original.TLSHandshakeTimeout,
|
||||||
|
DisableKeepAlives: original.DisableKeepAlives,
|
||||||
|
DisableCompression: original.DisableCompression,
|
||||||
|
MaxIdleConns: original.MaxIdleConns,
|
||||||
|
MaxIdleConnsPerHost: original.MaxIdleConnsPerHost,
|
||||||
|
IdleConnTimeout: original.IdleConnTimeout,
|
||||||
|
ResponseHeaderTimeout: original.ResponseHeaderTimeout,
|
||||||
|
ExpectContinueTimeout: original.ExpectContinueTimeout,
|
||||||
|
TLSNextProto: original.TLSNextProto,
|
||||||
|
ProxyConnectHeader: original.ProxyConnectHeader,
|
||||||
|
MaxResponseHeaderBytes: original.MaxResponseHeaderBytes,
|
||||||
|
WriteBufferSize: original.WriteBufferSize,
|
||||||
|
ReadBufferSize: original.ReadBufferSize,
|
||||||
|
}
|
||||||
|
return newTransport
|
||||||
|
}
|
||||||
func (r *Request) Method() string {
|
func (r *Request) Method() string {
|
||||||
return r.method
|
return r.method
|
||||||
}
|
}
|
||||||
@ -1071,10 +1251,17 @@ type Body struct {
|
|||||||
full []byte
|
full []byte
|
||||||
raw io.ReadCloser
|
raw io.ReadCloser
|
||||||
isFull bool
|
isFull bool
|
||||||
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Body) readAll() {
|
func (b *Body) readAll() {
|
||||||
|
b.Lock()
|
||||||
|
defer b.Unlock()
|
||||||
if !b.isFull {
|
if !b.isFull {
|
||||||
|
if b.raw == nil {
|
||||||
|
b.isFull = true
|
||||||
|
return
|
||||||
|
}
|
||||||
b.full, _ = io.ReadAll(b.raw)
|
b.full, _ = io.ReadAll(b.raw)
|
||||||
b.isFull = true
|
b.isFull = true
|
||||||
b.raw.Close()
|
b.raw.Close()
|
||||||
@ -1099,6 +1286,8 @@ func (b *Body) Unmarshal(u interface{}) error {
|
|||||||
// Reader returns a reader for the body
|
// Reader returns a reader for the body
|
||||||
// if this function is called, other functions like String, Bytes, Unmarshal may not work
|
// if this function is called, other functions like String, Bytes, Unmarshal may not work
|
||||||
func (b *Body) Reader() io.ReadCloser {
|
func (b *Body) Reader() io.ReadCloser {
|
||||||
|
b.Lock()
|
||||||
|
defer b.Unlock()
|
||||||
if b.isFull {
|
if b.isFull {
|
||||||
return io.NopCloser(bytes.NewReader(b.full))
|
return io.NopCloser(bytes.NewReader(b.full))
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user