mirror of
https://github.com/gofiber/fiber.git
synced 2025-05-31 11:52:41 +00:00
* ✨ v3: Move the client module to the client folder and fix the error * ✨ v3: add xml encoder and decoder * 🚧 v3: design plugin and hook mechanism, complete simple get request * 🚧 v3: reset add some field * 🚧 v3: add doc and fix some error * 🚧 v3: add header merge * 🚧 v3: add query param * 🚧 v3: change to fasthttp's header and args * ✨ v3: add body and ua setting * 🚧 v3: add cookie support * 🚧 v3: add path param support * ✅ v3: fix error test case * 🚧 v3: add formdata and file support * 🚧 v3: referer support * 🚧 v3: reponse unmarshal * ✨ v3: finish API design * 🔥 v3: remove plugin mechanism * 🚧 v3: add timeout * 🚧 v3: change path params pattern and add unit test for core * ✏️ v3: error spell * ✅ v3: improve test coverage * ✅ perf: change test func name to fit project format * 🚧 v3: handle error * 🚧 v3: add unit test and fix error * ⚡️ chore: change func to improve performance * ✅ v3: add some unit test * ✅ v3: fix error test * 🐛 fix: add cookie to response * ✅ v3: add unit test * ✨ v3: export raw field * 🐛 fix: fix data race * 🔒️ chore: change package * 🐛 fix: data race * 🐛 fix: test fail * ✨ feat: move core to req * 🐛 fix: connection reuse * 🐛 fix: data race * 🐛 fix: data race * 🔀 fix: change to testify * ✅ fix: fail test in windows * ✨ feat: response body save to file * ✨ feat: support tls config * 🐛 fix: add err check * 🎨 perf: fix some static check * ✨ feat: add proxy support * ✨ feat: add retry feature * 🐛 fix: static check error * 🎨 refactor: move som code * docs: change readme * ✨ feat: extend axios API * perf: change field to export field * ✅ chore: disable startup message * 🐛 fix: fix test error * chore: fix error test * chore: fix test case * feat: add some test to client * chore: add test case * chore: add test case * ✨ feat: add peek for client * ✅ chore: add test case * ⚡️ feat: lazy generate rand string * 🚧 perf: add config test case * 🐛 fix: fix merge error * 🐛 fix utils error * ✨ add redirection * 🔥 chore: delete deps * perf: fix spell error * 🎨 perf: spell error * ✨ feat: add logger * ✨ feat: add cookie jar * ✨ feat: logger with level * 🎨 perf: change the field name * perf: add jar test * fix proxy test * improve test coverage * fix proxy tests * add cookiejar support from pending fasthttp PR * fix some lint errors. * add benchmark for SetValWithStruct * optimize * update * fix proxy middleware * use panicf instead of errorf and fix panic on default logger * update * update * cleanup comments * cleanup comments * fix golang-lint errors * Update helper_test.go * add more test cases * add hostclient pool * make it more thread safe -> there is still something which is shared between the requests * fixed some golangci-lint errors * fix Test_Request_FormData test * create new test suite * just create client for once * use random port instead of 3000 * remove client pooling and fix test suite * fix data races on logger tests * fix proxy tests * fix global tests * remove unused code * fix logger test * fix proxy tests * fix linter * use lock instead of rlock * fix cookiejar data-race * fix(client): race conditions * fix(client): race conditions * apply some reviews * change client property name * apply review * add parallel benchmark for simple request * apply review * apply review * fix log tests * fix linter * fix(client): return error in SetProxyURL instead of panic --------- Co-authored-by: Muhammed Efe Çetin <efectn@protonmail.com> Co-authored-by: René Werner <rene.werner@verivox.com> Co-authored-by: Joey <fenny@gofiber.io> Co-authored-by: René <rene@gofiber.io>
185 lines
4.4 KiB
Go
185 lines
4.4 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/gofiber/utils/v2"
|
|
"github.com/valyala/fasthttp"
|
|
)
|
|
|
|
// Response is the result of a request. This object is used to access the response data.
|
|
type Response struct {
|
|
client *Client
|
|
request *Request
|
|
cookie []*fasthttp.Cookie
|
|
|
|
RawResponse *fasthttp.Response
|
|
}
|
|
|
|
// setClient method sets client object in response instance.
|
|
// Use core object in the client.
|
|
func (r *Response) setClient(c *Client) {
|
|
r.client = c
|
|
}
|
|
|
|
// setRequest method sets Request object in response instance.
|
|
// The request will be released when the Response.Close is called.
|
|
func (r *Response) setRequest(req *Request) {
|
|
r.request = req
|
|
}
|
|
|
|
// Status method returns the HTTP status string for the executed request.
|
|
func (r *Response) Status() string {
|
|
return string(r.RawResponse.Header.StatusMessage())
|
|
}
|
|
|
|
// StatusCode method returns the HTTP status code for the executed request.
|
|
func (r *Response) StatusCode() int {
|
|
return r.RawResponse.StatusCode()
|
|
}
|
|
|
|
// Protocol method returns the HTTP response protocol used for the request.
|
|
func (r *Response) Protocol() string {
|
|
return string(r.RawResponse.Header.Protocol())
|
|
}
|
|
|
|
// Header method returns the response headers.
|
|
func (r *Response) Header(key string) string {
|
|
return utils.UnsafeString(r.RawResponse.Header.Peek(key))
|
|
}
|
|
|
|
// Cookies method to access all the response cookies.
|
|
func (r *Response) Cookies() []*fasthttp.Cookie {
|
|
return r.cookie
|
|
}
|
|
|
|
// Body method returns HTTP response as []byte array for the executed request.
|
|
func (r *Response) Body() []byte {
|
|
return r.RawResponse.Body()
|
|
}
|
|
|
|
// String method returns the body of the server response as String.
|
|
func (r *Response) String() string {
|
|
return strings.TrimSpace(string(r.Body()))
|
|
}
|
|
|
|
// JSON method will unmarshal body to json.
|
|
func (r *Response) JSON(v any) error {
|
|
return r.client.jsonUnmarshal(r.Body(), v)
|
|
}
|
|
|
|
// XML method will unmarshal body to xml.
|
|
func (r *Response) XML(v any) error {
|
|
return r.client.xmlUnmarshal(r.Body(), v)
|
|
}
|
|
|
|
// Save method will save the body to a file or io.Writer.
|
|
func (r *Response) Save(v any) error {
|
|
switch p := v.(type) {
|
|
case string:
|
|
file := filepath.Clean(p)
|
|
dir := filepath.Dir(file)
|
|
|
|
// create directory
|
|
if _, err := os.Stat(dir); err != nil {
|
|
if !errors.Is(err, fs.ErrNotExist) {
|
|
return fmt.Errorf("failed to check directory: %w", err)
|
|
}
|
|
|
|
if err = os.MkdirAll(dir, 0o750); err != nil {
|
|
return fmt.Errorf("failed to create directory: %w", err)
|
|
}
|
|
}
|
|
|
|
// create file
|
|
outFile, err := os.Create(file)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create file: %w", err)
|
|
}
|
|
defer func() { _ = outFile.Close() }() //nolint:errcheck // not needed
|
|
|
|
_, err = io.Copy(outFile, bytes.NewReader(r.Body()))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write response body to file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
case io.Writer:
|
|
_, err := io.Copy(p, bytes.NewReader(r.Body()))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to write response body to io.Writer: %w", err)
|
|
}
|
|
defer func() {
|
|
if pc, ok := p.(io.WriteCloser); ok {
|
|
_ = pc.Close() //nolint:errcheck // not needed
|
|
}
|
|
}()
|
|
|
|
return nil
|
|
default:
|
|
return ErrNotSupportSaveMethod
|
|
}
|
|
}
|
|
|
|
// Reset clear Response object.
|
|
func (r *Response) Reset() {
|
|
r.client = nil
|
|
r.request = nil
|
|
|
|
for len(r.cookie) != 0 {
|
|
t := r.cookie[0]
|
|
r.cookie = r.cookie[1:]
|
|
fasthttp.ReleaseCookie(t)
|
|
}
|
|
|
|
r.RawResponse.Reset()
|
|
}
|
|
|
|
// Close method will release Request object and Response object,
|
|
// after call Close please don't use these object.
|
|
func (r *Response) Close() {
|
|
if r.request != nil {
|
|
tmp := r.request
|
|
r.request = nil
|
|
ReleaseRequest(tmp)
|
|
}
|
|
ReleaseResponse(r)
|
|
}
|
|
|
|
var responsePool = &sync.Pool{
|
|
New: func() any {
|
|
return &Response{
|
|
cookie: []*fasthttp.Cookie{},
|
|
RawResponse: fasthttp.AcquireResponse(),
|
|
}
|
|
},
|
|
}
|
|
|
|
// AcquireResponse returns an empty response object from the pool.
|
|
//
|
|
// The returned response may be returned to the pool with ReleaseResponse when no longer needed.
|
|
// This allows reducing GC load.
|
|
func AcquireResponse() *Response {
|
|
resp, ok := responsePool.Get().(*Response)
|
|
if !ok {
|
|
panic("unexpected type from responsePool.Get()")
|
|
}
|
|
return resp
|
|
}
|
|
|
|
// ReleaseResponse returns the object acquired via AcquireResponse to the pool.
|
|
//
|
|
// Do not access the released Response object, otherwise data races may occur.
|
|
func ReleaseResponse(resp *Response) {
|
|
resp.Reset()
|
|
responsePool.Put(resp)
|
|
}
|