A plugin of protoc that for using a service of Protocol Buffers as http.Handler definition

protoc-gen-gohttp

protoc-gen-gohttp is a plugin of protoc that for using a service of Protocol Buffers as http.Handler definition.

The generated interface is compatible with the interface generated by the gRPC plugin.

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

The code generated by this plugin imports only the standard library, google.golang.org/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 google.golang.org/protobuf/encoding/protojson
application/protobuf google.golang.org/protobuf/proto
application/x-protobuf google.golang.org/protobuf/proto

Install

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

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

brew install protobuf
go get -u google.golang.org/protobuf/cmd/protoc-gen-go

How to use

protoc --go_out=. --gohttp_out=. *.proto

Example

Run

You can execute examples with the following command.

make gen_examples
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 http handle 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":
			buf, err := protojson.Marshal(p)
			if err != nil {
				return
			}
			if _, err := io.Copy(w, bytes.NewBuffer(buf)); 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
}

Generated interface

protoc-gen-gohttp generates the following interface.

type GreeterHTTPService interface {
	SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}

This interface is compatible with the interface generated by gRPC plugin.

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"]
}

HTTP Handle Callback

A http handle callback is a function to handle RPC calls with HTTP.

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

The 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 the callback, the error is always handled as an InternalServerError.

grpc.UnaryServerInterceptor

The convert method can receive multiple grpc.UnaryServerInterceptor.

Execution is done in left-to-right order, including passing of context.

For example, it is executed in the order described in the comments in the following example.

conv := NewGreeterHTTPConverter(&EchoGreeterServer{})
conv.SayHello(nil,
	grpc.UnaryServerInterceptor(
		func(ctx context.Context, arg interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
			// one (before RPC is called)
			ret, err := handler(ctx, arg)
			// four (after RPC is called)
			return ret, err
		},
	),
	grpc.UnaryServerInterceptor(
		func(ctx context.Context, arg interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
			// two (before RPC is called)
			ret, err := handler(ctx, arg)
			// three (after RPC is called)
			return ret, err
		},
	),
)

If you passed interceptors, you must call handler to return the correct return type.

If interceptors return the return value that is not expected by the RPC, http.Handler will pass an error like the following to the http handle callback and exit.

/helloworld.Greeter/SayHello: interceptors have not return HelloReply

NOT SUPPORTED

Similar Resources

This is a golang C2 + Implant that communicates via Protocol Buffers (aka. protobufs).

Br4vo6ix DISCLAIMER: This tool is for educational, competition, and training purposes only. I am in no way responsible for any abuse of this tool This

Nov 9, 2022

The canonical location of the Clusternet API definition.

apis Schema of the external API types that are served by Clusternet. Purpose This library is the canonical location of the Clusternet API definition.

May 3, 2022

Kobiton-execute-test-buildkite-plugin - A Buildkite Plugin to (synchronously) execute an automated test script on Kobiton service

Kobiton Execute Test Buildkite Plugin A Buildkite Plugin to (synchronously) exec

Feb 10, 2022

A simple tool to convert socket5 proxy protocol to http proxy protocol

Socket5 to HTTP 这是一个超简单的 Socket5 代理转换成 HTTP 代理的小工具。 如何安装? Golang 用户 # Required Go 1.17+ go install github.com/mritd/s2h@master Docker 用户 docker pull m

Jan 2, 2023

A protoc-gen-go wrapper including an RPC stub generator

// Copyright 2013 Google. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE fi

Nov 17, 2022

protoc-gen-grpc-gateway-ts is a Typescript client generator for the grpc-gateway project. It generates idiomatic Typescript clients that connect the web frontend and golang backend fronted by grpc-gateway.

protoc-gen-grpc-gateway-ts protoc-gen-grpc-gateway-ts is a Typescript client generator for the grpc-gateway project. It generates idiomatic Typescript

Dec 19, 2022

protobuf ではなく JSON でやり取りするファイルを出力する protoc プラグイン

protoc-gen-jsonif proto ファイルから、JSON フォーマットでやりとりする型定義ファイルを出力する protoc プラグインです。 proto ファイルで言語を越えて型定義が出来るのはとても良い しかし protobuf ライブラリを入れるのが面倒 今のプロジェクトには既に

Feb 28, 2022

🎉 An awesome version control tool for protoc and its related plugins.

🎉 An awesome version control tool for protoc and its related plugins.

❤️ PowerProto is actively maintained! Any questions in use can be directly raised issue, I will respond to you as fast as possible. If you think the p

Dec 29, 2022

pb: a tool for managing protoc builds and dependencies

pb pb is a Protocol Buffers Build tool that manages dependencies and build confi

Nov 20, 2022
Protoc plugin to generate contract tests for gRPC in Go

Deal - Go Introduction WE DO NOT SUPPORT THE SERVER SIDE YET This plugin allows us to write Consumer-Driver Contracts tests! Usage example Proto servi

Sep 3, 2022
Protoc plugin used to generate go-kit grpc code

protoc-gen-gokit-endpoint protoc plugin used to generate go-kit grpc code 安装 go install github.com/wwbweibo/protoc-gen-gokit-endpoint/cmd/protoc-gen-g

Sep 29, 2022
Protoc plugin used to generate go-kit grpc code

protoc-gen-gokit-endpoint protoc plugin used to generate go-kit grpc code 安装 go

Sep 29, 2022
The `protoc` compiler plugin which dumps the generation request details

Progotgen DUMP The protoc compiler plugin which dumps the generation request details in "google.golang.org/protobuf/compiler/protogen format to stderr

Jan 23, 2022
A Twirp RPC OpenAPI generator implemented as `protoc` plugin

twirp-openapi-gen A Twirp RPC OpenAPI generator implemented as protoc plugin Currently supports only OpenAPI 2.0 Usage Installing the generator for pr

May 26, 2022
A new way of working with Protocol Buffers.

Buf All documentation is hosted at https://buf.build. Please head over there for more details. Goal Buf’s long-term goal is to enable schema-driven de

Jan 1, 2023
A Protocol Buffers compiler that generates optimized marshaling & unmarshaling Go code for ProtoBuf APIv2

vtprotobuf, the Vitess Protocol Buffers compiler This repository provides the protoc-gen-go-vtproto plug-in for protoc, which is used by Vitess to gen

Jan 1, 2023
Protocol Buffers - Google's data interchange format

Protocol Buffers - Google's data interchange format Copyright 2008 Google Inc. https://developers.google.com/protocol-buffers/ Overview Protocol Buffe

Jan 3, 2023
Go support for Protocol Buffers - Google's data interchange format

Go support for Protocol Buffers - Google's data interchange format Google's data interchange format. Copyright 2010 The Go Authors. https://github.com

Dec 15, 2021
Estudos com Golang, GRPC e Protocol Buffers

Golang, GRPC e Protocol Buffers Estudos com Golang, GRPC e Protocol Buffers Projeto feito para fins de estudos. Para rodar basta seguir os passos abai

Feb 10, 2022