Skip to content

Commit

Permalink
feat(cbor): allow encoding response bodies in cbor
Browse files Browse the repository at this point in the history
  • Loading branch information
imsk17 committed Oct 20, 2024
1 parent 298975a commit 7f7fb38
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 4 deletions.
22 changes: 21 additions & 1 deletion app.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import (
"sync"
"time"

"github.com/fxamacker/cbor/v2"
"github.com/gofiber/fiber/v3/log"
"github.com/gofiber/utils/v2"

"github.com/valyala/fasthttp"
)

Expand Down Expand Up @@ -318,6 +318,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
//
Expand Down Expand Up @@ -535,6 +549,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
}
Expand Down
1 change: 1 addition & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
18 changes: 18 additions & 0 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,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.
Expand Down
5 changes: 5 additions & 0 deletions ctx_interface_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 81 additions & 0 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"context"
"crypto/tls"
"embed"
"encoding/hex"
"encoding/xml"
"errors"
"fmt"
Expand Down Expand Up @@ -3572,6 +3573,86 @@ func Benchmark_Ctx_JSON(b *testing.B) {
require.Equal(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)))

// Test without ctype
err := c.CBOR(Map{ // 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(Map{ // map has no order
"Name": "Grame",
"Age": 20,
}, "application/problem+cbor")
require.NoError(t, err)
require.Equal(t, `a26341676514644e616d65654772616d65`, 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")

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()
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.22

require (
github.com/gofiber/schema v1.2.0
github.com/gofiber/utils/v2 v2.0.0-beta.6
github.com/gofiber/utils/v2 v2.0.0-beta.7
github.com/google/uuid v1.6.0
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-isatty v0.0.20
Expand All @@ -17,10 +17,12 @@ require (
require (
github.com/andybalholm/brotli v1.1.0 // 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.9 // indirect
github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // 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.29.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gofiber/schema v1.2.0 h1:j+ZRrNnUa/0ZuWrn/6kAtAufEr4jCJ+JuTURAMxNSZg=
github.com/gofiber/schema v1.2.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c=
github.com/gofiber/utils/v2 v2.0.0-beta.6 h1:ED62bOmpRXdgviPlfTmf0Q+AXzhaTUAFtdWjgx+XkYI=
github.com/gofiber/utils/v2 v2.0.0-beta.6/go.mod h1:3Kz8Px3jInKFvqxDzDeoSygwEOO+3uyubTmUa6PqY+0=
github.com/gofiber/utils/v2 v2.0.0-beta.7 h1:NnHFrRHvhrufPABdWajcKZejz9HnCWmT/asoxRsiEbQ=
github.com/gofiber/utils/v2 v2.0.0-beta.7/go.mod h1:J/M03s+HMdZdvhAeyh76xT72IfVqBzuz/OJkrMa7cwU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
Expand All @@ -29,6 +31,8 @@ github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW
github.com/valyala/fasthttp v1.56.0/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
6 changes: 6 additions & 0 deletions middleware/cache/manager_msgp.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 7f7fb38

Please sign in to comment.