GithubHelp home page GithubHelp logo

yamashou / protoc-gen-gohttp Goto Github PK

View Code? Open in Web Editor NEW

This project forked from nametake/protoc-gen-gohttp

0.0 1.0 0.0 213 KB

License: BSD 2-Clause "Simplified" License

Makefile 2.48% Go 97.52%

protoc-gen-gohttp's Introduction

protoc-gen-gohttp

CircleCI

protoc-gen-gohttp is a plugin for converting Server's interface generated by protoc-gen-go's gRPC plugin to http.Handler.

In addition to this plugin, you need the protoc command, the proto-gen-go plugin and Google gRPC package.

The code generated by this plugin imports only the standard library, github.com/golang/protobuf and google.golang.org/grpc.

The converted http.Handler checks Content-Type Header, and changes Marshal/Unmarshal packages. The correspondence table is as follows.

Content-Type package
application/json github.com/golang/protobuf/jsonpb
application/protobuf github.com/golang/protobuf/proto
application/x-protobuf github.com/golang/protobuf/proto

Install

go get -u github.com/nametake/protoc-gen-gohttp

And install dependent tools. (e.g. macOS)

brew install protobuf
go get -u github.com/golang/protobuf/protoc-gen-go
go get -u google.golang.org/grpc

How to use

protoc --go_out=plugins=grpc:. --gohttp_out=. *.proto

Example

Run

You can execute examples with the following command.

make run_examples

You can confirm the operation with the following command.

curl -H "Content-Type: application/json" localhost:8080/sayhello -d '{"name": "john"}'
curl -H "Content-Type: application/json" localhost:8080/greeter/sayhello -d '{"name": "john"}'

Description

Define greeter.proto.

syntax = "proto3";

package helloworld;

option go_package = "main";

service Greeter {
  rpc SayHello(HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

From greeter.proto you defined, use the following command to generate greeter.pb.go and greeter.http.go.

protoc --go_out=plugins=grpc:. --gohttp_out=. examples/greeter.proto

Using the generated Go file, implement as follows.

// EchoGreeterServer has implemented the GreeterServer interface that created from the service in proto file.
type EchoGreeterServer struct {
}

// SayHello implements the GreeterServer interface method.
// SayHello returns a greeting to the name sent.
func (s *EchoGreeterServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) {
	return &HelloReply{
		Message: fmt.Sprintf("Hello, %s!", req.Name),
	}, nil
}

func main() {
	// Create the GreeterServer.
	srv := &EchoGreeterServer{}

	// Create the GreeterHTTPConverter generated by protoc-gen-gohttp.
	// This converter converts the GreeterServer interface that created from the service in proto to http.HandlerFunc.
	conv := NewGreeterHTTPConverter(srv)

	// Register SayHello HandlerFunc to the server.
	// If you do not need a callback, pass nil as argument.
	http.Handle("/sayhello", conv.SayHello(logCallback))
	// If you want to create a path from Proto's service name and method name, use the SayHelloWithName method.
	// In this case, the strings 'Greeter' and 'SayHello' are returned.
	http.Handle(restPath(conv.SayHelloWithName(logCallback)))

	log.Fatal(http.ListenAndServe(":8080", nil))
}

// logCallback is called when exiting ServeHTTP
// and receives Context, ResponseWriter, Request, service argument, service return value and error.
func logCallback(ctx context.Context, w http.ResponseWriter, r *http.Request, arg, ret proto.Message, err error) {
	log.Printf("INFO: call %s: arg: {%v}, ret: {%s}", r.RequestURI, arg, ret)
	// YOU MUST HANDLE ERROR
	if err != nil {
		log.Printf("ERROR: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		p := status.New(codes.Unknown, err.Error()).Proto()
		switch r.Header.Get("Content-Type") {
		case "application/protobuf", "application/x-protobuf":
			buf, err := proto.Marshal(p)
			if err != nil {
				return
			}
			if _, err := io.Copy(w, bytes.NewBuffer(buf)); err != nil {
				return
			}
		case "application/json":
			if err := json.NewEncoder(w).Encode(p); err != nil {
				return
			}
		default:
		}
	}
}

func restPath(service, method string, hf http.HandlerFunc) (string, http.HandlerFunc) {
	return fmt.Sprintf("/%s/%s", strings.ToLower(service), strings.ToLower(method)), hf
}

HTTPRule

protoc-gen-gohttp supports google.api.HttpRule option.

When the Service is defined using HttpRule, Converter implements the {RpcName}HTTPRule method. {RpcName}HTTPRule method returns Request Method, Path and http.HandlerFunc.

In the following example, Converter implements GetMessageHTTPRule. GetMessageHTTPRule returns http.MethodGet, "/v1/messages/{message_id}" and http.HandlerFunc.

syntax = "proto3";

package example;

option go_package = "main";

import "google/api/annotations.proto";

service Messaging {
  rpc GetMessage(GetMessageRequest) returns (GetMessageResponse) {
    option (google.api.http).get = "/v1/messages/{message_id}";
  }
}

message GetMessageRequest {
  string message_id = 1;
  string message = 2;
  repeated string tags = 3;
}

message GetMessageResponse {
  string message_id = 1;
  string message = 2;
  repeated string tags = 4;
}

{RpcName}HTTPRule method is intended for use with HTTP libraries like go-chi/chi and gorilla/mux as follows:

type Messaging struct{}

func (m *Messaging) GetMessage(ctx context.Context, req *GetMessageRequest) (*GetMessageResponse, error) {
	return &GetMessageResponse{
		MessageId: req.MessageId,
		Message:   req.Message,
		Tags:      req.Tags,
	}, nil
}

func main() {
	conv := NewMessagingHTTPConverter(&Messaging{})
	r := chi.NewRouter()

	r.Method(conv.GetMessageHTTPRule(nil))

	log.Fatal(http.ListenAndServe(":8080", r))
}

protoc-gen-gohttp parses Get Method according to google.api.HttpRule option. Therefore, you can pass values to the server in the above example with query string like /v1/messages/abc1234?message=hello&tags=a&tags=b.

When you actually execute the above server and execute curl -H "Content-Type: application/json" "localhost:8080/v1/messages/abc1234?message=hello&tags=a&tags=b", the following JOSN is returned.

{
  "messageId": "abc1234",
  "message": "hello",
  "tags": ["a", "b"]
}

Callback

Callback is called when the end of the generated code is reached without error or when an error occurs.

Callback is passed HTTP context and http.ResponseWriter and http.Request, RPC arguments and return values, and error.

RPC arguments and return values, and errors may be nil. Here's when nil is passed:

Timing RPC argument RPC return value error
When an error occurs before calling RPC nil nil err
When RPC returns an error arg nil err
When an error occurs after calling RPC arg ret err
When no error occurred arg ret nil

You MUST HANDLE ERROR in the callback. If you do not handle it, the error is ignored.

If nil is passed to callback, the error is always handled as an InternalServerError.

NOT SUPPORTED

protoc-gen-gohttp's People

Contributors

nametake avatar yamashou avatar sonatard avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.