package fiberimport ()// Request represents HTTP request.//// It is forbidden copying Request instances. Create new instances// and use CopyTo instead.//// Request instance MUST NOT be used from concurrently running goroutines.// Copy from fasthttptypeRequest = fasthttp.Request// Response represents HTTP response.//// It is forbidden copying Response instances. Create new instances// and use CopyTo instead.//// Response instance MUST NOT be used from concurrently running goroutines.// Copy from fasthttptypeResponse = fasthttp.Response// Args represents query arguments.//// It is forbidden copying Args instances. Create new instances instead// and use CopyTo().//// Args instance MUST NOT be used from concurrently running goroutines.// Copy from fasthttptypeArgs = fasthttp.Args// RetryIfFunc signature of retry if function// Request argument passed to RetryIfFunc, if there are any request errors.// Copy from fasthttptypeRetryIfFunc = fasthttp.RetryIfFuncvar defaultClient Client// Client implements http client.//// It is safe calling Client methods from concurrently running goroutines.typeClientstruct { mutex sync.RWMutex// UserAgent is used in User-Agent request header. UserAgent string// NoDefaultUserAgentHeader when set to true, causes the default // User-Agent header to be excluded from the Request. NoDefaultUserAgentHeader bool// When set by an external client of Fiber it will use the provided implementation of a // JSONMarshal // // Allowing for flexibility in using another json library for encoding JSONEncoder utils.JSONMarshal// When set by an external client of Fiber it will use the provided implementation of a // JSONUnmarshal // // Allowing for flexibility in using another json library for decoding JSONDecoder utils.JSONUnmarshal}// Get returns an agent with http method GET.func ( string) *Agent { returndefaultClient.Get() }// Get returns an agent with http method GET.func ( *Client) ( string) *Agent {return .createAgent(MethodGet, )}// Head returns an agent with http method HEAD.func ( string) *Agent { returndefaultClient.Head() }// Head returns an agent with http method GET.func ( *Client) ( string) *Agent {return .createAgent(MethodHead, )}// Post sends POST request to the given URL.func ( string) *Agent { returndefaultClient.Post() }// Post sends POST request to the given URL.func ( *Client) ( string) *Agent {return .createAgent(MethodPost, )}// Put sends PUT request to the given URL.func ( string) *Agent { returndefaultClient.Put() }// Put sends PUT request to the given URL.func ( *Client) ( string) *Agent {return .createAgent(MethodPut, )}// Patch sends PATCH request to the given URL.func ( string) *Agent { returndefaultClient.Patch() }// Patch sends PATCH request to the given URL.func ( *Client) ( string) *Agent {return .createAgent(MethodPatch, )}// Delete sends DELETE request to the given URL.func ( string) *Agent { returndefaultClient.Delete() }// Delete sends DELETE request to the given URL.func ( *Client) ( string) *Agent {return .createAgent(MethodDelete, )}func ( *Client) (, string) *Agent { := AcquireAgent() .req.Header.SetMethod() .req.SetRequestURI() .mutex.RLock() .Name = .UserAgent .NoDefaultUserAgentHeader = .NoDefaultUserAgentHeader .jsonDecoder = .JSONDecoder .jsonEncoder = .JSONEncoderif .jsonDecoder == nil { .jsonDecoder = json.Unmarshal } .mutex.RUnlock()if := .Parse(); != nil { .errs = append(.errs, ) }return}// Agent is an object storing all request data for client.// Agent instance MUST NOT be used from concurrently running goroutines.typeAgentstruct {// Name is used in User-Agent request header. Name string// NoDefaultUserAgentHeader when set to true, causes the default // User-Agent header to be excluded from the Request. NoDefaultUserAgentHeader bool// HostClient is an embedded fasthttp HostClient *fasthttp.HostClient req *Request resp *Response dest []byte args *Args timeout time.Duration errs []error formFiles []*FormFile debugWriter io.Writer mw multipartWriter jsonEncoder utils.JSONMarshal jsonDecoder utils.JSONUnmarshal maxRedirectsCount int boundary string reuse bool parsed bool}// Parse initializes URI and HostClient.func ( *Agent) () error {if .parsed {returnnil } .parsed = true := .req.URI()varbool := .Scheme()ifbytes.Equal(, []byte(schemeHTTPS)) { = true } elseif !bytes.Equal(, []byte(schemeHTTP)) {returnfmt.Errorf("unsupported protocol %q. http and https are supported", ) } := .Nameif == "" && !.NoDefaultUserAgentHeader { = defaultUserAgent } .HostClient = &fasthttp.HostClient{Addr: fasthttp.AddMissingPort(string(.Host()), ),Name: ,NoDefaultUserAgentHeader: .NoDefaultUserAgentHeader,IsTLS: , }returnnil}/************************** Header Setting **************************/// Set sets the given 'key: value' header.//// Use Add for setting multiple header values under the same key.func ( *Agent) (, string) *Agent { .req.Header.Set(, )return}// SetBytesK sets the given 'key: value' header.//// Use AddBytesK for setting multiple header values under the same key.func ( *Agent) ( []byte, string) *Agent { .req.Header.SetBytesK(, )return}// SetBytesV sets the given 'key: value' header.//// Use AddBytesV for setting multiple header values under the same key.func ( *Agent) ( string, []byte) *Agent { .req.Header.SetBytesV(, )return}// SetBytesKV sets the given 'key: value' header.//// Use AddBytesKV for setting multiple header values under the same key.func ( *Agent) (, []byte) *Agent { .req.Header.SetBytesKV(, )return}// Add adds the given 'key: value' header.//// Multiple headers with the same key may be added with this function.// Use Set for setting a single header for the given key.func ( *Agent) (, string) *Agent { .req.Header.Add(, )return}// AddBytesK adds the given 'key: value' header.//// Multiple headers with the same key may be added with this function.// Use SetBytesK for setting a single header for the given key.func ( *Agent) ( []byte, string) *Agent { .req.Header.AddBytesK(, )return}// AddBytesV adds the given 'key: value' header.//// Multiple headers with the same key may be added with this function.// Use SetBytesV for setting a single header for the given key.func ( *Agent) ( string, []byte) *Agent { .req.Header.AddBytesV(, )return}// AddBytesKV adds the given 'key: value' header.//// Multiple headers with the same key may be added with this function.// Use SetBytesKV for setting a single header for the given key.func ( *Agent) (, []byte) *Agent { .req.Header.AddBytesKV(, )return}// ConnectionClose sets 'Connection: close' header.func ( *Agent) () *Agent { .req.Header.SetConnectionClose()return}// UserAgent sets User-Agent header value.func ( *Agent) ( string) *Agent { .req.Header.SetUserAgent()return}// UserAgentBytes sets User-Agent header value.func ( *Agent) ( []byte) *Agent { .req.Header.SetUserAgentBytes()return}// Cookie sets one 'key: value' cookie.func ( *Agent) (, string) *Agent { .req.Header.SetCookie(, )return}// CookieBytesK sets one 'key: value' cookie.func ( *Agent) ( []byte, string) *Agent { .req.Header.SetCookieBytesK(, )return}// CookieBytesKV sets one 'key: value' cookie.func ( *Agent) (, []byte) *Agent { .req.Header.SetCookieBytesKV(, )return}// Cookies sets multiple 'key: value' cookies.func ( *Agent) ( ...string) *Agent {for := 1; < len(); += 2 { .req.Header.SetCookie([-1], []) }return}// CookiesBytesKV sets multiple 'key: value' cookies.func ( *Agent) ( ...[]byte) *Agent {for := 1; < len(); += 2 { .req.Header.SetCookieBytesKV([-1], []) }return}// Referer sets Referer header value.func ( *Agent) ( string) *Agent { .req.Header.SetReferer()return}// RefererBytes sets Referer header value.func ( *Agent) ( []byte) *Agent { .req.Header.SetRefererBytes()return}// ContentType sets Content-Type header value.func ( *Agent) ( string) *Agent { .req.Header.SetContentType()return}// ContentTypeBytes sets Content-Type header value.func ( *Agent) ( []byte) *Agent { .req.Header.SetContentTypeBytes()return}/************************** End Header Setting **************************//************************** URI Setting **************************/// Host sets host for the URI.func ( *Agent) ( string) *Agent { .req.URI().SetHost()return}// HostBytes sets host for the URI.func ( *Agent) ( []byte) *Agent { .req.URI().SetHostBytes()return}// QueryString sets URI query string.func ( *Agent) ( string) *Agent { .req.URI().SetQueryString()return}// QueryStringBytes sets URI query string.func ( *Agent) ( []byte) *Agent { .req.URI().SetQueryStringBytes()return}// BasicAuth sets URI username and password.func ( *Agent) (, string) *Agent { .req.URI().SetUsername() .req.URI().SetPassword()return}// BasicAuthBytes sets URI username and password.func ( *Agent) (, []byte) *Agent { .req.URI().SetUsernameBytes() .req.URI().SetPasswordBytes()return}/************************** End URI Setting **************************//************************** Request Setting **************************/// BodyString sets request body.func ( *Agent) ( string) *Agent { .req.SetBodyString()return}// Body sets request body.func ( *Agent) ( []byte) *Agent { .req.SetBody()return}// BodyStream sets request body stream and, optionally body size.//// If bodySize is >= 0, then the bodyStream must provide exactly bodySize bytes// before returning io.EOF.//// If bodySize < 0, then bodyStream is read until io.EOF.//// bodyStream.Close() is called after finishing reading all body data// if it implements io.Closer.//// Note that GET and HEAD requests cannot have body.func ( *Agent) ( io.Reader, int) *Agent { .req.SetBodyStream(, )return}// JSON sends a JSON request.func ( *Agent) ( interface{}) *Agent {if .jsonEncoder == nil { .jsonEncoder = json.Marshal } .req.Header.SetContentType(MIMEApplicationJSON)if , := .jsonEncoder(); != nil { .errs = append(.errs, ) } else { .req.SetBody() }return}// XML sends an XML request.func ( *Agent) ( interface{}) *Agent { .req.Header.SetContentType(MIMEApplicationXML)if , := xml.Marshal(); != nil { .errs = append(.errs, ) } else { .req.SetBody() }return}// Form sends form request with body if args is non-nil.//// It is recommended obtaining args via AcquireArgs and release it// manually in performance-critical code.func ( *Agent) ( *Args) *Agent { .req.Header.SetContentType(MIMEApplicationForm)if != nil { .req.SetBody(.QueryString()) }return}// FormFile represents multipart form filetypeFormFilestruct {// Fieldname is form file's field name Fieldname string// Name is form file's name Name string// Content is form file's content Content []byte// autoRelease indicates if returns the object // acquired via AcquireFormFile to the pool. autoRelease bool}// FileData appends files for multipart form request.//// It is recommended obtaining formFile via AcquireFormFile and release it// manually in performance-critical code.func ( *Agent) ( ...*FormFile) *Agent { .formFiles = append(.formFiles, ...)return}// SendFile reads file and appends it to multipart form request.func ( *Agent) ( string, ...string) *Agent { , := os.ReadFile(filepath.Clean())if != nil { .errs = append(.errs, )return } := AcquireFormFile()iflen() > 0 && [0] != "" { .Fieldname = [0] } else { .Fieldname = "file" + strconv.Itoa(len(.formFiles)+1) } .Name = filepath.Base() .Content = append(.Content, ...) .autoRelease = true .formFiles = append(.formFiles, )return}// SendFiles reads files and appends them to multipart form request.//// Examples://// SendFile("/path/to/file1", "fieldname1", "/path/to/file2")func ( *Agent) ( ...string) *Agent { := len()if &1 == 1 { = append(, "") }for := 0; < ; += 2 { .SendFile([], [+1]) }return}// Boundary sets boundary for multipart form request.func ( *Agent) ( string) *Agent { .boundary = return}// MultipartForm sends multipart form request with k-v and files.//// It is recommended obtaining args via AcquireArgs and release it// manually in performance-critical code.func ( *Agent) ( *Args) *Agent {if .mw == nil { .mw = multipart.NewWriter(.req.BodyWriter()) }if .boundary != "" {if := .mw.SetBoundary(.boundary); != nil { .errs = append(.errs, )return } } .req.Header.SetMultipartFormBoundary(.mw.Boundary())if != nil { .VisitAll(func(, []byte) {if := .mw.WriteField(utils.UnsafeString(), utils.UnsafeString()); != nil { .errs = append(.errs, ) } }) }for , := range .formFiles { , := .mw.CreateFormFile(.Fieldname, .Name)if != nil { .errs = append(.errs, )continue }if _, = .Write(.Content); != nil { .errs = append(.errs, ) } }if := .mw.Close(); != nil { .errs = append(.errs, ) }return}/************************** End Request Setting **************************//************************** Agent Setting **************************/// Debug mode enables logging request and response detailfunc ( *Agent) ( ...io.Writer) *Agent { .debugWriter = os.Stdoutiflen() > 0 { .debugWriter = [0] }return}// Timeout sets request timeout duration.func ( *Agent) ( time.Duration) *Agent { .timeout = return}// Reuse enables the Agent instance to be used again after one request.//// If agent is reusable, then it should be released manually when it is no// longer used.func ( *Agent) () *Agent { .reuse = truereturn}// InsecureSkipVerify controls whether the Agent verifies the server// certificate chain and host name.func ( *Agent) () *Agent {if .HostClient.TLSConfig == nil { .HostClient.TLSConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec // We explicitly let the user set insecure mode here } else { .HostClient.TLSConfig.InsecureSkipVerify = true }return}// TLSConfig sets tls config.func ( *Agent) ( *tls.Config) *Agent { .HostClient.TLSConfig = return}// MaxRedirectsCount sets max redirect count for GET and HEAD.func ( *Agent) ( int) *Agent { .maxRedirectsCount = return}// JSONEncoder sets custom json encoder.func ( *Agent) ( utils.JSONMarshal) *Agent { .jsonEncoder = return}// JSONDecoder sets custom json decoder.func ( *Agent) ( utils.JSONUnmarshal) *Agent { .jsonDecoder = return}// Request returns Agent request instance.func ( *Agent) () *Request {return .req}// SetResponse sets custom response for the Agent instance.//// It is recommended obtaining custom response via AcquireResponse and release it// manually in performance-critical code.func ( *Agent) ( *Response) *Agent { .resp = return}// Dest sets custom dest.//// The contents of dest will be replaced by the response body, if the dest// is too small a new slice will be allocated.func ( *Agent) ( []byte) *Agent { .dest = return}// RetryIf controls whether a retry should be attempted after an error.//// By default, will use isIdempotent function from fasthttpfunc ( *Agent) ( RetryIfFunc) *Agent { .HostClient.RetryIf = return}/************************** End Agent Setting **************************/// Bytes returns the status code, bytes body and errors of url.//// it's not safe to use Agent after calling [Agent.Bytes]func ( *Agent) () (int, []byte, []error) {defer .release()return .bytes()}func ( *Agent) () ( int, []byte, []error) { //nolint:nonamedreturns,revive // We want to overwrite the body in a deferred func. TODO: Check if we really need to do this. We eventually want to get rid of all named returns.if = append(, .errs...); len() > 0 {return , , }var ( = .req *Responsebool )if .resp == nil { = AcquireResponse() = true } else { = .resp }deferfunc() {if .debugWriter != nil {printDebugInfo(, , .debugWriter) }iflen() == 0 { = .StatusCode() } = append(.dest, .Body()...) //nolint:gocritic // We want to append to the returned slice hereif {ReleaseResponse() } }()if .timeout > 0 {if := .HostClient.DoTimeout(, , .timeout); != nil { = append(, )return , , } } elseif .maxRedirectsCount > 0 && (string(.Header.Method()) == MethodGet || string(.Header.Method()) == MethodHead) {if := .HostClient.DoRedirects(, , .maxRedirectsCount); != nil { = append(, )return , , } } elseif := .HostClient.Do(, ); != nil { = append(, ) }return , , }func printDebugInfo( *Request, *Response, io.Writer) { := fmt.Sprintf("Connected to %s(%s)\r\n\r\n", .URI().Host(), .RemoteAddr()) _, _ = .Write(utils.UnsafeBytes()) //nolint:errcheck // This will never fail _, _ = .WriteTo() //nolint:errcheck // This will never fail _, _ = .WriteTo() //nolint:errcheck // This will never fail}// String returns the status code, string body and errors of url.//// it's not safe to use Agent after calling [Agent.String]func ( *Agent) () (int, string, []error) {defer .release() , , := .bytes()// TODO: There might be a data race here on body. Maybe use utils.CopyBytes on it?return , utils.UnsafeString(), }// Struct returns the status code, bytes body and errors of URL.// And bytes body will be unmarshalled to given v.//// it's not safe to use Agent after calling [Agent.Struct]func ( *Agent) ( interface{}) (int, []byte, []error) {defer .release() , , := .bytes()iflen() > 0 {return , , }// TODO: This should only be done onceif .jsonDecoder == nil { .jsonDecoder = json.Unmarshal }if := .jsonDecoder(, ); != nil { = append(, ) }return , , }func ( *Agent) () {if !.reuse {ReleaseAgent() } else { .errs = .errs[:0] }}func ( *Agent) () { .HostClient = nil .req.Reset() .resp = nil .dest = nil .timeout = 0 .args = nil .errs = .errs[:0] .debugWriter = nil .mw = nil .reuse = false .parsed = false .maxRedirectsCount = 0 .boundary = "" .Name = "" .NoDefaultUserAgentHeader = falsefor , := range .formFiles {if .autoRelease {ReleaseFormFile() } .formFiles[] = nil } .formFiles = .formFiles[:0]}var ( clientPool sync.Pool agentPool = sync.Pool{New: func() interface{} {return &Agent{req: &Request{}} }, } responsePool sync.Pool argsPool sync.Pool formFilePool sync.Pool)// AcquireClient returns an empty Client instance from client pool.//// The returned Client instance may be passed to ReleaseClient when it is// no longer needed. This allows Client recycling, reduces GC pressure// and usually improves performance.func () *Client { := clientPool.Get()if == nil {return &Client{} } , := .(*Client)if ! {panic(fmt.Errorf("failed to type-assert to *Client")) }return}// ReleaseClient returns c acquired via AcquireClient to client pool.//// It is forbidden accessing req and/or it's members after returning// it to client pool.func ( *Client) { .UserAgent = "" .NoDefaultUserAgentHeader = false .JSONEncoder = nil .JSONDecoder = nilclientPool.Put()}// AcquireAgent returns an empty Agent instance from Agent pool.//// The returned Agent instance may be passed to ReleaseAgent when it is// no longer needed. This allows Agent recycling, reduces GC pressure// and usually improves performance.func () *Agent { , := agentPool.Get().(*Agent)if ! {panic(fmt.Errorf("failed to type-assert to *Agent")) }return}// ReleaseAgent returns an acquired via AcquireAgent to Agent pool.//// It is forbidden accessing req and/or it's members after returning// it to Agent pool.func ( *Agent) { .reset()agentPool.Put()}// AcquireResponse returns an empty Response instance from response pool.//// The returned Response instance may be passed to ReleaseResponse when it is// no longer needed. This allows Response recycling, reduces GC pressure// and usually improves performance.// Copy from fasthttpfunc () *Response { := responsePool.Get()if == nil {return &Response{} } , := .(*Response)if ! {panic(fmt.Errorf("failed to type-assert to *Response")) }return}// ReleaseResponse return resp acquired via AcquireResponse to response pool.//// It is forbidden accessing resp and/or it's members after returning// it to response pool.// Copy from fasthttpfunc ( *Response) { .Reset()responsePool.Put()}// AcquireArgs returns an empty Args object from the pool.//// The returned Args may be returned to the pool with ReleaseArgs// when no longer needed. This allows reducing GC load.// Copy from fasthttpfunc () *Args { := argsPool.Get()if == nil {return &Args{} } , := .(*Args)if ! {panic(fmt.Errorf("failed to type-assert to *Args")) }return}// ReleaseArgs returns the object acquired via AcquireArgs to the pool.//// String not access the released Args object, otherwise data races may occur.// Copy from fasthttpfunc ( *Args) { .Reset()argsPool.Put()}// AcquireFormFile returns an empty FormFile object from the pool.//// The returned FormFile may be returned to the pool with ReleaseFormFile// when no longer needed. This allows reducing GC load.func () *FormFile { := formFilePool.Get()if == nil {return &FormFile{} } , := .(*FormFile)if ! {panic(fmt.Errorf("failed to type-assert to *FormFile")) }return}// ReleaseFormFile returns the object acquired via AcquireFormFile to the pool.//// String not access the released FormFile object, otherwise data races may occur.func ( *FormFile) { .Fieldname = "" .Name = "" .Content = .Content[:0] .autoRelease = falseformFilePool.Put()}const ( defaultUserAgent = "fiber")type multipartWriter interface { Boundary() string SetBoundary(boundary string) error CreateFormFile(fieldname, filename string) (io.Writer, error) WriteField(fieldname, value string) error Close() error}
The pages are generated with Goldsv0.6.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.