-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🔥 feat: Add support for CBOR encoding #3173
base: main
Are you sure you want to change the base?
Changes from 7 commits
7f7fb38
ea59267
db00468
84c71a5
ba23305
7ee9548
ae8407c
988fd26
b316b36
a8192d1
2b11150
90f6ae8
4be558c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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" | ||||||||||||||||||||||||||||||||||||||||||
|
@@ -927,6 +928,14 @@ func Test_Bind_Body(t *testing.T) { | |||||||||||||||||||||||||||||||||||||||||
t.Run("JSON", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||
testDecodeParser(t, MIMEApplicationJSON, `{"name":"john"}`) | ||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||
t.Run("CBOR", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||
enc, err := cbor.Marshal(&Demo{Name: "john"}) | ||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||
t.Error(err) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
str := string(enc) | ||||||||||||||||||||||||||||||||||||||||||
testDecodeParser(t, MIMEApplicationCBOR, str) | ||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
t.Run("XML", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||
testDecodeParser(t, MIMEApplicationXML, `<Demo><name>john</name></Demo>`) | ||||||||||||||||||||||||||||||||||||||||||
|
@@ -1091,6 +1100,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 | ||||||||||||||||||||||||||||||||||||||||||
|
@@ -1710,9 +1748,16 @@ func Test_Bind_RepeatParserWithSameStruct(t *testing.T) { | |||||||||||||||||||||||||||||||||||||||||
require.NoError(t, c.Bind().Body(r)) | ||||||||||||||||||||||||||||||||||||||||||
require.Equal(t, "body_param", r.BodyParam) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
cb, err := cbor.Marshal(&Request{ | ||||||||||||||||||||||||||||||||||||||||||
BodyParam: "body_param", | ||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||||||
t.Error(err) | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
testDecodeParser(MIMEApplicationJSON, `{"body_param":"body_param"}`) | ||||||||||||||||||||||||||||||||||||||||||
testDecodeParser(MIMEApplicationXML, `<Demo><body_param>body_param</body_param></Demo>`) | ||||||||||||||||||||||||||||||||||||||||||
testDecodeParser(MIMEApplicationCBOR, string(cb)) | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1750
to
+1759
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix potential binary data corruption Converting CBOR binary data to string and back could lead to data corruption. The - testDecodeParser(MIMEApplicationCBOR, string(cb))
+ testDecodeParser(MIMEApplicationCBOR, cb) 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
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--") | ||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,15 @@ | ||||||||||||||
package binder | ||||||||||||||
|
||||||||||||||
import ( | ||||||||||||||
"github.com/gofiber/utils/v2" | ||||||||||||||
) | ||||||||||||||
gaby marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
||||||||||||||
type cborBinding struct{} | ||||||||||||||
|
||||||||||||||
func (*cborBinding) Name() string { | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This whole file is missing tests |
||||||||||||||
return "json" | ||||||||||||||
} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix incorrect binding name. The Apply this fix: func (*cborBinding) Name() string {
- return "json"
+ return "cbor"
} 📝 Committable suggestion
Suggested change
|
||||||||||||||
|
||||||||||||||
func (*cborBinding) Bind(body []byte, cborDecoder utils.CBORUnmarshal, out any) error { | ||||||||||||||
return cborDecoder(body, out) | ||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package client | |
import ( | ||
"context" | ||
"crypto/tls" | ||
"encoding/hex" | ||
"errors" | ||
"io" | ||
"net" | ||
|
@@ -202,6 +203,33 @@ func Test_Client_Marshal(t *testing.T) { | |
require.Equal(t, errors.New("empty xml"), err) | ||
}) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is missing a test using the default CBOR Marshall and UnMarshall functions as noted by |
||
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) | ||
}) | ||
gaby marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
t.Run("set xml unmarshal", func(t *testing.T) { | ||
t.Parallel() | ||
client := New(). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing hook test for |
||
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: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ const ( | |
formBody | ||
filesBody | ||
rawBody | ||
cborBody | ||
) | ||
|
||
var ErrClientNil = errors.New("client can not be nil") | ||
|
@@ -337,6 +338,12 @@ func (r *Request) SetXML(v any) *Request { | |
return r | ||
} | ||
|
||
func (r *Request) SetCBOR(v any) *Request { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing test using |
||
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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing test receiving |
||
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) | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid converting CBOR binary data to a string
CBOR data is binary, and converting it to a string can lead to data corruption or unexpected behavior. Modify
testDecodeParser
to accept a byte slice instead of a string to handle binary data properly.Refactor
testDecodeParser
to accept[]byte
for the body parameter:Update the test case to pass the byte slice directly:
📝 Committable suggestion