From 5e5200f749b690e18a1cbba9ef65ba44410b444a Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:47:29 -0500 Subject: [PATCH] Update client package inline documentation --- client/client.go | 295 +++++++++++++-------------------- client/cookiejar.go | 86 +++++----- client/core.go | 102 +++++------- client/hooks.go | 91 +++++------ client/request.go | 389 +++++++++++++++++++------------------------- client/response.go | 73 ++++----- 6 files changed, 443 insertions(+), 593 deletions(-) diff --git a/client/client.go b/client/client.go index 2c4086aed3..fc9171c8ab 100644 --- a/client/client.go +++ b/client/client.go @@ -24,16 +24,13 @@ import ( var ErrFailedToAppendCert = errors.New("failed to append certificate") -// The Client is used to create a Fiber Client with -// client-level settings that apply to all requests -// raise from the client. +// Client is used to create a Fiber client with client-level settings that +// apply to all requests made by the client. // -// Fiber Client also provides an option to override -// or merge most of the client settings at the request. +// The Fiber client also provides an option to override or merge most of the +// client settings at the request level. type Client struct { - // logger - logger log.CommonLogger - + logger log.CommonLogger fasthttp *fasthttp.Client header *Header @@ -48,45 +45,32 @@ type Client struct { cborMarshal utils.CBORMarshal cborUnmarshal utils.CBORUnmarshal - cookieJar *CookieJar - - // retry - retryConfig *RetryConfig - - baseURL string - userAgent string - referer string - - // user defined request hooks - userRequestHooks []RequestHook - - // client package defined request hooks + cookieJar *CookieJar + retryConfig *RetryConfig + baseURL string + userAgent string + referer string + userRequestHooks []RequestHook builtinRequestHooks []RequestHook - - // user defined response hooks - userResponseHooks []ResponseHook - - // client package defined response hooks + userResponseHooks []ResponseHook builtinResponseHooks []ResponseHook timeout time.Duration - - mu sync.RWMutex - - debug bool + mu sync.RWMutex + debug bool } -// R raise a request from the client. +// R creates a new Request associated with the client. func (c *Client) R() *Request { return AcquireRequest().SetClient(c) } -// RequestHook Request returns user-defined request hooks. +// RequestHook returns the user-defined request hooks. func (c *Client) RequestHook() []RequestHook { return c.userRequestHooks } -// AddRequestHook Add user-defined request hooks. +// AddRequestHook adds user-defined request hooks. func (c *Client) AddRequestHook(h ...RequestHook) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -95,12 +79,12 @@ func (c *Client) AddRequestHook(h ...RequestHook) *Client { return c } -// ResponseHook return user-define response hooks. +// ResponseHook returns the user-defined response hooks. func (c *Client) ResponseHook() []ResponseHook { return c.userResponseHooks } -// AddResponseHook Add user-defined response hooks. +// AddResponseHook adds user-defined response hooks. func (c *Client) AddResponseHook(h ...ResponseHook) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -109,74 +93,74 @@ func (c *Client) AddResponseHook(h ...ResponseHook) *Client { return c } -// JSONMarshal returns json marshal function in Core. +// JSONMarshal returns the JSON marshal function used by the client. func (c *Client) JSONMarshal() utils.JSONMarshal { return c.jsonMarshal } -// SetJSONMarshal sets the JSON encoder. +// SetJSONMarshal sets the JSON marshal function to use. func (c *Client) SetJSONMarshal(f utils.JSONMarshal) *Client { c.jsonMarshal = f return c } -// JSONUnmarshal returns json unmarshal function in Core. +// JSONUnmarshal returns the JSON unmarshal function used by the client. func (c *Client) JSONUnmarshal() utils.JSONUnmarshal { return c.jsonUnmarshal } -// Set json decoder. +// SetJSONUnmarshal sets the JSON unmarshal function to use. func (c *Client) SetJSONUnmarshal(f utils.JSONUnmarshal) *Client { c.jsonUnmarshal = f return c } -// XMLMarshal returns xml marshal function in Core. +// XMLMarshal returns the XML marshal function used by the client. func (c *Client) XMLMarshal() utils.XMLMarshal { return c.xmlMarshal } -// SetXMLMarshal Set xml encoder. +// SetXMLMarshal sets the XML marshal function to use. func (c *Client) SetXMLMarshal(f utils.XMLMarshal) *Client { c.xmlMarshal = f return c } -// XMLUnmarshal returns xml unmarshal function in Core. +// XMLUnmarshal returns the XML unmarshal function used by the client. func (c *Client) XMLUnmarshal() utils.XMLUnmarshal { return c.xmlUnmarshal } -// SetXMLUnmarshal Set xml decoder. +// SetXMLUnmarshal sets the XML unmarshal function to use. func (c *Client) SetXMLUnmarshal(f utils.XMLUnmarshal) *Client { c.xmlUnmarshal = f return c } -// CBORMarshal returns CBOR marshal function in Core. +// CBORMarshal returns the CBOR marshal function used by the client. func (c *Client) CBORMarshal() utils.CBORMarshal { return c.cborMarshal } -// SetCBORMarshal sets CBOR encoder. +// SetCBORMarshal sets the CBOR marshal function to use. func (c *Client) SetCBORMarshal(f utils.CBORMarshal) *Client { c.cborMarshal = f return c } -// CBORUnmarshal returns CBOR unmarshal function in Core. +// CBORUnmarshal returns the CBOR unmarshal function used by the client. func (c *Client) CBORUnmarshal() utils.CBORUnmarshal { return c.cborUnmarshal } -// SetCBORUnmarshal sets CBOR decoder. +// SetCBORUnmarshal sets the CBOR unmarshal function to use. func (c *Client) SetCBORUnmarshal(f utils.CBORUnmarshal) *Client { c.cborUnmarshal = f return c } -// TLSConfig returns tlsConfig in client. -// If client don't have tlsConfig, this function will init it. +// TLSConfig returns the client's TLS configuration. +// If none is set, it initializes a new one. func (c *Client) TLSConfig() *tls.Config { if c.fasthttp.TLSConfig == nil { c.fasthttp.TLSConfig = &tls.Config{ @@ -187,20 +171,20 @@ func (c *Client) TLSConfig() *tls.Config { return c.fasthttp.TLSConfig } -// SetTLSConfig sets tlsConfig in client. +// SetTLSConfig sets the TLS configuration for the client. func (c *Client) SetTLSConfig(config *tls.Config) *Client { c.fasthttp.TLSConfig = config return c } -// SetCertificates method sets client certificates into client. +// SetCertificates adds certificates to the client's TLS configuration. func (c *Client) SetCertificates(certs ...tls.Certificate) *Client { config := c.TLSConfig() config.Certificates = append(config.Certificates, certs...) return c } -// SetRootCertificate adds one or more root certificates into client. +// SetRootCertificate adds one or more root certificates to the client's TLS configuration. func (c *Client) SetRootCertificate(path string) *Client { cleanPath := filepath.Clean(path) file, err := os.Open(cleanPath) @@ -230,7 +214,7 @@ func (c *Client) SetRootCertificate(path string) *Client { return c } -// SetRootCertificateFromString method adds one or more root certificates into client. +// SetRootCertificateFromString adds one or more root certificates from a string to the client's TLS configuration. func (c *Client) SetRootCertificateFromString(pem string) *Client { config := c.TLSConfig() @@ -245,19 +229,18 @@ func (c *Client) SetRootCertificateFromString(pem string) *Client { return c } -// SetProxyURL sets proxy url in client. It will apply via core to hostclient. +// SetProxyURL sets the proxy URL for the client. This affects all subsequent requests. func (c *Client) SetProxyURL(proxyURL string) error { c.fasthttp.Dial = fasthttpproxy.FasthttpHTTPDialer(proxyURL) - return nil } -// RetryConfig returns retry config in client. +// RetryConfig returns the current retry configuration. func (c *Client) RetryConfig() *RetryConfig { return c.retryConfig } -// SetRetryConfig sets retry config in client which is impl by addon/retry package. +// SetRetryConfig sets the retry configuration for the client. func (c *Client) SetRetryConfig(config *RetryConfig) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -266,58 +249,47 @@ func (c *Client) SetRetryConfig(config *RetryConfig) *Client { return c } -// BaseURL returns baseurl in Client instance. +// BaseURL returns the client's base URL. func (c *Client) BaseURL() string { return c.baseURL } -// SetBaseURL Set baseUrl which is prefix of real url. +// SetBaseURL sets the base URL prefix for all requests made by the client. func (c *Client) SetBaseURL(url string) *Client { c.baseURL = url return c } -// Header method returns header value via key, -// this method will visit all field in the header, -// then sort them. +// Header returns all header values associated with the provided key. func (c *Client) Header(key string) []string { return c.header.PeekMultiple(key) } -// AddHeader method adds a single header field and its value in the client instance. -// These headers will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level header options. +// AddHeader adds a single header field and its value to the client. These headers apply to all requests. func (c *Client) AddHeader(key, val string) *Client { c.header.Add(key, val) return c } -// SetHeader method sets a single header field and its value in the client instance. -// These headers will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level header options. +// SetHeader sets a single header field and its value in the client. func (c *Client) SetHeader(key, val string) *Client { c.header.Set(key, val) return c } -// AddHeaders method adds multiple headers field and its values at one go in the client instance. -// These headers will be applied to all requests raised from this client instance. Also it can be -// overridden at request level headers options. +// AddHeaders adds multiple header fields and their values to the client. func (c *Client) AddHeaders(h map[string][]string) *Client { c.header.AddHeaders(h) return c } -// SetHeaders method sets multiple headers field and its values at one go in the client instance. -// These headers will be applied to all requests raised from this client instance. Also it can be -// overridden at request level headers options. +// SetHeaders sets multiple header fields and their values in the client. func (c *Client) SetHeaders(h map[string]string) *Client { c.header.SetHeaders(h) return c } -// Param method returns params value via key, -// this method will visit all field in the query param. +// Param returns all values of the specified query parameter. func (c *Client) Param(key string) []string { res := []string{} tmp := c.params.PeekMulti(key) @@ -328,47 +300,37 @@ func (c *Client) Param(key string) []string { return res } -// AddParam method adds a single query param field and its value in the client instance. -// These params will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level param options. +// AddParam adds a single query parameter and its value to the client. func (c *Client) AddParam(key, val string) *Client { c.params.Add(key, val) return c } -// SetParam method sets a single query param field and its value in the client instance. -// These params will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level param options. +// SetParam sets a single query parameter and its value in the client. func (c *Client) SetParam(key, val string) *Client { c.params.Set(key, val) return c } -// AddParams method adds multiple query params field and its values at one go in the client instance. -// These params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level params options. +// AddParams adds multiple query parameters and their values to the client. func (c *Client) AddParams(m map[string][]string) *Client { c.params.AddParams(m) return c } -// SetParams method sets multiple params field and its values at one go in the client instance. -// These params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level params options. +// SetParams sets multiple query parameters and their values in the client. func (c *Client) SetParams(m map[string]string) *Client { c.params.SetParams(m) return c } -// SetParamsWithStruct method sets multiple params field and its values at one go in the client instance. -// These params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level params options. +// SetParamsWithStruct sets multiple query parameters and their values using a struct. func (c *Client) SetParamsWithStruct(v any) *Client { c.params.SetParamsWithStruct(v) return c } -// DelParams method deletes single or multiple params field and its values in client. +// DelParams deletes one or more query parameters and their values from the client. func (c *Client) DelParams(key ...string) *Client { for _, v := range key { c.params.Del(v) @@ -376,64 +338,51 @@ func (c *Client) DelParams(key ...string) *Client { return c } -// SetUserAgent method sets userAgent field and its value in the client instance. -// This ua will be applied to all requests raised from this client instance. -// Also it can be overridden at request level ua options. +// SetUserAgent sets the User-Agent header for the client. func (c *Client) SetUserAgent(ua string) *Client { c.userAgent = ua return c } -// SetReferer method sets referer field and its value in the client instance. -// This referer will be applied to all requests raised from this client instance. -// Also it can be overridden at request level referer options. +// SetReferer sets the Referer header for the client. func (c *Client) SetReferer(r string) *Client { c.referer = r return c } -// PathParam returns the path param be set in request instance. -// if path param doesn't exist, return empty string. +// PathParam returns the value of the specified path parameter. Returns an empty string if it does not exist. func (c *Client) PathParam(key string) string { if val, ok := (*c.path)[key]; ok { return val } - return "" } -// SetPathParam method sets a single path param field and its value in the client instance. -// These path params will be applied to all requests raised from this client instance. -// Also it can be overridden at request level path params options. +// SetPathParam sets a single path parameter and its value in the client. func (c *Client) SetPathParam(key, val string) *Client { c.path.SetParam(key, val) return c } -// SetPathParams method sets multiple path params field and its values at one go in the client instance. -// These path params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level path params options. +// SetPathParams sets multiple path parameters and their values in the client. func (c *Client) SetPathParams(m map[string]string) *Client { c.path.SetParams(m) return c } -// SetPathParamsWithStruct method sets multiple path params field and its values at one go in the client instance. -// These path params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level path params options. +// SetPathParamsWithStruct sets multiple path parameters and their values using a struct. func (c *Client) SetPathParamsWithStruct(v any) *Client { c.path.SetParamsWithStruct(v) return c } -// DelPathParams method deletes single or multiple path params field and its values in client. +// DelPathParams deletes one or more path parameters and their values from the client. func (c *Client) DelPathParams(key ...string) *Client { c.path.DelParams(key...) return c } -// Cookie returns the cookie be set in request instance. -// if cookie doesn't exist, return empty string. +// Cookie returns the value of the specified cookie. Returns an empty string if it does not exist. func (c *Client) Cookie(key string) string { if val, ok := (*c.cookies)[key]; ok { return val @@ -441,127 +390,111 @@ func (c *Client) Cookie(key string) string { return "" } -// SetCookie method sets a single cookie field and its value in the client instance. -// These cookies will be applied to all requests raised from this client instance. -// Also it can be overridden at request level cookie options. +// SetCookie sets a single cookie and its value in the client. func (c *Client) SetCookie(key, val string) *Client { c.cookies.SetCookie(key, val) return c } -// SetCookies method sets multiple cookies field and its values at one go in the client instance. -// These cookies will be applied to all requests raised from this client instance. Also it can be -// overridden at request level cookie options. +// SetCookies sets multiple cookies and their values in the client. func (c *Client) SetCookies(m map[string]string) *Client { c.cookies.SetCookies(m) return c } -// SetCookiesWithStruct method sets multiple cookies field and its values at one go in the client instance. -// These cookies will be applied to all requests raised from this client instance. Also it can be -// overridden at request level cookies options. +// SetCookiesWithStruct sets multiple cookies and their values using a struct. func (c *Client) SetCookiesWithStruct(v any) *Client { c.cookies.SetCookiesWithStruct(v) return c } -// DelCookies method deletes single or multiple cookies field and its values in client. +// DelCookies deletes one or more cookies and their values from the client. func (c *Client) DelCookies(key ...string) *Client { c.cookies.DelCookies(key...) return c } -// SetTimeout method sets timeout val in client instance. -// This value will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level timeout options. +// SetTimeout sets the timeout value for the client. This applies to all requests unless overridden at the request level. func (c *Client) SetTimeout(t time.Duration) *Client { c.timeout = t return c } -// Debug enable log debug level output. +// Debug enables debug-level logging output. func (c *Client) Debug() *Client { c.debug = true return c } -// DisableDebug disables log debug level output. +// DisableDebug disables debug-level logging output. func (c *Client) DisableDebug() *Client { c.debug = false return c } -// SetCookieJar sets cookie jar in client instance. +// SetCookieJar sets the cookie jar for the client. func (c *Client) SetCookieJar(cookieJar *CookieJar) *Client { c.cookieJar = cookieJar return c } -// Get provide an API like axios which send get request. +// Get sends a GET request to the specified URL, similar to axios. func (c *Client) Get(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Get(url) } -// Post provide an API like axios which send post request. +// Post sends a POST request to the specified URL, similar to axios. func (c *Client) Post(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Post(url) } -// Head provide a API like axios which send head request. +// Head sends a HEAD request to the specified URL, similar to axios. func (c *Client) Head(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Head(url) } -// Put provide an API like axios which send put request. +// Put sends a PUT request to the specified URL, similar to axios. func (c *Client) Put(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Put(url) } -// Delete provide an API like axios which send delete request. +// Delete sends a DELETE request to the specified URL, similar to axios. func (c *Client) Delete(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Delete(url) } -// Options provide an API like axios which send options request. +// Options sends an OPTIONS request to the specified URL, similar to axios. func (c *Client) Options(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Options(url) } -// Patch provide an API like axios which send patch request. +// Patch sends a PATCH request to the specified URL, similar to axios. func (c *Client) Patch(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Patch(url) } -// Custom provide an API like axios which send custom request. +// Custom sends a request with a custom method to the specified URL, similar to axios. func (c *Client) Custom(url, method string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Custom(url, method) } -// SetDial sets dial function in client. +// SetDial sets the custom dial function for the client. func (c *Client) SetDial(dial fasthttp.DialFunc) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -570,7 +503,7 @@ func (c *Client) SetDial(dial fasthttp.DialFunc) *Client { return c } -// SetLogger sets logger instance in client. +// SetLogger sets the logger instance used by the client. func (c *Client) SetLogger(logger log.CommonLogger) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -579,12 +512,12 @@ func (c *Client) SetLogger(logger log.CommonLogger) *Client { return c } -// Logger returns logger instance of client. +// Logger returns the logger instance used by the client. func (c *Client) Logger() log.CommonLogger { return c.logger } -// Reset clears the Client object +// Reset resets the client to its default state, clearing most configurations. func (c *Client) Reset() { c.fasthttp = &fasthttp.Client{} c.baseURL = "" @@ -605,31 +538,25 @@ func (c *Client) Reset() { c.params.Reset() } -// Config for easy to set the request parameters, it should be -// noted that when setting the request body will use JSON as -// the default serialization mechanism, while the priority of -// Body is higher than FormData, and the priority of FormData -// is higher than File. +// Config is used to easily set request parameters. Note that when setting a request body, +// JSON is used as the default serialization mechanism. The priority is: +// Body > FormData > File. type Config struct { - Ctx context.Context //nolint:containedctx // It's needed to be stored in the config. - - Body any - Header map[string]string - Param map[string]string - Cookie map[string]string - PathParam map[string]string - - FormData map[string]string - - UserAgent string - Referer string - File []*File - - Timeout time.Duration + Ctx context.Context //nolint:containedctx // It's needed to be stored in the config. + Body any + Header map[string]string + Param map[string]string + Cookie map[string]string + PathParam map[string]string + FormData map[string]string + UserAgent string + Referer string + File []*File + Timeout time.Duration MaxRedirects int } -// setConfigToRequest Set the parameters passed via Config to Request. +// setConfigToRequest sets the parameters passed via Config to the Request. func setConfigToRequest(req *Request, config ...Config) { if len(config) == 0 { return @@ -694,21 +621,19 @@ var ( defaultUserAgent = "fiber" ) -// init acquire a default client. func init() { defaultClient = New() } // New creates and returns a new Client object. func New() *Client { - // FOllOW-UP performance optimization - // trie to use a pool to reduce the cost of memory allocation - // for the fiber client and the fasthttp client - // if possible also for other structs -> request header, cookie, query param, path param... + // Follow-up performance optimizations: + // Try to use a pool to reduce the memory allocation cost for the Fiber client and the fasthttp client. + // If possible, also consider pooling other structs (e.g., request headers, cookies, query parameters, path parameters). return NewWithClient(&fasthttp.Client{}) } -// NewWithClient creates and returns a new Client object from an existing client. +// NewWithClient creates and returns a new Client object from an existing fasthttp.Client. func NewWithClient(c *fasthttp.Client) *Client { if c == nil { panic("fasthttp.Client must not be nil") @@ -738,12 +663,12 @@ func NewWithClient(c *fasthttp.Client) *Client { } } -// C get default client. +// C returns the default client. func C() *Client { return defaultClient } -// Replace the defaultClient, the returned function can undo. +// Replace replaces the defaultClient with a new one, returning a function to restore the old client. func Replace(c *Client) func() { replaceMu.Lock() defer replaceMu.Unlock() @@ -759,37 +684,37 @@ func Replace(c *Client) func() { } } -// Get send a get request use defaultClient, a convenient method. +// Get sends a GET request using the default client. func Get(url string, cfg ...Config) (*Response, error) { return C().Get(url, cfg...) } -// Post send a post request use defaultClient, a convenient method. +// Post sends a POST request using the default client. func Post(url string, cfg ...Config) (*Response, error) { return C().Post(url, cfg...) } -// Head send a head request use defaultClient, a convenient method. +// Head sends a HEAD request using the default client. func Head(url string, cfg ...Config) (*Response, error) { return C().Head(url, cfg...) } -// Put send a put request use defaultClient, a convenient method. +// Put sends a PUT request using the default client. func Put(url string, cfg ...Config) (*Response, error) { return C().Put(url, cfg...) } -// Delete send a delete request use defaultClient, a convenient method. +// Delete sends a DELETE request using the default client. func Delete(url string, cfg ...Config) (*Response, error) { return C().Delete(url, cfg...) } -// Options send a options request use defaultClient, a convenient method. +// Options sends an OPTIONS request using the default client. func Options(url string, cfg ...Config) (*Response, error) { return C().Options(url, cfg...) } -// Patch send a patch request use defaultClient, a convenient method. +// Patch sends a PATCH request using the default client. func Patch(url string, cfg ...Config) (*Response, error) { return C().Patch(url, cfg...) } diff --git a/client/cookiejar.go b/client/cookiejar.go index 834357fbbe..92669b70b0 100644 --- a/client/cookiejar.go +++ b/client/cookiejar.go @@ -1,4 +1,4 @@ -// The code has been taken from https://github.com/valyala/fasthttp/pull/526 originally. +// The code was originally taken from https://github.com/valyala/fasthttp/pull/526. package client import ( @@ -18,7 +18,7 @@ var cookieJarPool = sync.Pool{ }, } -// AcquireCookieJar returns an empty CookieJar object from pool. +// AcquireCookieJar returns an empty CookieJar object from the pool. func AcquireCookieJar() *CookieJar { jar, ok := cookieJarPool.Get().(*CookieJar) if !ok { @@ -28,22 +28,23 @@ func AcquireCookieJar() *CookieJar { return jar } -// ReleaseCookieJar returns CookieJar to the pool. +// ReleaseCookieJar returns a CookieJar object to the pool. func ReleaseCookieJar(c *CookieJar) { c.Release() cookieJarPool.Put(c) } -// CookieJar manages cookie storage. It is used by the client to store cookies. +// CookieJar manages cookie storage for the client. It stores cookies keyed by host. type CookieJar struct { hostCookies map[string][]*fasthttp.Cookie mu sync.Mutex } -// Get returns the cookies stored from a specific domain. -// If there were no cookies related with host returned slice will be nil. +// Get returns all cookies stored for a given URI. If there are no cookies for the +// provided host, the returned slice will be nil. // -// CookieJar keeps a copy of the cookies, so the returned cookies can be released safely. +// The CookieJar keeps its own copies of cookies, so it is safe to release the returned +// cookies after use. func (cj *CookieJar) Get(uri *fasthttp.URI) []*fasthttp.Cookie { if uri == nil { return nil @@ -52,7 +53,7 @@ func (cj *CookieJar) Get(uri *fasthttp.URI) []*fasthttp.Cookie { return cj.getByHostAndPath(uri.Host(), uri.Path()) } -// get returns the cookies stored from a specific host and path. +// getByHostAndPath returns cookies stored for a specific host and path. func (cj *CookieJar) getByHostAndPath(host, path []byte) []*fasthttp.Cookie { if cj.hostCookies == nil { return nil @@ -84,8 +85,7 @@ func (cj *CookieJar) getByHostAndPath(host, path []byte) []*fasthttp.Cookie { return newCookies } -// getCookiesByHost returns the cookies stored from a specific host. -// If cookies are expired they will be deleted. +// getCookiesByHost returns cookies stored for a specific host, removing any that have expired. func (cj *CookieJar) getCookiesByHost(host string) []*fasthttp.Cookie { cj.mu.Lock() defer cj.mu.Unlock() @@ -95,7 +95,8 @@ func (cj *CookieJar) getCookiesByHost(host string) []*fasthttp.Cookie { for i := 0; i < len(cookies); i++ { c := cookies[i] - if !c.Expire().Equal(fasthttp.CookieExpireUnlimited) && c.Expire().Before(now) { // release cookie if expired + // Remove expired cookies. + if !c.Expire().Equal(fasthttp.CookieExpireUnlimited) && c.Expire().Before(now) { cookies = append(cookies[:i], cookies[i+1:]...) fasthttp.ReleaseCookie(c) i-- @@ -105,23 +106,21 @@ func (cj *CookieJar) getCookiesByHost(host string) []*fasthttp.Cookie { return cookies } -// Set sets cookies for a specific host. -// The host is get from uri.Host(). -// If the cookie key already exists it will be replaced by the new cookie value. +// Set stores the given cookies for the specified URI host. If a cookie key already exists, +// it will be replaced by the new cookie value. // -// CookieJar keeps a copy of the cookies, so the parsed cookies can be released safely. +// CookieJar stores copies of the provided cookies, so they may be safely released after use. func (cj *CookieJar) Set(uri *fasthttp.URI, cookies ...*fasthttp.Cookie) { if uri == nil { return } - cj.SetByHost(uri.Host(), cookies...) } -// SetByHost sets cookies for a specific host. -// If the cookie key already exists it will be replaced by the new cookie value. +// SetByHost stores the given cookies for the specified host. If a cookie key already exists, +// it will be replaced by the new cookie value. // -// CookieJar keeps a copy of the cookies, so the parsed cookies can be released safely. +// CookieJar stores copies of the provided cookies, so they may be safely released after use. func (cj *CookieJar) SetByHost(host []byte, cookies ...*fasthttp.Cookie) { hostStr := utils.UnsafeString(host) @@ -134,26 +133,25 @@ func (cj *CookieJar) SetByHost(host []byte, cookies ...*fasthttp.Cookie) { hostCookies, ok := cj.hostCookies[hostStr] if !ok { - // If the key does not exist in the map, then we must make a copy for the key to avoid unsafe usage. + // If the key does not exist in the map, make a copy to avoid unsafe usage. hostStr = string(host) } for _, cookie := range cookies { - c := searchCookieByKeyAndPath(cookie.Key(), cookie.Path(), hostCookies) - if c == nil { - // If the cookie does not exist in the slice, let's acquire new cookie and store it. - c = fasthttp.AcquireCookie() - hostCookies = append(hostCookies, c) + existing := searchCookieByKeyAndPath(cookie.Key(), cookie.Path(), hostCookies) + if existing == nil { + // If the cookie does not exist, acquire a new one. + existing = fasthttp.AcquireCookie() + hostCookies = append(hostCookies, existing) } - c.CopyTo(cookie) // override cookie properties + existing.CopyTo(cookie) // Override cookie properties. } cj.hostCookies[hostStr] = hostCookies } -// SetKeyValue sets a cookie by key and value for a specific host. +// SetKeyValue sets a cookie for the specified host with the given key and value. // -// This function prevents extra allocations by making repeated cookies -// not being duplicated. +// This function helps prevent extra allocations by avoiding duplication of repeated cookies. func (cj *CookieJar) SetKeyValue(host, key, value string) { c := fasthttp.AcquireCookie() c.SetKey(key) @@ -162,10 +160,9 @@ func (cj *CookieJar) SetKeyValue(host, key, value string) { cj.SetByHost(utils.UnsafeBytes(host), c) } -// SetKeyValueBytes sets a cookie by key and value for a specific host. +// SetKeyValueBytes sets a cookie for the specified host using byte slices for the key and value. // -// This function prevents extra allocations by making repeated cookies -// not being duplicated. +// This function helps prevent extra allocations by avoiding duplication of repeated cookies. func (cj *CookieJar) SetKeyValueBytes(host string, key, value []byte) { c := fasthttp.AcquireCookie() c.SetKeyBytes(key) @@ -174,17 +171,16 @@ func (cj *CookieJar) SetKeyValueBytes(host string, key, value []byte) { cj.SetByHost(utils.UnsafeBytes(host), c) } -// dumpCookiesToReq dumps the stored cookies to the request. +// dumpCookiesToReq writes the stored cookies to the given request. func (cj *CookieJar) dumpCookiesToReq(req *fasthttp.Request) { uri := req.URI() - cookies := cj.getByHostAndPath(uri.Host(), uri.Path()) for _, cookie := range cookies { req.Header.SetCookieBytesKV(cookie.Key(), cookie.Value()) } } -// parseCookiesFromResp parses the response cookies and stores them. +// parseCookiesFromResp parses the cookies from the response and stores them for the specified host and path. func (cj *CookieJar) parseCookiesFromResp(host, path []byte, resp *fasthttp.Response) { hostStr := utils.UnsafeString(host) @@ -194,35 +190,36 @@ func (cj *CookieJar) parseCookiesFromResp(host, path []byte, resp *fasthttp.Resp if cj.hostCookies == nil { cj.hostCookies = make(map[string][]*fasthttp.Cookie) } + cookies, ok := cj.hostCookies[hostStr] if !ok { - // If the key does not exist in the map then - // we must make a copy for the key to avoid unsafe usage. + // If the key does not exist in the map, make a copy to avoid unsafe usage. hostStr = string(host) } now := time.Now() resp.Header.VisitAllCookie(func(key, value []byte) { - isCreated := false + created := false c := searchCookieByKeyAndPath(key, path, cookies) if c == nil { - c, isCreated = fasthttp.AcquireCookie(), true + c, created = fasthttp.AcquireCookie(), true } _ = c.ParseBytes(value) //nolint:errcheck // ignore error if c.Expire().Equal(fasthttp.CookieExpireUnlimited) || c.Expire().After(now) { cookies = append(cookies, c) - } else if isCreated { + } else if created { fasthttp.ReleaseCookie(c) } }) cj.hostCookies[hostStr] = cookies } -// Release releases all cookie values. +// Release releases all stored cookies. After this, the CookieJar is empty. func (cj *CookieJar) Release() { - // FOllOW-UP performance optimization - // currently a race condition is found because the reset method modifies a value which is not a copy but a reference -> solution should be to make a copy + // FOLLOW-UP performance optimization: + // Currently, a race condition is found because the reset method modifies a value + // that is not a copy but a reference. A solution would be to make a copy. // for _, v := range cj.hostCookies { // for _, c := range v { // fasthttp.ReleaseCookie(c) @@ -231,7 +228,7 @@ func (cj *CookieJar) Release() { cj.hostCookies = nil } -// searchCookieByKeyAndPath searches for a cookie by key and path. +// searchCookieByKeyAndPath looks up a cookie by its key and path from the provided slice of cookies. func searchCookieByKeyAndPath(key, path []byte, cookies []*fasthttp.Cookie) *fasthttp.Cookie { for _, c := range cookies { if bytes.Equal(key, c.Key()) { @@ -240,6 +237,5 @@ func searchCookieByKeyAndPath(key, path []byte, cookies []*fasthttp.Cookie) *fas } } } - return nil } diff --git a/client/core.go b/client/core.go index 315d12d474..c669d3ac82 100644 --- a/client/core.go +++ b/client/core.go @@ -16,23 +16,21 @@ import ( var boundary = "--FiberFormBoundary" -// RequestHook is a function that receives Agent and Request, -// it can change the data in Request and Agent. -// -// Called before a request is sent. +// RequestHook is a function invoked before the request is sent. +// It receives a Client and a Request, allowing you to modify the Request or Client data. type RequestHook func(*Client, *Request) error -// ResponseHook is a function that receives Agent, Response and Request, -// it can change the data is Response or deal with some effects. -// -// Called after a response has been received. +// ResponseHook is a function invoked after a response is received. +// It receives a Client, Response, and Request, allowing you to modify the Response data +// or perform actions based on the response. type ResponseHook func(*Client, *Response, *Request) error -// RetryConfig is an alias for config in the `addon/retry` package. +// RetryConfig is an alias for the `retry.Config` type from the `addon/retry` package. type RetryConfig = retry.Config -// addMissingPort will add the corresponding port number for host. -func addMissingPort(addr string, isTLS bool) string { //revive:disable-line:flag-parameter // Accepting a bool param named isTLS if fine here +// addMissingPort appends the appropriate port number to the given address if it doesn't have one. +// If isTLS is true, it uses port 443; otherwise, it uses port 80. +func addMissingPort(addr string, isTLS bool) string { //revive:disable-line:flag-parameter n := strings.Index(addr, ":") if n >= 0 { return addr @@ -44,15 +42,14 @@ func addMissingPort(addr string, isTLS bool) string { //revive:disable-line:flag return net.JoinHostPort(addr, strconv.Itoa(port)) } -// `core` stores middleware and plugin definitions, -// and defines the execution process +// core stores middleware and plugin definitions and defines the request execution process. type core struct { client *Client req *Request - ctx context.Context //nolint:containedctx // It's needed to be stored in the core. + ctx context.Context //nolint:containedctx // Context is needed here. } -// getRetryConfig returns the retry configuration of the client. +// getRetryConfig returns a copy of the client's retry configuration. func (c *core) getRetryConfig() *RetryConfig { c.client.mu.RLock() defer c.client.mu.RUnlock() @@ -70,19 +67,16 @@ func (c *core) getRetryConfig() *RetryConfig { } } -// execFunc is the core function of the client. -// It sends the request and receives the response. +// execFunc is the core logic to send the request and receive the response. +// It leverages the fasthttp client, optionally with retries or redirects. func (c *core) execFunc() (*Response, error) { resp := AcquireResponse() resp.setClient(c.client) resp.setRequest(c.req) - // To avoid memory allocation reuse of data structures such as errch. done := int32(0) errCh, reqv := acquireErrChan(), fasthttp.AcquireRequest() - defer func() { - releaseErrChan(errCh) - }() + defer releaseErrChan(errCh) c.req.RawRequest.CopyTo(reqv) cfg := c.getRetryConfig() @@ -96,11 +90,11 @@ func (c *core) execFunc() (*Response, error) { }() if cfg != nil { + // Use an exponential backoff retry strategy. err = retry.NewExponentialBackoff(*cfg).Retry(func() error { if c.req.maxRedirects > 0 && (string(reqv.Header.Method()) == fiber.MethodGet || string(reqv.Header.Method()) == fiber.MethodHead) { return c.client.fasthttp.DoRedirects(reqv, respv, c.req.maxRedirects) } - return c.client.fasthttp.Do(reqv, respv) }) } else { @@ -124,7 +118,7 @@ func (c *core) execFunc() (*Response, error) { select { case err := <-errCh: if err != nil { - // When get error should release Response + // Release the response if an error occurs. ReleaseResponse(resp) return nil, err } @@ -136,21 +130,19 @@ func (c *core) execFunc() (*Response, error) { } } -// preHooks Exec request hook +// preHooks runs all request hooks before sending the request. func (c *core) preHooks() error { c.client.mu.Lock() defer c.client.mu.Unlock() for _, f := range c.client.userRequestHooks { - err := f(c.client, c.req) - if err != nil { + if err := f(c.client, c.req); err != nil { return err } } for _, f := range c.client.builtinRequestHooks { - err := f(c.client, c.req) - if err != nil { + if err := f(c.client, c.req); err != nil { return err } } @@ -158,21 +150,19 @@ func (c *core) preHooks() error { return nil } -// afterHooks Exec response hooks +// afterHooks runs all response hooks after receiving the response. func (c *core) afterHooks(resp *Response) error { c.client.mu.Lock() defer c.client.mu.Unlock() for _, f := range c.client.builtinResponseHooks { - err := f(c.client, resp, c.req) - if err != nil { + if err := f(c.client, resp, c.req); err != nil { return err } } for _, f := range c.client.userResponseHooks { - err := f(c.client, resp, c.req) - if err != nil { + if err := f(c.client, resp, c.req); err != nil { return err } } @@ -180,7 +170,7 @@ func (c *core) afterHooks(resp *Response) error { return nil } -// timeout deals with timeout +// timeout applies the configured timeout to the request, if any. func (c *core) timeout() context.CancelFunc { var cancel context.CancelFunc @@ -193,35 +183,32 @@ func (c *core) timeout() context.CancelFunc { return cancel } -// execute will exec each hooks and plugins. +// execute runs all hooks, applies timeouts, sends the request, and runs response hooks. func (c *core) execute(ctx context.Context, client *Client, req *Request) (*Response, error) { - // keep a reference, because pass param is boring + // Store references locally. c.ctx = ctx c.client = client c.req = req - // The built-in hooks will be executed only - // after the user-defined hooks are executed. - err := c.preHooks() - if err != nil { + // Execute pre request hooks (user-defined and built-in). + if err := c.preHooks(); err != nil { return nil, err } + // Apply timeout if specified. cancel := c.timeout() if cancel != nil { defer cancel() } - // Do http request + // Perform the actual HTTP request. resp, err := c.execFunc() if err != nil { return nil, err } - // The built-in hooks will be executed only - // before the user-defined hooks are executed. - err = c.afterHooks(resp) - if err != nil { + // Execute after response hooks (built-in and then user-defined). + if err := c.afterHooks(resp); err != nil { resp.Close() return nil, err } @@ -235,38 +222,35 @@ var errChanPool = &sync.Pool{ }, } -// acquireErrChan returns an empty error chan from the pool. +// acquireErrChan returns an empty error channel from the pool. // -// The returned error chan may be returned to the pool with releaseErrChan when no longer needed. -// This allows reducing GC load. +// The returned channel may be returned to the pool with releaseErrChan when no longer needed, +// reducing GC load. func acquireErrChan() chan error { ch, ok := errChanPool.Get().(chan error) if !ok { panic(errors.New("failed to type-assert to chan error")) } - return ch } -// releaseErrChan returns the object acquired via acquireErrChan to the pool. +// releaseErrChan returns the error channel to the pool. // -// Do not access the released core object, otherwise data races may occur. +// Do not use the released channel afterward to avoid data races. func releaseErrChan(ch chan error) { errChanPool.Put(ch) } -// newCore returns an empty core object. +// newCore returns a new core object. func newCore() *core { - c := &core{} - - return c + return &core{} } var ( ErrTimeoutOrCancel = errors.New("timeout or cancel") - ErrURLFormat = errors.New("the url is a mistake") - ErrNotSupportSchema = errors.New("the protocol is not support, only http or https") - ErrFileNoName = errors.New("the file should have name") + ErrURLFormat = errors.New("the URL is incorrect") + ErrNotSupportSchema = errors.New("protocol not supported; only http or https are allowed") + ErrFileNoName = errors.New("the file should have a name") ErrBodyType = errors.New("the body type should be []byte") - ErrNotSupportSaveMethod = errors.New("file path and io.Writer are supported") + ErrNotSupportSaveMethod = errors.New("only file paths and io.Writer are supported") ) diff --git a/client/hooks.go b/client/hooks.go index 4c36145f2f..9dbfc89e72 100644 --- a/client/hooks.go +++ b/client/hooks.go @@ -28,12 +28,12 @@ var ( multipartFormData = "multipart/form-data" letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - letterIdxBits = 6 // 6 bits to represent a letter index - letterIdxMask = 1<