Skip to content

Commit ace6006

Browse files
authored
feat(encoding): support protocol buffers (#117)
1 parent e95b4ae commit ace6006

8 files changed

Lines changed: 306 additions & 40 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,13 @@ MIME: `application/toml`
171171
Parses TOML requests & encodes responses as TOML. Use the `toml` tag in your request & response
172172
structs.
173173

174+
#### Protocol Buffers
175+
176+
MIME: `application/protobuf`
177+
178+
Parses protobuf requests & encodes responses as protobuf. Use pointer types generated with `protoc`
179+
as your request & response structs.
180+
174181
### Adding custom encoding
175182

176183
Adding your own is easy. See [encoding/json/json.go](./encoding/json/json.go).

encoding/protobuf/protobuf.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package protobuf
2+
3+
import (
4+
"reflect"
5+
6+
"github.com/abemedia/go-don"
7+
"github.com/abemedia/go-don/encoding"
8+
"google.golang.org/protobuf/proto"
9+
)
10+
11+
var messageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
12+
13+
func unmarshal(b []byte, v any) error {
14+
// TODO: Cache reflect results to improve performance.
15+
elem := reflect.ValueOf(v).Elem()
16+
if elem.Type().Implements(messageType) && elem.IsNil() {
17+
elem.Set(reflect.New(elem.Type().Elem()))
18+
v = elem.Interface()
19+
}
20+
21+
m, ok := v.(proto.Message)
22+
if !ok {
23+
return don.ErrUnsupportedMediaType
24+
}
25+
return proto.Unmarshal(b, m)
26+
}
27+
28+
func marshal(v any) ([]byte, error) {
29+
m, ok := v.(proto.Message)
30+
if !ok {
31+
return nil, don.ErrNotAcceptable
32+
}
33+
return proto.Marshal(m)
34+
}
35+
36+
func init() {
37+
mediaType := "application/protobuf"
38+
alias := "application/x-protobuf"
39+
40+
encoding.RegisterDecoder(unmarshal, mediaType, alias)
41+
encoding.RegisterEncoder(marshal, mediaType, alias)
42+
}

encoding/protobuf/protobuf_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package protobuf_test
2+
3+
import (
4+
"errors"
5+
"testing"
6+
7+
"github.com/abemedia/go-don"
8+
"github.com/abemedia/go-don/encoding"
9+
"github.com/abemedia/go-don/encoding/protobuf/testdata"
10+
"github.com/abemedia/go-don/internal/test"
11+
"github.com/abemedia/go-don/pkg/httptest"
12+
)
13+
14+
//go:generate protoc testdata/test.proto --go_out=. --go_opt=paths=source_relative
15+
16+
var opt = test.EncodingOptions[*testdata.Item]{
17+
Mime: "application/protobuf",
18+
Raw: "\n\x03bar",
19+
Parsed: &testdata.Item{Foo: "bar"},
20+
}
21+
22+
func TestProtobuf(t *testing.T) {
23+
test.Encoding(t, opt)
24+
}
25+
26+
func TestProtobufError(t *testing.T) {
27+
ctx := httptest.NewRequest("", "", "", nil)
28+
v := "test"
29+
30+
t.Run("Decode", func(t *testing.T) {
31+
dec := encoding.GetDecoder("application/protobuf")
32+
err := dec(ctx, &v)
33+
if !errors.Is(err, don.ErrUnsupportedMediaType) {
34+
t.Fatal("should fail")
35+
}
36+
})
37+
38+
t.Run("Encode", func(t *testing.T) {
39+
enc := encoding.GetEncoder("application/protobuf")
40+
err := enc(ctx, &v)
41+
if !errors.Is(err, don.ErrNotAcceptable) {
42+
t.Fatal("should fail")
43+
}
44+
})
45+
}
46+
47+
func BenchmarkProtobuf(b *testing.B) {
48+
test.BenchmarkEncoding(b, opt)
49+
}

encoding/protobuf/testdata/test.pb.go

Lines changed: 143 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
syntax = "proto3";
2+
3+
option go_package = "github.com/abemedia/go-don/encoding/protobuf/testdata";
4+
5+
message Item { string foo = 1; }

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/pelletier/go-toml v1.9.5
1010
github.com/valyala/fasthttp v1.47.0
1111
github.com/vmihailenco/msgpack/v5 v5.3.5
12+
google.golang.org/protobuf v1.30.0
1213
gopkg.in/yaml.v3 v3.0.1
1314
)
1415

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
1010
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1111
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
1212
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
13+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
14+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
1315
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
1416
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
1517
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
@@ -75,6 +77,10 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
7577
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
7678
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
7779
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
80+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
81+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
82+
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
83+
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
7884
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
7985
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
8086
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)