Skip to content

Commit

Permalink
Merge pull request grpc-ecosystem#151 from gengo/cleanup/custom-marsh…
Browse files Browse the repository at this point in the history
…aler

Simplify custom marshaler API
  • Loading branch information
yugui committed May 10, 2016
2 parents 8122de5 + a2c3ae5 commit ef6e393
Show file tree
Hide file tree
Showing 12 changed files with 799 additions and 387 deletions.
11 changes: 8 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ OPTIONS_PROTO=$(GOOGLEAPIS_DIR)/google/api/annotations.proto $(GOOGLEAPIS_DIR)/g
OPTIONS_GO=$(OPTIONS_PROTO:.proto=.pb.go)
OUTPUT_DIR=_output

RUNTIME_PROTO=runtime/stream_chunk.proto
RUNTIME_GO=$(RUNTIME_PROTO:.proto=.pb.go)

PKGMAP=Mgoogle/protobuf/descriptor.proto=$(GO_PLUGIN_PKG)/descriptor,Mgoogle/api/annotations.proto=$(PKG)/$(GOOGLEAPIS_DIR)/google/api,Mexamples/sub/message.proto=$(PKG)/examples/sub
SWAGGER_EXAMPLES=examples/examplepb/echo_service.proto \
examples/examplepb/streamless_everything.proto
Expand All @@ -54,7 +57,7 @@ EXAMPLE_DEPS=examples/sub/message.proto examples/sub2/message.proto
EXAMPLE_DEPSRCS=$(EXAMPLE_DEPS:.proto=.pb.go)
PROTOC_INC_PATH=$(dir $(shell which protoc))/../include

generate: $(OPTIONS_GO)
generate: $(OPTIONS_GO) $(RUNTIME_GO)

.SUFFIXES: .go .proto

Expand All @@ -63,9 +66,11 @@ $(GO_PLUGIN):
go build -o $@ $(GO_PLUGIN_PKG)

$(OPTIONS_GO): $(OPTIONS_PROTO) $(GO_PLUGIN)
protoc -I $(PROTOC_INC_PATH) -I$(GOOGLEAPIS_DIR) --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP):$(GOOGLEAPIS_DIR) $(OPTIONS_PROTO)
protoc -I $(PROTOC_INC_PATH) -I$(GOOGLEAPIS_DIR) --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP):$(GOOGLEAPIS_DIR) $(OPTIONS_PROTO)
$(RUNTIME_GO): $(RUNTIME_PROTO) $(GO_PLUGIN)
protoc -I $(PROTOC_INC_PATH) --plugin=$(GO_PLUGIN) -I. --go_out=$(PKGMAP):. $(RUNTIME_PROTO)

$(GATEWAY_PLUGIN): $(OPTIONS_GO) $(GATEWAY_PLUGIN_SRC)
$(GATEWAY_PLUGIN): $(OPTIONS_GO) $(RUNTIME_GO) $(GATEWAY_PLUGIN_SRC)
go build -o $@ $(GATEWAY_PLUGIN_PKG)

$(SWAGGER_PLUGIN): $(OPTIONS_GO) $(SWAGGER_PLUGIN_SRC)
Expand Down
86 changes: 47 additions & 39 deletions examples/integration_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"io"
Expand All @@ -13,13 +12,13 @@ import (
"testing"
"time"

"golang.org/x/net/context"

gw "github.com/gengo/grpc-gateway/examples/examplepb"
server "github.com/gengo/grpc-gateway/examples/server"
sub "github.com/gengo/grpc-gateway/examples/sub"
"github.com/gengo/grpc-gateway/runtime"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
)

Expand Down Expand Up @@ -97,8 +96,8 @@ func testEcho(t *testing.T, port int, contentType string) {
}

