1
0
mirror of https://github.com/gofiber/fiber.git synced 2025-02-06 11:02:01 +00:00

🔥 feat: Add support for CBOR encoding (#3173)

* feat(cbor): allow encoding response bodies in cbor

* fix(tests::cbor): encode struct instead of a randomly ordered hashmap

* docs(whats_new): add cbor in context section

* feat(binder): introduce CBOR

* feat(client): allow cbor in fiber client

* chore(tests): add more test

* chore(packages): go mod tidy

* fix(binder): update CBOR name and test

* improve test coverage

* improve test coverage

* update1

* add docs

* doc fixes

* update

* Fix markdown lint

* Add missing entry from binder README

* add/refresh documentation

---------

Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com>
Co-authored-by: M. Efe Çetin <efectn@protonmail.com>
Co-authored-by: RW <rene@gofiber.io>
This commit is contained in:
Sumit Kumar 2024-12-01 15:33:50 +05:30 committed by GitHub
parent 89452fea32
commit 26cc477500
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 760 additions and 198 deletions

22
app.go
View File

@ -25,9 +25,9 @@ import (
"sync"
"time"
"github.com/fxamacker/cbor/v2"
"github.com/gofiber/fiber/v3/log"
"github.com/gofiber/utils/v2"
"github.com/valyala/fasthttp"
)
@ -320,6 +320,20 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
// Default: json.Unmarshal
JSONDecoder utils.JSONUnmarshal `json:"-"`
// When set by an external client of Fiber it will use the provided implementation of a
// CBORMarshal
//
// Allowing for flexibility in using another cbor library for encoding
// Default: cbor.Marshal
CBOREncoder utils.CBORMarshal `json:"-"`
// When set by an external client of Fiber it will use the provided implementation of a
// CBORUnmarshal
//
// Allowing for flexibility in using another cbor library for decoding
// Default: cbor.Unmarshal
CBORDecoder utils.CBORUnmarshal `json:"-"`
// XMLEncoder set by an external client of Fiber it will use the provided implementation of a
// XMLMarshal
//
@ -537,6 +551,12 @@ func New(config ...Config) *App {
if app.config.JSONDecoder == nil {
app.config.JSONDecoder = json.Unmarshal
}
if app.config.CBOREncoder == nil {
app.config.CBOREncoder = cbor.Marshal
}
if app.config.CBORDecoder == nil {
app.config.CBORDecoder = cbor.Unmarshal
}
if app.config.XMLEncoder == nil {
app.config.XMLEncoder = xml.Marshal
}

14
bind.go
View File

@ -23,7 +23,7 @@ type Bind struct {
dontHandleErrs bool
}
// If you want to handle binder errors manually, you can use `WithoutAutoHandling`.
// WithoutAutoHandling If you want to handle binder errors manually, you can use `WithoutAutoHandling`.
// It's default behavior of binder.
func (b *Bind) WithoutAutoHandling() *Bind {
b.dontHandleErrs = true
@ -31,7 +31,7 @@ func (b *Bind) WithoutAutoHandling() *Bind {
return b
}
// If you want to handle binder errors automatically, you can use `WithAutoHandling`.
// WithAutoHandling If you want to handle binder errors automatically, you can use `WithAutoHandling`.
// If there's an error, it will return the error and set HTTP status to `400 Bad Request`.
// You must still return on error explicitly
func (b *Bind) WithAutoHandling() *Bind {
@ -121,6 +121,14 @@ func (b *Bind) JSON(out any) error {
return b.validateStruct(out)
}
// CBOR binds the body string into the struct.
func (b *Bind) CBOR(out any) error {
if err := b.returnErr(binder.CBORBinder.Bind(b.ctx.Body(), b.ctx.App().Config().CBORDecoder, out)); err != nil {
return err
}
return b.validateStruct(out)
}
// XML binds the body string into the struct.
func (b *Bind) XML(out any) error {
if err := b.returnErr(binder.XMLBinder.Bind(b.ctx.Body(), out)); err != nil {
@ -183,6 +191,8 @@ func (b *Bind) Body(out any) error {
return b.JSON(out)
case MIMETextXML, MIMEApplicationXML:
return b.XML(out)
case MIMEApplicationCBOR:
return b.CBOR(out)
case MIMEApplicationForm:
return b.Form(out)
case MIMEMultipartForm:

View File

@ -12,6 +12,7 @@ import (
"testing"
"time"
"github.com/fxamacker/cbor/v2"
"github.com/gofiber/fiber/v3/binder"
"github.com/stretchr/testify/require"
"github.com/valyala/fasthttp"
@ -157,7 +158,7 @@ func Test_Bind_Query_WithSetParserDecoder(t *testing.T) {
}
nonRFCTime := binder.ParserType{
Customtype: NonRFCTime{},
CustomType: NonRFCTime{},
Converter: nonRFCConverter,
}
@ -411,7 +412,7 @@ func Test_Bind_Header_WithSetParserDecoder(t *testing.T) {
}
nonRFCTime := binder.ParserType{
Customtype: NonRFCTime{},
CustomType: NonRFCTime{},
Converter: nonRFCConverter,
}
@ -922,11 +923,11 @@ func Test_Bind_Body(t *testing.T) {
testCompressedBody(t, compressedBody, "zstd")
})
testDecodeParser := func(t *testing.T, contentType, body string) {
testDecodeParser := func(t *testing.T, contentType string, body []byte) {
t.Helper()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.SetContentType(contentType)
c.Request().SetBody([]byte(body))
c.Request().SetBody(body)
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
require.NoError(t, c.Bind().Body(d))
@ -934,19 +935,36 @@ func Test_Bind_Body(t *testing.T) {
}
t.Run("JSON", func(t *testing.T) {
testDecodeParser(t, MIMEApplicationJSON, `{"name":"john"}`)
testDecodeParser(t, MIMEApplicationJSON, []byte(`{"name":"john"}`))
})
t.Run("CBOR", func(t *testing.T) {
enc, err := cbor.Marshal(&Demo{Name: "john"})
if err != nil {
t.Error(err)
}
testDecodeParser(t, MIMEApplicationCBOR, enc)
// Test invalid CBOR data
t.Run("Invalid", func(t *testing.T) {
invalidData := []byte{0xFF, 0xFF} // Invalid CBOR data
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.SetContentType(MIMEApplicationCBOR)
c.Request().SetBody(invalidData)
d := new(Demo)
require.Error(t, c.Bind().Body(d))
})
})
t.Run("XML", func(t *testing.T) {
testDecodeParser(t, MIMEApplicationXML, `<Demo><name>john</name></Demo>`)
testDecodeParser(t, MIMEApplicationXML, []byte(`<Demo><name>john</name></Demo>`))
})
t.Run("Form", func(t *testing.T) {
testDecodeParser(t, MIMEApplicationForm, "name=john")
testDecodeParser(t, MIMEApplicationForm, []byte("name=john"))
})
t.Run("MultipartForm", func(t *testing.T) {
testDecodeParser(t, MIMEMultipartForm+`;boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\njohn\r\n--b--")
testDecodeParser(t, MIMEMultipartForm+`;boundary="b"`, []byte("--b\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\njohn\r\n--b--"))
})
testDecodeParserError := func(t *testing.T, contentType, body string) {
@ -1009,7 +1027,7 @@ func Test_Bind_Body_WithSetParserDecoder(t *testing.T) {
}
customTime := binder.ParserType{
Customtype: CustomTime{},
CustomType: CustomTime{},
Converter: timeConverter,
}
@ -1100,6 +1118,35 @@ func Benchmark_Bind_Body_XML(b *testing.B) {
require.Equal(b, "john", d.Name)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Body_CBOR -benchmem -count=4
func Benchmark_Bind_Body_CBOR(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Demo struct {
Name string `json:"name"`
}
body, err := cbor.Marshal(&Demo{Name: "john"})
if err != nil {
b.Error(err)
}
c.Request().SetBody(body)
c.Request().Header.SetContentType(MIMEApplicationCBOR)
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Body(d)
}
require.NoError(b, err)
require.Equal(b, "john", d.Name)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Body_Form -benchmem -count=4
func Benchmark_Bind_Body_Form(b *testing.B) {
var err error
@ -1404,7 +1451,7 @@ func Test_Bind_Cookie_WithSetParserDecoder(t *testing.T) {
}
nonRFCTime := binder.ParserType{
Customtype: NonRFCTime{},
CustomType: NonRFCTime{},
Converter: nonRFCConverter,
}
@ -1720,8 +1767,12 @@ func Test_Bind_RepeatParserWithSameStruct(t *testing.T) {
require.Equal(t, "body_param", r.BodyParam)
}
cb, err := cbor.Marshal(&Request{BodyParam: "body_param"})
require.NoError(t, err, "Failed to marshal CBOR data")
testDecodeParser(MIMEApplicationJSON, `{"body_param":"body_param"}`)
testDecodeParser(MIMEApplicationXML, `<Demo><body_param>body_param</body_param></Demo>`)
testDecodeParser(MIMEApplicationCBOR, string(cb))
testDecodeParser(MIMEApplicationForm, "body_param=body_param")
testDecodeParser(MIMEMultipartForm+`;boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"body_param\"\r\n\r\nbody_param\r\n--b--")
}

View File

@ -22,6 +22,7 @@ Fiber provides several default binders out of the box:
- [Cookie](cookie.go)
- [JSON](json.go)
- [XML](xml.go)
- [CBOR](cbor.go)
## Guides

View File

@ -20,4 +20,5 @@ var (
URIBinder = &uriBinding{}
XMLBinder = &xmlBinding{}
JSONBinder = &jsonBinding{}
CBORBinder = &cborBinding{}
)

18
binder/cbor.go Normal file
View File

@ -0,0 +1,18 @@
package binder
import (
"github.com/gofiber/utils/v2"
)
// cborBinding is the CBOR binder for CBOR request body.
type cborBinding struct{}
// Name returns the binding name.
func (*cborBinding) Name() string {
return "cbor"
}
// Bind parses the request body as CBOR and returns the result.
func (*cborBinding) Bind(body []byte, cborDecoder utils.CBORUnmarshal, out any) error {
return cborDecoder(body, out)
}

View File

@ -8,12 +8,15 @@ import (
"github.com/valyala/fasthttp"
)
// cookieBinding is the cookie binder for cookie request body.
type cookieBinding struct{}
// Name returns the binding name.
func (*cookieBinding) Name() string {
return "cookie"
}
// Bind parses the request cookie and returns the result.
func (b *cookieBinding) Bind(reqCtx *fasthttp.RequestCtx, out any) error {
data := make(map[string][]string)
var err error

View File

@ -8,12 +8,15 @@ import (
"github.com/valyala/fasthttp"
)
// formBinding is the form binder for form request body.
type formBinding struct{}
// Name returns the binding name.
func (*formBinding) Name() string {
return "form"
}
// Bind parses the request body and returns the result.
func (b *formBinding) Bind(reqCtx *fasthttp.RequestCtx, out any) error {
data := make(map[string][]string)
var err error
@ -47,6 +50,7 @@ func (b *formBinding) Bind(reqCtx *fasthttp.RequestCtx, out any) error {
return parse(b.Name(), out, data)
}
// BindMultipart parses the request body and returns the result.
func (b *formBinding) BindMultipart(reqCtx *fasthttp.RequestCtx, out any) error {
data, err := reqCtx.MultipartForm()
if err != nil {

View File

@ -8,12 +8,15 @@ import (
"github.com/valyala/fasthttp"
)
// headerBinding is the header binder for header request body.
type headerBinding struct{}
// Name returns the binding name.
func (*headerBinding) Name() string {
return "header"
}
// Bind parses the request header and returns the result.
func (b *headerBinding) Bind(req *fasthttp.Request, out any) error {
data := make(map[string][]string)
req.Header.VisitAll(func(key, val []byte) {

View File

@ -4,12 +4,15 @@ import (
"github.com/gofiber/utils/v2"
)
// jsonBinding is the JSON binder for JSON request body.
type jsonBinding struct{}
// Name returns the binding name.
func (*jsonBinding) Name() string {
return "json"
}
// Bind parses the request body as JSON and returns the result.
func (*jsonBinding) Bind(body []byte, jsonDecoder utils.JSONUnmarshal, out any) error {
return jsonDecoder(body, out)
}

View File

@ -24,7 +24,7 @@ type ParserConfig struct {
// ParserType require two element, type and converter for register.
// Use ParserType with BodyParser for parsing custom type in form data.
type ParserType struct {
Customtype any
CustomType any
Converter func(string) reflect.Value
}
@ -51,7 +51,7 @@ func decoderBuilder(parserConfig ParserConfig) any {
decoder.SetAliasTag(parserConfig.SetAliasTag)
}
for _, v := range parserConfig.ParserType {
decoder.RegisterConverter(reflect.ValueOf(v.Customtype).Interface(), v.Converter)
decoder.RegisterConverter(reflect.ValueOf(v.CustomType).Interface(), v.Converter)
}
decoder.ZeroEmpty(parserConfig.ZeroEmpty)
return decoder

View File

@ -8,12 +8,15 @@ import (
"github.com/valyala/fasthttp"
)
// queryBinding is the query binder for query request body.
type queryBinding struct{}
// Name returns the binding name.
func (*queryBinding) Name() string {
return "query"
}
// Bind parses the request query and returns the result.
func (b *queryBinding) Bind(reqCtx *fasthttp.RequestCtx, out any) error {
data := make(map[string][]string)
var err error

View File

@ -8,12 +8,15 @@ import (
"github.com/valyala/fasthttp"
)
// respHeaderBinding is the respHeader binder for response header.
type respHeaderBinding struct{}
// Name returns the binding name.
func (*respHeaderBinding) Name() string {
return "respHeader"
}
// Bind parses the response header and returns the result.
func (b *respHeaderBinding) Bind(resp *fasthttp.Response, out any) error {
data := make(map[string][]string)
resp.Header.VisitAll(func(key, val []byte) {

View File

@ -1,11 +1,14 @@
package binder
// uriBinding is the URI binder for URI parameters.
type uriBinding struct{}
// Name returns the binding name.
func (*uriBinding) Name() string {
return "uri"
}
// Bind parses the URI parameters and returns the result.
func (b *uriBinding) Bind(params []string, paramsFunc func(key string, defaultValue ...string) string, out any) error {
data := make(map[string][]string, len(params))
for _, param := range params {

View File

@ -5,12 +5,15 @@ import (
"fmt"
)
// xmlBinding is the XML binder for XML request body.
type xmlBinding struct{}
// Name returns the binding name.
func (*xmlBinding) Name() string {
return "xml"
}
// Bind parses the request body as XML and returns the result.
func (*xmlBinding) Bind(body []byte, out any) error {
if err := xml.Unmarshal(body, out); err != nil {
return fmt.Errorf("failed to unmarshal xml: %w", err)

View File

@ -13,6 +13,7 @@ import (
"sync"
"time"
"github.com/fxamacker/cbor/v2"
"github.com/gofiber/fiber/v3/log"
"github.com/gofiber/utils/v2"
@ -44,6 +45,8 @@ type Client struct {
jsonUnmarshal utils.JSONUnmarshal
xmlMarshal utils.XMLMarshal
xmlUnmarshal utils.XMLUnmarshal
cborMarshal utils.CBORMarshal
cborUnmarshal utils.CBORUnmarshal
cookieJar *CookieJar
@ -150,6 +153,28 @@ func (c *Client) SetXMLUnmarshal(f utils.XMLUnmarshal) *Client {
return c
}
// CBORMarshal returns CBOR marshal function in Core.
func (c *Client) CBORMarshal() utils.CBORMarshal {
return c.cborMarshal
}
// SetCBORMarshal sets CBOR encoder.
func (c *Client) SetCBORMarshal(f utils.CBORMarshal) *Client {
c.cborMarshal = f
return c
}
// CBORUnmarshal returns CBOR unmarshal function in Core.
func (c *Client) CBORUnmarshal() utils.CBORUnmarshal {
return c.cborUnmarshal
}
// SetCBORUnmarshal sets CBOR decoder.
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.
func (c *Client) TLSConfig() *tls.Config {
@ -706,6 +731,8 @@ func NewWithClient(c *fasthttp.Client) *Client {
jsonMarshal: json.Marshal,
jsonUnmarshal: json.Unmarshal,
xmlMarshal: xml.Marshal,
cborMarshal: cbor.Marshal,
cborUnmarshal: cbor.Unmarshal,
xmlUnmarshal: xml.Unmarshal,
logger: log.DefaultLogger(),
}

View File

@ -3,6 +3,7 @@ package client
import (
"context"
"crypto/tls"
"encoding/hex"
"errors"
"io"
"net"
@ -225,6 +226,33 @@ func Test_Client_Marshal(t *testing.T) {
require.Equal(t, errors.New("empty xml"), err)
})
t.Run("set cbor marshal", func(t *testing.T) {
t.Parallel()
bs, err := hex.DecodeString("f6")
if err != nil {
t.Error(err)
}
client := New().
SetCBORMarshal(func(_ any) ([]byte, error) {
return bs, nil
})
val, err := client.CBORMarshal()(nil)
require.NoError(t, err)
require.Equal(t, bs, val)
})
t.Run("set cbor marshal error", func(t *testing.T) {
t.Parallel()
client := New().SetCBORMarshal(func(_ any) ([]byte, error) {
return nil, errors.New("invalid struct")
})
val, err := client.CBORMarshal()(nil)
require.Nil(t, val)
require.Equal(t, errors.New("invalid struct"), err)
})
t.Run("set xml unmarshal", func(t *testing.T) {
t.Parallel()
client := New().
@ -1443,11 +1471,12 @@ func Test_Set_Config_To_Request(t *testing.T) {
t.Run("set ctx", func(t *testing.T) {
t.Parallel()
key := struct{}{}
type ctxKey struct{}
var key ctxKey = struct{}{}
ctx := context.Background()
ctx = context.WithValue(ctx, key, "v1") //nolint: staticcheck // not needed for tests
ctx = context.WithValue(ctx, key, "v1")
req := AcquireRequest()
setConfigToRequest(req, Config{Ctx: ctx})

View File

@ -23,6 +23,7 @@ var (
headerAccept = "Accept"
applicationJSON = "application/json"
applicationCBOR = "application/cbor"
applicationXML = "application/xml"
applicationForm = "application/x-www-form-urlencoded"
multipartFormData = "multipart/form-data"
@ -129,6 +130,8 @@ func parserRequestHeader(c *Client, req *Request) error {
req.RawRequest.Header.Set(headerAccept, applicationJSON)
case xmlBody:
req.RawRequest.Header.SetContentType(applicationXML)
case cborBody:
req.RawRequest.Header.SetContentType(applicationCBOR)
case formBody:
req.RawRequest.Header.SetContentType(applicationForm)
case filesBody:
@ -189,6 +192,12 @@ func parserRequestBody(c *Client, req *Request) error {
return err
}
req.RawRequest.SetBody(body)
case cborBody:
body, err := c.cborMarshal(req.body)
if err != nil {
return err
}
req.RawRequest.SetBody(body)
case formBody:
req.RawRequest.SetBody(req.formData.QueryString())
case filesBody:

View File

@ -10,6 +10,7 @@ import (
"strings"
"testing"
"github.com/fxamacker/cbor/v2"
"github.com/gofiber/fiber/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -456,6 +457,30 @@ func Test_Parser_Request_Body(t *testing.T) {
require.Equal(t, []byte("<body><name>foo</name></body>"), req.RawRequest.Body())
})
t.Run("CBOR body", func(t *testing.T) {
t.Parallel()
type cborData struct {
Name string `cbor:"name"`
Age int `cbor:"age"`
}
data := cborData{
Name: "foo",
Age: 12,
}
client := New()
req := AcquireRequest().
SetCBOR(data)
err := parserRequestBody(client, req)
require.NoError(t, err)
encoded, err := cbor.Marshal(data)
require.NoError(t, err)
require.Equal(t, encoded, req.RawRequest.Body())
})
t.Run("form data body", func(t *testing.T) {
t.Parallel()
client := New()

View File

@ -34,6 +34,7 @@ const (
formBody
filesBody
rawBody
cborBody
)
var ErrClientNil = errors.New("client can not be nil")
@ -337,6 +338,13 @@ func (r *Request) SetXML(v any) *Request {
return r
}
// SetCBOR method sets CBOR body in request.
func (r *Request) SetCBOR(v any) *Request {
r.body = v
r.bodyType = cborBody
return r
}
// SetRawBody method sets body with raw data in request.
func (r *Request) SetRawBody(v []byte) *Request {
r.body = v

View File

@ -80,11 +80,12 @@ func Test_Request_Context(t *testing.T) {
req := AcquireRequest()
ctx := req.Context()
key := struct{}{}
type ctxKey struct{}
var key ctxKey = struct{}{}
require.Nil(t, ctx.Value(key))
ctx = context.WithValue(ctx, key, "string") //nolint: staticcheck // not needed for tests
ctx = context.WithValue(ctx, key, "string")
req.SetContext(ctx)
ctx = req.Context()
@ -1006,6 +1007,25 @@ func Test_Request_Body_With_Server(t *testing.T) {
)
})
t.Run("cbor body", func(t *testing.T) {
t.Parallel()
testRequest(t,
func(c fiber.Ctx) error {
require.Equal(t, "application/cbor", string(c.Request().Header.ContentType()))
return c.SendString(string(c.Request().Body()))
},
func(agent *Request) {
type args struct {
Content string `cbor:"content"`
}
agent.SetCBOR(args{
Content: "hello",
})
},
"\xa1gcontentehello",
)
})
t.Run("formdata", func(t *testing.T) {
t.Parallel()
testRequest(t,

View File

@ -75,6 +75,11 @@ func (r *Response) JSON(v any) error {
return r.client.jsonUnmarshal(r.Body(), v)
}
// CBOR method will unmarshal body to CBOR.
func (r *Response) CBOR(v any) error {
return r.client.cborUnmarshal(r.Body(), v)
}
// XML method will unmarshal body to xml.
func (r *Response) XML(v any) error {
return r.client.xmlUnmarshal(r.Body(), v)

View File

@ -240,6 +240,18 @@ func Test_Response_Body(t *testing.T) {
app.Get("/xml", func(c fiber.Ctx) error {
return c.SendString("<status><name>success</name></status>")
})
app.Get("/cbor", func(c fiber.Ctx) error {
type cborData struct {
Name string `cbor:"name"`
Age int `cbor:"age"`
}
return c.CBOR(cborData{
Name: "foo",
Age: 12,
})
})
})
return server
@ -327,6 +339,36 @@ func Test_Response_Body(t *testing.T) {
require.Equal(t, "success", tmp.Status)
resp.Close()
})
t.Run("cbor body", func(t *testing.T) {
t.Parallel()
type cborData struct {
Name string `cbor:"name"`
Age int `cbor:"age"`
}
data := cborData{
Name: "foo",
Age: 12,
}
server := setupApp()
defer server.stop()
client := New().SetDial(server.dial())
resp, err := AcquireRequest().
SetClient(client).
Get("http://example.com/cbor")
require.NoError(t, err)
tmp := &cborData{}
err = resp.CBOR(tmp)
require.NoError(t, err)
require.Equal(t, data, *tmp)
resp.Close()
})
}
func Test_Response_Save(t *testing.T) {

View File

@ -23,6 +23,7 @@ const (
MIMETextCSS = "text/css"
MIMEApplicationXML = "application/xml"
MIMEApplicationJSON = "application/json"
MIMEApplicationCBOR = "application/cbor"
// Deprecated: use MIMETextJavaScript instead
MIMEApplicationJavaScript = "application/javascript"
MIMEApplicationForm = "application/x-www-form-urlencoded"

18
ctx.go
View File

@ -884,6 +884,24 @@ func (c *DefaultCtx) JSON(data any, ctype ...string) error {
return nil
}
// CBOR converts any interface or string to CBOR encoded bytes.
// If the ctype parameter is given, this method will set the
// Content-Type header equal to ctype. If ctype is not given,
// The Content-Type header will be set to application/cbor.
func (c *DefaultCtx) CBOR(data any, ctype ...string) error {
raw, err := c.app.config.CBOREncoder(data)
if err != nil {
return err
}
c.fasthttp.Response.SetBodyRaw(raw)
if len(ctype) > 0 {
c.fasthttp.Response.Header.SetContentType(ctype[0])
} else {
c.fasthttp.Response.Header.SetContentType(MIMEApplicationCBOR)
}
return nil
}
// JSONP sends a JSON response with JSONP support.
// This method is identical to JSON, except that it opts-in to JSONP callback support.
// By default, the callback name is simply callback.

View File

@ -164,6 +164,11 @@ type Ctx interface {
// Content-Type header equal to ctype. If ctype is not given,
// The Content-Type header will be set to application/json.
JSON(data any, ctype ...string) error
// CBOR converts any interface or string to CBOR encoded bytes.
// If the ctype parameter is given, this method will set the
// Content-Type header equal to ctype. If ctype is not given,
// The Content-Type header will be set to application/cbor.
CBOR(data any, ctype ...string) error
// JSONP sends a JSON response with JSONP support.
// This method is identical to JSON, except that it opts-in to JSONP callback support.
// By default, the callback name is simply callback.

View File

@ -12,6 +12,7 @@ import (
"context"
"crypto/tls"
"embed"
"encoding/hex"
"encoding/xml"
"errors"
"fmt"
@ -3618,6 +3619,98 @@ func Benchmark_Ctx_JSON(b *testing.B) {
require.JSONEq(b, `{"Name":"Grame","Age":20}`, string(c.Response().Body()))
}
// go test -run Test_Ctx_CBOR
func Test_Ctx_CBOR(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
require.Error(t, c.CBOR(complex(1, 1)))
type dummyStruct struct {
Name string
Age int
}
// Test without ctype
err := c.CBOR(dummyStruct{ // map has no order
Name: "Grame",
Age: 20,
})
require.NoError(t, err)
require.Equal(t, `a2644e616d65654772616d656341676514`, hex.EncodeToString(c.Response().Body()))
require.Equal(t, "application/cbor", string(c.Response().Header.Peek("content-type")))
// Test with ctype
err = c.CBOR(dummyStruct{ // map has no order
Name: "Grame",
Age: 20,
}, "application/problem+cbor")
require.NoError(t, err)
require.Equal(t, `a2644e616d65654772616d656341676514`, hex.EncodeToString(c.Response().Body()))
require.Equal(t, "application/problem+cbor", string(c.Response().Header.Peek("content-type")))
testEmpty := func(v any, r string) {
err := c.CBOR(v)
require.NoError(t, err)
require.Equal(t, r, hex.EncodeToString(c.Response().Body()))
}
testEmpty(nil, "f6")
testEmpty("", `60`)
testEmpty(0, "00")
testEmpty([]int{}, "80")
// Test invalid types
err = c.CBOR(make(chan int))
require.Error(t, err)
err = c.CBOR(func() {})
require.Error(t, err)
t.Run("custom cbor encoder", func(t *testing.T) {
t.Parallel()
app := New(Config{
CBOREncoder: func(_ any) ([]byte, error) {
return []byte(hex.EncodeToString([]byte("random"))), nil
},
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
err := c.CBOR(Map{ // map has no order
"Name": "Grame",
"Age": 20,
})
require.NoError(t, err)
require.Equal(t, `72616e646f6d`, string(c.Response().Body()))
require.Equal(t, "application/cbor", string(c.Response().Header.Peek("content-type")))
})
}
// go test -run=^$ -bench=Benchmark_Ctx_CBOR -benchmem -count=4
func Benchmark_Ctx_CBOR(b *testing.B) {
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type SomeStruct struct {
Name string
Age uint8
}
data := SomeStruct{
Name: "Grame",
Age: 20,
}
var err error
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.CBOR(data)
}
require.NoError(b, err)
require.Equal(b, `a2644e616d65654772616d656341676514`, hex.EncodeToString(c.Response().Body()))
}
// go test -run=^$ -bench=Benchmark_Ctx_JSON_Ctype -benchmem -count=4
func Benchmark_Ctx_JSON_Ctype(b *testing.B) {
app := New()

View File

@ -20,6 +20,7 @@ Make copies or use the [**`Immutable`**](./ctx.md) setting instead. [Read more..
- [JSON](#json)
- [MultipartForm](#multipartform)
- [XML](#xml)
- [CBOR](#cbor)
- [Cookie](#cookie)
- [Header](#header)
- [Query](#query)
@ -226,6 +227,43 @@ Run tests with the following `curl` command:
curl -X POST -H "Content-Type: application/xml" --data "<login><name>john</name><pass>doe</pass></login>" localhost:3000
```
### CBOR
Binds the request CBOR body to a struct.
It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a CBOR body with a field called `Pass`, you would use a struct field with `cbor:"pass"`.
```go title="Signature"
func (b *Bind) CBOR(out any) error
```
```go title="Example"
// Field names should start with an uppercase letter
type Person struct {
Name string `cbor:"name"`
Pass string `cbor:"pass"`
}
app.Post("/", func(c fiber.Ctx) error {
p := new(Person)
if err := c.Bind().CBOR(p); err != nil {
return err
}
log.Println(p.Name) // john
log.Println(p.Pass) // doe
// ...
})
```
Run tests with the following `curl` command:
```bash
curl -X POST -H "Content-Type: application/cbor" --data "\xa2dnamedjohndpasscdoe" localhost:3000
```
### Cookie
This method is similar to [Body Binding](#body), but for cookie parameters.

View File

@ -26,25 +26,26 @@ const (
```go
const (
MIMETextXML = "text/xml"
MIMETextHTML = "text/html"
MIMETextPlain = "text/plain"
MIMETextJavaScript = "text/javascript"
MIMETextCSS = "text/css"
MIMEApplicationXML = "application/xml"
MIMEApplicationJSON = "application/json"
MIMEApplicationJavaScript = "application/javascript"
MIMEApplicationForm = "application/x-www-form-urlencoded"
MIMEOctetStream = "application/octet-stream"
MIMEMultipartForm = "multipart/form-data"
MIMETextXML = "text/xml"
MIMETextHTML = "text/html"
MIMETextPlain = "text/plain"
MIMETextJavaScript = "text/javascript"
MIMETextCSS = "text/css"
MIMEApplicationXML = "application/xml"
MIMEApplicationJSON = "application/json"
MIMEApplicationCBOR = "application/cbor"
MIMEApplicationJavaScript = "application/javascript"
MIMEApplicationForm = "application/x-www-form-urlencoded"
MIMEOctetStream = "application/octet-stream"
MIMEMultipartForm = "multipart/form-data"
MIMETextXMLCharsetUTF8 = "text/xml; charset=utf-8"
MIMETextHTMLCharsetUTF8 = "text/html; charset=utf-8"
MIMETextPlainCharsetUTF8 = "text/plain; charset=utf-8"
MIMETextJavaScriptCharsetUTF8 = "text/javascript; charset=utf-8"
MIMETextCSSCharsetUTF8 = "text/css; charset=utf-8"
MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=utf-8"
MIMEApplicationJSONCharsetUTF8 = "application/json; charset=utf-8"
MIMETextXMLCharsetUTF8 = "text/xml; charset=utf-8"
MIMETextHTMLCharsetUTF8 = "text/html; charset=utf-8"
MIMETextPlainCharsetUTF8 = "text/plain; charset=utf-8"
MIMETextJavaScriptCharsetUTF8 = "text/javascript; charset=utf-8"
MIMETextCSSCharsetUTF8 = "text/css; charset=utf-8"
MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=utf-8"
MIMEApplicationJSONCharsetUTF8 = "application/json; charset=utf-8"
MIMEApplicationJavaScriptCharsetUTF8 = "application/javascript; charset=utf-8"
)```
@ -72,6 +73,7 @@ const (
StatusSeeOther = 303 // RFC 7231, 6.4.4
StatusNotModified = 304 // RFC 7232, 4.1
StatusUseProxy = 305 // RFC 7231, 6.4.5
StatusSwitchProxy = 306 // RFC 9110, 15.4.7 (Unused)
StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
StatusPermanentRedirect = 308 // RFC 7538, 3
StatusBadRequest = 400 // RFC 7231, 6.5.1
@ -168,127 +170,129 @@ HTTP Headers were copied from net/http.
```go
const (
HeaderAuthorization = "Authorization"
HeaderProxyAuthenticate = "Proxy-Authenticate"
HeaderProxyAuthorization = "Proxy-Authorization"
HeaderWWWAuthenticate = "WWW-Authenticate"
HeaderAge = "Age"
HeaderCacheControl = "Cache-Control"
HeaderClearSiteData = "Clear-Site-Data"
HeaderExpires = "Expires"
HeaderPragma = "Pragma"
HeaderWarning = "Warning"
HeaderAcceptCH = "Accept-CH"
HeaderAcceptCHLifetime = "Accept-CH-Lifetime"
HeaderContentDPR = "Content-DPR"
HeaderDPR = "DPR"
HeaderEarlyData = "Early-Data"
HeaderSaveData = "Save-Data"
HeaderViewportWidth = "Viewport-Width"
HeaderWidth = "Width"
HeaderETag = "ETag"
HeaderIfMatch = "If-Match"
HeaderIfModifiedSince = "If-Modified-Since"
HeaderIfNoneMatch = "If-None-Match"
HeaderIfUnmodifiedSince = "If-Unmodified-Since"
HeaderLastModified = "Last-Modified"
HeaderVary = "Vary"
HeaderConnection = "Connection"
HeaderKeepAlive = "Keep-Alive"
HeaderAccept = "Accept"
HeaderAcceptCharset = "Accept-Charset"
HeaderAcceptEncoding = "Accept-Encoding"
HeaderAcceptLanguage = "Accept-Language"
HeaderCookie = "Cookie"
HeaderExpect = "Expect"
HeaderMaxForwards = "Max-Forwards"
HeaderSetCookie = "Set-Cookie"
HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
HeaderAccessControlMaxAge = "Access-Control-Max-Age"
HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
HeaderOrigin = "Origin"
HeaderTimingAllowOrigin = "Timing-Allow-Origin"
HeaderXPermittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies"
HeaderDNT = "DNT"
HeaderTk = "Tk"
HeaderContentDisposition = "Content-Disposition"
HeaderContentEncoding = "Content-Encoding"
HeaderContentLanguage = "Content-Language"
HeaderContentLength = "Content-Length"
HeaderContentLocation = "Content-Location"
HeaderContentType = "Content-Type"
HeaderForwarded = "Forwarded"
HeaderVia = "Via"
HeaderXForwardedFor = "X-Forwarded-For"
HeaderXForwardedHost = "X-Forwarded-Host"
HeaderXForwardedProto = "X-Forwarded-Proto"
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
HeaderXForwardedSsl = "X-Forwarded-Ssl"
HeaderXUrlScheme = "X-Url-Scheme"
HeaderLocation = "Location"
HeaderFrom = "From"
HeaderHost = "Host"
HeaderReferer = "Referer"
HeaderReferrerPolicy = "Referrer-Policy"
HeaderUserAgent = "User-Agent"
HeaderAllow = "Allow"
HeaderServer = "Server"
HeaderAcceptRanges = "Accept-Ranges"
HeaderContentRange = "Content-Range"
HeaderIfRange = "If-Range"
HeaderRange = "Range"
HeaderContentSecurityPolicy = "Content-Security-Policy"
HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
HeaderCrossOriginResourcePolicy = "Cross-Origin-Resource-Policy"
HeaderExpectCT = "Expect-CT"
HeaderFeaturePolicy = "Feature-Policy"
HeaderPublicKeyPins = "Public-Key-Pins"
HeaderPublicKeyPinsReportOnly = "Public-Key-Pins-Report-Only"
HeaderStrictTransportSecurity = "Strict-Transport-Security"
HeaderUpgradeInsecureRequests = "Upgrade-Insecure-Requests"
HeaderXContentTypeOptions = "X-Content-Type-Options"
HeaderXDownloadOptions = "X-Download-Options"
HeaderXFrameOptions = "X-Frame-Options"
HeaderXPoweredBy = "X-Powered-By"
HeaderXXSSProtection = "X-XSS-Protection"
HeaderLastEventID = "Last-Event-ID"
HeaderNEL = "NEL"
HeaderPingFrom = "Ping-From"
HeaderPingTo = "Ping-To"
HeaderReportTo = "Report-To"
HeaderTE = "TE"
HeaderTrailer = "Trailer"
HeaderTransferEncoding = "Transfer-Encoding"
HeaderSecWebSocketAccept = "Sec-WebSocket-Accept"
HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions"
HeaderSecWebSocketKey = "Sec-WebSocket-Key"
HeaderSecWebSocketProtocol = "Sec-WebSocket-Protocol"
HeaderSecWebSocketVersion = "Sec-WebSocket-Version"
HeaderAcceptPatch = "Accept-Patch"
HeaderAcceptPushPolicy = "Accept-Push-Policy"
HeaderAcceptSignature = "Accept-Signature"
HeaderAltSvc = "Alt-Svc"
HeaderDate = "Date"
HeaderIndex = "Index"
HeaderLargeAllocation = "Large-Allocation"
HeaderLink = "Link"
HeaderPushPolicy = "Push-Policy"
HeaderRetryAfter = "Retry-After"
HeaderServerTiming = "Server-Timing"
HeaderSignature = "Signature"
HeaderSignedHeaders = "Signed-Headers"
HeaderSourceMap = "SourceMap"
HeaderUpgrade = "Upgrade"
HeaderXDNSPrefetchControl = "X-DNS-Prefetch-Control"
HeaderXPingback = "X-Pingback"
HeaderXRequestID = "X-Request-ID"
HeaderXRequestedWith = "X-Requested-With"
HeaderXRobotsTag = "X-Robots-Tag"
HeaderXUACompatible = "X-UA-Compatible"
HeaderAuthorization = "Authorization"
HeaderProxyAuthenticate = "Proxy-Authenticate"
HeaderProxyAuthorization = "Proxy-Authorization"
HeaderWWWAuthenticate = "WWW-Authenticate"
HeaderAge = "Age"
HeaderCacheControl = "Cache-Control"
HeaderClearSiteData = "Clear-Site-Data"
HeaderExpires = "Expires"
HeaderPragma = "Pragma"
HeaderWarning = "Warning"
HeaderAcceptCH = "Accept-CH"
HeaderAcceptCHLifetime = "Accept-CH-Lifetime"
HeaderContentDPR = "Content-DPR"
HeaderDPR = "DPR"
HeaderEarlyData = "Early-Data"
HeaderSaveData = "Save-Data"
HeaderViewportWidth = "Viewport-Width"
HeaderWidth = "Width"
HeaderETag = "ETag"
HeaderIfMatch = "If-Match"
HeaderIfModifiedSince = "If-Modified-Since"
HeaderIfNoneMatch = "If-None-Match"
HeaderIfUnmodifiedSince = "If-Unmodified-Since"
HeaderLastModified = "Last-Modified"
HeaderVary = "Vary"
HeaderConnection = "Connection"
HeaderKeepAlive = "Keep-Alive"
HeaderAccept = "Accept"
HeaderAcceptCharset = "Accept-Charset"
HeaderAcceptEncoding = "Accept-Encoding"
HeaderAcceptLanguage = "Accept-Language"
HeaderCookie = "Cookie"
HeaderExpect = "Expect"
HeaderMaxForwards = "Max-Forwards"
HeaderSetCookie = "Set-Cookie"
HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
HeaderAccessControlMaxAge = "Access-Control-Max-Age"
HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
HeaderOrigin = "Origin"
HeaderTimingAllowOrigin = "Timing-Allow-Origin"
HeaderXPermittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies"
HeaderDNT = "DNT"
HeaderTk = "Tk"
HeaderContentDisposition = "Content-Disposition"
HeaderContentEncoding = "Content-Encoding"
HeaderContentLanguage = "Content-Language"
HeaderContentLength = "Content-Length"
HeaderContentLocation = "Content-Location"
HeaderContentType = "Content-Type"
HeaderForwarded = "Forwarded"
HeaderVia = "Via"
HeaderXForwardedFor = "X-Forwarded-For"
HeaderXForwardedHost = "X-Forwarded-Host"
HeaderXForwardedProto = "X-Forwarded-Proto"
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
HeaderXForwardedSsl = "X-Forwarded-Ssl"
HeaderXUrlScheme = "X-Url-Scheme"
HeaderLocation = "Location"
HeaderFrom = "From"
HeaderHost = "Host"
HeaderReferer = "Referer"
HeaderReferrerPolicy = "Referrer-Policy"
HeaderUserAgent = "User-Agent"
HeaderAllow = "Allow"
HeaderServer = "Server"
HeaderAcceptRanges = "Accept-Ranges"
HeaderContentRange = "Content-Range"
HeaderIfRange = "If-Range"
HeaderRange = "Range"
HeaderContentSecurityPolicy = "Content-Security-Policy"
HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
HeaderCrossOriginResourcePolicy = "Cross-Origin-Resource-Policy"
HeaderExpectCT = "Expect-CT"
HeaderFeaturePolicy = "Feature-Policy"
HeaderPublicKeyPins = "Public-Key-Pins"
HeaderPublicKeyPinsReportOnly = "Public-Key-Pins-Report-Only"
HeaderStrictTransportSecurity = "Strict-Transport-Security"
HeaderUpgradeInsecureRequests = "Upgrade-Insecure-Requests"
HeaderXContentTypeOptions = "X-Content-Type-Options"
HeaderXDownloadOptions = "X-Download-Options"
HeaderXFrameOptions = "X-Frame-Options"
HeaderXPoweredBy = "X-Powered-By"
HeaderXXSSProtection = "X-XSS-Protection"
HeaderLastEventID = "Last-Event-ID"
HeaderNEL = "NEL"
HeaderPingFrom = "Ping-From"
HeaderPingTo = "Ping-To"
HeaderReportTo = "Report-To"
HeaderTE = "TE"
HeaderTrailer = "Trailer"
HeaderTransferEncoding = "Transfer-Encoding"
HeaderSecWebSocketAccept = "Sec-WebSocket-Accept"
HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions"
HeaderSecWebSocketKey = "Sec-WebSocket-Key"
HeaderSecWebSocketProtocol = "Sec-WebSocket-Protocol"
HeaderSecWebSocketVersion = "Sec-WebSocket-Version"
HeaderAcceptPatch = "Accept-Patch"
HeaderAcceptPushPolicy = "Accept-Push-Policy"
HeaderAcceptSignature = "Accept-Signature"
HeaderAltSvc = "Alt-Svc"
HeaderDate = "Date"
HeaderIndex = "Index"
HeaderLargeAllocation = "Large-Allocation"
HeaderLink = "Link"
HeaderPushPolicy = "Push-Policy"
HeaderRetryAfter = "Retry-After"
HeaderServerTiming = "Server-Timing"
HeaderSignature = "Signature"
HeaderSignedHeaders = "Signed-Headers"
HeaderSourceMap = "SourceMap"
HeaderUpgrade = "Upgrade"
HeaderXDNSPrefetchControl = "X-DNS-Prefetch-Control"
HeaderXPingback = "X-Pingback"
HeaderXRequestID = "X-Request-ID"
HeaderXRequestedWith = "X-Requested-With"
HeaderXRobotsTag = "X-Robots-Tag"
HeaderXUACompatible = "X-UA-Compatible"
HeaderAccessControlAllowPrivateNetwork = "Access-Control-Allow-Private-Network"
HeaderAccessControlRequestPrivateNetwork = "Access-Control-Request-Private-Network"
)
```

View File

@ -924,6 +924,54 @@ app.Get("/", func(c fiber.Ctx) error {
})
```
## CBOR
CBOR converts any interface or string to CBOR encoded bytes.
:::info
CBOR also sets the content header to the `ctype` parameter. If no `ctype` is passed in, the header is set to `application/cbor`.
:::
```go title="Signature"
func (c fiber.Ctx) CBOR(data any, ctype ...string) error
```
```go title="Example"
type SomeStruct struct {
Name string `cbor:"name"`
Age uint8 `cbor:"age"`
}
app.Get("/cbor", func(c fiber.Ctx) error {
// Create data struct:
data := SomeStruct{
Name: "Grame",
Age: 20,
}
return c.CBOR(data)
// => Content-Type: application/cbor
// => \xa2dnameeGramecage\x14
return c.CBOR(fiber.Map{
"name": "Grame",
"age": 20,
})
// => Content-Type: application/cbor
// => \xa2dnameeGramecage\x14
return c.CBOR(fiber.Map{
"type": "https://example.com/probs/out-of-credit",
"title": "You do not have enough credit.",
"status": 403,
"detail": "Your current balance is 30, but that costs 50.",
"instance": "/account/12345/msgs/abc",
})
// => Content-Type: application/cbor
// => \xa5dtypex'https://example.com/probs/out-of-creditetitlex\x1eYou do not have enough credit.fstatus\x19\x01\x93fdetailx.Your current balance is 30, but that costs 50.hinstancew/account/12345/msgs/abc
})
```
## Links
Joins the links followed by the property to populate the responses [Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link) HTTP header field.

View File

@ -42,45 +42,47 @@ app := fiber.New(fiber.Config{
#### Config fields
| Property | Type | Description | Default |
|---------------------------------------------------------------------------------------|-------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| <Reference id="appname">AppName</Reference> | `string` | This allows to setup app name for the app | `""` |
| <Reference id="bodylimit">BodyLimit</Reference> | `int` | Sets the maximum allowed size for a request body, if the size exceeds the configured limit, it sends `413 - Request Entity Too Large` response. | `4 * 1024 * 1024` |
| <Reference id="casesensitive">CaseSensitive</Reference> | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo`and `/foo` are treated the same. | `false` |
| <Reference id="colorscheme">ColorScheme</Reference> | [`Colors`](https://github.com/gofiber/fiber/blob/master/color.go) | You can define custom color scheme. They'll be used for startup message, route list and some middlewares. | [`DefaultColors`](https://github.com/gofiber/fiber/blob/master/color.go) |
| <Reference id="compressedfilesuffixes">CompressedFileSuffixes</Reference> | `map[string]string` | Adds a suffix to the original file name and tries saving the resulting compressed file under the new file name. | `{"gzip": ".fiber.gz", "br": ".fiber.br", "zstd": ".fiber.zst"}` |
| <Reference id="concurrency">Concurrency</Reference> | `int` | Maximum number of concurrent connections. | `256 * 1024` |
| <Reference id="disabledefaultcontenttype">DisableDefaultContentType</Reference> | `bool` | When set to true, causes the default Content-Type header to be excluded from the Response. | `false` |
| <Reference id="disabledefaultdate">DisableDefaultDate</Reference> | `bool` | When set to true causes the default date header to be excluded from the response. | `false` |
| <Reference id="disableheadernormalizing">DisableHeaderNormalizing</Reference> | `bool` | By default all header names are normalized: conteNT-tYPE -&gt; Content-Type | `false` |
| <Reference id="disablekeepalive">DisableKeepalive</Reference> | `bool` | Disable keep-alive connections, the server will close incoming connections after sending the first response to the client | `false` |
| <Reference id="disablepreparsemultipartform">DisablePreParseMultipartForm</Reference> | `bool` | Will not pre parse Multipart Form data if set to true. This option is useful for servers that desire to treat multipart form data as a binary blob, or choose when to parse the data. | `false` |
| <Reference id="enableipvalidation">EnableIPValidation</Reference> | `bool` | If set to true, `c.IP()` and `c.IPs()` will validate IP addresses before returning them. Also, `c.IP()` will return only the first valid IP rather than just the raw header value that may be a comma separated string.<br /><br />**WARNING:** There is a small performance cost to doing this validation. Keep disabled if speed is your only concern and your application is behind a trusted proxy that already validates this header. | `false` |
| <Reference id="enablesplittingonparsers">EnableSplittingOnParsers</Reference> | `bool` | EnableSplittingOnParsers splits the query/body/header parameters by comma when it's true. <br /> <br /> For example, you can use it to parse multiple values from a query parameter like this: `/api?foo=bar,baz == foo[]=bar&foo[]=baz` | `false` |
| Property | Type | Description | Default |
|---------------------------------------------------------------------------------------|-------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| <Reference id="appname">AppName</Reference> | `string` | This allows to setup app name for the app | `""` |
| <Reference id="bodylimit">BodyLimit</Reference> | `int` | Sets the maximum allowed size for a request body, if the size exceeds the configured limit, it sends `413 - Request Entity Too Large` response. | `4 * 1024 * 1024` |
| <Reference id="casesensitive">CaseSensitive</Reference> | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo`and `/foo` are treated the same. | `false` |
| <Reference id="colorscheme">ColorScheme</Reference> | [`Colors`](https://github.com/gofiber/fiber/blob/master/color.go) | You can define custom color scheme. They'll be used for startup message, route list and some middlewares. | [`DefaultColors`](https://github.com/gofiber/fiber/blob/master/color.go) |
| <Reference id="compressedfilesuffixes">CompressedFileSuffixes</Reference> | `map[string]string` | Adds a suffix to the original file name and tries saving the resulting compressed file under the new file name. | `{"gzip": ".fiber.gz", "br": ".fiber.br", "zstd": ".fiber.zst"}` |
| <Reference id="concurrency">Concurrency</Reference> | `int` | Maximum number of concurrent connections. | `256 * 1024` |
| <Reference id="disabledefaultcontenttype">DisableDefaultContentType</Reference> | `bool` | When set to true, causes the default Content-Type header to be excluded from the Response. | `false` |
| <Reference id="disabledefaultdate">DisableDefaultDate</Reference> | `bool` | When set to true causes the default date header to be excluded from the response. | `false` |
| <Reference id="disableheadernormalizing">DisableHeaderNormalizing</Reference> | `bool` | By default all header names are normalized: conteNT-tYPE -&gt; Content-Type | `false` |
| <Reference id="disablekeepalive">DisableKeepalive</Reference> | `bool` | Disable keep-alive connections, the server will close incoming connections after sending the first response to the client | `false` |
| <Reference id="disablepreparsemultipartform">DisablePreParseMultipartForm</Reference> | `bool` | Will not pre parse Multipart Form data if set to true. This option is useful for servers that desire to treat multipart form data as a binary blob, or choose when to parse the data. | `false` |
| <Reference id="enableipvalidation">EnableIPValidation</Reference> | `bool` | If set to true, `c.IP()` and `c.IPs()` will validate IP addresses before returning them. Also, `c.IP()` will return only the first valid IP rather than just the raw header value that may be a comma separated string.<br /><br />**WARNING:** There is a small performance cost to doing this validation. Keep disabled if speed is your only concern and your application is behind a trusted proxy that already validates this header. | `false` |
| <Reference id="enablesplittingonparsers">EnableSplittingOnParsers</Reference> | `bool` | EnableSplittingOnParsers splits the query/body/header parameters by comma when it's true. <br /> <br /> For example, you can use it to parse multiple values from a query parameter like this: `/api?foo=bar,baz == foo[]=bar&foo[]=baz` | `false` |
| <Reference id="trustproxy">TrustProxy</Reference> | `bool` | When set to true, fiber will check whether proxy is trusted, using TrustProxyConfig.Proxies list. <br /><br />By default `c.Protocol()` will get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header, `c.IP()` will get value from `ProxyHeader` header, `c.Hostname()` will get value from X-Forwarded-Host header. <br /> If `TrustProxy` is true, and `RemoteIP` is in the list of `TrustProxyConfig.Proxies` `c.Protocol()`, `c.IP()`, and `c.Hostname()` will have the same behaviour when `TrustProxy` disabled, if `RemoteIP` isn't in the list, `c.Protocol()` will return https when a TLS connection is handled by the app, or http otherwise, `c.IP()` will return RemoteIP() from fasthttp context, `c.Hostname()` will return `fasthttp.Request.URI().Host()` | `false` |
| <Reference id="errorhandler">ErrorHandler</Reference> | `ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. Mounted fiber error handlers are retained by the top-level app and applied on prefix associated requests. | `DefaultErrorHandler` |
| <Reference id="getonly">GETOnly</Reference> | `bool` | Rejects all non-GET requests if set to true. This option is useful as anti-DoS protection for servers accepting only GET requests. The request size is limited by ReadBufferSize if GETOnly is set. | `false` |
| <Reference id="idletimeout">IdleTimeout</Reference> | `time.Duration` | The maximum amount of time to wait for the next request when keep-alive is enabled. If IdleTimeout is zero, the value of ReadTimeout is used. | `nil` |
| <Reference id="immutable">Immutable</Reference> | `bool` | When enabled, all values returned by context methods are immutable. By default, they are valid until you return from the handler; see issue [\#185](https://github.com/gofiber/fiber/issues/185). | `false` |
| <Reference id="jsondecoder">JSONDecoder</Reference> | `utils.JSONUnmarshal` | Allowing for flexibility in using another json library for decoding. | `json.Unmarshal` |
| <Reference id="jsonencoder">JSONEncoder</Reference> | `utils.JSONMarshal` | Allowing for flexibility in using another json library for encoding. | `json.Marshal` |
| <Reference id="passlocalstoviews">PassLocalsToViews</Reference> | `bool` | PassLocalsToViews Enables passing of the locals set on a fiber.Ctx to the template engine. See our **Template Middleware** for supported engines. | `false` |
| <Reference id="proxyheader">ProxyHeader</Reference> | `string` | This will enable `c.IP()` to return the value of the given header key. By default `c.IP()`will return the Remote IP from the TCP connection, this property can be useful if you are behind a load balancer e.g. _X-Forwarded-\*_. | `""` |
| <Reference id="readbuffersize">ReadBufferSize</Reference> | `int` | per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers \(for example, BIG cookies\). | `4096` |
| <Reference id="readtimeout">ReadTimeout</Reference> | `time.Duration` | The amount of time allowed to read the full request, including the body. The default timeout is unlimited. | `nil` |
| <Reference id="reducememoryusage">ReduceMemoryUsage</Reference> | `bool` | Aggressively reduces memory usage at the cost of higher CPU usage if set to true. | `false` |
| <Reference id="requestmethods">RequestMethods</Reference> | `[]string` | RequestMethods provides customizability for HTTP methods. You can add/remove methods as you wish. | `DefaultMethods` |
| <Reference id="serverheader">ServerHeader</Reference> | `string` | Enables the `Server` HTTP header with the given value. | `""` |
| <Reference id="streamrequestbody">StreamRequestBody</Reference> | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger than the current limit. | `false` |
| <Reference id="strictrouting">StrictRouting</Reference> | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` |
| <Reference id="structvalidator">StructValidator</Reference> | `StructValidator` | If you want to validate header/form/query... automatically when to bind, you can define struct validator. Fiber doesn't have default validator, so it'll skip validator step if you don't use any validator. | `nil` |
| <Reference id="trustproxyconfig">TrustProxyConfig</Reference> | `TrustProxyConfig` | Configure trusted proxy IP's. Look at `TrustProxy` doc. <br /> <br /> `TrustProxyConfig.Proxies` can take IP or IP range addresses. | `nil` |
| <Reference id="unescapepath">UnescapePath</Reference> | `bool` | Converts all encoded characters in the route back before setting the path for the context, so that the routing can also work with URL encoded special characters | `false` |
| <Reference id="views">Views</Reference> | `Views` | Views is the interface that wraps the Render function. See our **Template Middleware** for supported engines. | `nil` |
| <Reference id="viewslayout">ViewsLayout</Reference> | `string` | Views Layout is the global layout for all template render until override on Render function. See our **Template Middleware** for supported engines. | `""` |
| <Reference id="writebuffersize">WriteBufferSize</Reference> | `int` | Per-connection buffer size for responses' writing. | `4096` |
| <Reference id="writetimeout">WriteTimeout</Reference> | `time.Duration` | The maximum duration before timing out writes of the response. The default timeout is unlimited. | `nil` |
| <Reference id="xmlencoder">XMLEncoder</Reference> | `utils.XMLMarshal` | Allowing for flexibility in using another XML library for encoding. | `xml.Marshal` |
| <Reference id="errorhandler">ErrorHandler</Reference> | `ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. Mounted fiber error handlers are retained by the top-level app and applied on prefix associated requests. | `DefaultErrorHandler` |
| <Reference id="getonly">GETOnly</Reference> | `bool` | Rejects all non-GET requests if set to true. This option is useful as anti-DoS protection for servers accepting only GET requests. The request size is limited by ReadBufferSize if GETOnly is set. | `false` |
| <Reference id="idletimeout">IdleTimeout</Reference> | `time.Duration` | The maximum amount of time to wait for the next request when keep-alive is enabled. If IdleTimeout is zero, the value of ReadTimeout is used. | `nil` |
| <Reference id="immutable">Immutable</Reference> | `bool` | When enabled, all values returned by context methods are immutable. By default, they are valid until you return from the handler; see issue [\#185](https://github.com/gofiber/fiber/issues/185). | `false` |
| <Reference id="jsonencoder">JSONEncoder</Reference> | `utils.JSONMarshal` | Allowing for flexibility in using another json library for encoding. | `json.Marshal` |
| <Reference id="jsondecoder">JSONDecoder</Reference> | `utils.JSONUnmarshal` | Allowing for flexibility in using another json library for decoding. | `json.Unmarshal` |
| <Reference id="cborencoder">CBOREncoder</Reference> | `utils.CBORMarshal` | Allowing for flexibility in using another cbor library for encoding. | `cbor.Marshal` |
| <Reference id="cbordecoder">CBORDecoder</Reference> | `utils.CBORUnmarshal` | Allowing for flexibility in using another cbor library for decoding. | `cbor.Unmarshal` |
| <Reference id="passlocalstoviews">PassLocalsToViews</Reference> | `bool` | PassLocalsToViews Enables passing of the locals set on a fiber.Ctx to the template engine. See our **Template Middleware** for supported engines. | `false` |
| <Reference id="proxyheader">ProxyHeader</Reference> | `string` | This will enable `c.IP()` to return the value of the given header key. By default `c.IP()`will return the Remote IP from the TCP connection, this property can be useful if you are behind a load balancer e.g. _X-Forwarded-\*_. | `""` |
| <Reference id="readbuffersize">ReadBufferSize</Reference> | `int` | per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers \(for example, BIG cookies\). | `4096` |
| <Reference id="readtimeout">ReadTimeout</Reference> | `time.Duration` | The amount of time allowed to read the full request, including the body. The default timeout is unlimited. | `nil` |
| <Reference id="reducememoryusage">ReduceMemoryUsage</Reference> | `bool` | Aggressively reduces memory usage at the cost of higher CPU usage if set to true. | `false` |
| <Reference id="requestmethods">RequestMethods</Reference> | `[]string` | RequestMethods provides customizability for HTTP methods. You can add/remove methods as you wish. | `DefaultMethods` |
| <Reference id="serverheader">ServerHeader</Reference> | `string` | Enables the `Server` HTTP header with the given value. | `""` |
| <Reference id="streamrequestbody">StreamRequestBody</Reference> | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger than the current limit. | `false` |
| <Reference id="strictrouting">StrictRouting</Reference> | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` |
| <Reference id="structvalidator">StructValidator</Reference> | `StructValidator` | If you want to validate header/form/query... automatically when to bind, you can define struct validator. Fiber doesn't have default validator, so it'll skip validator step if you don't use any validator. | `nil` |
| <Reference id="trustproxyconfig">TrustProxyConfig</Reference> | `TrustProxyConfig` | Configure trusted proxy IP's. Look at `TrustProxy` doc. <br /> <br /> `TrustProxyConfig.Proxies` can take IP or IP range addresses. | `nil` |
| <Reference id="unescapepath">UnescapePath</Reference> | `bool` | Converts all encoded characters in the route back before setting the path for the context, so that the routing can also work with URL encoded special characters | `false` |
| <Reference id="views">Views</Reference> | `Views` | Views is the interface that wraps the Render function. See our **Template Middleware** for supported engines. | `nil` |
| <Reference id="viewslayout">ViewsLayout</Reference> | `string` | Views Layout is the global layout for all template render until override on Render function. See our **Template Middleware** for supported engines. | `""` |
| <Reference id="writebuffersize">WriteBufferSize</Reference> | `int` | Per-connection buffer size for responses' writing. | `4096` |
| <Reference id="writetimeout">WriteTimeout</Reference> | `time.Duration` | The maximum duration before timing out writes of the response. The default timeout is unlimited. | `nil` |
| <Reference id="xmlencoder">XMLEncoder</Reference> | `utils.XMLMarshal` | Allowing for flexibility in using another XML library for encoding. | `xml.Marshal` |
## Server listening

View File

@ -657,6 +657,15 @@ SetXML method sets XML body in request.
func (r *Request) SetXML(v any) *Request
```
## SetCBOR
SetCBOR method sets the request body using [CBOR](https://cbor.io/) encoding format.
It automatically sets the Content-Type header to `"application/cbor"`.
```go title="Signature"
func (r *Request) SetCBOR(v any) *Request
```
## SetRawBody
SetRawBody method sets body with raw data in request.

View File

@ -187,6 +187,14 @@ XML method will unmarshal body to xml.
func (r *Response) XML(v any) error
```
## CBOR
CBOR method will unmarshal body to CBOR.
```go title="Signature"
func (r *Response) CBOR(v any) error
```
## Save
Save method will save the body to a file or io.Writer.

View File

@ -81,6 +81,8 @@ type Client struct {
jsonUnmarshal utils.JSONUnmarshal
xmlMarshal utils.XMLMarshal
xmlUnmarshal utils.XMLUnmarshal
cborMarshal utils.CBORMarshal
cborUnmarshal utils.CBORUnmarshal
cookieJar *CookieJar
@ -314,6 +316,40 @@ SetXMLUnmarshal sets the XML decoder.
func (c *Client) SetXMLUnmarshal(f utils.XMLUnmarshal) *Client
```
### CBOR
#### CBORMarshal
CBORMarshal returns CBOR marshal function in Core.
```go title="Signature"
func (c *Client) CBORMarshal() utils.CBORMarshal
```
#### CBORUnmarshal
CBORUnmarshal returns CBOR unmarshal function in Core.
```go title="Signature"
func (c *Client) CBORUnmarshal() utils.CBORUnmarshal
```
#### SetCBORMarshal
SetCBORMarshal sets CBOR encoder.
```go title="Signature"
func (c *Client) SetCBORMarshal(f utils.CBORMarshal) *Client
```
#### SetCBORUnmarshal
SetCBORUnmarshal sets CBOR decoder.
```go title="Signature"
func (c *Client) SetCBORUnmarshal(f utils.CBORUnmarshal) *Client
```
### TLS
#### TLSConfig

View File

@ -310,6 +310,7 @@ testConfig := fiber.TestConfig{
- **SendString**: Similar to Express.js, sends a string as the response.
- **String**: Similar to Express.js, converts a value to a string.
- **ViewBind**: Binds data to a view, replacing the old `Bind` method.
- **CBOR**: Introducing [CBOR](https://cbor.io/) binary encoding format for both request & response body. CBOR is a binary data serialization format which is both compact and efficient, making it ideal for use in web applications.
### Removed Methods

2
go.mod
View File

@ -17,10 +17,12 @@ require (
require (
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // direct
github.com/klauspost/compress v1.17.11 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect

View File

@ -52,6 +52,9 @@ func (z *item) DecodeMsg(dc *msgp.Reader) (err error) {
err = msgp.WrapError(err, "headers", za0001)
return
}
if za0002 == nil {
za0002 = make([]byte, 0)
}
z.headers[za0001] = za0002
}
case "body":
@ -267,6 +270,9 @@ func (z *item) UnmarshalMsg(bts []byte) (o []byte, err error) {
err = msgp.WrapError(err, "headers", za0001)
return
}
if za0002 == nil {
za0002 = make([]byte, 0)
}
z.headers[za0001] = za0002
}
case "body":