Skip to content

Commit

Permalink
The JSON encoder/decoder provided by gRPC-gateway runtime silently co…
Browse files Browse the repository at this point in the history
…nverts (u)int64 to strings (see: grpc-ecosystem/grpc-gateway#920 ),

which is compliant with the Proto3 spec. However, this affects our response negatively because it breaks backwards compatibilty. Go's
JSON encoder/decoder doesn't have such surprises. This commit replaces runtime.JSONPb (from gRPC-Gateway) with a wrapper
around the stdlib JSON encoder/decoder.
  • Loading branch information
mohammed90 committed May 16, 2019
1 parent 655feb9 commit 21b06d9
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 10 deletions.
6 changes: 4 additions & 2 deletions grpc/internal/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"io/ioutil"
"net/http"
"strings"
"time"

log "github.com/sirupsen/logrus"

Expand All @@ -21,10 +20,13 @@ import (

type GatewayResponseMiddleware func(ctx context.Context, response http.ResponseWriter, m proto.Message) error

func JSONMarshaler() runtime.Marshaler {
return new(customJSONMarshaler)
}

// StatusCodeMutator changes the HTTP Repsonse status code to the desired mapping. The default mapping
// by gRPC-Gateway doesn't have some of the desired responses (e.g. 201), which is why this function is needed.
func StatusCodeMutator(ctx context.Context, response http.ResponseWriter, m proto.Message) error {
log.Printf("%s: message type: %T", time.Now(), m)
switch m.(type) {
case *authnpb.SignupResponseEnvelope, *authnpb.LoginResponseEnvelope, *authnpb.RefreshSessionResponseEnvelope, *authnpb.SubmitPasswordlessLoginResponseEnvelope, *authnpb.ChangePasswordResponseEnvelope:
response.WriteHeader(http.StatusCreated)
Expand Down
36 changes: 36 additions & 0 deletions grpc/internal/gateway/marshaler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package gateway

import (
"encoding/json"
"io"

"github.com/grpc-ecosystem/grpc-gateway/runtime"
)

// customJSONMarshaler implements runtime.Marshaler
type customJSONMarshaler struct{}

// Marshal marshals "v" into byte sequence.
func (j *customJSONMarshaler) Marshal(v interface{}) ([]byte, error) {
return json.Marshal(v)
}

// Unmarshal unmarshals "data" into "v".
func (j *customJSONMarshaler) Unmarshal(data []byte, v interface{}) error {
return json.Unmarshal(data, v)
}

// NewDecoder returns a Decoder which reads byte sequence from "r".
func (j *customJSONMarshaler) NewDecoder(r io.Reader) runtime.Decoder {
return json.NewDecoder(r)
}

// NewEncoder returns an Encoder which writes bytes sequence into "w".
func (j *customJSONMarshaler) NewEncoder(w io.Writer) runtime.Encoder {
return json.NewEncoder(w)
}

// ContentType returns the Content-Type which this marshaler is responsible for.
func (j *customJSONMarshaler) ContentType() string {
return "application/json"
}
7 changes: 3 additions & 4 deletions grpc/private/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ func RunPrivateGateway(ctx context.Context, app *app.App, r *mux.Router, conn *g

gmux := runtime.NewServeMux(
runtime.WithForwardResponseOption(gateway.StatusCodeMutator),
runtime.WithMarshalerOption("*", &runtime.JSONPb{
OrigName: true,
EmitDefaults: true,
}),
// Workaround this limitation: https://github.com/grpc-ecosystem/grpc-gateway/issues/920.
// Go's JSON encoder doesn't convert (u)int64 to strings silently.
runtime.WithMarshalerOption(runtime.MIMEWildcard, gateway.JSONMarshaler()),
)

r.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
Expand Down
7 changes: 3 additions & 4 deletions grpc/public/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ func RunPublicGateway(ctx context.Context, app *app.App, r *mux.Router, conn *gr
gmux := runtime.NewServeMux(
runtime.WithForwardResponseOption(gateway.CookieSetter(app.Config)), // Cookies always have to go first
runtime.WithForwardResponseOption(gateway.StatusCodeMutator),
runtime.WithMarshalerOption("*", &runtime.JSONPb{
OrigName: true,
EmitDefaults: true,
}),
// Workaround this limitation: https://github.com/grpc-ecosystem/grpc-gateway/issues/920.
// Go's JSON encoder doesn't convert (u)int64 to strings silently.
runtime.WithMarshalerOption(runtime.MIMEWildcard, gateway.JSONMarshaler()),
)

RegisterRoutes(r, app, gmux)
Expand Down

0 comments on commit 21b06d9

Please sign in to comment.