var msg gw.SimpleMessage
if err := json.Unmarshal(buf, &msg); err != nil {
t.Errorf("json.Unmarshal(%s, &msg) failed with %v; want success", buf, err)
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if got, want := msg.Id, "myid"; got != want {
Expand All @@ -112,19 +111,20 @@ func testEcho(t *testing.T, port int, contentType string) {

func testEchoBody(t *testing.T) {
sent := gw.SimpleMessage{Id: "example"}
buf, err := json.Marshal(sent)
var m jsonpb.Marshaler
payload, err := m.MarshalToString(&sent)
if err != nil {
t.Fatalf("json.Marshal(%#v) failed with %v; want success", sent, err)
t.Fatalf("m.MarshalToString(%#v) failed with %v; want success", payload, err)
}

url := "http://localhost:8080/v1/example/echo_body"
resp, err := http.Post(url, "", bytes.NewReader(buf))
resp, err := http.Post(url, "", strings.NewReader(payload))
if err != nil {
t.Errorf("http.Post(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
buf, err = ioutil.ReadAll(resp.Body)
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err)
return
Expand All @@ -136,8 +136,8 @@ func testEchoBody(t *testing.T) {
}

var received gw.SimpleMessage
if err := json.Unmarshal(buf, &received); err != nil {
t.Errorf("json.Unmarshal(%s, &msg) failed with %v; want success", buf, err)
if err := jsonpb.UnmarshalString(string(buf), &received); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if got, want := received, sent; !reflect.DeepEqual(got, want) {
Expand Down Expand Up @@ -197,8 +197,8 @@ func testABECreate(t *testing.T) {
}

var msg gw.ABitOfEverything
if err := json.Unmarshal(buf, &msg); err != nil {
t.Errorf("json.Unmarshal(%s, &msg) failed with %v; want success", buf, err)
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if msg.Uuid == "" {
Expand Down Expand Up @@ -240,18 +240,19 @@ func testABECreateBody(t *testing.T) {
},
}
url := "http://localhost:8080/v1/example/a_bit_of_everything"
buf, err := json.Marshal(want)
var m jsonpb.Marshaler
payload, err := m.MarshalToString(&want)
if err != nil {
t.Fatalf("json.Marshal(%#v) failed with %v; want success", want, err)
t.Fatalf("m.MarshalToString(%#v) failed with %v; want success", want, err)
}

resp, err := http.Post(url, "application/json", bytes.NewReader(buf))
resp, err := http.Post(url, "application/json", strings.NewReader(payload))
if err != nil {
t.Errorf("http.Post(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
buf, err = ioutil.ReadAll(resp.Body)
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err)
return
Expand All @@ -263,8 +264,8 @@ func testABECreateBody(t *testing.T) {
}

var msg gw.ABitOfEverything
if err := json.Unmarshal(buf, &msg); err != nil {
t.Errorf("json.Unmarshal(%s, &msg) failed with %v; want success", buf, err)
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if msg.Uuid == "" {
Expand Down Expand Up @@ -316,16 +317,12 @@ func testABEBulkCreate(t *testing.T) {
},
},
}
buf, err := json.Marshal(want)
if err != nil {
t.Fatalf("json.Marshal(%#v) failed with %v; want success", want, err)
}
if _, err := w.Write(buf); err != nil {
t.Errorf("w.Write(%s) failed with %v; want success", buf, err)
return
var m jsonpb.Marshaler
if err := m.Marshal(w, &want); err != nil {
t.Fatalf("m.Marshal(%#v, w) failed with %v; want success", want, err)
}
if _, err := io.WriteString(w, "\n"); err != nil {
t.Errorf("w.Write(%s) failed with %v; want success", buf, err)
t.Errorf("w.Write(%q) failed with %v; want success", "\n", err)
return
}
count++
Expand All @@ -350,8 +347,8 @@ func testABEBulkCreate(t *testing.T) {
}

var msg gw.EmptyMessage
if err := json.Unmarshal(buf, &msg); err != nil {
t.Errorf("json.Unmarshal(%s, &msg) failed with %v; want success", buf, err)
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}

Expand Down Expand Up @@ -389,8 +386,8 @@ func testABELookup(t *testing.T) {
}

var want gw.ABitOfEverything
if err := json.Unmarshal(buf, &want); err != nil {
t.Errorf("json.Unmarshal(%s, &want) failed with %v; want success", buf, err)
if err := jsonpb.UnmarshalString(string(buf), &want); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &want) failed with %v; want success", buf, err)
return
}

Expand All @@ -409,8 +406,8 @@ func testABELookup(t *testing.T) {
}

var msg gw.ABitOfEverything
if err := json.Unmarshal(buf, &msg); err != nil {
t.Errorf("json.Unmarshal(%s, &msg) failed with %v; want success", buf, err)
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if got := msg; !reflect.DeepEqual(got, want) {
Expand Down Expand Up @@ -447,7 +444,7 @@ func testABELookupNotFound(t *testing.T) {

var msg errorBody
if err := json.Unmarshal(buf, &msg); err != nil {
t.Errorf("json.Unmarshal(%s, &msg) failed with %v; want success", buf, err)
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}

Expand All @@ -473,13 +470,24 @@ func testABEList(t *testing.T) {
dec := json.NewDecoder(resp.Body)
var i int
for i = 0; ; i++ {
var msg gw.ABitOfEverything
err := dec.Decode(&msg)
var item struct {
Result json.RawMessage `json:"result"`
Error map[string]interface{} `json:"error"`
}
err := dec.Decode(&item)
if err == io.EOF {
break
}
if err != nil {
t.Errorf("dec.Decode(&msg) failed with %v; want success; i = %d", err, i)
t.Errorf("dec.Decode(&item) failed with %v; want success; i = %d", err, i)
}
if len(item.Error) != 0 {
t.Errorf("item.Error = %#v; want empty; i = %d", item.Error, i)
continue
}
var msg gw.ABitOfEverything
if err := jsonpb.UnmarshalString(string(item.Result), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", item.Result, err)
}
}
if i <= 0 {
Expand Down Expand Up @@ -548,8 +556,8 @@ func testAdditionalBindings(t *testing.T) {
}

var msg sub.StringMessage
if err := json.Unmarshal(buf, &msg); err != nil {
t.Errorf("json.Unmarshal(%s, &msg) failed with %v; want success; %d", buf, err, i)
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success; %d", buf, err, i)
return
}
if got, want := msg.GetValue(), "hello"; got != want {
Expand Down
54 changes: 21 additions & 33 deletions runtime/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,6 @@ import (
"google.golang.org/grpc/grpclog"
)

type responseStreamChunk struct {
Result proto.Message `json:"result,omitempty"`
Error *responseStreamError `json:"error,omitempty"`
}

//Make this also conform to proto.Message for builtin JSONPb Marshaler
func (m *responseStreamChunk) Reset() { *m = responseStreamChunk{} }
func (m *responseStreamChunk) String() string { return proto.CompactTextString(m) }
func (*responseStreamChunk) ProtoMessage() {}

type responseStreamError struct {
GrpcCode int `json:"grpc_code,omitempty"`
HTTPCode int `json:"http_code,omitempty"`
Message string `json:"message,omitempty"`
HTTPStatus string `json:"http_status,omitempty"`
}

//Make this also conform to proto.Message for builtin JSONPb Marshaler
func (m *responseStreamError) Reset() { *m = responseStreamError{} }
func (m *responseStreamError) String() string { return proto.CompactTextString(m) }
func (*responseStreamError) ProtoMessage() {}

// ForwardResponseStream forwards the stream from gRPC server to REST client.
func ForwardResponseStream(ctx context.Context, marshaler Marshaler, w http.ResponseWriter, req *http.Request, recv func() (proto.Message, error), opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
f, ok := w.(http.Flusher)
Expand Down Expand Up @@ -73,7 +51,7 @@ func ForwardResponseStream(ctx context.Context, marshaler Marshaler, w http.Resp
return
}

buf, err := marshaler.Marshal(&responseStreamChunk{Result: resp})
buf, err := marshaler.Marshal(streamChunk(resp, nil))
if err != nil {
grpclog.Printf("Failed to marshal response chunk: %v", err)
return
Expand Down Expand Up @@ -154,16 +132,7 @@ func handleForwardResponseOptions(ctx context.Context, w http.ResponseWriter, re
}

func handleForwardResponseStreamError(marshaler Marshaler, w http.ResponseWriter, err error) {
grpcCode := grpc.Code(err)
httpCode := HTTPStatusFromCode(grpcCode)
resp := &responseStreamChunk{
Error: &responseStreamError{
GrpcCode: int(grpcCode),
HTTPCode: httpCode,
Message: err.Error(),
HTTPStatus: http.StatusText(httpCode),
}}
buf, merr := marshaler.Marshal(resp)
buf, merr := marshaler.Marshal(streamChunk(nil, err))
if merr != nil {
grpclog.Printf("Failed to marshal an error: %v", merr)
return
Expand All @@ -173,3 +142,22 @@ func handleForwardResponseStreamError(marshaler Marshaler, w http.ResponseWriter
return
}
}

func streamChunk(result proto.Message, err error) map[string]proto.Message {
if err != nil {
grpcCode := grpc.Code(err)
httpCode := HTTPStatusFromCode(grpcCode)
return map[string]proto.Message{
"error": &StreamError{
GrpcCode: int32(grpcCode),
HttpCode: int32(httpCode),
Message: err.Error(),
HttpStatus: http.StatusText(httpCode),
},
}
}
if result == nil {
return streamChunk(nil, fmt.Errorf("empty response"))
}
return map[string]proto.Message{"result": result}
}
Loading

0 comments on commit ef6e393

Please sign in to comment.