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" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| @ -108,6 +109,185 @@ type Request struct { | ||||
| 	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 { | ||||
| 	return r.method | ||||
| } | ||||
| @ -1071,10 +1251,17 @@ type Body struct { | ||||
| 	full   []byte | ||||
| 	raw    io.ReadCloser | ||||
| 	isFull bool | ||||
| 	sync.Mutex | ||||
| } | ||||
| 
 | ||||
| func (b *Body) readAll() { | ||||
| 	b.Lock() | ||||
| 	defer b.Unlock() | ||||
| 	if !b.isFull { | ||||
| 		if b.raw == nil { | ||||
| 			b.isFull = true | ||||
| 			return | ||||
| 		} | ||||
| 		b.full, _ = io.ReadAll(b.raw) | ||||
| 		b.isFull = true | ||||
| 		b.raw.Close() | ||||
| @ -1099,6 +1286,8 @@ func (b *Body) Unmarshal(u interface{}) error { | ||||
| // Reader returns a reader for the body | ||||
| // if this function is called, other functions like String, Bytes, Unmarshal may not work | ||||
| func (b *Body) Reader() io.ReadCloser { | ||||
| 	b.Lock() | ||||
| 	defer b.Unlock() | ||||
| 	if b.isFull { | ||||
| 		return io.NopCloser(bytes.NewReader(b.full)) | ||||
| 	} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user