From a4298762f6bee29dea609d87fb807136d4f2bb44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Jochum?= Date: Mon, 12 Aug 2024 22:08:47 +0200 Subject: [PATCH] feat(server/cmd/protoc-gen-go-orb): Add our own protoc builder for {drpc,grpc,http,hertz} --- benchmarks/rps/handler/echo/handler.go | 6 +- benchmarks/rps/proto/echo/echo.pb.go | 21 +- benchmarks/rps/proto/echo/echo.proto | 9 +- .../{echo_drpc.pb.go => echo_orb-drpc.pb.go} | 37 +- .../{echo_grpc.pb.go => echo_orb-grpc.pb.go} | 63 +- benchmarks/rps/proto/echo/echo_orb.pb.go | 60 +- benchmarks/rps/proto/gen.go | 7 +- .../rps/proto/google/api/annotations.proto | 31 - benchmarks/rps/proto/google/api/http.proto | 371 ---------- .../cmd/orb-rps-nh-client/main.go | 2 +- .../cmd/orb-rps-nh-server/wire.go | 12 +- .../cmd/orb-rps-nh-server/wire_gen.go | 4 +- benchmarks/rps_no_hertz/go.mod | 7 +- benchmarks/rps_no_hertz/go.sum | 53 +- .../rps_no_hertz/handler/echo/handler.go | 6 +- benchmarks/rps_no_hertz/proto/echo/echo.pb.go | 21 +- benchmarks/rps_no_hertz/proto/echo/echo.proto | 9 +- .../rps_no_hertz/proto/echo/echo_grpc.pb.go | 101 --- .../{echo_drpc.pb.go => echo_orb-drpc.pb.go} | 37 +- .../rps_no_hertz/proto/echo/echo_orb.pb.go | 34 +- benchmarks/rps_no_hertz/proto/gen.go | 6 +- .../proto/google/api/annotations.proto | 31 - .../rps_no_hertz/proto/google/api/http.proto | 371 ---------- server/cmd/protoc-gen-go-orb/go.mod | 5 + server/cmd/protoc-gen-go-orb/go.sum | 6 + server/cmd/protoc-gen-go-orb/main.go | 103 +++ server/cmd/protoc-gen-go-orb/orb/orb.go | 230 +++++++ server/cmd/protoc-gen-go-orb/orb/template.go | 157 +++++ server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go | 391 +++++++++++ .../protoc-gen-go-orb/orbdrpc/drpc.go.patch | 330 +++++++++ .../orbdrpc/drpc.go.upstream | 534 +++++++++++++++ .../cmd/protoc-gen-go-orb/orbgrpc/README.md | 5 + server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go | 476 +++++++++++++ .../protoc-gen-go-orb/orbgrpc/grpc.go.diff | 145 ++++ .../protoc-gen-go-orb/orbgrpc/grpc.go.patch | 338 ++++++++++ .../orbgrpc/grpc.go.upstream | 634 ++++++++++++++++++ .../http/cmd/protoc-gen-go-micro-http/http.go | 379 ----------- .../cmd/protoc-gen-go-micro-http/http_test.go | 88 --- .../http/cmd/protoc-gen-go-micro-http/main.go | 40 -- .../cmd/protoc-gen-go-micro-http/template.go | 91 --- .../cmd/protoc-gen-go-micro-http/version.go | 4 - 41 files changed, 3483 insertions(+), 1772 deletions(-) rename benchmarks/rps/proto/echo/{echo_drpc.pb.go => echo_orb-drpc.pb.go} (78%) rename benchmarks/rps/proto/echo/{echo_grpc.pb.go => echo_orb-grpc.pb.go} (60%) delete mode 100644 benchmarks/rps/proto/google/api/annotations.proto delete mode 100644 benchmarks/rps/proto/google/api/http.proto delete mode 100644 benchmarks/rps_no_hertz/proto/echo/echo_grpc.pb.go rename benchmarks/rps_no_hertz/proto/echo/{echo_drpc.pb.go => echo_orb-drpc.pb.go} (78%) delete mode 100644 benchmarks/rps_no_hertz/proto/google/api/annotations.proto delete mode 100644 benchmarks/rps_no_hertz/proto/google/api/http.proto create mode 100644 server/cmd/protoc-gen-go-orb/go.mod create mode 100644 server/cmd/protoc-gen-go-orb/go.sum create mode 100644 server/cmd/protoc-gen-go-orb/main.go create mode 100644 server/cmd/protoc-gen-go-orb/orb/orb.go create mode 100644 server/cmd/protoc-gen-go-orb/orb/template.go create mode 100644 server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go create mode 100644 server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go.patch create mode 100644 server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go.upstream create mode 100644 server/cmd/protoc-gen-go-orb/orbgrpc/README.md create mode 100644 server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go create mode 100644 server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.diff create mode 100644 server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.patch create mode 100644 server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.upstream delete mode 100644 server/http/cmd/protoc-gen-go-micro-http/http.go delete mode 100644 server/http/cmd/protoc-gen-go-micro-http/http_test.go delete mode 100644 server/http/cmd/protoc-gen-go-micro-http/main.go delete mode 100644 server/http/cmd/protoc-gen-go-micro-http/template.go delete mode 100644 server/http/cmd/protoc-gen-go-micro-http/version.go diff --git a/benchmarks/rps/handler/echo/handler.go b/benchmarks/rps/handler/echo/handler.go index ef33b644..daa2535a 100644 --- a/benchmarks/rps/handler/echo/handler.go +++ b/benchmarks/rps/handler/echo/handler.go @@ -7,12 +7,10 @@ import ( "github.com/go-orb/plugins/benchmarks/rps/proto/echo" ) -var _ echo.EchoServer = (*Handler)(nil) +var _ echo.EchoHandler = (*Handler)(nil) // Handler is a test handler. -type Handler struct { - echo.UnsafeEchoServer -} +type Handler struct{} // Echo implements the echo method. func (c *Handler) Echo(_ context.Context, req *echo.Req) (*echo.Resp, error) { diff --git a/benchmarks/rps/proto/echo/echo.pb.go b/benchmarks/rps/proto/echo/echo.pb.go index 085b5256..2b3cdb3b 100644 --- a/benchmarks/rps/proto/echo/echo.pb.go +++ b/benchmarks/rps/proto/echo/echo.pb.go @@ -7,7 +7,6 @@ package echo import ( - _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -119,19 +118,15 @@ var File_echo_echo_proto protoreflect.FileDescriptor var file_echo_echo_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x04, 0x65, 0x63, 0x68, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1f, 0x0a, 0x03, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x20, 0x0a, 0x04, 0x52, 0x65, 0x73, 0x70, 0x12, 0x18, + 0x6f, 0x12, 0x04, 0x65, 0x63, 0x68, 0x6f, 0x22, 0x1f, 0x0a, 0x03, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x32, 0x41, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, - 0x12, 0x39, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x09, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, - 0x52, 0x65, 0x71, 0x1a, 0x0a, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x22, - 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x22, 0x0f, 0x2f, 0x65, 0x63, 0x68, - 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x2f, 0x45, 0x63, 0x68, 0x6f, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, - 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x3b, 0x65, 0x63, 0x68, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x20, 0x0a, 0x04, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x32, 0x25, 0x0a, 0x04, 0x45, 0x63, + 0x68, 0x6f, 0x12, 0x1d, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x09, 0x2e, 0x65, 0x63, 0x68, + 0x6f, 0x2e, 0x52, 0x65, 0x71, 0x1a, 0x0a, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x3b, 0x65, 0x63, 0x68, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/benchmarks/rps/proto/echo/echo.proto b/benchmarks/rps/proto/echo/echo.proto index ca517176..03d175e5 100644 --- a/benchmarks/rps/proto/echo/echo.proto +++ b/benchmarks/rps/proto/echo/echo.proto @@ -2,17 +2,10 @@ syntax = "proto3"; package echo; -import "google/api/annotations.proto"; - option go_package = "./echo;echo"; service Echo { - rpc Echo(Req) returns (Resp) { - option (google.api.http) = { - post : "/echo.Echo/Echo" - body : "*" - }; - } + rpc Echo(Req) returns (Resp); } message Req { bytes payload = 1; } diff --git a/benchmarks/rps/proto/echo/echo_drpc.pb.go b/benchmarks/rps/proto/echo/echo_orb-drpc.pb.go similarity index 78% rename from benchmarks/rps/proto/echo/echo_drpc.pb.go rename to benchmarks/rps/proto/echo/echo_orb-drpc.pb.go index 5dc800da..900cc37c 100644 --- a/benchmarks/rps/proto/echo/echo_drpc.pb.go +++ b/benchmarks/rps/proto/echo/echo_orb-drpc.pb.go @@ -1,5 +1,9 @@ -// Code generated by protoc-gen-go-drpc. DO NOT EDIT. -// protoc-gen-go-drpc version: v0.0.34 +// Code generated by protoc-gen-go-orb. DO NOT EDIT. +// +// version: +// - protoc-gen-go-orb v0.0.1 +// - protoc v5.27.2 +// // source: echo/echo.proto package echo @@ -35,31 +39,6 @@ func (drpcEncoding_File_echo_echo_proto) JSONUnmarshal(buf []byte, msg drpc.Mess return protojson.Unmarshal(buf, msg.(proto.Message)) } -type DRPCEchoClient interface { - DRPCConn() drpc.Conn - - Echo(ctx context.Context, in *Req) (*Resp, error) -} - -type drpcEchoClient struct { - cc drpc.Conn -} - -func NewDRPCEchoClient(cc drpc.Conn) DRPCEchoClient { - return &drpcEchoClient{cc} -} - -func (c *drpcEchoClient) DRPCConn() drpc.Conn { return c.cc } - -func (c *drpcEchoClient) Echo(ctx context.Context, in *Req) (*Resp, error) { - out := new(Resp) - err := c.cc.Invoke(ctx, "/echo.Echo/Echo", drpcEncoding_File_echo_echo_proto{}, in, out) - if err != nil { - return nil, err - } - return out, nil -} - type DRPCEchoServer interface { Echo(context.Context, *Req) (*Resp, error) } @@ -103,6 +82,10 @@ type drpcEcho_EchoStream struct { drpc.Stream } +func (x *drpcEcho_EchoStream) GetStream() drpc.Stream { + return x.Stream +} + func (x *drpcEcho_EchoStream) SendAndClose(m *Resp) error { if err := x.MsgSend(m, drpcEncoding_File_echo_echo_proto{}); err != nil { return err diff --git a/benchmarks/rps/proto/echo/echo_grpc.pb.go b/benchmarks/rps/proto/echo/echo_orb-grpc.pb.go similarity index 60% rename from benchmarks/rps/proto/echo/echo_grpc.pb.go rename to benchmarks/rps/proto/echo/echo_orb-grpc.pb.go index 6d45e0d2..89265ffc 100644 --- a/benchmarks/rps/proto/echo/echo_grpc.pb.go +++ b/benchmarks/rps/proto/echo/echo_orb-grpc.pb.go @@ -1,4 +1,8 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// Code generated by protoc-gen-go-orb-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-orb v0.0.1 +// - protoc v5.27.2 +// source: echo/echo.proto package echo @@ -11,49 +15,31 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 -// EchoClient is the client API for Echo service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type EchoClient interface { - Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Resp, error) -} - -type echoClient struct { - cc grpc.ClientConnInterface -} - -func NewEchoClient(cc grpc.ClientConnInterface) EchoClient { - return &echoClient{cc} -} - -func (c *echoClient) Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Resp, error) { - out := new(Resp) - err := c.cc.Invoke(ctx, "/echo.Echo/Echo", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} +const ( + Echo_Echo_FullMethodName = "/echo.Echo/Echo" +) // EchoServer is the server API for Echo service. -// All implementations must embed UnimplementedEchoServer -// for forward compatibility +// All implementations should embed UnimplementedEchoServer +// for forward compatibility. type EchoServer interface { Echo(context.Context, *Req) (*Resp, error) - mustEmbedUnimplementedEchoServer() } -// UnimplementedEchoServer must be embedded to have forward compatible implementations. -type UnimplementedEchoServer struct { -} +// UnimplementedEchoServer should be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedEchoServer struct{} func (UnimplementedEchoServer) Echo(context.Context, *Req) (*Resp, error) { return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented") } -func (UnimplementedEchoServer) mustEmbedUnimplementedEchoServer() {} +func (UnimplementedEchoServer) testEmbeddedByValue() {} // UnsafeEchoServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to EchoServer will @@ -62,7 +48,14 @@ type UnsafeEchoServer interface { mustEmbedUnimplementedEchoServer() } -func RegisterEchoServer(s grpc.ServiceRegistrar, srv EchoServer) { +func registerEchoGRPCHandler(s grpc.ServiceRegistrar, srv EchoServer) { + // If the following call panics, it indicates UnimplementedEchoServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Echo_ServiceDesc, srv) } @@ -76,7 +69,7 @@ func _Echo_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/echo.Echo/Echo", + FullMethod: Echo_Echo_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(EchoServer).Echo(ctx, req.(*Req)) diff --git a/benchmarks/rps/proto/echo/echo_orb.pb.go b/benchmarks/rps/proto/echo/echo_orb.pb.go index 66d021a0..864ef23f 100644 --- a/benchmarks/rps/proto/echo/echo_orb.pb.go +++ b/benchmarks/rps/proto/echo/echo_orb.pb.go @@ -1,40 +1,31 @@ -// Code generated by protoc-gen-go-orb-http. DO NOT EDIT. +// Code generated by protoc-gen-go-orb. DO NOT EDIT. // // version: -// - protoc-gen-go-orb-http v1.0.0 -// - protoc v4.25.1 +// - protoc-gen-go-orb v0.0.1 +// - protoc v5.27.2 // -// Proto source: echo.proto +// Proto source: echo/echo.proto package echo import ( "context" + "github.com/go-orb/go-orb/log" "github.com/go-orb/go-orb/server" - "google.golang.org/grpc" + + grpc "google.golang.org/grpc" mdrpc "github.com/go-orb/plugins/server/drpc" mhertz "github.com/go-orb/plugins/server/hertz" mhttp "github.com/go-orb/plugins/server/http" ) -type orbEchoHandler interface { +type EchoHandler interface { Echo(ctx context.Context, req *Req) (*Resp, error) - mustEmbedUnimplementedEchoServer() -} - -func registerEchoHTTPHandler(srv *mhttp.ServerHTTP, handler orbEchoHandler) { - r := srv.Router() - r.Post("/echo.Echo/Echo", mhttp.NewGRPCHandler(srv, handler.Echo)) } -func registerEchoHertzHandler(srv *mhertz.Server, handler orbEchoHandler) { - s := srv.Router() - s.POST("/echo.Echo/Echo", mhertz.NewGRPCHandler(srv, handler.Echo)) -} - -func registerEchoDRPCHandler(srv *mdrpc.Server, handler orbEchoHandler) error { +func registerEchoDRPCHandler(srv *mdrpc.Server, handler EchoHandler) error { desc := DRPCEchoDescription{} // Register with DRPC. @@ -55,21 +46,34 @@ func registerEchoDRPCHandler(srv *mdrpc.Server, handler orbEchoHandler) error { return nil } -// OrbRegister will return a registration function that can be +// registerEchoHTTPHandler registers the service to an HTTP server. +func registerEchoHTTPHandler(srv *mhttp.ServerHTTP, handler EchoHandler) { + r := srv.Router() + r.Post("/echo.Echo/Echo", mhttp.NewGRPCHandler(srv, handler.Echo)) +} + +// registerEchoHertzHandler registers the service to an Hertz server. +func registerEchoHertzHandler(srv *mhertz.Server, handler EchoHandler) { + r := srv.Router() + r.POST("/echo.Echo/Echo", mhertz.NewGRPCHandler(srv, handler.Echo)) +} + +// RegisterEchoService will return a registration function that can be // provided to entrypoints as a handler registration. -func OrbRegister(handler EchoServer) server.RegistrationFunc { +func RegisterEchoService(handler EchoHandler) server.RegistrationFunc { return server.RegistrationFunc(func(s any) { switch srv := s.(type) { - case *mhttp.ServerHTTP: - registerEchoHTTPHandler(srv, handler.(orbEchoHandler)) - case *mhertz.Server: - registerEchoHertzHandler(srv, handler.(orbEchoHandler)) - case *mdrpc.Server: - registerEchoDRPCHandler(srv, handler.(orbEchoHandler)) //nolint:errcheck,gosec + case grpc.ServiceRegistrar: - RegisterEchoServer(srv, handler) + registerEchoGRPCHandler(srv, handler) + case *mdrpc.Server: + registerEchoDRPCHandler(srv, handler) + case *mhertz.Server: + registerEchoHertzHandler(srv, handler) + case *mhttp.ServerHTTP: + registerEchoHTTPHandler(srv, handler) default: - // Maybe we should log here with slog global logger + log.Warn("No provider for this server found", "proto", "echo/echo.proto", "handler", "Echo", "server", s) } }) } diff --git a/benchmarks/rps/proto/gen.go b/benchmarks/rps/proto/gen.go index 7be6ce72..f76ef990 100644 --- a/benchmarks/rps/proto/gen.go +++ b/benchmarks/rps/proto/gen.go @@ -1,9 +1,4 @@ // Package proto ... package proto -// Download Google proto HTTP annotation libs -//go:generate wget -q -O google/api/annotations.proto https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto -//go:generate wget -q -O google/api/http.proto https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto - -// Generate proto files -//go:generate protoc -I . --go-grpc_out=paths=source_relative:. --go-micro-http_out=paths=source_relative:. --go_out=paths=source_relative:. --go-drpc_out=paths=source_relative:. ./echo/echo.proto +//go:generate protoc -I . --go-orb_out=paths=source_relative:. --go-orb_opt="supported_servers=drpc;grpc;http;hertz" ./echo/echo.proto diff --git a/benchmarks/rps/proto/google/api/annotations.proto b/benchmarks/rps/proto/google/api/annotations.proto deleted file mode 100644 index 84c48164..00000000 --- a/benchmarks/rps/proto/google/api/annotations.proto +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.api; - -import "google/api/http.proto"; -import "google/protobuf/descriptor.proto"; - -option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; -option java_multiple_files = true; -option java_outer_classname = "AnnotationsProto"; -option java_package = "com.google.api"; -option objc_class_prefix = "GAPI"; - -extend google.protobuf.MethodOptions { - // See `HttpRule`. - HttpRule http = 72295728; -} diff --git a/benchmarks/rps/proto/google/api/http.proto b/benchmarks/rps/proto/google/api/http.proto deleted file mode 100644 index e3270371..00000000 --- a/benchmarks/rps/proto/google/api/http.proto +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.api; - -option cc_enable_arenas = true; -option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; -option java_multiple_files = true; -option java_outer_classname = "HttpProto"; -option java_package = "com.google.api"; -option objc_class_prefix = "GAPI"; - -// Defines the HTTP configuration for an API service. It contains a list of -// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method -// to one or more HTTP REST API methods. -message Http { - // A list of HTTP configuration rules that apply to individual API methods. - // - // **NOTE:** All service configuration rules follow "last one wins" order. - repeated HttpRule rules = 1; - - // When set to true, URL path parameters will be fully URI-decoded except in - // cases of single segment matches in reserved expansion, where "%2F" will be - // left encoded. - // - // The default behavior is to not decode RFC 6570 reserved characters in multi - // segment matches. - bool fully_decode_reserved_expansion = 2; -} - -// gRPC Transcoding -// -// gRPC Transcoding is a feature for mapping between a gRPC method and one or -// more HTTP REST endpoints. It allows developers to build a single API service -// that supports both gRPC APIs and REST APIs. Many systems, including [Google -// APIs](https://github.com/googleapis/googleapis), -// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC -// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), -// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature -// and use it for large scale production services. -// -// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies -// how different portions of the gRPC request message are mapped to the URL -// path, URL query parameters, and HTTP request body. It also controls how the -// gRPC response message is mapped to the HTTP response body. `HttpRule` is -// typically specified as an `google.api.http` annotation on the gRPC method. -// -// Each mapping specifies a URL path template and an HTTP method. The path -// template may refer to one or more fields in the gRPC request message, as long -// as each field is a non-repeated field with a primitive (non-message) type. -// The path template controls how fields of the request message are mapped to -// the URL path. -// -// Example: -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get: "/v1/{name=messages/*}" -// }; -// } -// } -// message GetMessageRequest { -// string name = 1; // Mapped to URL path. -// } -// message Message { -// string text = 1; // The resource content. -// } -// -// This enables an HTTP REST to gRPC mapping as below: -// -// - HTTP: `GET /v1/messages/123456` -// - gRPC: `GetMessage(name: "messages/123456")` -// -// Any fields in the request message which are not bound by the path template -// automatically become HTTP query parameters if there is no HTTP request body. -// For example: -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get:"/v1/messages/{message_id}" -// }; -// } -// } -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // Mapped to URL path. -// int64 revision = 2; // Mapped to URL query parameter `revision`. -// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. -// } -// -// This enables a HTTP JSON to RPC mapping as below: -// -// - HTTP: `GET /v1/messages/123456?revision=2&sub.subfield=foo` -// - gRPC: `GetMessage(message_id: "123456" revision: 2 sub: -// SubMessage(subfield: "foo"))` -// -// Note that fields which are mapped to URL query parameters must have a -// primitive type or a repeated primitive type or a non-repeated message type. -// In the case of a repeated type, the parameter can be repeated in the URL -// as `...?param=A¶m=B`. In the case of a message type, each field of the -// message is mapped to a separate parameter, such as -// `...?foo.a=A&foo.b=B&foo.c=C`. -// -// For HTTP methods that allow a request body, the `body` field -// specifies the mapping. Consider a REST update method on the -// message resource collection: -// -// service Messaging { -// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { -// option (google.api.http) = { -// patch: "/v1/messages/{message_id}" -// body: "message" -// }; -// } -// } -// message UpdateMessageRequest { -// string message_id = 1; // mapped to the URL -// Message message = 2; // mapped to the body -// } -// -// The following HTTP JSON to RPC mapping is enabled, where the -// representation of the JSON in the request body is determined by -// protos JSON encoding: -// -// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` -// - gRPC: `UpdateMessage(message_id: "123456" message { text: "Hi!" })` -// -// The special name `*` can be used in the body mapping to define that -// every field not bound by the path template should be mapped to the -// request body. This enables the following alternative definition of -// the update method: -// -// service Messaging { -// rpc UpdateMessage(Message) returns (Message) { -// option (google.api.http) = { -// patch: "/v1/messages/{message_id}" -// body: "*" -// }; -// } -// } -// message Message { -// string message_id = 1; -// string text = 2; -// } -// -// -// The following HTTP JSON to RPC mapping is enabled: -// -// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` -// - gRPC: `UpdateMessage(message_id: "123456" text: "Hi!")` -// -// Note that when using `*` in the body mapping, it is not possible to -// have HTTP parameters, as all fields not bound by the path end in -// the body. This makes this option more rarely used in practice when -// defining REST APIs. The common usage of `*` is in custom methods -// which don't use the URL at all for transferring data. -// -// It is possible to define multiple HTTP methods for one RPC by using -// the `additional_bindings` option. Example: -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get: "/v1/messages/{message_id}" -// additional_bindings { -// get: "/v1/users/{user_id}/messages/{message_id}" -// } -// }; -// } -// } -// message GetMessageRequest { -// string message_id = 1; -// string user_id = 2; -// } -// -// This enables the following two alternative HTTP JSON to RPC mappings: -// -// - HTTP: `GET /v1/messages/123456` -// - gRPC: `GetMessage(message_id: "123456")` -// -// - HTTP: `GET /v1/users/me/messages/123456` -// - gRPC: `GetMessage(user_id: "me" message_id: "123456")` -// -// Rules for HTTP mapping -// -// 1. Leaf request fields (recursive expansion nested messages in the request -// message) are classified into three categories: -// - Fields referred by the path template. They are passed via the URL path. -// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They -// are passed via the HTTP -// request body. -// - All other fields are passed via the URL query parameters, and the -// parameter name is the field path in the request message. A repeated -// field can be represented as multiple query parameters under the same -// name. -// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL -// query parameter, all fields -// are passed via URL path and HTTP request body. -// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP -// request body, all -// fields are passed via URL path and URL query parameters. -// -// Path template syntax -// -// Template = "/" Segments [ Verb ] ; -// Segments = Segment { "/" Segment } ; -// Segment = "*" | "**" | LITERAL | Variable ; -// Variable = "{" FieldPath [ "=" Segments ] "}" ; -// FieldPath = IDENT { "." IDENT } ; -// Verb = ":" LITERAL ; -// -// The syntax `*` matches a single URL path segment. The syntax `**` matches -// zero or more URL path segments, which must be the last part of the URL path -// except the `Verb`. -// -// The syntax `Variable` matches part of the URL path as specified by its -// template. A variable template must not contain other variables. If a variable -// matches a single path segment, its template may be omitted, e.g. `{var}` -// is equivalent to `{var=*}`. -// -// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` -// contains any reserved character, such characters should be percent-encoded -// before the matching. -// -// If a variable contains exactly one path segment, such as `"{var}"` or -// `"{var=*}"`, when such a variable is expanded into a URL path on the client -// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The -// server side does the reverse decoding. Such variables show up in the -// [Discovery -// Document](https://developers.google.com/discovery/v1/reference/apis) as -// `{var}`. -// -// If a variable contains multiple path segments, such as `"{var=foo/*}"` -// or `"{var=**}"`, when such a variable is expanded into a URL path on the -// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. -// The server side does the reverse decoding, except "%2F" and "%2f" are left -// unchanged. Such variables show up in the -// [Discovery -// Document](https://developers.google.com/discovery/v1/reference/apis) as -// `{+var}`. -// -// Using gRPC API Service Configuration -// -// gRPC API Service Configuration (service config) is a configuration language -// for configuring a gRPC service to become a user-facing product. The -// service config is simply the YAML representation of the `google.api.Service` -// proto message. -// -// As an alternative to annotating your proto file, you can configure gRPC -// transcoding in your service config YAML files. You do this by specifying a -// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same -// effect as the proto annotation. This can be particularly useful if you -// have a proto that is reused in multiple services. Note that any transcoding -// specified in the service config will override any matching transcoding -// configuration in the proto. -// -// The following example selects a gRPC method and applies an `HttpRule` to it: -// -// http: -// rules: -// - selector: example.v1.Messaging.GetMessage -// get: /v1/messages/{message_id}/{sub.subfield} -// -// Special notes -// -// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the -// proto to JSON conversion must follow the [proto3 -// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). -// -// While the single segment variable follows the semantics of -// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String -// Expansion, the multi segment variable **does not** follow RFC 6570 Section -// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion -// does not expand special characters like `?` and `#`, which would lead -// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding -// for multi segment variables. -// -// The path variables **must not** refer to any repeated or mapped field, -// because client libraries are not capable of handling such variable expansion. -// -// The path variables **must not** capture the leading "/" character. The reason -// is that the most common use case "{var}" does not capture the leading "/" -// character. For consistency, all path variables must share the same behavior. -// -// Repeated message fields must not be mapped to URL query parameters, because -// no client library can support such complicated mapping. -// -// If an API needs to use a JSON array for request or response body, it can map -// the request or response body to a repeated field. However, some gRPC -// Transcoding implementations may not support this feature. -message HttpRule { - // Selects a method to which this rule applies. - // - // Refer to [selector][google.api.DocumentationRule.selector] for syntax - // details. - string selector = 1; - - // Determines the URL pattern is matched by this rules. This pattern can be - // used with any of the {get|put|post|delete|patch} methods. A custom method - // can be defined using the 'custom' field. - oneof pattern { - // Maps to HTTP GET. Used for listing and getting information about - // resources. - string get = 2; - - // Maps to HTTP PUT. Used for replacing a resource. - string put = 3; - - // Maps to HTTP POST. Used for creating a resource or performing an action. - string post = 4; - - // Maps to HTTP DELETE. Used for deleting a resource. - string delete = 5; - - // Maps to HTTP PATCH. Used for updating a resource. - string patch = 6; - - // The custom pattern is used for specifying an HTTP method that is not - // included in the `pattern` field, such as HEAD, or "*" to leave the - // HTTP method unspecified for this rule. The wild-card rule is useful - // for services that provide content to Web (HTML) clients. - CustomHttpPattern custom = 8; - } - - // The name of the request field whose value is mapped to the HTTP request - // body, or `*` for mapping all request fields not captured by the path - // pattern to the HTTP body, or omitted for not having any HTTP request body. - // - // NOTE: the referred field must be present at the top-level of the request - // message type. - string body = 7; - - // Optional. The name of the response field whose value is mapped to the HTTP - // response body. When omitted, the entire response message will be used - // as the HTTP response body. - // - // NOTE: The referred field must be present at the top-level of the response - // message type. - string response_body = 12; - - // Additional HTTP bindings for the selector. Nested bindings must - // not contain an `additional_bindings` field themselves (that is, - // the nesting may only be one level deep). - repeated HttpRule additional_bindings = 11; -} - -// A custom pattern is used for defining custom HTTP verb. -message CustomHttpPattern { - // The name of this custom HTTP verb. - string kind = 1; - - // The path matched by this custom verb. - string path = 2; -} diff --git a/benchmarks/rps_no_hertz/cmd/orb-rps-nh-client/main.go b/benchmarks/rps_no_hertz/cmd/orb-rps-nh-client/main.go index 111297b6..99f7f28c 100644 --- a/benchmarks/rps_no_hertz/cmd/orb-rps-nh-client/main.go +++ b/benchmarks/rps_no_hertz/cmd/orb-rps-nh-client/main.go @@ -199,7 +199,7 @@ func bench( logger.Info("Using transport", "transport", node.Transport) } - // Create random bytes to ping-pong on each request. + // Create random bytes once to ping-pong on each request. msg := make([]byte, cfg.PackageSize) if _, err := rand.Reader.Read(msg); err != nil { logger.Error("Failed to make a request", "error", err) diff --git a/benchmarks/rps_no_hertz/cmd/orb-rps-nh-server/wire.go b/benchmarks/rps_no_hertz/cmd/orb-rps-nh-server/wire.go index 0f874301..e120037c 100644 --- a/benchmarks/rps_no_hertz/cmd/orb-rps-nh-server/wire.go +++ b/benchmarks/rps_no_hertz/cmd/orb-rps-nh-server/wire.go @@ -58,36 +58,36 @@ func provideServerOpts() ([]server.Option, error) { mgrpc.WithName("grpc"), mgrpc.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[0])), mgrpc.WithInsecure(true), - mgrpc.WithRegistration("Streams", echopb.OrbRegister(hInstance)), + mgrpc.WithRegistration("Streams", echopb.RegisterEchoService(hInstance)), ), mhttp.WithEntrypoint( mhttp.WithName("http"), mhttp.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[3])), mhttp.WithInsecure(), - mhttp.WithRegistration("Streams", echopb.OrbRegister(hInstance)), + mhttp.WithRegistration("Streams", echopb.RegisterEchoService(hInstance)), ), mhttp.WithEntrypoint( mhttp.WithName("h2c"), mhttp.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[4])), mhttp.WithInsecure(), mhttp.WithAllowH2C(), - mhttp.WithRegistration("Streams", echopb.OrbRegister(hInstance)), + mhttp.WithRegistration("Streams", echopb.RegisterEchoService(hInstance)), ), mhttp.WithEntrypoint( mhttp.WithName("http3"), mhttp.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[5])), mhttp.WithHTTP3(), - mhttp.WithRegistration("Streams", echopb.OrbRegister(hInstance)), + mhttp.WithRegistration("Streams", echopb.RegisterEchoService(hInstance)), ), mhttp.WithEntrypoint( mhttp.WithName("https"), mhttp.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[6])), - mhttp.WithRegistration("Streams", echopb.OrbRegister(hInstance)), + mhttp.WithRegistration("Streams", echopb.RegisterEchoService(hInstance)), ), drpc.WithEntrypoint( drpc.WithName("dprc"), drpc.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[7])), - drpc.WithRegistration("Streams", echopb.OrbRegister(hInstance)), + drpc.WithRegistration("Streams", echopb.RegisterEchoService(hInstance)), ), }, nil } diff --git a/benchmarks/rps_no_hertz/cmd/orb-rps-nh-server/wire_gen.go b/benchmarks/rps_no_hertz/cmd/orb-rps-nh-server/wire_gen.go index afdad712..5ba69062 100644 --- a/benchmarks/rps_no_hertz/cmd/orb-rps-nh-server/wire_gen.go +++ b/benchmarks/rps_no_hertz/cmd/orb-rps-nh-server/wire_gen.go @@ -16,8 +16,6 @@ import ( "github.com/go-orb/plugins/benchmarks/rps_no_hertz/handler/echo" echo2 "github.com/go-orb/plugins/benchmarks/rps_no_hertz/proto/echo" "github.com/go-orb/plugins/server/drpc" - "github.com/go-orb/plugins/server/grpc" - "github.com/go-orb/plugins/server/http" "github.com/hashicorp/consul/sdk/freeport" "net/url" ) @@ -103,7 +101,7 @@ func provideServerOpts() ([]server.Option, error) { hInstance := new(echo.Handler) - return []server.Option{grpc.WithEntrypoint(grpc.WithName("grpc"), grpc.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[0])), grpc.WithInsecure(true), grpc.WithRegistration("Streams", echo2.OrbRegister(hInstance))), http.WithEntrypoint(http.WithName("http"), http.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[3])), http.WithInsecure(), http.WithRegistration("Streams", echo2.OrbRegister(hInstance))), http.WithEntrypoint(http.WithName("h2c"), http.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[4])), http.WithInsecure(), http.WithAllowH2C(), http.WithRegistration("Streams", echo2.OrbRegister(hInstance))), http.WithEntrypoint(http.WithName("http3"), http.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[5])), http.WithHTTP3(), http.WithRegistration("Streams", echo2.OrbRegister(hInstance))), http.WithEntrypoint(http.WithName("https"), http.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[6])), http.WithRegistration("Streams", echo2.OrbRegister(hInstance))), drpc.WithEntrypoint(drpc.WithName("dprc"), drpc.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[7])), drpc.WithRegistration("Streams", echo2.OrbRegister(hInstance)))}, nil + return []server.Option{drpc.WithEntrypoint(drpc.WithName("dprc"), drpc.WithAddress(fmt.Sprintf("127.0.0.1:%d", ports[7])), drpc.WithRegistration("Streams", echo2.RegisterEchoService(hInstance)))}, nil } // provideComponents creates a slice of components out of the arguments. diff --git a/benchmarks/rps_no_hertz/go.mod b/benchmarks/rps_no_hertz/go.mod index e5f75e80..2c74de8b 100644 --- a/benchmarks/rps_no_hertz/go.mod +++ b/benchmarks/rps_no_hertz/go.mod @@ -1,6 +1,6 @@ module github.com/go-orb/plugins/benchmarks/rps_no_hertz -go 1.21 +go 1.22.5 require ( github.com/go-orb/go-orb v0.0.0-20240810234651-a01190e49d61 @@ -22,12 +22,10 @@ require ( github.com/go-orb/plugins/registry/consul v0.0.0-20240810233646-0b3616b1829d github.com/go-orb/plugins/registry/mdns v0.0.0-20240810233646-0b3616b1829d github.com/go-orb/plugins/server/drpc v0.0.0-20240810233646-0b3616b1829d - github.com/go-orb/plugins/server/grpc v0.0.0-20240810233646-0b3616b1829d + github.com/go-orb/plugins/server/grpc v0.0.0-20240810235835-5acdad770676 github.com/go-orb/plugins/server/http v0.0.0-20240810233646-0b3616b1829d github.com/google/wire v0.6.0 github.com/hashicorp/consul/sdk v0.16.1 - google.golang.org/genproto/googleapis/api v0.0.0-20240808171019-573a1156607a - google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 storj.io/drpc v0.0.34 ) @@ -77,6 +75,7 @@ require ( golang.org/x/text v0.17.0 // indirect golang.org/x/tools v0.24.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240808171019-573a1156607a // indirect + google.golang.org/grpc v1.65.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/benchmarks/rps_no_hertz/go.sum b/benchmarks/rps_no_hertz/go.sum index 5d99bee9..b3317915 100644 --- a/benchmarks/rps_no_hertz/go.sum +++ b/benchmarks/rps_no_hertz/go.sum @@ -38,102 +38,55 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-orb/go-orb v0.0.0-20240808121121-1f65fceb24af h1:OiJ62ZLgz831+Ubv5wrC+5prZzS6LvYswUQGcAIXa7o= -github.com/go-orb/go-orb v0.0.0-20240808121121-1f65fceb24af/go.mod h1:1QerXe4TRJ0YMR2cQlyPhfdKUCVwD/kAHvsuYcwoTfw= github.com/go-orb/go-orb v0.0.0-20240810234651-a01190e49d61 h1:Kw94tna2x4TywAwUKRs10+oZ/9fGy8av9KbNygsm9IE= github.com/go-orb/go-orb v0.0.0-20240810234651-a01190e49d61/go.mod h1:1QerXe4TRJ0YMR2cQlyPhfdKUCVwD/kAHvsuYcwoTfw= -github.com/go-orb/plugins/client/middleware/log v0.0.0-20240809105731-a8ee45a15344 h1:DfNrPyxIYUT/Rd4uWsETECnp8q5cB8YgaUgPIkyW5LE= -github.com/go-orb/plugins/client/middleware/log v0.0.0-20240809105731-a8ee45a15344/go.mod h1:oPVHt6hodX2DKs9FqiYnWlPjEcDFlERDEnGk2OToKZ0= github.com/go-orb/plugins/client/middleware/log v0.0.0-20240810233646-0b3616b1829d h1:k4xJBiC1OEHkF9ioEhrfSzIAek21xxO8NyaFtR4oOd0= github.com/go-orb/plugins/client/middleware/log v0.0.0-20240810233646-0b3616b1829d/go.mod h1:oPVHt6hodX2DKs9FqiYnWlPjEcDFlERDEnGk2OToKZ0= -github.com/go-orb/plugins/client/orb v0.0.0-20240809105731-a8ee45a15344 h1:zq877xERb7sxXZpJWl/E75aoOikgX82VOmPIn3y5/2E= -github.com/go-orb/plugins/client/orb v0.0.0-20240809105731-a8ee45a15344/go.mod h1:2FSjbWXkZDsPkV2Zm2B1UXf9OHGQTxrjUlK3p55kuAk= github.com/go-orb/plugins/client/orb v0.0.0-20240810233646-0b3616b1829d h1:1gcD2tRPCoiXw6bqQL2tYHtcDDCcRqTjmOmyv3Z8mRw= github.com/go-orb/plugins/client/orb v0.0.0-20240810233646-0b3616b1829d/go.mod h1:2FSjbWXkZDsPkV2Zm2B1UXf9OHGQTxrjUlK3p55kuAk= -github.com/go-orb/plugins/client/orb_transport/basehttp v0.0.0-20240809105731-a8ee45a15344 h1:c/gwE8RUD2uahmA8cojlDQrknh1+gf49/sDYIgrTtvo= -github.com/go-orb/plugins/client/orb_transport/basehttp v0.0.0-20240809105731-a8ee45a15344/go.mod h1:CmZh5NKlbmWCwydNwu7RFF9sLEXhdOyNULt4snSlkkQ= github.com/go-orb/plugins/client/orb_transport/basehttp v0.0.0-20240810233646-0b3616b1829d h1:i8BfITwQJ1xRvRGVJOpJIk9Y07RRT4WB3tcwKJimgnE= github.com/go-orb/plugins/client/orb_transport/basehttp v0.0.0-20240810233646-0b3616b1829d/go.mod h1:Iq9DafWdJa6duEuRwaVNrRjSsm6JkP18O5ohnwE1yQc= -github.com/go-orb/plugins/client/orb_transport/drpc v0.0.0-20240809105731-a8ee45a15344 h1:jlKb25ub4uNW3NtCQZ/bPUAR3FVLpKtR0Wqmvxmo9Yk= -github.com/go-orb/plugins/client/orb_transport/drpc v0.0.0-20240809105731-a8ee45a15344/go.mod h1:iuR4khJ7eoB0sxxF7CiX20VHeMNhRtyGgfGoZXJOqHM= github.com/go-orb/plugins/client/orb_transport/drpc v0.0.0-20240810233646-0b3616b1829d h1:Ko8hSyA90/325lz5kNBVlfrurG8HyrHjDbMqRKrqrGs= github.com/go-orb/plugins/client/orb_transport/drpc v0.0.0-20240810233646-0b3616b1829d/go.mod h1:fE6HmDP0h0LP6i+Svxwq6lxdTtbkHC7g1zTdrspkk2I= -github.com/go-orb/plugins/client/orb_transport/grpc v0.0.0-20240809105731-a8ee45a15344 h1:BhY94ynCwTyHyaiNgTNAuVdzBJl7f1p8ikwzmxxytdE= -github.com/go-orb/plugins/client/orb_transport/grpc v0.0.0-20240809105731-a8ee45a15344/go.mod h1:X3DoQpQGzXu0/+8h4OLfBhZeWIhtq3X9PMwJ+/sauKs= github.com/go-orb/plugins/client/orb_transport/grpc v0.0.0-20240810233646-0b3616b1829d h1:gMF5hE2u+deN890txwgYvz8JQgouS8+TLX3hqZKR6xY= github.com/go-orb/plugins/client/orb_transport/grpc v0.0.0-20240810233646-0b3616b1829d/go.mod h1:pDcpmADAMrSP7P7YchkNHK5LeCbV0VXlqX70I8wmaDk= -github.com/go-orb/plugins/client/orb_transport/h2c v0.0.0-20240809105731-a8ee45a15344 h1:TsyQ60KAefoj0AkVlVAzbmpEbs4vS01HdX3OB67iUPw= -github.com/go-orb/plugins/client/orb_transport/h2c v0.0.0-20240809105731-a8ee45a15344/go.mod h1:h6/i3r8s85vjFushMTQm5S7ZYDrI8x2DxeQ8pYEFF48= github.com/go-orb/plugins/client/orb_transport/h2c v0.0.0-20240810233646-0b3616b1829d h1:CV5lqzhahX5KxE7cQo5Z7QtTRUDLEWyX12dFJG3eKtE= github.com/go-orb/plugins/client/orb_transport/h2c v0.0.0-20240810233646-0b3616b1829d/go.mod h1:Np1pij+aXhk4mojEgnOjZiVZ7VNHF1HrLvLpnSLlQrM= -github.com/go-orb/plugins/client/orb_transport/http v0.0.0-20240809105731-a8ee45a15344 h1:k3I9D5dwan5gyPTgbADoB/ZUFFPI+YSJTJq+Uj+1hkU= -github.com/go-orb/plugins/client/orb_transport/http v0.0.0-20240809105731-a8ee45a15344/go.mod h1:e7HIyY398VAmeo4mEaHGPAGAeHrimfenbj21jwYzZjg= github.com/go-orb/plugins/client/orb_transport/http v0.0.0-20240810233646-0b3616b1829d h1:BN5gbwc3yWGOVX5M+9kPsy3DwZHzDSBQ86Uk0I/WnVw= github.com/go-orb/plugins/client/orb_transport/http v0.0.0-20240810233646-0b3616b1829d/go.mod h1:No1WLkrkkkQ4kidmL4P5GRQOD15WK1TNnHk4CtodKbg= -github.com/go-orb/plugins/client/orb_transport/http3 v0.0.0-20240809105731-a8ee45a15344 h1:ghHHytnDkQZDOyX3vJiRfOl745KtjEDOww8w/BTuHpo= -github.com/go-orb/plugins/client/orb_transport/http3 v0.0.0-20240809105731-a8ee45a15344/go.mod h1:/GjfiwD4/6m8nE2DMK9uC5GLwoygQCUpBQsZISw9Ork= github.com/go-orb/plugins/client/orb_transport/http3 v0.0.0-20240810233646-0b3616b1829d h1:nIkhYMy4c4C1mNexjV4zBg5vYsTbA6/RcSLu/ue75OE= github.com/go-orb/plugins/client/orb_transport/http3 v0.0.0-20240810233646-0b3616b1829d/go.mod h1:r8CbSDhkf08+XJ6KisZNAID7dgTqhDoamlJNWiymZyY= -github.com/go-orb/plugins/client/orb_transport/https v0.0.0-20240809105731-a8ee45a15344 h1:KZtcEQN72ET18TLuo33Y2FQoQGd6s1Vd85NX6JXI/t4= -github.com/go-orb/plugins/client/orb_transport/https v0.0.0-20240809105731-a8ee45a15344/go.mod h1:WB+BlbcWdJkrXlI+krVFyYwfsHUhCuDUxikxXHZtdpA= github.com/go-orb/plugins/client/orb_transport/https v0.0.0-20240810233646-0b3616b1829d h1:LpYc1wJUKLQum9H5HRQKLKMVUe+G4uwN+SQEhuGGBig= github.com/go-orb/plugins/client/orb_transport/https v0.0.0-20240810233646-0b3616b1829d/go.mod h1:1d2gteSZn+c3MCpXUpvdpusIuDoVucy1ZKPWnYTCI4s= -github.com/go-orb/plugins/codecs/jsonpb v0.0.0-20240809105731-a8ee45a15344 h1:nwVRgQI7AvQmbYlNbN717Ar8yNYMqhVtx8QgARr8lVU= -github.com/go-orb/plugins/codecs/jsonpb v0.0.0-20240809105731-a8ee45a15344/go.mod h1:UV/iBjHZJNfRLNYu9N8fTSZtb2TlIHpA2CMLhPHaU6k= github.com/go-orb/plugins/codecs/jsonpb v0.0.0-20240810233646-0b3616b1829d h1:oZBOvjUaNCQVJcpjaxetSROKJQnm+PtvO5ePxmVUGDY= github.com/go-orb/plugins/codecs/jsonpb v0.0.0-20240810233646-0b3616b1829d/go.mod h1:UV/iBjHZJNfRLNYu9N8fTSZtb2TlIHpA2CMLhPHaU6k= -github.com/go-orb/plugins/codecs/proto v0.0.0-20240809105731-a8ee45a15344 h1:pj2q+SsYiEZLgQFyEPWDoAm9Ww/5t1eeXB93fSUZTNU= -github.com/go-orb/plugins/codecs/proto v0.0.0-20240809105731-a8ee45a15344/go.mod h1:vswlMB9l6tEiA46yioHR3uZfn4+Xkl7GQmEAmvqTXUM= github.com/go-orb/plugins/codecs/proto v0.0.0-20240810233646-0b3616b1829d h1:4Sh72grDFUSTKwVLL9opQGBbDarr3ixBMFn0I1F7R2c= github.com/go-orb/plugins/codecs/proto v0.0.0-20240810233646-0b3616b1829d/go.mod h1:vswlMB9l6tEiA46yioHR3uZfn4+Xkl7GQmEAmvqTXUM= -github.com/go-orb/plugins/codecs/yaml v0.0.0-20240809105731-a8ee45a15344 h1:vRaYil5O7famjGkzaAWJGbWQz1MGFvKSyTzB9XPC8bw= -github.com/go-orb/plugins/codecs/yaml v0.0.0-20240809105731-a8ee45a15344/go.mod h1:zmA37Ucv7ghekZQN6dp+Ddfz0RIV2mM925iBSPLh4E0= github.com/go-orb/plugins/codecs/yaml v0.0.0-20240810233646-0b3616b1829d h1:yWyp4ZB1Kz9vbfqB41AXJXSQNSicJgDyd4/BdjAN22g= github.com/go-orb/plugins/codecs/yaml v0.0.0-20240810233646-0b3616b1829d/go.mod h1:zmA37Ucv7ghekZQN6dp+Ddfz0RIV2mM925iBSPLh4E0= -github.com/go-orb/plugins/config/source/cli/urfave v0.0.0-20240809105731-a8ee45a15344 h1:jmi4pbYK0+XQpC9gGWuHDCkYLxa/ujRwu3v9VOlqKkw= -github.com/go-orb/plugins/config/source/cli/urfave v0.0.0-20240809105731-a8ee45a15344/go.mod h1:3YzVCtmupoG6NC9Z9RRZdrvQAdIioB1fXU5LggXBhzY= github.com/go-orb/plugins/config/source/cli/urfave v0.0.0-20240810233646-0b3616b1829d h1:eMW0ejgmHS4IXFYjqhy9SojiVsKzSj/WshASYjBohgM= github.com/go-orb/plugins/config/source/cli/urfave v0.0.0-20240810233646-0b3616b1829d/go.mod h1:+n7LR/UI/vFdRtfwlmnl2ukkB4Q+BgzgoXf0Kgtvu04= -github.com/go-orb/plugins/config/source/file v0.0.0-20240809105731-a8ee45a15344 h1:gi5TYcYF4AQDG/C8A/Tf3j7krk738eSsjCNVvvjPNoA= -github.com/go-orb/plugins/config/source/file v0.0.0-20240809105731-a8ee45a15344/go.mod h1:SvmpQVImGnOhEThUSTyzznN4kxI95yZNediz3nRBl1E= github.com/go-orb/plugins/config/source/file v0.0.0-20240810233646-0b3616b1829d h1:2WQ/WDDgROcqXVVXoCBp3XA6RurqiG6TVusvsg6rNW4= github.com/go-orb/plugins/config/source/file v0.0.0-20240810233646-0b3616b1829d/go.mod h1:SvmpQVImGnOhEThUSTyzznN4kxI95yZNediz3nRBl1E= -github.com/go-orb/plugins/log/lumberjack v0.0.0-20240809105731-a8ee45a15344 h1:uPm+8RWd3h1GjDpoF2eisNbkyUc/1KtaZtXKw4hPCoI= -github.com/go-orb/plugins/log/lumberjack v0.0.0-20240809105731-a8ee45a15344/go.mod h1:R7hEOJddbCcDMkogwmsvfNoILHN9h2zwCsEV6eeXEfg= github.com/go-orb/plugins/log/lumberjack v0.0.0-20240810233646-0b3616b1829d h1:vgUV2poIEUAYkRUxJT8Kq6yiZ55u2pb4PxcxR1AowZc= github.com/go-orb/plugins/log/lumberjack v0.0.0-20240810233646-0b3616b1829d/go.mod h1:R7hEOJddbCcDMkogwmsvfNoILHN9h2zwCsEV6eeXEfg= -github.com/go-orb/plugins/log/slog v0.0.0-20240809105731-a8ee45a15344 h1:fwwuy3n+TsjXCw29btHuJCf98DkuHcSQqGSfQZiynRg= -github.com/go-orb/plugins/log/slog v0.0.0-20240809105731-a8ee45a15344/go.mod h1:wq6lcSHat1njpuPhNINaLMhkdrNqtKlNq/4fdMjEtQM= github.com/go-orb/plugins/log/slog v0.0.0-20240810233646-0b3616b1829d h1:ZpFOQ+F3bFuJCMHOMjMjK7t7WTfyX0D6pf+bfFefUkA= github.com/go-orb/plugins/log/slog v0.0.0-20240810233646-0b3616b1829d/go.mod h1:wq6lcSHat1njpuPhNINaLMhkdrNqtKlNq/4fdMjEtQM= -github.com/go-orb/plugins/registry/consul v0.0.0-20240809105731-a8ee45a15344 h1:cz187MNPilczTvJH0G5CyrPtoFpMiCwRfaCWUK+nmrE= -github.com/go-orb/plugins/registry/consul v0.0.0-20240809105731-a8ee45a15344/go.mod h1:HVbqSVBNsWnG7AC8G6x9/G0kBVJORh1Nir+MgftXgrk= github.com/go-orb/plugins/registry/consul v0.0.0-20240810233646-0b3616b1829d h1:nj0+PCGcL9LcfZmR2DGKt8ZZzsIwrIt1Oe1JsoWla+8= github.com/go-orb/plugins/registry/consul v0.0.0-20240810233646-0b3616b1829d/go.mod h1:7/cFxuCjDQEeSdukLDoOapIQ/a8YEK28jBXjrqon/fo= -github.com/go-orb/plugins/registry/mdns v0.0.0-20240809105731-a8ee45a15344 h1:aniZdMTwgTgJumRXzDi5Z2P6/L6zbMxeGAWhpt0l6lA= -github.com/go-orb/plugins/registry/mdns v0.0.0-20240809105731-a8ee45a15344/go.mod h1:rB5upz6/aCHCM2nXQOzbcNr6CxnR2PQSso3yJ5qwRVU= github.com/go-orb/plugins/registry/mdns v0.0.0-20240810233646-0b3616b1829d h1:xpBeFfIgmHisMwvbTyYznoCAXsRnQfUEzV7BQngGU/g= github.com/go-orb/plugins/registry/mdns v0.0.0-20240810233646-0b3616b1829d/go.mod h1:E+gUd1JlFfVgCQRWgNvmZkNt86QJ8s/dSIQUtcHciBo= -github.com/go-orb/plugins/registry/regutil v0.0.0-20240809105731-a8ee45a15344 h1:ZK1P7typdeR8N3qIpMSio6SN6z/i2H1etmoOQssOzNo= -github.com/go-orb/plugins/registry/regutil v0.0.0-20240809105731-a8ee45a15344/go.mod h1:T4mDJhWMICaOwJGZWBUhTw6bScvWvQ2gnc+xJk0joxU= github.com/go-orb/plugins/registry/regutil v0.0.0-20240810233646-0b3616b1829d h1:S1qa/yZ5LD1Eze5azoIcAF49pUIVNDO93tn/xUVvb1Q= github.com/go-orb/plugins/registry/regutil v0.0.0-20240810233646-0b3616b1829d/go.mod h1:T4mDJhWMICaOwJGZWBUhTw6bScvWvQ2gnc+xJk0joxU= github.com/go-orb/plugins/registry/tests v0.0.0-20231207012411-f06c92b21588 h1:LGeQZPQkBPEoNSWV2meZnxM/XOUkLHONt3DxgAnw8pM= github.com/go-orb/plugins/registry/tests v0.0.0-20231207012411-f06c92b21588/go.mod h1:mfYafEabvCHUuUJKTiVOazxYq+aFwBhlEiOr0OUitW0= -github.com/go-orb/plugins/server/drpc v0.0.0-20240809105731-a8ee45a15344 h1:Al/RXH+J2viSicCm0frCuUFTukflaZoE+4MSAHFGC18= -github.com/go-orb/plugins/server/drpc v0.0.0-20240809105731-a8ee45a15344/go.mod h1:Ip9gdbcTu/jts280aNU7qz+aVOEahywqwcnGII8xzWg= github.com/go-orb/plugins/server/drpc v0.0.0-20240810233646-0b3616b1829d h1:cBhIbAoKPQPCWKGTiToMQPtnGj1k80bbmz/NF0F0SP4= github.com/go-orb/plugins/server/drpc v0.0.0-20240810233646-0b3616b1829d/go.mod h1:ysLpN+xDkVnMge7BqixySCtrGsJbebbExy8ExqNrjes= -github.com/go-orb/plugins/server/grpc v0.0.0-20240809105731-a8ee45a15344 h1:2jVzbW52Ef9VOjmxhyDlr41YgDBtudncr2uGsfLrIKk= -github.com/go-orb/plugins/server/grpc v0.0.0-20240809105731-a8ee45a15344/go.mod h1:0m/KRBzP61PoOZetoSUnh2jxOPJvdTwtjm0v+LSWKmU= -github.com/go-orb/plugins/server/grpc v0.0.0-20240810233646-0b3616b1829d h1:UZdfwBCFPwjZVGfVfjfxGSkQmY+PH9leojnF/nKD/ys= -github.com/go-orb/plugins/server/grpc v0.0.0-20240810233646-0b3616b1829d/go.mod h1:aKlvsYbeJaSgVCS5iMZJ6XruThefaX9v/ZsTCTM7duk= -github.com/go-orb/plugins/server/http v0.0.0-20240809105731-a8ee45a15344 h1:hiHlbHut0bCqZ0ePD85GFZT3Paw98HQ20khMVeAVY78= -github.com/go-orb/plugins/server/http v0.0.0-20240809105731-a8ee45a15344/go.mod h1:JrTTXKKfbhe9Ka4aHaROK1r0Agu1Iwi1Js7VqziI45w= +github.com/go-orb/plugins/server/grpc v0.0.0-20240810235835-5acdad770676 h1:PUj3++fxW8OqAmSbl0JTmA5yMe44MVoBOUziIxNut1Y= +github.com/go-orb/plugins/server/grpc v0.0.0-20240810235835-5acdad770676/go.mod h1:k0gN/VJEVv5kDpoyqMMiovsaRI6IRjnGykgKX9dRw4E= github.com/go-orb/plugins/server/http v0.0.0-20240810233646-0b3616b1829d h1:m45u8pyayT3ZODU1EdoeYf942WvO8x5PJ3MMIg78isA= github.com/go-orb/plugins/server/http v0.0.0-20240810233646-0b3616b1829d/go.mod h1:YH5AI5HoVyUIqOhSj8S80ySfU78AI9bxz+W36S6QPFE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -414,8 +367,6 @@ golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240808171019-573a1156607a h1:KyUe15n7B1YCu+kMmPtlXxgkLQbp+Dw0tCRZf9Sd+CE= -google.golang.org/genproto/googleapis/api v0.0.0-20240808171019-573a1156607a/go.mod h1:4+X6GvPs+25wZKbQq9qyAXrwIRExv7w0Ea6MgZLZiDM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240808171019-573a1156607a h1:EKiZZXueP9/T68B8Nl0GAx9cjbQnCId0yP3qPMgaaHs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240808171019-573a1156607a/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= diff --git a/benchmarks/rps_no_hertz/handler/echo/handler.go b/benchmarks/rps_no_hertz/handler/echo/handler.go index b22ca3ee..682fc16a 100644 --- a/benchmarks/rps_no_hertz/handler/echo/handler.go +++ b/benchmarks/rps_no_hertz/handler/echo/handler.go @@ -7,12 +7,10 @@ import ( "github.com/go-orb/plugins/benchmarks/rps_no_hertz/proto/echo" ) -var _ echo.EchoServer = (*Handler)(nil) +var _ echo.EchoHandler = (*Handler)(nil) // Handler is a test handler. -type Handler struct { - echo.UnsafeEchoServer -} +type Handler struct{} // Echo implements the echo method. func (c *Handler) Echo(_ context.Context, req *echo.Req) (*echo.Resp, error) { diff --git a/benchmarks/rps_no_hertz/proto/echo/echo.pb.go b/benchmarks/rps_no_hertz/proto/echo/echo.pb.go index 085b5256..2b3cdb3b 100644 --- a/benchmarks/rps_no_hertz/proto/echo/echo.pb.go +++ b/benchmarks/rps_no_hertz/proto/echo/echo.pb.go @@ -7,7 +7,6 @@ package echo import ( - _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -119,19 +118,15 @@ var File_echo_echo_proto protoreflect.FileDescriptor var file_echo_echo_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x04, 0x65, 0x63, 0x68, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1f, 0x0a, 0x03, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x20, 0x0a, 0x04, 0x52, 0x65, 0x73, 0x70, 0x12, 0x18, + 0x6f, 0x12, 0x04, 0x65, 0x63, 0x68, 0x6f, 0x22, 0x1f, 0x0a, 0x03, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x32, 0x41, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, - 0x12, 0x39, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x09, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, - 0x52, 0x65, 0x71, 0x1a, 0x0a, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x22, - 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x22, 0x0f, 0x2f, 0x65, 0x63, 0x68, - 0x6f, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x2f, 0x45, 0x63, 0x68, 0x6f, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, - 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x3b, 0x65, 0x63, 0x68, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x20, 0x0a, 0x04, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x32, 0x25, 0x0a, 0x04, 0x45, 0x63, + 0x68, 0x6f, 0x12, 0x1d, 0x0a, 0x04, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x09, 0x2e, 0x65, 0x63, 0x68, + 0x6f, 0x2e, 0x52, 0x65, 0x71, 0x1a, 0x0a, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x52, 0x65, 0x73, + 0x70, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x3b, 0x65, 0x63, 0x68, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/benchmarks/rps_no_hertz/proto/echo/echo.proto b/benchmarks/rps_no_hertz/proto/echo/echo.proto index ca517176..03d175e5 100644 --- a/benchmarks/rps_no_hertz/proto/echo/echo.proto +++ b/benchmarks/rps_no_hertz/proto/echo/echo.proto @@ -2,17 +2,10 @@ syntax = "proto3"; package echo; -import "google/api/annotations.proto"; - option go_package = "./echo;echo"; service Echo { - rpc Echo(Req) returns (Resp) { - option (google.api.http) = { - post : "/echo.Echo/Echo" - body : "*" - }; - } + rpc Echo(Req) returns (Resp); } message Req { bytes payload = 1; } diff --git a/benchmarks/rps_no_hertz/proto/echo/echo_grpc.pb.go b/benchmarks/rps_no_hertz/proto/echo/echo_grpc.pb.go deleted file mode 100644 index 6d45e0d2..00000000 --- a/benchmarks/rps_no_hertz/proto/echo/echo_grpc.pb.go +++ /dev/null @@ -1,101 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. - -package echo - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -// EchoClient is the client API for Echo service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type EchoClient interface { - Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Resp, error) -} - -type echoClient struct { - cc grpc.ClientConnInterface -} - -func NewEchoClient(cc grpc.ClientConnInterface) EchoClient { - return &echoClient{cc} -} - -func (c *echoClient) Echo(ctx context.Context, in *Req, opts ...grpc.CallOption) (*Resp, error) { - out := new(Resp) - err := c.cc.Invoke(ctx, "/echo.Echo/Echo", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// EchoServer is the server API for Echo service. -// All implementations must embed UnimplementedEchoServer -// for forward compatibility -type EchoServer interface { - Echo(context.Context, *Req) (*Resp, error) - mustEmbedUnimplementedEchoServer() -} - -// UnimplementedEchoServer must be embedded to have forward compatible implementations. -type UnimplementedEchoServer struct { -} - -func (UnimplementedEchoServer) Echo(context.Context, *Req) (*Resp, error) { - return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented") -} -func (UnimplementedEchoServer) mustEmbedUnimplementedEchoServer() {} - -// UnsafeEchoServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to EchoServer will -// result in compilation errors. -type UnsafeEchoServer interface { - mustEmbedUnimplementedEchoServer() -} - -func RegisterEchoServer(s grpc.ServiceRegistrar, srv EchoServer) { - s.RegisterService(&Echo_ServiceDesc, srv) -} - -func _Echo_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Req) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(EchoServer).Echo(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/echo.Echo/Echo", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(EchoServer).Echo(ctx, req.(*Req)) - } - return interceptor(ctx, in, info, handler) -} - -// Echo_ServiceDesc is the grpc.ServiceDesc for Echo service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Echo_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "echo.Echo", - HandlerType: (*EchoServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Echo", - Handler: _Echo_Echo_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "echo/echo.proto", -} diff --git a/benchmarks/rps_no_hertz/proto/echo/echo_drpc.pb.go b/benchmarks/rps_no_hertz/proto/echo/echo_orb-drpc.pb.go similarity index 78% rename from benchmarks/rps_no_hertz/proto/echo/echo_drpc.pb.go rename to benchmarks/rps_no_hertz/proto/echo/echo_orb-drpc.pb.go index 5dc800da..900cc37c 100644 --- a/benchmarks/rps_no_hertz/proto/echo/echo_drpc.pb.go +++ b/benchmarks/rps_no_hertz/proto/echo/echo_orb-drpc.pb.go @@ -1,5 +1,9 @@ -// Code generated by protoc-gen-go-drpc. DO NOT EDIT. -// protoc-gen-go-drpc version: v0.0.34 +// Code generated by protoc-gen-go-orb. DO NOT EDIT. +// +// version: +// - protoc-gen-go-orb v0.0.1 +// - protoc v5.27.2 +// // source: echo/echo.proto package echo @@ -35,31 +39,6 @@ func (drpcEncoding_File_echo_echo_proto) JSONUnmarshal(buf []byte, msg drpc.Mess return protojson.Unmarshal(buf, msg.(proto.Message)) } -type DRPCEchoClient interface { - DRPCConn() drpc.Conn - - Echo(ctx context.Context, in *Req) (*Resp, error) -} - -type drpcEchoClient struct { - cc drpc.Conn -} - -func NewDRPCEchoClient(cc drpc.Conn) DRPCEchoClient { - return &drpcEchoClient{cc} -} - -func (c *drpcEchoClient) DRPCConn() drpc.Conn { return c.cc } - -func (c *drpcEchoClient) Echo(ctx context.Context, in *Req) (*Resp, error) { - out := new(Resp) - err := c.cc.Invoke(ctx, "/echo.Echo/Echo", drpcEncoding_File_echo_echo_proto{}, in, out) - if err != nil { - return nil, err - } - return out, nil -} - type DRPCEchoServer interface { Echo(context.Context, *Req) (*Resp, error) } @@ -103,6 +82,10 @@ type drpcEcho_EchoStream struct { drpc.Stream } +func (x *drpcEcho_EchoStream) GetStream() drpc.Stream { + return x.Stream +} + func (x *drpcEcho_EchoStream) SendAndClose(m *Resp) error { if err := x.MsgSend(m, drpcEncoding_File_echo_echo_proto{}); err != nil { return err diff --git a/benchmarks/rps_no_hertz/proto/echo/echo_orb.pb.go b/benchmarks/rps_no_hertz/proto/echo/echo_orb.pb.go index f9645aa2..e5616c80 100644 --- a/benchmarks/rps_no_hertz/proto/echo/echo_orb.pb.go +++ b/benchmarks/rps_no_hertz/proto/echo/echo_orb.pb.go @@ -1,34 +1,27 @@ -// Code generated by protoc-gen-go-orb-http. DO NOT EDIT. +// Code generated by protoc-gen-go-orb. DO NOT EDIT. // // version: -// - protoc-gen-go-orb-http v1.0.0 -// - protoc v4.25.1 +// - protoc-gen-go-orb v0.0.1 +// - protoc v5.27.2 // -// Proto source: echo.proto +// Proto source: echo/echo.proto package echo import ( "context" + "github.com/go-orb/go-orb/log" "github.com/go-orb/go-orb/server" - "google.golang.org/grpc" mdrpc "github.com/go-orb/plugins/server/drpc" - mhttp "github.com/go-orb/plugins/server/http" ) -type orbEchoHandler interface { +type EchoHandler interface { Echo(ctx context.Context, req *Req) (*Resp, error) - mustEmbedUnimplementedEchoServer() } -func registerEchoHTTPHandler(srv *mhttp.ServerHTTP, handler orbEchoHandler) { - r := srv.Router() - r.Post("/echo.Echo/Echo", mhttp.NewGRPCHandler(srv, handler.Echo)) -} - -func registerEchoDRPCHandler(srv *mdrpc.Server, handler orbEchoHandler) error { +func registerEchoDRPCHandler(srv *mdrpc.Server, handler EchoHandler) error { desc := DRPCEchoDescription{} // Register with DRPC. @@ -49,19 +42,16 @@ func registerEchoDRPCHandler(srv *mdrpc.Server, handler orbEchoHandler) error { return nil } -// OrbRegister will return a registration function that can be +// RegisterEchoService will return a registration function that can be // provided to entrypoints as a handler registration. -func OrbRegister(handler EchoServer) server.RegistrationFunc { +func RegisterEchoService(handler EchoHandler) server.RegistrationFunc { return server.RegistrationFunc(func(s any) { switch srv := s.(type) { - case *mhttp.ServerHTTP: - registerEchoHTTPHandler(srv, handler.(orbEchoHandler)) + case *mdrpc.Server: - registerEchoDRPCHandler(srv, handler.(orbEchoHandler)) //nolint:errcheck,gosec - case grpc.ServiceRegistrar: - RegisterEchoServer(srv, handler) + registerEchoDRPCHandler(srv, handler) default: - // Maybe we should log here with slog global logger + log.Warn("No provider for this server found", "proto", "echo/echo.proto", "handler", "Echo", "server", s) } }) } diff --git a/benchmarks/rps_no_hertz/proto/gen.go b/benchmarks/rps_no_hertz/proto/gen.go index 7be6ce72..d62c5911 100644 --- a/benchmarks/rps_no_hertz/proto/gen.go +++ b/benchmarks/rps_no_hertz/proto/gen.go @@ -1,9 +1,5 @@ // Package proto ... package proto -// Download Google proto HTTP annotation libs -//go:generate wget -q -O google/api/annotations.proto https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto -//go:generate wget -q -O google/api/http.proto https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto - // Generate proto files -//go:generate protoc -I . --go-grpc_out=paths=source_relative:. --go-micro-http_out=paths=source_relative:. --go_out=paths=source_relative:. --go-drpc_out=paths=source_relative:. ./echo/echo.proto +//go:generate protoc -I . --orb_out=paths=source_relative:. --go-orb_opt="supported_servers=drpc;grpc;http" ./echo/echo.proto diff --git a/benchmarks/rps_no_hertz/proto/google/api/annotations.proto b/benchmarks/rps_no_hertz/proto/google/api/annotations.proto deleted file mode 100644 index 84c48164..00000000 --- a/benchmarks/rps_no_hertz/proto/google/api/annotations.proto +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.api; - -import "google/api/http.proto"; -import "google/protobuf/descriptor.proto"; - -option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; -option java_multiple_files = true; -option java_outer_classname = "AnnotationsProto"; -option java_package = "com.google.api"; -option objc_class_prefix = "GAPI"; - -extend google.protobuf.MethodOptions { - // See `HttpRule`. - HttpRule http = 72295728; -} diff --git a/benchmarks/rps_no_hertz/proto/google/api/http.proto b/benchmarks/rps_no_hertz/proto/google/api/http.proto deleted file mode 100644 index e3270371..00000000 --- a/benchmarks/rps_no_hertz/proto/google/api/http.proto +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -syntax = "proto3"; - -package google.api; - -option cc_enable_arenas = true; -option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; -option java_multiple_files = true; -option java_outer_classname = "HttpProto"; -option java_package = "com.google.api"; -option objc_class_prefix = "GAPI"; - -// Defines the HTTP configuration for an API service. It contains a list of -// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method -// to one or more HTTP REST API methods. -message Http { - // A list of HTTP configuration rules that apply to individual API methods. - // - // **NOTE:** All service configuration rules follow "last one wins" order. - repeated HttpRule rules = 1; - - // When set to true, URL path parameters will be fully URI-decoded except in - // cases of single segment matches in reserved expansion, where "%2F" will be - // left encoded. - // - // The default behavior is to not decode RFC 6570 reserved characters in multi - // segment matches. - bool fully_decode_reserved_expansion = 2; -} - -// gRPC Transcoding -// -// gRPC Transcoding is a feature for mapping between a gRPC method and one or -// more HTTP REST endpoints. It allows developers to build a single API service -// that supports both gRPC APIs and REST APIs. Many systems, including [Google -// APIs](https://github.com/googleapis/googleapis), -// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC -// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), -// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature -// and use it for large scale production services. -// -// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies -// how different portions of the gRPC request message are mapped to the URL -// path, URL query parameters, and HTTP request body. It also controls how the -// gRPC response message is mapped to the HTTP response body. `HttpRule` is -// typically specified as an `google.api.http` annotation on the gRPC method. -// -// Each mapping specifies a URL path template and an HTTP method. The path -// template may refer to one or more fields in the gRPC request message, as long -// as each field is a non-repeated field with a primitive (non-message) type. -// The path template controls how fields of the request message are mapped to -// the URL path. -// -// Example: -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get: "/v1/{name=messages/*}" -// }; -// } -// } -// message GetMessageRequest { -// string name = 1; // Mapped to URL path. -// } -// message Message { -// string text = 1; // The resource content. -// } -// -// This enables an HTTP REST to gRPC mapping as below: -// -// - HTTP: `GET /v1/messages/123456` -// - gRPC: `GetMessage(name: "messages/123456")` -// -// Any fields in the request message which are not bound by the path template -// automatically become HTTP query parameters if there is no HTTP request body. -// For example: -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get:"/v1/messages/{message_id}" -// }; -// } -// } -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // Mapped to URL path. -// int64 revision = 2; // Mapped to URL query parameter `revision`. -// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. -// } -// -// This enables a HTTP JSON to RPC mapping as below: -// -// - HTTP: `GET /v1/messages/123456?revision=2&sub.subfield=foo` -// - gRPC: `GetMessage(message_id: "123456" revision: 2 sub: -// SubMessage(subfield: "foo"))` -// -// Note that fields which are mapped to URL query parameters must have a -// primitive type or a repeated primitive type or a non-repeated message type. -// In the case of a repeated type, the parameter can be repeated in the URL -// as `...?param=A¶m=B`. In the case of a message type, each field of the -// message is mapped to a separate parameter, such as -// `...?foo.a=A&foo.b=B&foo.c=C`. -// -// For HTTP methods that allow a request body, the `body` field -// specifies the mapping. Consider a REST update method on the -// message resource collection: -// -// service Messaging { -// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { -// option (google.api.http) = { -// patch: "/v1/messages/{message_id}" -// body: "message" -// }; -// } -// } -// message UpdateMessageRequest { -// string message_id = 1; // mapped to the URL -// Message message = 2; // mapped to the body -// } -// -// The following HTTP JSON to RPC mapping is enabled, where the -// representation of the JSON in the request body is determined by -// protos JSON encoding: -// -// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` -// - gRPC: `UpdateMessage(message_id: "123456" message { text: "Hi!" })` -// -// The special name `*` can be used in the body mapping to define that -// every field not bound by the path template should be mapped to the -// request body. This enables the following alternative definition of -// the update method: -// -// service Messaging { -// rpc UpdateMessage(Message) returns (Message) { -// option (google.api.http) = { -// patch: "/v1/messages/{message_id}" -// body: "*" -// }; -// } -// } -// message Message { -// string message_id = 1; -// string text = 2; -// } -// -// -// The following HTTP JSON to RPC mapping is enabled: -// -// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` -// - gRPC: `UpdateMessage(message_id: "123456" text: "Hi!")` -// -// Note that when using `*` in the body mapping, it is not possible to -// have HTTP parameters, as all fields not bound by the path end in -// the body. This makes this option more rarely used in practice when -// defining REST APIs. The common usage of `*` is in custom methods -// which don't use the URL at all for transferring data. -// -// It is possible to define multiple HTTP methods for one RPC by using -// the `additional_bindings` option. Example: -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get: "/v1/messages/{message_id}" -// additional_bindings { -// get: "/v1/users/{user_id}/messages/{message_id}" -// } -// }; -// } -// } -// message GetMessageRequest { -// string message_id = 1; -// string user_id = 2; -// } -// -// This enables the following two alternative HTTP JSON to RPC mappings: -// -// - HTTP: `GET /v1/messages/123456` -// - gRPC: `GetMessage(message_id: "123456")` -// -// - HTTP: `GET /v1/users/me/messages/123456` -// - gRPC: `GetMessage(user_id: "me" message_id: "123456")` -// -// Rules for HTTP mapping -// -// 1. Leaf request fields (recursive expansion nested messages in the request -// message) are classified into three categories: -// - Fields referred by the path template. They are passed via the URL path. -// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They -// are passed via the HTTP -// request body. -// - All other fields are passed via the URL query parameters, and the -// parameter name is the field path in the request message. A repeated -// field can be represented as multiple query parameters under the same -// name. -// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL -// query parameter, all fields -// are passed via URL path and HTTP request body. -// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP -// request body, all -// fields are passed via URL path and URL query parameters. -// -// Path template syntax -// -// Template = "/" Segments [ Verb ] ; -// Segments = Segment { "/" Segment } ; -// Segment = "*" | "**" | LITERAL | Variable ; -// Variable = "{" FieldPath [ "=" Segments ] "}" ; -// FieldPath = IDENT { "." IDENT } ; -// Verb = ":" LITERAL ; -// -// The syntax `*` matches a single URL path segment. The syntax `**` matches -// zero or more URL path segments, which must be the last part of the URL path -// except the `Verb`. -// -// The syntax `Variable` matches part of the URL path as specified by its -// template. A variable template must not contain other variables. If a variable -// matches a single path segment, its template may be omitted, e.g. `{var}` -// is equivalent to `{var=*}`. -// -// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` -// contains any reserved character, such characters should be percent-encoded -// before the matching. -// -// If a variable contains exactly one path segment, such as `"{var}"` or -// `"{var=*}"`, when such a variable is expanded into a URL path on the client -// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The -// server side does the reverse decoding. Such variables show up in the -// [Discovery -// Document](https://developers.google.com/discovery/v1/reference/apis) as -// `{var}`. -// -// If a variable contains multiple path segments, such as `"{var=foo/*}"` -// or `"{var=**}"`, when such a variable is expanded into a URL path on the -// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. -// The server side does the reverse decoding, except "%2F" and "%2f" are left -// unchanged. Such variables show up in the -// [Discovery -// Document](https://developers.google.com/discovery/v1/reference/apis) as -// `{+var}`. -// -// Using gRPC API Service Configuration -// -// gRPC API Service Configuration (service config) is a configuration language -// for configuring a gRPC service to become a user-facing product. The -// service config is simply the YAML representation of the `google.api.Service` -// proto message. -// -// As an alternative to annotating your proto file, you can configure gRPC -// transcoding in your service config YAML files. You do this by specifying a -// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same -// effect as the proto annotation. This can be particularly useful if you -// have a proto that is reused in multiple services. Note that any transcoding -// specified in the service config will override any matching transcoding -// configuration in the proto. -// -// The following example selects a gRPC method and applies an `HttpRule` to it: -// -// http: -// rules: -// - selector: example.v1.Messaging.GetMessage -// get: /v1/messages/{message_id}/{sub.subfield} -// -// Special notes -// -// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the -// proto to JSON conversion must follow the [proto3 -// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). -// -// While the single segment variable follows the semantics of -// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String -// Expansion, the multi segment variable **does not** follow RFC 6570 Section -// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion -// does not expand special characters like `?` and `#`, which would lead -// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding -// for multi segment variables. -// -// The path variables **must not** refer to any repeated or mapped field, -// because client libraries are not capable of handling such variable expansion. -// -// The path variables **must not** capture the leading "/" character. The reason -// is that the most common use case "{var}" does not capture the leading "/" -// character. For consistency, all path variables must share the same behavior. -// -// Repeated message fields must not be mapped to URL query parameters, because -// no client library can support such complicated mapping. -// -// If an API needs to use a JSON array for request or response body, it can map -// the request or response body to a repeated field. However, some gRPC -// Transcoding implementations may not support this feature. -message HttpRule { - // Selects a method to which this rule applies. - // - // Refer to [selector][google.api.DocumentationRule.selector] for syntax - // details. - string selector = 1; - - // Determines the URL pattern is matched by this rules. This pattern can be - // used with any of the {get|put|post|delete|patch} methods. A custom method - // can be defined using the 'custom' field. - oneof pattern { - // Maps to HTTP GET. Used for listing and getting information about - // resources. - string get = 2; - - // Maps to HTTP PUT. Used for replacing a resource. - string put = 3; - - // Maps to HTTP POST. Used for creating a resource or performing an action. - string post = 4; - - // Maps to HTTP DELETE. Used for deleting a resource. - string delete = 5; - - // Maps to HTTP PATCH. Used for updating a resource. - string patch = 6; - - // The custom pattern is used for specifying an HTTP method that is not - // included in the `pattern` field, such as HEAD, or "*" to leave the - // HTTP method unspecified for this rule. The wild-card rule is useful - // for services that provide content to Web (HTML) clients. - CustomHttpPattern custom = 8; - } - - // The name of the request field whose value is mapped to the HTTP request - // body, or `*` for mapping all request fields not captured by the path - // pattern to the HTTP body, or omitted for not having any HTTP request body. - // - // NOTE: the referred field must be present at the top-level of the request - // message type. - string body = 7; - - // Optional. The name of the response field whose value is mapped to the HTTP - // response body. When omitted, the entire response message will be used - // as the HTTP response body. - // - // NOTE: The referred field must be present at the top-level of the response - // message type. - string response_body = 12; - - // Additional HTTP bindings for the selector. Nested bindings must - // not contain an `additional_bindings` field themselves (that is, - // the nesting may only be one level deep). - repeated HttpRule additional_bindings = 11; -} - -// A custom pattern is used for defining custom HTTP verb. -message CustomHttpPattern { - // The name of this custom HTTP verb. - string kind = 1; - - // The path matched by this custom verb. - string path = 2; -} diff --git a/server/cmd/protoc-gen-go-orb/go.mod b/server/cmd/protoc-gen-go-orb/go.mod new file mode 100644 index 00000000..e5c11a90 --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/go.mod @@ -0,0 +1,5 @@ +module github.com/go-orb/plugins/server/cmd/protoc-gen-go-orb + +go 1.22.5 + +require google.golang.org/protobuf v1.34.2 diff --git a/server/cmd/protoc-gen-go-orb/go.sum b/server/cmd/protoc-gen-go-orb/go.sum new file mode 100644 index 00000000..8ce26723 --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/go.sum @@ -0,0 +1,6 @@ +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= diff --git a/server/cmd/protoc-gen-go-orb/main.go b/server/cmd/protoc-gen-go-orb/main.go new file mode 100644 index 00000000..5cc1fbef --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/main.go @@ -0,0 +1,103 @@ +// protoc-gen-go-orb is a plugin for the Google protocol buffer compiler to +// generate Go code. Install it by building this program and making it +// accessible within your PATH with the name: +// +// protoc-gen-go-orb +package main + +import ( + "flag" + "fmt" + "slices" + "strings" + + gengo "google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/types/descriptorpb" + "google.golang.org/protobuf/types/pluginpb" + + "github.com/go-orb/plugins/server/cmd/protoc-gen-go-orb/orb" + "github.com/go-orb/plugins/server/cmd/protoc-gen-go-orb/orbdrpc" + "github.com/go-orb/plugins/server/cmd/protoc-gen-go-orb/orbgrpc" +) + +// Blabala... +// +//nolint:gochecknoglobals +var ( + // That version will be shown in each _orb.pb.go. + Version = "0.0.1" + requireUnimplemented *bool + useGenericStreams *bool + servers *string +) + +func main() { + showVersion := flag.Bool("version", false, "print the version and exit") + flag.Parse() + + if *showVersion { + fmt.Printf("protoc-gen-go-orb %v\n", Version) //nolint:forbidigo + return + } + + var drpcConf orbdrpc.Config + + var flags flag.FlagSet + requireUnimplemented = flags.Bool("require_unimplemented_servers", false, "set to false to match legacy behavior (gRPC)") + //nolint:lll + useGenericStreams = flags.Bool( + "use_generic_streams_experimental", + true, + "set to true to use generic types for streaming client and server objects; this flag is EXPERIMENTAL and may be changed or removed in a future release (gRPC)", + ) + servers = flags.String("supported_servers", "drpc;grpc;hertz;http", "semicolon separated list of servers to generate for") + + flags.StringVar(&drpcConf.Protolib, "protolib", "google.golang.org/protobuf", "which protobuf library to use for encoding") + flags.BoolVar(&drpcConf.Json, "json", true, "generate encoders with json support") + + protogen.Options{ + ParamFunc: flags.Set, + }.Run(func(gen *protogen.Plugin) error { + gen.SupportedFeatures = uint64( + pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) | + uint64(pluginpb.CodeGeneratorResponse_FEATURE_SUPPORTS_EDITIONS) + gen.SupportedEditionsMinimum = descriptorpb.Edition_EDITION_PROTO2 + gen.SupportedEditionsMaximum = descriptorpb.Edition_EDITION_2023 + + sSplit := strings.Split(*servers, ";") + for i := range sSplit { + sSplit[i] = strings.Trim(strings.ToLower(sSplit[i]), " ") + } + + for _, f := range gen.Files { + if !f.Generate { + continue + } + + // protoc-gen-go + gengo.GenerateFile(gen, f) + + // dRPC + if slices.Contains(sSplit, "drpc") { + orbdrpc.Version = Version + orbdrpc.GenerateFile(gen, f, drpcConf) + } + + // gRPC + if slices.Contains(sSplit, "grpc") { + orbgrpc.Version = Version + orbgrpc.RequireUnimplemented = requireUnimplemented + orbgrpc.UseGenericStreams = useGenericStreams + orbgrpc.GenerateFile(gen, f) + } + + // ORB + orb.Version = Version + orb.Servers = sSplit + orb.GenerateFile(gen, f) + } + + return nil + }) +} diff --git a/server/cmd/protoc-gen-go-orb/orb/orb.go b/server/cmd/protoc-gen-go-orb/orb/orb.go new file mode 100644 index 00000000..100ae351 --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orb/orb.go @@ -0,0 +1,230 @@ +// Package orb is the protoc generator for orb. +// +//notlint:nosnakecase,lll +package orb + +import ( + "fmt" + "net/http" + "os" + "regexp" + "slices" + "strings" + + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" +) + +// Version will be set by protoc-gen-go-orb. +// +//nolint:gochecknoglobals +var Version string + +// Servers as well. +// +//nolint:gochecknoglobals +var Servers []string + +var methodSets = make(map[string]int) //nolint:gochecknoglobals + +// GenerateFile generates a _orb-http.pb.go file containing orb specs. +func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { + filename := file.GeneratedFilenamePrefix + "_orb.pb.go" + + gFile := gen.NewGeneratedFile(filename, file.GoImportPath) + + // Generate header message. + gFile.P("// Code generated by protoc-gen-go-orb. DO NOT EDIT.") + gFile.P("//") + gFile.P("// version:") + gFile.P("// - protoc-gen-go-orb v" + Version) + gFile.P("// - protoc ", protocVersion(gen)) + gFile.P("//") + + if file.Proto.GetOptions().GetDeprecated() { + gFile.P("// ", file.Desc.Path(), " is a deprecated file.") + } else { + gFile.P("// Proto source: ", file.Desc.Path()) + } + + // Generate package name. + gFile.P() + gFile.P("package ", file.GoPackageName) + gFile.P() + + // Generate service code. + if len(file.Services) > 0 { + mainTemplate := protoFile{ + ServerGRPC: slices.Contains(Servers, "grpc"), + ServerDRPC: slices.Contains(Servers, "drpc"), + ServerHTTP: slices.Contains(Servers, "http"), + ServerHertz: slices.Contains(Servers, "hertz"), + Services: genServices(file, gFile), + }.Render() + + gFile.P(mainTemplate) + } + + return gFile +} + +// genServices generates a list of service definitions from a proto file. +func genServices(file *protogen.File, generated *protogen.GeneratedFile) []serviceDesc { + services := make([]serviceDesc, len(file.Services)) + + for i, service := range file.Services { + services[i] = genService(file, generated, service) + } + + return services +} + +// genService will generate a single service description. +func genService(file *protogen.File, generated *protogen.GeneratedFile, service *protogen.Service) serviceDesc { + if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { + generated.P("//") + generated.P(deprecationComment) + } + + // HTTP Server. + serviceDescription := serviceDesc{ + Type: service.GoName, + TypeLower: strings.ToLower(service.GoName), + Name: string(service.Desc.FullName()), + Metadata: file.Desc.Path(), + Methods: make([]methodDesc, 0, 1), + } + + for _, method := range service.Methods { + // gRPC streaming is not suppoerted at the moment + if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { + continue + } + + serviceDescription.AddMethod( + buildMethodDesc(generated, method, http.MethodPost, + fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name()))) + } + + return serviceDescription +} + +func buildMethodDesc(generated *protogen.GeneratedFile, protogenMethod *protogen.Method, method, path string) methodDesc { + defer func() { methodSets[protogenMethod.GoName]++ }() + + vars := buildPathVars(path) + + for v, s := range vars { + fields := protogenMethod.Input.Desc.Fields() + + if s != nil { + path = replacePath(v, *s, path) + } + + for _, field := range strings.Split(v, ".") { + if strings.TrimSpace(field) == "" { + continue + } + + if strings.Contains(field, ":") { + field = strings.Split(field, ":")[0] + } + + fd := fields.ByName(protoreflect.Name(field)) + if fd == nil { + errMessage("The corresponding field '%s' declaration in message could not be found in '%s'", v, path) + os.Exit(2) //nolint:gocritic + } + + switch { + case fd.IsMap(): + errMessage("The field in path:'%s' shouldn't be a map", v) + case fd.IsList(): + errMessage("The field in path:'%s' shouldn't be a list.", v) + case fd.Kind() == protoreflect.MessageKind || fd.Kind() == protoreflect.GroupKind: + fields = fd.Message().Fields() + } + } + } + + return methodDesc{ + Name: protogenMethod.GoName, + OriginalName: string(protogenMethod.Desc.Name()), + Num: methodSets[protogenMethod.GoName], + Request: generated.QualifiedGoIdent(protogenMethod.Input.GoIdent), + Reply: generated.QualifiedGoIdent(protogenMethod.Output.GoIdent), + Path: path, + Method: fMethod(method), + MethodUpper: method, + } +} + +func buildPathVars(path string) (res map[string]*string) { + if strings.HasSuffix(path, "/") { + errMessage("Path %s should not end with \"/\"", path) + } + + pattern := regexp.MustCompile(`(?i){([a-z.0-9_\s]*)=?([^{}]*)}`) + matches := pattern.FindAllStringSubmatch(path, -1) + res = make(map[string]*string, len(matches)) + + for _, m := range matches { + name := strings.TrimSpace(m[1]) + if len(name) > 1 && len(m[2]) > 0 { + res[name] = &m[2] + } else { + res[name] = nil + } + } + + return +} + +func replacePath(name string, value string, path string) string { + pattern := regexp.MustCompile(fmt.Sprintf(`(?i){([\s]*%s[\s]*)=?([^{}]*)}`, name)) + + idx := pattern.FindStringIndex(path) + if len(idx) > 0 { + path = fmt.Sprintf("%s{%s:%s}%s", + path[:idx[0]], // The start of the match + name, + strings.ReplaceAll(value, "*", ".*"), + path[idx[1]:], + ) + } + + return path +} + +func protocVersion(gen *protogen.Plugin) string { + v := gen.Request.GetCompilerVersion() + if v == nil { + return "(unknown)" + } + + var suffix string + + if s := v.GetSuffix(); s != "" { + suffix = "-" + s + } + + return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix) +} + +func fMethod(s string) string { + if len(s) < 2 { + return s + } + + l := strings.ToLower(s) + + return strings.ToUpper(string(l[0])) + l[1:] +} + +func errMessage(msg string, args ...any) { + f := fmt.Sprintf("\u001B[31mWARN\u001B[m: %s\n", msg) + fmt.Fprintf(os.Stderr, f, args...) +} + +const deprecationComment = "// Deprecated: Do not use." diff --git a/server/cmd/protoc-gen-go-orb/orb/template.go b/server/cmd/protoc-gen-go-orb/orb/template.go new file mode 100644 index 00000000..b80329bb --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orb/template.go @@ -0,0 +1,157 @@ +package orb + +import ( + "bytes" + "strings" + "text/template" +) + +//nolint:gochecknoglobals +var orbTemplate = ` +import ( + "context" + + "github.com/go-orb/go-orb/log" + "github.com/go-orb/go-orb/server" + + {{ if .ServerGRPC }}grpc "google.golang.org/grpc"{{ end }} + + {{ if .ServerDRPC }}mdrpc "github.com/go-orb/plugins/server/drpc"{{ end }} + {{ if .ServerHertz }}mhertz "github.com/go-orb/plugins/server/hertz"{{ end }} + {{ if .ServerHTTP }}mhttp "github.com/go-orb/plugins/server/http"{{ end }} +) + +{{- range .Services }} +type {{.Type}}Handler interface { + {{.Type}}(ctx context.Context, req *Req) (*Resp, error) +} + +{{- if $.ServerDRPC }} + +func register{{.Type}}DRPCHandler(srv *mdrpc.Server, handler {{.Type}}Handler) error { + desc := DRPCEchoDescription{} + + // Register with DRPC. + r := srv.Router() + + // Register with the drpcmux. + err := r.Register(handler, desc) + if err != nil { + return err + } + + // Add each endpoint name of this handler to the orb drpc server. + for i := 0; i < desc.NumMethods(); i++ { + name, _, _, _, _ := desc.Method(i) + srv.AddEndpoint(name) + } + + return nil +} + +{{ end -}} + +{{- if $.ServerHTTP }} + +// register{{.Type}}HTTPHandler registers the service to an HTTP server. +func register{{.Type}}HTTPHandler(srv *mhttp.ServerHTTP, handler {{.Type}}Handler) { + r := srv.Router() + {{- range .Methods}} + r.{{.Method}}("{{.Path}}", mhttp.NewGRPCHandler(srv, handler.{{.Name}})) + {{- end}} +} + +{{ end -}} + +{{- if $.ServerHertz }} + +// register{{.Type}}HertzHandler registers the service to an Hertz server. +func register{{.Type}}HertzHandler(srv *mhertz.Server, handler {{.Type}}Handler) { + r := srv.Router() + {{- range .Methods}} + r.{{.MethodUpper}}("{{.Path}}", mhertz.NewGRPCHandler(srv, handler.{{.Name}})) + {{- end}} +} + +{{ end -}} + +// Register{{.Type}}Service will return a registration function that can be +// provided to entrypoints as a handler registration. +func Register{{.Type}}Service(handler {{.Type}}Handler) server.RegistrationFunc { + return server.RegistrationFunc(func(s any) { + switch srv := s.(type) { + {{ if $.ServerGRPC }} + case grpc.ServiceRegistrar: + register{{.Type}}GRPCHandler(srv, handler) + {{- end -}} + {{ if $.ServerDRPC }} + case *mdrpc.Server: + register{{.Type}}DRPCHandler(srv, handler) + {{- end -}} + {{ if $.ServerHertz }} + case *mhertz.Server: + register{{.Type}}HertzHandler(srv, handler) + {{- end -}} + {{ if $.ServerHTTP }} + case *mhttp.ServerHTTP: + register{{.Type}}HTTPHandler(srv, handler) + {{- end }} + default: + log.Warn("No provider for this server found", "proto", "{{.Metadata}}", "handler", "{{.Type}}", "server", s) + } + }) +} + +{{- end }} +` + +// protoFile is the object passed to the template. +type protoFile struct { + ServerGRPC bool + ServerDRPC bool + ServerHTTP bool + ServerHertz bool + + Services []serviceDesc +} + +func (p protoFile) Render() string { + tmpl, err := template.New("http").Parse(strings.TrimSpace(orbTemplate)) + if err != nil { + panic(err) + } + + buf := new(bytes.Buffer) + if err := tmpl.Execute(buf, p); err != nil { + panic(err) + } + + return strings.Trim(buf.String(), "\r\n") +} + +// serviceDesc describes a service. +type serviceDesc struct { + Type string // Greeter + TypeLower string // greeter + Name string // helloworld.Greeter + Metadata string // api/helloworld/helloworld.proto + Methods []methodDesc +} + +// methodDesc describes a service method. +type methodDesc struct { + Name string + OriginalName string // The parsed original name + Num int + Request string + Reply string + + // http_rule annotation properties. + Path string + Method string + MethodUpper string +} + +func (s *serviceDesc) AddMethod(method methodDesc) { + s.Methods = append(s.Methods, method) +} diff --git a/server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go b/server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go new file mode 100644 index 00000000..c3edf400 --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go @@ -0,0 +1,391 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + +// Package orbdrpc generates DRPC code for protobuf services. +// +//nolint:lll,funlen,wsl,dupword +package orbdrpc + +import ( + "fmt" + "strconv" + "strings" + + "google.golang.org/protobuf/compiler/protogen" +) + +// Version will be set by orb/main. +var Version string //nolint:gochecknoglobals + +// Config will be set by orb/main. +type Config struct { + Protolib string + Json bool +} + +func protocVersion(gen *protogen.Plugin) string { + v := gen.Request.GetCompilerVersion() + if v == nil { + return "(unknown)" + } + + var suffix string + + if s := v.GetSuffix(); s != "" { + suffix = "-" + s + } + + return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix) +} + +// GenerateFile writes the file. +func GenerateFile(plugin *protogen.Plugin, file *protogen.File, conf Config) { + gf := plugin.NewGeneratedFile(file.GeneratedFilenamePrefix+"_orb-drpc.pb.go", file.GoImportPath) + d := &drpc{gf, file} + + // Generate header message. + d.P("// Code generated by protoc-gen-go-orb. DO NOT EDIT.") + d.P("//") + d.P("// version:") + d.P("// - protoc-gen-go-orb v" + Version) + d.P("// - protoc ", protocVersion(plugin)) + d.P("//") + d.P("// source: ", file.Desc.Path()) + d.P() + d.P("package ", file.GoPackageName) + d.P() + + d.generateEncoding(conf) + + for _, service := range file.Services { + d.generateService(service) + } +} + +type drpc struct { + *protogen.GeneratedFile + file *protogen.File +} + +// +// name helpers +// + +func (d *drpc) Ident(path, ident string) string { + return d.QualifiedGoIdent(protogen.GoImportPath(path).Ident(ident)) +} + +func (d *drpc) EncodingName() string { + return "drpcEncoding_" + d.file.GoDescriptorIdent.GoName +} + +func (d *drpc) RPCGoString(method *protogen.Method) string { + return strconv.Quote(fmt.Sprintf("/%s/%s", method.Parent.Desc.FullName(), method.Desc.Name())) +} + +func (d *drpc) InputType(method *protogen.Method) string { + return d.QualifiedGoIdent(method.Input.GoIdent) +} + +func (d *drpc) OutputType(method *protogen.Method) string { + return d.QualifiedGoIdent(method.Output.GoIdent) +} + +func (d *drpc) ServerIface(service *protogen.Service) string { + return "DRPC" + service.GoName + "Server" +} + +func (d *drpc) ServerImpl(service *protogen.Service) string { + return "drpc" + service.GoName + "Server" +} + +func (d *drpc) ServerUnimpl(service *protogen.Service) string { + return "DRPC" + service.GoName + "UnimplementedServer" +} + +func (d *drpc) ServerDesc(service *protogen.Service) string { + return "DRPC" + service.GoName + "Description" +} + +func (d *drpc) ClientStreamIface(method *protogen.Method) string { + return "DRPC" + + strings.ReplaceAll(method.Parent.GoName, "_", "__") + "_" + + strings.ReplaceAll(method.GoName, "_", "__") + + "Client" +} + +func (d *drpc) ClientStreamImpl(method *protogen.Method) string { + return "drpc" + + strings.ReplaceAll(method.Parent.GoName, "_", "__") + "_" + + strings.ReplaceAll(method.GoName, "_", "__") + + "Client" +} + +func (d *drpc) ServerStreamIface(method *protogen.Method) string { + return "DRPC" + + strings.ReplaceAll(method.Parent.GoName, "_", "__") + "_" + + strings.ReplaceAll(method.GoName, "_", "__") + + "Stream" +} + +func (d *drpc) ServerStreamImpl(method *protogen.Method) string { + return "drpc" + + strings.ReplaceAll(method.Parent.GoName, "_", "__") + "_" + + strings.ReplaceAll(method.GoName, "_", "__") + + "Stream" +} + +// +// encoding generation +// + +func (d *drpc) generateEncoding(conf Config) { + d.P("type ", d.EncodingName(), " struct{}") + d.P() + + switch conf.Protolib { + case "google.golang.org/protobuf": + d.P("func (", d.EncodingName(), ") Marshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("google.golang.org/protobuf/proto", "Marshal"), "(msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") MarshalAppend(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("google.golang.org/protobuf/proto", "MarshalOptions"), "{}.MarshalAppend(buf, msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") Unmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident("google.golang.org/protobuf/proto", "Unmarshal"), "(buf, msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + if conf.Json { + d.P("func (", d.EncodingName(), ") JSONMarshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("google.golang.org/protobuf/encoding/protojson", "Marshal"), "(msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") JSONUnmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident("google.golang.org/protobuf/encoding/protojson", "Unmarshal"), "(buf, msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + } + + case "github.com/gogo/protobuf": + d.P("func (", d.EncodingName(), ") Marshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("github.com/gogo/protobuf/proto", "Marshal"), "(msg.(", d.Ident("github.com/gogo/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") Unmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident("github.com/gogo/protobuf/proto", "Unmarshal"), "(buf, msg.(", d.Ident("github.com/gogo/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + if conf.Json { + d.P("func (", d.EncodingName(), ") JSONMarshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("var buf ", d.Ident("bytes", "Buffer")) + d.P("err := new(", d.Ident("github.com/gogo/protobuf/jsonpb", "Marshaler"), ").Marshal(&buf, msg.(", d.Ident("github.com/gogo/protobuf/proto", "Message"), "))") + d.P("if err != nil {") + d.P("return nil, err") + d.P("}") + d.P("return buf.Bytes(), nil") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") JSONUnmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident("github.com/gogo/protobuf/jsonpb", "Unmarshal"), "(", d.Ident("bytes", "NewReader"), "(buf), msg.(", d.Ident("github.com/gogo/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + } + + default: + d.P("func (", d.EncodingName(), ") Marshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident(conf.Protolib, "Marshal"), "(msg)") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") Unmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident(conf.Protolib, "Unmarshal"), "(buf, msg)") + d.P("}") + d.P() + + if conf.Json { + d.P("func (", d.EncodingName(), ") JSONMarshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident(conf.Protolib, "JSONMarshal"), "(msg)") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") JSONUnmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident(conf.Protolib, "JSONUnmarshal"), "(buf, msg)") + d.P("}") + d.P() + } + } +} + +// +// service generation +// + +func (d *drpc) generateService(service *protogen.Service) { + // Server interface + d.P("type ", d.ServerIface(service), " interface {") + for _, method := range service.Methods { + d.P(d.generateServerSignature(method)) + } + d.P("}") + d.P() + + // Server Unimplemented struct + d.P("type ", d.ServerUnimpl(service), " struct {}") + d.P() + for _, method := range service.Methods { + d.generateUnimplementedServerMethod(method) + } + d.P() + + // Server description. + d.P("type ", d.ServerDesc(service), " struct{}") + d.P() + d.P("func (", d.ServerDesc(service), ") NumMethods() int { return ", len(service.Methods), " }") + d.P() + d.P("func (", d.ServerDesc(service), ") Method(n int) (string, ", d.Ident("storj.io/drpc", "Encoding"), ", ", d.Ident("storj.io/drpc", "Receiver"), ", interface{}, bool) {") + d.P("switch n {") + for i, method := range service.Methods { + d.P("case ", i, ":") + d.P("return ", d.RPCGoString(method), ", ", d.EncodingName(), "{}, ") + d.generateServerReceiver(method) + d.P("}, ", d.ServerIface(service), ".", method.GoName, ", true") + } + d.P("default:") + d.P(`return "", nil, nil, nil, false`) + d.P("}") + d.P("}") + d.P() + + // Registration helper + d.P("func DRPCRegister", service.GoName, "(mux ", d.Ident("storj.io/drpc", "Mux"), ", impl ", d.ServerIface(service), ") error {") + d.P("return mux.Register(impl, ", d.ServerDesc(service), "{})") + d.P("}") + + // Server methods + for _, method := range service.Methods { + d.generateServerMethod(method) + } +} + +// +// server methods +// + +func (d *drpc) generateServerSignature(method *protogen.Method) string { + var reqArgs []string + ret := "error" + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + reqArgs = append(reqArgs, d.Ident("context", "Context")) + ret = "(*" + d.OutputType(method) + ", error)" + } + if !method.Desc.IsStreamingClient() { + reqArgs = append(reqArgs, "*"+d.InputType(method)) + } + if method.Desc.IsStreamingServer() || method.Desc.IsStreamingClient() { + reqArgs = append(reqArgs, d.ServerStreamIface(method)) + } + return method.GoName + "(" + strings.Join(reqArgs, ", ") + ") " + ret +} + +func (d *drpc) generateUnimplementedServerMethod(method *protogen.Method) { + d.P("func (s *", d.ServerUnimpl(method.Parent), ") ", d.generateServerSignature(method), " {") + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + d.P("return nil, ", d.Ident("storj.io/drpc/drpcerr", "WithCode"), "(", d.Ident("errors", "New"), "(\"Unimplemented\"), ", d.Ident("storj.io/drpc/drpcerr", "Unimplemented"), ")") + } else { + d.P("return ", d.Ident("storj.io/drpc/drpcerr", "WithCode"), "(", d.Ident("errors", "New"), "(\"Unimplemented\"), ", d.Ident("storj.io/drpc/drpcerr", "Unimplemented"), ")") + } + d.P("}") + d.P() +} + +func (d *drpc) generateServerReceiver(method *protogen.Method) { + d.P("func (srv interface{}, ctx context.Context, in1, in2 interface{}) (" + d.Ident("storj.io/drpc", "Message") + ", error) {") + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + d.P("return srv.(", d.ServerIface(method.Parent), ").") + } else { + d.P("return nil, srv.(", d.ServerIface(method.Parent), ").") + } + d.P(method.GoName, "(") + + n := 1 + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + d.P("ctx,") + } + if !method.Desc.IsStreamingClient() { + d.P("in", n, ".(*", d.InputType(method), "),") + n++ + } + if method.Desc.IsStreamingServer() || method.Desc.IsStreamingClient() { + d.P("&", d.ServerStreamImpl(method), "{in", n, ".(", d.Ident("storj.io/drpc", "Stream"), ")},") + } + d.P(")") +} + +func (d *drpc) generateServerMethod(method *protogen.Method) { + genSend := method.Desc.IsStreamingServer() + genSendAndClose := !method.Desc.IsStreamingServer() + genRecv := method.Desc.IsStreamingClient() + + // Stream auxiliary types and methods. + d.P("type ", d.ServerStreamIface(method), " interface {") + d.P(d.Ident("storj.io/drpc", "Stream")) + if genSend { + d.P("Send(*", d.OutputType(method), ") error") + } + if genSendAndClose { + d.P("SendAndClose(*", d.OutputType(method), ") error") + } + if genRecv { + d.P("Recv() (*", d.InputType(method), ", error)") + } + d.P("}") + d.P() + + d.P("type ", d.ServerStreamImpl(method), " struct {") + d.P(d.Ident("storj.io/drpc", "Stream")) + d.P("}") + d.P() + + d.P("func (x *", d.ServerStreamImpl(method), ") GetStream() drpc.Stream {") + d.P("return x.Stream") + d.P("}") + d.P() + + if genSend { + d.P("func (x *", d.ServerStreamImpl(method), ") Send(m *", d.OutputType(method), ") error {") + d.P("return x.MsgSend(m, ", d.EncodingName(), "{})") + d.P("}") + d.P() + } + + if genSendAndClose { + d.P("func (x *", d.ServerStreamImpl(method), ") SendAndClose(m *", d.OutputType(method), ") error {") + d.P("if err := x.MsgSend(m, ", d.EncodingName(), "{}); err != nil { return err }") + d.P("return x.CloseSend()") + d.P("}") + d.P() + } + + if genRecv { + d.P("func (x *", d.ServerStreamImpl(method), ") Recv() (*", d.InputType(method), ", error) {") + d.P("m := new(", d.InputType(method), ")") + d.P("if err := x.MsgRecv(m, ", d.EncodingName(), "{}); err != nil { return nil, err }") + d.P("return m, nil") + d.P("}") + d.P() + + d.P("func (x *", d.ServerStreamImpl(method), ") RecvMsg(m *", d.InputType(method), ") error {") + d.P("return x.MsgRecv(m, ", d.EncodingName(), "{})") + d.P("}") + d.P() + } +} diff --git a/server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go.patch b/server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go.patch new file mode 100644 index 00000000..4ff8d660 --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go.patch @@ -0,0 +1,330 @@ +--- drpc.go.upstream 2024-08-12 19:02:01.737448650 +0200 ++++ drpc.go 2024-08-12 22:01:14.687030408 +0200 +@@ -1,59 +1,62 @@ + // Copyright (C) 2019 Storj Labs, Inc. + // See LICENSE for copying information. + +-// protoc-gen-go-drpc generates DRPC code for protobuf services. +-package main ++// Package orbdrpc generates DRPC code for protobuf services. ++// ++//nolint:lll,funlen,wsl,dupword ++package orbdrpc + + import ( +- "flag" + "fmt" +- "runtime/debug" + "strconv" + "strings" + + "google.golang.org/protobuf/compiler/protogen" +- "google.golang.org/protobuf/types/pluginpb" + ) + +-type config struct { +- protolib string +- json bool +-} +- +-func main() { +- var flags flag.FlagSet +- var conf config +- flags.StringVar(&conf.protolib, "protolib", "google.golang.org/protobuf", "which protobuf library to use for encoding") +- flags.BoolVar(&conf.json, "json", true, "generate encoders with json support") +- +- protogen.Options{ +- ParamFunc: flags.Set, +- }.Run(func(plugin *protogen.Plugin) error { +- for _, f := range plugin.Files { +- if !f.Generate || len(f.Services) == 0 { +- continue +- } +- generateFile(plugin, f, conf) +- } +- plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) +- return nil +- }) ++// Version will be set by orb/main. ++var Version string //nolint:gochecknoglobals ++ ++// Config will be set by orb/main. ++type Config struct { ++ Protolib string ++ Json bool + } + +-func generateFile(plugin *protogen.Plugin, file *protogen.File, conf config) { +- gf := plugin.NewGeneratedFile(file.GeneratedFilenamePrefix+"_drpc.pb.go", file.GoImportPath) +- d := &drpc{gf, file} ++func protocVersion(gen *protogen.Plugin) string { ++ v := gen.Request.GetCompilerVersion() ++ if v == nil { ++ return "(unknown)" ++ } + +- d.P("// Code generated by protoc-gen-go-drpc. DO NOT EDIT.") +- if bi, ok := debug.ReadBuildInfo(); ok { +- d.P("// protoc-gen-go-drpc version: ", bi.Main.Version) ++ var suffix string ++ ++ if s := v.GetSuffix(); s != "" { ++ suffix = "-" + s + } ++ ++ return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix) ++} ++ ++// GenerateFile writes the file. ++func GenerateFile(plugin *protogen.Plugin, file *protogen.File, conf Config) { ++ gf := plugin.NewGeneratedFile(file.GeneratedFilenamePrefix+"_orb-drpc.pb.go", file.GoImportPath) ++ d := &drpc{gf, file} ++ ++ // Generate header message. ++ d.P("// Code generated by protoc-gen-go-orb. DO NOT EDIT.") ++ d.P("//") ++ d.P("// version:") ++ d.P("// - protoc-gen-go-orb v" + Version) ++ d.P("// - protoc ", protocVersion(plugin)) ++ d.P("//") + d.P("// source: ", file.Desc.Path()) + d.P() + d.P("package ", file.GoPackageName) + d.P() + + d.generateEncoding(conf) ++ + for _, service := range file.Services { + d.generateService(service) + } +@@ -88,14 +91,6 @@ + return d.QualifiedGoIdent(method.Output.GoIdent) + } + +-func (d *drpc) ClientIface(service *protogen.Service) string { +- return "DRPC" + service.GoName + "Client" +-} +- +-func (d *drpc) ClientImpl(service *protogen.Service) string { +- return "drpc" + service.GoName + "Client" +-} +- + func (d *drpc) ServerIface(service *protogen.Service) string { + return "DRPC" + service.GoName + "Server" + } +@@ -144,11 +139,11 @@ + // encoding generation + // + +-func (d *drpc) generateEncoding(conf config) { ++func (d *drpc) generateEncoding(conf Config) { + d.P("type ", d.EncodingName(), " struct{}") + d.P() + +- switch conf.protolib { ++ switch conf.Protolib { + case "google.golang.org/protobuf": + d.P("func (", d.EncodingName(), ") Marshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("google.golang.org/protobuf/proto", "Marshal"), "(msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") +@@ -165,7 +160,7 @@ + d.P("}") + d.P() + +- if conf.json { ++ if conf.Json { + d.P("func (", d.EncodingName(), ") JSONMarshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("google.golang.org/protobuf/encoding/protojson", "Marshal"), "(msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") +@@ -188,7 +183,7 @@ + d.P("}") + d.P() + +- if conf.json { ++ if conf.Json { + d.P("func (", d.EncodingName(), ") JSONMarshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("var buf ", d.Ident("bytes", "Buffer")) + d.P("err := new(", d.Ident("github.com/gogo/protobuf/jsonpb", "Marshaler"), ").Marshal(&buf, msg.(", d.Ident("github.com/gogo/protobuf/proto", "Message"), "))") +@@ -207,27 +202,26 @@ + + default: + d.P("func (", d.EncodingName(), ") Marshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") +- d.P("return ", d.Ident(conf.protolib, "Marshal"), "(msg)") ++ d.P("return ", d.Ident(conf.Protolib, "Marshal"), "(msg)") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") Unmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") +- d.P("return ", d.Ident(conf.protolib, "Unmarshal"), "(buf, msg)") ++ d.P("return ", d.Ident(conf.Protolib, "Unmarshal"), "(buf, msg)") + d.P("}") + d.P() + +- if conf.json { ++ if conf.Json { + d.P("func (", d.EncodingName(), ") JSONMarshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") +- d.P("return ", d.Ident(conf.protolib, "JSONMarshal"), "(msg)") ++ d.P("return ", d.Ident(conf.Protolib, "JSONMarshal"), "(msg)") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") JSONUnmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") +- d.P("return ", d.Ident(conf.protolib, "JSONUnmarshal"), "(buf, msg)") ++ d.P("return ", d.Ident(conf.Protolib, "JSONUnmarshal"), "(buf, msg)") + d.P("}") + d.P() + } +- + } + } + +@@ -236,35 +230,6 @@ + // + + func (d *drpc) generateService(service *protogen.Service) { +- // Client interface +- d.P("type ", d.ClientIface(service), " interface {") +- d.P("DRPCConn() ", d.Ident("storj.io/drpc", "Conn")) +- d.P() +- for _, method := range service.Methods { +- d.P(d.generateClientSignature(method)) +- } +- d.P("}") +- d.P() +- +- // Client implementation +- d.P("type ", d.ClientImpl(service), " struct {") +- d.P("cc ", d.Ident("storj.io/drpc", "Conn")) +- d.P("}") +- d.P() +- +- // Client constructor +- d.P("func New", d.ClientIface(service), "(cc ", d.Ident("storj.io/drpc", "Conn"), ") ", d.ClientIface(service), " {") +- d.P("return &", d.ClientImpl(service), "{cc}") +- d.P("}") +- d.P() +- +- // Client method implementations +- d.P("func (c *", d.ClientImpl(service), ") DRPCConn() ", d.Ident("storj.io/drpc", "Conn"), "{ return c.cc }") +- d.P() +- for _, method := range service.Methods { +- d.generateClientMethod(method) +- } +- + // Server interface + d.P("type ", d.ServerIface(service), " interface {") + for _, method := range service.Methods { +@@ -311,114 +276,6 @@ + } + } + +-// +-// client methods +-// +- +-func (d *drpc) generateClientSignature(method *protogen.Method) string { +- reqArg := ", in *" + d.InputType(method) +- if method.Desc.IsStreamingClient() { +- reqArg = "" +- } +- respName := "*" + d.OutputType(method) +- if method.Desc.IsStreamingServer() || method.Desc.IsStreamingClient() { +- respName = d.ClientStreamIface(method) +- } +- return fmt.Sprintf("%s(ctx %s%s) (%s, error)", method.GoName, d.Ident("context", "Context"), reqArg, respName) +-} +- +-func (d *drpc) generateClientMethod(method *protogen.Method) { +- recvType := d.ClientImpl(method.Parent) +- outType := d.OutputType(method) +- inType := d.InputType(method) +- +- d.P("func (c *", recvType, ") ", d.generateClientSignature(method), "{") +- if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { +- d.P("out := new(", outType, ")") +- d.P("err := c.cc.Invoke(ctx, ", d.RPCGoString(method), ", ", d.EncodingName(), "{}, in, out)") +- d.P("if err != nil { return nil, err }") +- d.P("return out, nil") +- d.P("}") +- d.P() +- return +- } +- +- d.P("stream, err := c.cc.NewStream(ctx, ", d.RPCGoString(method), ", ", d.EncodingName(), "{})") +- d.P("if err != nil { return nil, err }") +- d.P("x := &", d.ClientStreamImpl(method), "{stream}") +- if !method.Desc.IsStreamingClient() { +- d.P("if err := x.MsgSend(in, ", d.EncodingName(), "{}); err != nil { return nil, err }") +- d.P("if err := x.CloseSend(); err != nil { return nil, err }") +- } +- d.P("return x, nil") +- d.P("}") +- d.P() +- +- genSend := method.Desc.IsStreamingClient() +- genRecv := method.Desc.IsStreamingServer() +- genCloseAndRecv := !method.Desc.IsStreamingServer() +- +- // Stream auxiliary types and methods. +- d.P("type ", d.ClientStreamIface(method), " interface {") +- d.P(d.Ident("storj.io/drpc", "Stream")) +- if genSend { +- d.P("Send(*", inType, ") error") +- } +- if genRecv { +- d.P("Recv() (*", outType, ", error)") +- } +- if genCloseAndRecv { +- d.P("CloseAndRecv() (*", outType, ", error)") +- } +- d.P("}") +- d.P() +- +- d.P("type ", d.ClientStreamImpl(method), " struct {") +- d.P(d.Ident("storj.io/drpc", "Stream")) +- d.P("}") +- d.P() +- +- d.P("func (x *", d.ClientStreamImpl(method), ") GetStream() drpc.Stream {") +- d.P("return x.Stream") +- d.P("}") +- d.P() +- +- if genSend { +- d.P("func (x *", d.ClientStreamImpl(method), ") Send(m *", inType, ") error {") +- d.P("return x.MsgSend(m, ", d.EncodingName(), "{})") +- d.P("}") +- d.P() +- } +- if genRecv { +- d.P("func (x *", d.ClientStreamImpl(method), ") Recv() (*", outType, ", error) {") +- d.P("m := new(", outType, ")") +- d.P("if err := x.MsgRecv(m, ", d.EncodingName(), "{}); err != nil { return nil, err }") +- d.P("return m, nil") +- d.P("}") +- d.P() +- +- d.P("func (x *", d.ClientStreamImpl(method), ") RecvMsg(m *", outType, ") error {") +- d.P("return x.MsgRecv(m, ", d.EncodingName(), "{})") +- d.P("}") +- d.P() +- } +- if genCloseAndRecv { +- d.P("func (x *", d.ClientStreamImpl(method), ") CloseAndRecv() (*", outType, ", error) {") +- d.P("if err := x.CloseSend(); err != nil { return nil, err }") +- d.P("m := new(", outType, ")") +- d.P("if err := x.MsgRecv(m, ", d.EncodingName(), "{}); err != nil { return nil, err }") +- d.P("return m, nil") +- d.P("}") +- d.P() +- +- d.P("func (x *", d.ClientStreamImpl(method), ") CloseAndRecvMsg(m *", outType, ") error {") +- d.P("if err := x.CloseSend(); err != nil { return err }") +- d.P("return x.MsgRecv(m, ", d.EncodingName(), "{})") +- d.P("}") +- d.P() +- } +-} +- + // + // server methods + // diff --git a/server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go.upstream b/server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go.upstream new file mode 100644 index 00000000..2f44648d --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orbdrpc/drpc.go.upstream @@ -0,0 +1,534 @@ +// Copyright (C) 2019 Storj Labs, Inc. +// See LICENSE for copying information. + +// protoc-gen-go-drpc generates DRPC code for protobuf services. +package main + +import ( + "flag" + "fmt" + "runtime/debug" + "strconv" + "strings" + + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/types/pluginpb" +) + +type config struct { + protolib string + json bool +} + +func main() { + var flags flag.FlagSet + var conf config + flags.StringVar(&conf.protolib, "protolib", "google.golang.org/protobuf", "which protobuf library to use for encoding") + flags.BoolVar(&conf.json, "json", true, "generate encoders with json support") + + protogen.Options{ + ParamFunc: flags.Set, + }.Run(func(plugin *protogen.Plugin) error { + for _, f := range plugin.Files { + if !f.Generate || len(f.Services) == 0 { + continue + } + generateFile(plugin, f, conf) + } + plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) + return nil + }) +} + +func generateFile(plugin *protogen.Plugin, file *protogen.File, conf config) { + gf := plugin.NewGeneratedFile(file.GeneratedFilenamePrefix+"_drpc.pb.go", file.GoImportPath) + d := &drpc{gf, file} + + d.P("// Code generated by protoc-gen-go-drpc. DO NOT EDIT.") + if bi, ok := debug.ReadBuildInfo(); ok { + d.P("// protoc-gen-go-drpc version: ", bi.Main.Version) + } + d.P("// source: ", file.Desc.Path()) + d.P() + d.P("package ", file.GoPackageName) + d.P() + + d.generateEncoding(conf) + for _, service := range file.Services { + d.generateService(service) + } +} + +type drpc struct { + *protogen.GeneratedFile + file *protogen.File +} + +// +// name helpers +// + +func (d *drpc) Ident(path, ident string) string { + return d.QualifiedGoIdent(protogen.GoImportPath(path).Ident(ident)) +} + +func (d *drpc) EncodingName() string { + return "drpcEncoding_" + d.file.GoDescriptorIdent.GoName +} + +func (d *drpc) RPCGoString(method *protogen.Method) string { + return strconv.Quote(fmt.Sprintf("/%s/%s", method.Parent.Desc.FullName(), method.Desc.Name())) +} + +func (d *drpc) InputType(method *protogen.Method) string { + return d.QualifiedGoIdent(method.Input.GoIdent) +} + +func (d *drpc) OutputType(method *protogen.Method) string { + return d.QualifiedGoIdent(method.Output.GoIdent) +} + +func (d *drpc) ClientIface(service *protogen.Service) string { + return "DRPC" + service.GoName + "Client" +} + +func (d *drpc) ClientImpl(service *protogen.Service) string { + return "drpc" + service.GoName + "Client" +} + +func (d *drpc) ServerIface(service *protogen.Service) string { + return "DRPC" + service.GoName + "Server" +} + +func (d *drpc) ServerImpl(service *protogen.Service) string { + return "drpc" + service.GoName + "Server" +} + +func (d *drpc) ServerUnimpl(service *protogen.Service) string { + return "DRPC" + service.GoName + "UnimplementedServer" +} + +func (d *drpc) ServerDesc(service *protogen.Service) string { + return "DRPC" + service.GoName + "Description" +} + +func (d *drpc) ClientStreamIface(method *protogen.Method) string { + return "DRPC" + + strings.ReplaceAll(method.Parent.GoName, "_", "__") + "_" + + strings.ReplaceAll(method.GoName, "_", "__") + + "Client" +} + +func (d *drpc) ClientStreamImpl(method *protogen.Method) string { + return "drpc" + + strings.ReplaceAll(method.Parent.GoName, "_", "__") + "_" + + strings.ReplaceAll(method.GoName, "_", "__") + + "Client" +} + +func (d *drpc) ServerStreamIface(method *protogen.Method) string { + return "DRPC" + + strings.ReplaceAll(method.Parent.GoName, "_", "__") + "_" + + strings.ReplaceAll(method.GoName, "_", "__") + + "Stream" +} + +func (d *drpc) ServerStreamImpl(method *protogen.Method) string { + return "drpc" + + strings.ReplaceAll(method.Parent.GoName, "_", "__") + "_" + + strings.ReplaceAll(method.GoName, "_", "__") + + "Stream" +} + +// +// encoding generation +// + +func (d *drpc) generateEncoding(conf config) { + d.P("type ", d.EncodingName(), " struct{}") + d.P() + + switch conf.protolib { + case "google.golang.org/protobuf": + d.P("func (", d.EncodingName(), ") Marshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("google.golang.org/protobuf/proto", "Marshal"), "(msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") MarshalAppend(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("google.golang.org/protobuf/proto", "MarshalOptions"), "{}.MarshalAppend(buf, msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") Unmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident("google.golang.org/protobuf/proto", "Unmarshal"), "(buf, msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + if conf.json { + d.P("func (", d.EncodingName(), ") JSONMarshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("google.golang.org/protobuf/encoding/protojson", "Marshal"), "(msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") JSONUnmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident("google.golang.org/protobuf/encoding/protojson", "Unmarshal"), "(buf, msg.(", d.Ident("google.golang.org/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + } + + case "github.com/gogo/protobuf": + d.P("func (", d.EncodingName(), ") Marshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident("github.com/gogo/protobuf/proto", "Marshal"), "(msg.(", d.Ident("github.com/gogo/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") Unmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident("github.com/gogo/protobuf/proto", "Unmarshal"), "(buf, msg.(", d.Ident("github.com/gogo/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + + if conf.json { + d.P("func (", d.EncodingName(), ") JSONMarshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("var buf ", d.Ident("bytes", "Buffer")) + d.P("err := new(", d.Ident("github.com/gogo/protobuf/jsonpb", "Marshaler"), ").Marshal(&buf, msg.(", d.Ident("github.com/gogo/protobuf/proto", "Message"), "))") + d.P("if err != nil {") + d.P("return nil, err") + d.P("}") + d.P("return buf.Bytes(), nil") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") JSONUnmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident("github.com/gogo/protobuf/jsonpb", "Unmarshal"), "(", d.Ident("bytes", "NewReader"), "(buf), msg.(", d.Ident("github.com/gogo/protobuf/proto", "Message"), "))") + d.P("}") + d.P() + } + + default: + d.P("func (", d.EncodingName(), ") Marshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident(conf.protolib, "Marshal"), "(msg)") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") Unmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident(conf.protolib, "Unmarshal"), "(buf, msg)") + d.P("}") + d.P() + + if conf.json { + d.P("func (", d.EncodingName(), ") JSONMarshal(msg ", d.Ident("storj.io/drpc", "Message"), ") ([]byte, error) {") + d.P("return ", d.Ident(conf.protolib, "JSONMarshal"), "(msg)") + d.P("}") + d.P() + + d.P("func (", d.EncodingName(), ") JSONUnmarshal(buf []byte, msg ", d.Ident("storj.io/drpc", "Message"), ") error {") + d.P("return ", d.Ident(conf.protolib, "JSONUnmarshal"), "(buf, msg)") + d.P("}") + d.P() + } + + } +} + +// +// service generation +// + +func (d *drpc) generateService(service *protogen.Service) { + // Client interface + d.P("type ", d.ClientIface(service), " interface {") + d.P("DRPCConn() ", d.Ident("storj.io/drpc", "Conn")) + d.P() + for _, method := range service.Methods { + d.P(d.generateClientSignature(method)) + } + d.P("}") + d.P() + + // Client implementation + d.P("type ", d.ClientImpl(service), " struct {") + d.P("cc ", d.Ident("storj.io/drpc", "Conn")) + d.P("}") + d.P() + + // Client constructor + d.P("func New", d.ClientIface(service), "(cc ", d.Ident("storj.io/drpc", "Conn"), ") ", d.ClientIface(service), " {") + d.P("return &", d.ClientImpl(service), "{cc}") + d.P("}") + d.P() + + // Client method implementations + d.P("func (c *", d.ClientImpl(service), ") DRPCConn() ", d.Ident("storj.io/drpc", "Conn"), "{ return c.cc }") + d.P() + for _, method := range service.Methods { + d.generateClientMethod(method) + } + + // Server interface + d.P("type ", d.ServerIface(service), " interface {") + for _, method := range service.Methods { + d.P(d.generateServerSignature(method)) + } + d.P("}") + d.P() + + // Server Unimplemented struct + d.P("type ", d.ServerUnimpl(service), " struct {}") + d.P() + for _, method := range service.Methods { + d.generateUnimplementedServerMethod(method) + } + d.P() + + // Server description. + d.P("type ", d.ServerDesc(service), " struct{}") + d.P() + d.P("func (", d.ServerDesc(service), ") NumMethods() int { return ", len(service.Methods), " }") + d.P() + d.P("func (", d.ServerDesc(service), ") Method(n int) (string, ", d.Ident("storj.io/drpc", "Encoding"), ", ", d.Ident("storj.io/drpc", "Receiver"), ", interface{}, bool) {") + d.P("switch n {") + for i, method := range service.Methods { + d.P("case ", i, ":") + d.P("return ", d.RPCGoString(method), ", ", d.EncodingName(), "{}, ") + d.generateServerReceiver(method) + d.P("}, ", d.ServerIface(service), ".", method.GoName, ", true") + } + d.P("default:") + d.P(`return "", nil, nil, nil, false`) + d.P("}") + d.P("}") + d.P() + + // Registration helper + d.P("func DRPCRegister", service.GoName, "(mux ", d.Ident("storj.io/drpc", "Mux"), ", impl ", d.ServerIface(service), ") error {") + d.P("return mux.Register(impl, ", d.ServerDesc(service), "{})") + d.P("}") + + // Server methods + for _, method := range service.Methods { + d.generateServerMethod(method) + } +} + +// +// client methods +// + +func (d *drpc) generateClientSignature(method *protogen.Method) string { + reqArg := ", in *" + d.InputType(method) + if method.Desc.IsStreamingClient() { + reqArg = "" + } + respName := "*" + d.OutputType(method) + if method.Desc.IsStreamingServer() || method.Desc.IsStreamingClient() { + respName = d.ClientStreamIface(method) + } + return fmt.Sprintf("%s(ctx %s%s) (%s, error)", method.GoName, d.Ident("context", "Context"), reqArg, respName) +} + +func (d *drpc) generateClientMethod(method *protogen.Method) { + recvType := d.ClientImpl(method.Parent) + outType := d.OutputType(method) + inType := d.InputType(method) + + d.P("func (c *", recvType, ") ", d.generateClientSignature(method), "{") + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + d.P("out := new(", outType, ")") + d.P("err := c.cc.Invoke(ctx, ", d.RPCGoString(method), ", ", d.EncodingName(), "{}, in, out)") + d.P("if err != nil { return nil, err }") + d.P("return out, nil") + d.P("}") + d.P() + return + } + + d.P("stream, err := c.cc.NewStream(ctx, ", d.RPCGoString(method), ", ", d.EncodingName(), "{})") + d.P("if err != nil { return nil, err }") + d.P("x := &", d.ClientStreamImpl(method), "{stream}") + if !method.Desc.IsStreamingClient() { + d.P("if err := x.MsgSend(in, ", d.EncodingName(), "{}); err != nil { return nil, err }") + d.P("if err := x.CloseSend(); err != nil { return nil, err }") + } + d.P("return x, nil") + d.P("}") + d.P() + + genSend := method.Desc.IsStreamingClient() + genRecv := method.Desc.IsStreamingServer() + genCloseAndRecv := !method.Desc.IsStreamingServer() + + // Stream auxiliary types and methods. + d.P("type ", d.ClientStreamIface(method), " interface {") + d.P(d.Ident("storj.io/drpc", "Stream")) + if genSend { + d.P("Send(*", inType, ") error") + } + if genRecv { + d.P("Recv() (*", outType, ", error)") + } + if genCloseAndRecv { + d.P("CloseAndRecv() (*", outType, ", error)") + } + d.P("}") + d.P() + + d.P("type ", d.ClientStreamImpl(method), " struct {") + d.P(d.Ident("storj.io/drpc", "Stream")) + d.P("}") + d.P() + + d.P("func (x *", d.ClientStreamImpl(method), ") GetStream() drpc.Stream {") + d.P("return x.Stream") + d.P("}") + d.P() + + if genSend { + d.P("func (x *", d.ClientStreamImpl(method), ") Send(m *", inType, ") error {") + d.P("return x.MsgSend(m, ", d.EncodingName(), "{})") + d.P("}") + d.P() + } + if genRecv { + d.P("func (x *", d.ClientStreamImpl(method), ") Recv() (*", outType, ", error) {") + d.P("m := new(", outType, ")") + d.P("if err := x.MsgRecv(m, ", d.EncodingName(), "{}); err != nil { return nil, err }") + d.P("return m, nil") + d.P("}") + d.P() + + d.P("func (x *", d.ClientStreamImpl(method), ") RecvMsg(m *", outType, ") error {") + d.P("return x.MsgRecv(m, ", d.EncodingName(), "{})") + d.P("}") + d.P() + } + if genCloseAndRecv { + d.P("func (x *", d.ClientStreamImpl(method), ") CloseAndRecv() (*", outType, ", error) {") + d.P("if err := x.CloseSend(); err != nil { return nil, err }") + d.P("m := new(", outType, ")") + d.P("if err := x.MsgRecv(m, ", d.EncodingName(), "{}); err != nil { return nil, err }") + d.P("return m, nil") + d.P("}") + d.P() + + d.P("func (x *", d.ClientStreamImpl(method), ") CloseAndRecvMsg(m *", outType, ") error {") + d.P("if err := x.CloseSend(); err != nil { return err }") + d.P("return x.MsgRecv(m, ", d.EncodingName(), "{})") + d.P("}") + d.P() + } +} + +// +// server methods +// + +func (d *drpc) generateServerSignature(method *protogen.Method) string { + var reqArgs []string + ret := "error" + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + reqArgs = append(reqArgs, d.Ident("context", "Context")) + ret = "(*" + d.OutputType(method) + ", error)" + } + if !method.Desc.IsStreamingClient() { + reqArgs = append(reqArgs, "*"+d.InputType(method)) + } + if method.Desc.IsStreamingServer() || method.Desc.IsStreamingClient() { + reqArgs = append(reqArgs, d.ServerStreamIface(method)) + } + return method.GoName + "(" + strings.Join(reqArgs, ", ") + ") " + ret +} + +func (d *drpc) generateUnimplementedServerMethod(method *protogen.Method) { + d.P("func (s *", d.ServerUnimpl(method.Parent), ") ", d.generateServerSignature(method), " {") + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + d.P("return nil, ", d.Ident("storj.io/drpc/drpcerr", "WithCode"), "(", d.Ident("errors", "New"), "(\"Unimplemented\"), ", d.Ident("storj.io/drpc/drpcerr", "Unimplemented"), ")") + } else { + d.P("return ", d.Ident("storj.io/drpc/drpcerr", "WithCode"), "(", d.Ident("errors", "New"), "(\"Unimplemented\"), ", d.Ident("storj.io/drpc/drpcerr", "Unimplemented"), ")") + } + d.P("}") + d.P() +} + +func (d *drpc) generateServerReceiver(method *protogen.Method) { + d.P("func (srv interface{}, ctx context.Context, in1, in2 interface{}) (" + d.Ident("storj.io/drpc", "Message") + ", error) {") + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + d.P("return srv.(", d.ServerIface(method.Parent), ").") + } else { + d.P("return nil, srv.(", d.ServerIface(method.Parent), ").") + } + d.P(method.GoName, "(") + + n := 1 + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + d.P("ctx,") + } + if !method.Desc.IsStreamingClient() { + d.P("in", n, ".(*", d.InputType(method), "),") + n++ + } + if method.Desc.IsStreamingServer() || method.Desc.IsStreamingClient() { + d.P("&", d.ServerStreamImpl(method), "{in", n, ".(", d.Ident("storj.io/drpc", "Stream"), ")},") + } + d.P(")") +} + +func (d *drpc) generateServerMethod(method *protogen.Method) { + genSend := method.Desc.IsStreamingServer() + genSendAndClose := !method.Desc.IsStreamingServer() + genRecv := method.Desc.IsStreamingClient() + + // Stream auxiliary types and methods. + d.P("type ", d.ServerStreamIface(method), " interface {") + d.P(d.Ident("storj.io/drpc", "Stream")) + if genSend { + d.P("Send(*", d.OutputType(method), ") error") + } + if genSendAndClose { + d.P("SendAndClose(*", d.OutputType(method), ") error") + } + if genRecv { + d.P("Recv() (*", d.InputType(method), ", error)") + } + d.P("}") + d.P() + + d.P("type ", d.ServerStreamImpl(method), " struct {") + d.P(d.Ident("storj.io/drpc", "Stream")) + d.P("}") + d.P() + + d.P("func (x *", d.ServerStreamImpl(method), ") GetStream() drpc.Stream {") + d.P("return x.Stream") + d.P("}") + d.P() + + if genSend { + d.P("func (x *", d.ServerStreamImpl(method), ") Send(m *", d.OutputType(method), ") error {") + d.P("return x.MsgSend(m, ", d.EncodingName(), "{})") + d.P("}") + d.P() + } + + if genSendAndClose { + d.P("func (x *", d.ServerStreamImpl(method), ") SendAndClose(m *", d.OutputType(method), ") error {") + d.P("if err := x.MsgSend(m, ", d.EncodingName(), "{}); err != nil { return err }") + d.P("return x.CloseSend()") + d.P("}") + d.P() + } + + if genRecv { + d.P("func (x *", d.ServerStreamImpl(method), ") Recv() (*", d.InputType(method), ", error) {") + d.P("m := new(", d.InputType(method), ")") + d.P("if err := x.MsgRecv(m, ", d.EncodingName(), "{}); err != nil { return nil, err }") + d.P("return m, nil") + d.P("}") + d.P() + + d.P("func (x *", d.ServerStreamImpl(method), ") RecvMsg(m *", d.InputType(method), ") error {") + d.P("return x.MsgRecv(m, ", d.EncodingName(), "{})") + d.P("}") + d.P() + } +} diff --git a/server/cmd/protoc-gen-go-orb/orbgrpc/README.md b/server/cmd/protoc-gen-go-orb/orbgrpc/README.md new file mode 100644 index 00000000..41462c8e --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orbgrpc/README.md @@ -0,0 +1,5 @@ +# protoc-gen-go-orb -- grpc + +## This is based on + +[#1013847d133eccaa317d65ddf99c143930ee4a47](https://github.com/grpc/grpc-go/tree/1013847d133eccaa317d65ddf99c143930ee4a47/cmd/protoc-gen-go-orb-grpc) diff --git a/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go b/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go new file mode 100644 index 00000000..b9973799 --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go @@ -0,0 +1,476 @@ +/* + * + * Copyright 2020 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Package orbgrpc is copy&pasted version from protoc-gen-go-grpc. +// +//nolint:lll,wsl,gochecknoglobals,varnamelen,funlen,gocritic,revive +package orbgrpc + +import ( + "fmt" + "strconv" + "strings" + + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" +) + +// These get set by protoc-gen-go-orb. +// +//nolint:gochecknoglobals +var ( + Version = "" + RequireUnimplemented *bool + UseGenericStreams *bool +) + +const ( + contextPackage = protogen.GoImportPath("context") + grpcPackage = protogen.GoImportPath("google.golang.org/grpc") + codesPackage = protogen.GoImportPath("google.golang.org/grpc/codes") + statusPackage = protogen.GoImportPath("google.golang.org/grpc/status") +) + +type serviceGenerateHelperInterface interface { + formatFullMethodSymbol(service *protogen.Service, method *protogen.Method) string + genFullMethods(g *protogen.GeneratedFile, service *protogen.Service) + generateClientStruct(g *protogen.GeneratedFile, clientName string) + generateNewClientDefinitions(g *protogen.GeneratedFile, service *protogen.Service, clientName string) + generateUnimplementedServerType(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) + generateServerFunctions(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, serverType string, serviceDescVar string) + formatHandlerFuncName(service *protogen.Service, hname string) string +} + +type serviceGenerateHelper struct{} + +func (serviceGenerateHelper) formatFullMethodSymbol(service *protogen.Service, method *protogen.Method) string { + return fmt.Sprintf("%s_%s_FullMethodName", service.GoName, method.GoName) +} + +func (serviceGenerateHelper) genFullMethods(g *protogen.GeneratedFile, service *protogen.Service) { + if len(service.Methods) == 0 { + return + } + + g.P("const (") + for _, method := range service.Methods { + fmSymbol := helper.formatFullMethodSymbol(service, method) + fmName := fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name()) + g.P(fmSymbol, ` = "`, fmName, `"`) + } + g.P(")") + g.P() +} + +func (serviceGenerateHelper) generateClientStruct(g *protogen.GeneratedFile, clientName string) { + g.P("type ", unexport(clientName), " struct {") + g.P("cc ", grpcPackage.Ident("ClientConnInterface")) + g.P("}") + g.P() +} + +func (serviceGenerateHelper) generateNewClientDefinitions(g *protogen.GeneratedFile, service *protogen.Service, clientName string) { + g.P("return &", unexport(clientName), "{cc}") +} + +func (serviceGenerateHelper) generateUnimplementedServerType(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) { + serverType := service.GoName + "Server" + mustOrShould := "must" + if !*RequireUnimplemented { + mustOrShould = "should" + } + // Server Unimplemented struct for forward compatibility. + g.P("// Unimplemented", serverType, " ", mustOrShould, " be embedded to have") + g.P("// forward compatible implementations.") + g.P("//") + g.P("// NOTE: this should be embedded by value instead of pointer to avoid a nil") + g.P("// pointer dereference when methods are called.") + g.P("type Unimplemented", serverType, " struct {}") + g.P() + for _, method := range service.Methods { + nilArg := "" + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + nilArg = "nil," + } + g.P("func (Unimplemented", serverType, ") ", serverSignature(g, method), "{") + g.P("return ", nilArg, statusPackage.Ident("Errorf"), "(", codesPackage.Ident("Unimplemented"), `, "method `, method.GoName, ` not implemented")`) + g.P("}") + } + if *RequireUnimplemented { + g.P("func (Unimplemented", serverType, ") mustEmbedUnimplemented", serverType, "() {}") + } + g.P("func (Unimplemented", serverType, ") testEmbeddedByValue() {}") + g.P() +} + +func (serviceGenerateHelper) generateServerFunctions(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, serverType string, serviceDescVar string) { + // Server handler implementations. + handlerNames := make([]string, 0, len(service.Methods)) + for _, method := range service.Methods { + hname := genServerMethod(gen, file, g, method, func(hname string) string { + return hname + }) + handlerNames = append(handlerNames, hname) + } + genServiceDesc(file, g, serviceDescVar, serverType, service, handlerNames) +} + +func (serviceGenerateHelper) formatHandlerFuncName(service *protogen.Service, hname string) string { + return hname +} + +var helper serviceGenerateHelperInterface = serviceGenerateHelper{} + +// FileDescriptorProto.package field number. +const fileDescriptorProtoPackageFieldNumber = 2 + +// FileDescriptorProto.syntax field number. +const fileDescriptorProtoSyntaxFieldNumber = 12 + +// GenerateFile generates a _orb-grpc.pb.go file containing gRPC service definitions. +func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { + if len(file.Services) == 0 { + return nil + } + filename := file.GeneratedFilenamePrefix + "_orb-grpc.pb.go" + g := gen.NewGeneratedFile(filename, file.GoImportPath) + // Attach all comments associated with the syntax field. + genLeadingComments(g, file.Desc.SourceLocations().ByPath(protoreflect.SourcePath{fileDescriptorProtoSyntaxFieldNumber})) + g.P("// Code generated by protoc-gen-go-orb-grpc. DO NOT EDIT.") + g.P("// versions:") + g.P("// - protoc-gen-go-orb v", Version) + g.P("// - protoc ", protocVersion(gen)) + if file.Proto.GetOptions().GetDeprecated() { + g.P("// ", file.Desc.Path(), " is a deprecated file.") + } else { + g.P("// source: ", file.Desc.Path()) + } + g.P() + // Attach all comments associated with the package field. + genLeadingComments(g, file.Desc.SourceLocations().ByPath(protoreflect.SourcePath{fileDescriptorProtoPackageFieldNumber})) + g.P("package ", file.GoPackageName) + g.P() + generateFileContent(gen, file, g) + return g +} + +func protocVersion(gen *protogen.Plugin) string { + v := gen.Request.GetCompilerVersion() + if v == nil { + return "(unknown)" + } + var suffix string + if s := v.GetSuffix(); s != "" { + suffix = "-" + s + } + return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix) +} + +// generateFileContent generates the gRPC service definitions, excluding the package statement. +func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) { + if len(file.Services) == 0 { + return + } + + g.P("// This is a compile-time assertion to ensure that this generated file") + g.P("// is compatible with the grpc package it is being compiled against.") + if *UseGenericStreams { + g.P("// Requires gRPC-Go v1.64.0 or later.") + g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion9")) + } else { + g.P("// Requires gRPC-Go v1.62.0 or later.") + g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion8")) // When changing, update version number above. + } + g.P() + for _, service := range file.Services { + genService(gen, file, g, service) + } +} + +// genServiceComments copies the comments from the RPC proto definitions +// to the corresponding generated interface file. +func genServiceComments(g *protogen.GeneratedFile, service *protogen.Service) { + if service.Comments.Leading != "" { + // Add empty comment line to attach this service's comments to + // the godoc comments previously output for all services. + g.P("//") + g.P(strings.TrimSpace(service.Comments.Leading.String())) + } +} + +func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) { + // Full methods constants. + helper.genFullMethods(g, service) + + // Copy comments from proto file. + genServiceComments(g, service) + + if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { + g.P("//") + g.P(deprecationComment) + } + + mustOrShould := "must" + if !*RequireUnimplemented { + mustOrShould = "should" + } + + // Server interface. + serverType := service.GoName + "Server" + g.P("// ", serverType, " is the server API for ", service.GoName, " service.") + g.P("// All implementations ", mustOrShould, " embed Unimplemented", serverType) + g.P("// for forward compatibility.") + + // Copy comments from proto file. + genServiceComments(g, service) + + if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { + g.P("//") + g.P(deprecationComment) + } + g.AnnotateSymbol(serverType, protogen.Annotation{Location: service.Location}) + g.P("type ", serverType, " interface {") + for _, method := range service.Methods { + g.AnnotateSymbol(serverType+"."+method.GoName, protogen.Annotation{Location: method.Location}) + if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() { + g.P(deprecationComment) + } + g.P(method.Comments.Leading, + serverSignature(g, method)) + } + if *RequireUnimplemented { + g.P("mustEmbedUnimplemented", serverType, "()") + } + g.P("}") + g.P() + + // Server Unimplemented struct for forward compatibility. + helper.generateUnimplementedServerType(gen, file, g, service) + + // Unsafe Server interface to opt-out of forward compatibility. + g.P("// Unsafe", serverType, " may be embedded to opt out of forward compatibility for this service.") + g.P("// Use of this interface is not recommended, as added methods to ", serverType, " will") + g.P("// result in compilation errors.") + g.P("type Unsafe", serverType, " interface {") + g.P("mustEmbedUnimplemented", serverType, "()") + g.P("}") + + // Server registration. + if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { + g.P(deprecationComment) + } + serviceDescVar := service.GoName + "_ServiceDesc" + g.P("func register", service.GoName, "GRPCHandler(s ", grpcPackage.Ident("ServiceRegistrar"), ", srv ", serverType, ") {") + g.P("// If the following call panics, it indicates Unimplemented", serverType, " was") + g.P("// embedded by pointer and is nil. This will cause panics if an") + g.P("// unimplemented method is ever invoked, so we test this at initialization") + g.P("// time to prevent it from happening at runtime later due to I/O.") + g.P("if t, ok := srv.(interface { testEmbeddedByValue() }); ok {") + g.P("t.testEmbeddedByValue()") + g.P("}") + g.P("s.RegisterService(&", serviceDescVar, `, srv)`) + g.P("}") + g.P() + + helper.generateServerFunctions(gen, file, g, service, serverType, serviceDescVar) +} + +func serverSignature(g *protogen.GeneratedFile, method *protogen.Method) string { + var reqArgs []string + ret := "error" + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + reqArgs = append(reqArgs, g.QualifiedGoIdent(contextPackage.Ident("Context"))) + ret = "(*" + g.QualifiedGoIdent(method.Output.GoIdent) + ", error)" + } + if !method.Desc.IsStreamingClient() { + reqArgs = append(reqArgs, "*"+g.QualifiedGoIdent(method.Input.GoIdent)) + } + if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { + if *UseGenericStreams { + reqArgs = append(reqArgs, serverStreamInterface(g, method)) + } else { + reqArgs = append(reqArgs, method.Parent.GoName+"_"+method.GoName+"Server") + } + } + return method.GoName + "(" + strings.Join(reqArgs, ", ") + ") " + ret +} + +func genServiceDesc(file *protogen.File, g *protogen.GeneratedFile, serviceDescVar string, serverType string, service *protogen.Service, handlerNames []string) { + // Service descriptor. + g.P("// ", serviceDescVar, " is the ", grpcPackage.Ident("ServiceDesc"), " for ", service.GoName, " service.") + g.P("// It's only intended for direct use with ", grpcPackage.Ident("RegisterService"), ",") + g.P("// and not to be introspected or modified (even as a copy)") + g.P("var ", serviceDescVar, " = ", grpcPackage.Ident("ServiceDesc"), " {") + g.P("ServiceName: ", strconv.Quote(string(service.Desc.FullName())), ",") + g.P("HandlerType: (*", serverType, ")(nil),") + g.P("Methods: []", grpcPackage.Ident("MethodDesc"), "{") + for i, method := range service.Methods { + if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { + continue + } + g.P("{") + g.P("MethodName: ", strconv.Quote(string(method.Desc.Name())), ",") + g.P("Handler: ", handlerNames[i], ",") + g.P("},") + } + g.P("},") + g.P("Streams: []", grpcPackage.Ident("StreamDesc"), "{") + for i, method := range service.Methods { + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + continue + } + g.P("{") + g.P("StreamName: ", strconv.Quote(string(method.Desc.Name())), ",") + g.P("Handler: ", handlerNames[i], ",") + if method.Desc.IsStreamingServer() { + g.P("ServerStreams: true,") + } + if method.Desc.IsStreamingClient() { + g.P("ClientStreams: true,") + } + g.P("},") + } + g.P("},") + g.P("Metadata: \"", file.Desc.Path(), "\",") + g.P("}") + g.P() +} + +func serverStreamInterface(g *protogen.GeneratedFile, method *protogen.Method) string { + typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) + if method.Desc.IsStreamingClient() && method.Desc.IsStreamingServer() { + return g.QualifiedGoIdent(grpcPackage.Ident("BidiStreamingServer")) + "[" + typeParam + "]" + } else if method.Desc.IsStreamingClient() { + return g.QualifiedGoIdent(grpcPackage.Ident("ClientStreamingServer")) + "[" + typeParam + "]" + } else { // i.e. if method.Desc.IsStreamingServer() + return g.QualifiedGoIdent(grpcPackage.Ident("ServerStreamingServer")) + "[" + g.QualifiedGoIdent(method.Output.GoIdent) + "]" + } +} + +func genServerMethod(_ *protogen.Plugin, _ *protogen.File, g *protogen.GeneratedFile, method *protogen.Method, hnameFuncNameFormatter func(string) string) string { + service := method.Parent + hname := fmt.Sprintf("_%s_%s_Handler", service.GoName, method.GoName) + + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + g.P("func ", hnameFuncNameFormatter(hname), "(srv interface{}, ctx ", contextPackage.Ident("Context"), ", dec func(interface{}) error, interceptor ", grpcPackage.Ident("UnaryServerInterceptor"), ") (interface{}, error) {") + g.P("in := new(", method.Input.GoIdent, ")") + g.P("if err := dec(in); err != nil { return nil, err }") + g.P("if interceptor == nil { return srv.(", service.GoName, "Server).", method.GoName, "(ctx, in) }") + g.P("info := &", grpcPackage.Ident("UnaryServerInfo"), "{") + g.P("Server: srv,") + fmSymbol := helper.formatFullMethodSymbol(service, method) + g.P("FullMethod: ", fmSymbol, ",") + g.P("}") + g.P("handler := func(ctx ", contextPackage.Ident("Context"), ", req interface{}) (interface{}, error) {") + g.P("return srv.(", service.GoName, "Server).", method.GoName, "(ctx, req.(*", method.Input.GoIdent, "))") + g.P("}") + g.P("return interceptor(ctx, in, info, handler)") + g.P("}") + g.P() + return hname + } + + streamImpl := unexport(service.GoName) + method.GoName + "Server" + if *UseGenericStreams { + typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) + streamImpl = g.QualifiedGoIdent(grpcPackage.Ident("GenericServerStream")) + "[" + typeParam + "]" + } + + g.P("func ", hnameFuncNameFormatter(hname), "(srv interface{}, stream ", grpcPackage.Ident("ServerStream"), ") error {") + if !method.Desc.IsStreamingClient() { + g.P("m := new(", method.Input.GoIdent, ")") + g.P("if err := stream.RecvMsg(m); err != nil { return err }") + g.P("return srv.(", service.GoName, "Server).", method.GoName, "(m, &", streamImpl, "{ServerStream: stream})") + } else { + g.P("return srv.(", service.GoName, "Server).", method.GoName, "(&", streamImpl, "{ServerStream: stream})") + } + g.P("}") + g.P() + + // Auxiliary types aliases, for backwards compatibility. + if *UseGenericStreams { + g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.") + g.P("type ", service.GoName, "_", method.GoName, "Server = ", serverStreamInterface(g, method)) + g.P() + return hname + } + + // Stream auxiliary types and methods, if we're not taking advantage of the + // pre-implemented generic types and their methods. + genSend := method.Desc.IsStreamingServer() + genSendAndClose := !method.Desc.IsStreamingServer() + genRecv := method.Desc.IsStreamingClient() + + g.P("type ", service.GoName, "_", method.GoName, "Server interface {") + if genSend { + g.P("Send(*", method.Output.GoIdent, ") error") + } + if genSendAndClose { + g.P("SendAndClose(*", method.Output.GoIdent, ") error") + } + if genRecv { + g.P("Recv() (*", method.Input.GoIdent, ", error)") + } + g.P(grpcPackage.Ident("ServerStream")) + g.P("}") + g.P() + + g.P("type ", streamImpl, " struct {") + g.P(grpcPackage.Ident("ServerStream")) + g.P("}") + g.P() + + if genSend { + g.P("func (x *", streamImpl, ") Send(m *", method.Output.GoIdent, ") error {") + g.P("return x.ServerStream.SendMsg(m)") + g.P("}") + g.P() + } + if genSendAndClose { + g.P("func (x *", streamImpl, ") SendAndClose(m *", method.Output.GoIdent, ") error {") + g.P("return x.ServerStream.SendMsg(m)") + g.P("}") + g.P() + } + if genRecv { + g.P("func (x *", streamImpl, ") Recv() (*", method.Input.GoIdent, ", error) {") + g.P("m := new(", method.Input.GoIdent, ")") + g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }") + g.P("return m, nil") + g.P("}") + g.P() + } + + return hname +} + +func genLeadingComments(g *protogen.GeneratedFile, loc protoreflect.SourceLocation) { + for _, s := range loc.LeadingDetachedComments { + g.P(protogen.Comments(s)) + g.P() + } + if s := loc.LeadingComments; s != "" { + g.P(protogen.Comments(s)) + g.P() + } +} + +const deprecationComment = "// Deprecated: Do not use." + +func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] } diff --git a/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.diff b/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.diff new file mode 100644 index 00000000..ef721f08 --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.diff @@ -0,0 +1,145 @@ +--- grpc.go 2024-08-12 16:39:17.924741238 +0200 ++++ orb_grpc/grpc.go 2024-08-12 16:38:19.277375665 +0200 +@@ -16,7 +16,7 @@ + * + */ + +-package main ++package orb_grpc + + import ( + "fmt" +@@ -28,6 +28,10 @@ + "google.golang.org/protobuf/types/descriptorpb" + ) + ++var Version = "" // set by protoc-gen-go-orb ++var RequireUnimplemented *bool // set by protoc-gen-go-orb ++var UseGenericStreams *bool // set by protoc-gen-go-orb ++ + const ( + contextPackage = protogen.GoImportPath("context") + grpcPackage = protogen.GoImportPath("google.golang.org/grpc") +@@ -80,7 +84,7 @@ + func (serviceGenerateHelper) generateUnimplementedServerType(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) { + serverType := service.GoName + "Server" + mustOrShould := "must" +- if !*requireUnimplemented { ++ if !*RequireUnimplemented { + mustOrShould = "should" + } + // Server Unimplemented struct for forward compatibility. +@@ -100,7 +104,7 @@ + g.P("return ", nilArg, statusPackage.Ident("Errorf"), "(", codesPackage.Ident("Unimplemented"), `, "method `, method.GoName, ` not implemented")`) + g.P("}") + } +- if *requireUnimplemented { ++ if *RequireUnimplemented { + g.P("func (Unimplemented", serverType, ") mustEmbedUnimplemented", serverType, "() {}") + } + g.P("func (Unimplemented", serverType, ") testEmbeddedByValue() {}") +@@ -131,18 +135,18 @@ + // FileDescriptorProto.syntax field number + const fileDescriptorProtoSyntaxFieldNumber = 12 + +-// generateFile generates a _grpc.pb.go file containing gRPC service definitions. +-func generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { ++// GenerateFile generates a _orb-grpc.pb.go file containing gRPC service definitions. ++func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { + if len(file.Services) == 0 { + return nil + } +- filename := file.GeneratedFilenamePrefix + "_grpc.pb.go" ++ filename := file.GeneratedFilenamePrefix + "_orb-grpc.pb.go" + g := gen.NewGeneratedFile(filename, file.GoImportPath) + // Attach all comments associated with the syntax field. + genLeadingComments(g, file.Desc.SourceLocations().ByPath(protoreflect.SourcePath{fileDescriptorProtoSyntaxFieldNumber})) +- g.P("// Code generated by protoc-gen-go-grpc. DO NOT EDIT.") ++ g.P("// Code generated by protoc-gen-go-orb-grpc. DO NOT EDIT.") + g.P("// versions:") +- g.P("// - protoc-gen-go-grpc v", version) ++ g.P("// - protoc-gen-go-orb v", Version) + g.P("// - protoc ", protocVersion(gen)) + if file.Proto.GetOptions().GetDeprecated() { + g.P("// ", file.Desc.Path(), " is a deprecated file.") +@@ -178,7 +182,7 @@ + + g.P("// This is a compile-time assertion to ensure that this generated file") + g.P("// is compatible with the grpc package it is being compiled against.") +- if *useGenericStreams { ++ if *UseGenericStreams { + g.P("// Requires gRPC-Go v1.64.0 or later.") + g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion9")) + } else { +@@ -260,7 +264,7 @@ + } + + mustOrShould := "must" +- if !*requireUnimplemented { ++ if !*RequireUnimplemented { + mustOrShould = "should" + } + +@@ -287,7 +291,7 @@ + g.P(method.Comments.Leading, + serverSignature(g, method)) + } +- if *requireUnimplemented { ++ if *RequireUnimplemented { + g.P("mustEmbedUnimplemented", serverType, "()") + } + g.P("}") +@@ -333,7 +337,7 @@ + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + s += "*" + g.QualifiedGoIdent(method.Output.GoIdent) + } else { +- if *useGenericStreams { ++ if *UseGenericStreams { + s += clientStreamInterface(g, method) + } else { + s += method.Parent.GoName + "_" + method.GoName + "Client" +@@ -374,7 +378,7 @@ + } + + streamImpl := unexport(service.GoName) + method.GoName + "Client" +- if *useGenericStreams { ++ if *UseGenericStreams { + typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) + streamImpl = g.QualifiedGoIdent(grpcPackage.Ident("GenericClientStream")) + "[" + typeParam + "]" + } +@@ -392,7 +396,7 @@ + g.P() + + // Auxiliary types aliases, for backwards compatibility. +- if *useGenericStreams { ++ if *UseGenericStreams { + g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.") + g.P("type ", service.GoName, "_", method.GoName, "Client = ", clientStreamInterface(g, method)) + g.P() +@@ -460,7 +464,7 @@ + reqArgs = append(reqArgs, "*"+g.QualifiedGoIdent(method.Input.GoIdent)) + } + if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { +- if *useGenericStreams { ++ if *UseGenericStreams { + reqArgs = append(reqArgs, serverStreamInterface(g, method)) + } else { + reqArgs = append(reqArgs, method.Parent.GoName+"_"+method.GoName+"Server") +@@ -545,7 +549,7 @@ + } + + streamImpl := unexport(service.GoName) + method.GoName + "Server" +- if *useGenericStreams { ++ if *UseGenericStreams { + typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) + streamImpl = g.QualifiedGoIdent(grpcPackage.Ident("GenericServerStream")) + "[" + typeParam + "]" + } +@@ -562,7 +566,7 @@ + g.P() + + // Auxiliary types aliases, for backwards compatibility. +- if *useGenericStreams { ++ if *UseGenericStreams { + g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.") + g.P("type ", service.GoName, "_", method.GoName, "Server = ", serverStreamInterface(g, method)) + g.P() diff --git a/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.patch b/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.patch new file mode 100644 index 00000000..5c3466c5 --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.patch @@ -0,0 +1,338 @@ +--- grpc.go.upstream 2024-08-12 16:40:01.015276253 +0200 ++++ grpc.go 2024-08-12 22:02:36.178076565 +0200 +@@ -16,7 +16,10 @@ + * + */ + +-package main ++// Package orbgrpc is copy&pasted version from protoc-gen-go-grpc. ++// ++//nolint:lll,wsl,gochecknoglobals,varnamelen,funlen,gocritic,revive ++package orbgrpc + + import ( + "fmt" +@@ -28,6 +31,15 @@ + "google.golang.org/protobuf/types/descriptorpb" + ) + ++// These get set by protoc-gen-go-orb. ++// ++//nolint:gochecknoglobals ++var ( ++ Version = "" ++ RequireUnimplemented *bool ++ UseGenericStreams *bool ++) ++ + const ( + contextPackage = protogen.GoImportPath("context") + grpcPackage = protogen.GoImportPath("google.golang.org/grpc") +@@ -80,7 +92,7 @@ + func (serviceGenerateHelper) generateUnimplementedServerType(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) { + serverType := service.GoName + "Server" + mustOrShould := "must" +- if !*requireUnimplemented { ++ if !*RequireUnimplemented { + mustOrShould = "should" + } + // Server Unimplemented struct for forward compatibility. +@@ -100,7 +112,7 @@ + g.P("return ", nilArg, statusPackage.Ident("Errorf"), "(", codesPackage.Ident("Unimplemented"), `, "method `, method.GoName, ` not implemented")`) + g.P("}") + } +- if *requireUnimplemented { ++ if *RequireUnimplemented { + g.P("func (Unimplemented", serverType, ") mustEmbedUnimplemented", serverType, "() {}") + } + g.P("func (Unimplemented", serverType, ") testEmbeddedByValue() {}") +@@ -125,24 +137,24 @@ + + var helper serviceGenerateHelperInterface = serviceGenerateHelper{} + +-// FileDescriptorProto.package field number ++// FileDescriptorProto.package field number. + const fileDescriptorProtoPackageFieldNumber = 2 + +-// FileDescriptorProto.syntax field number ++// FileDescriptorProto.syntax field number. + const fileDescriptorProtoSyntaxFieldNumber = 12 + +-// generateFile generates a _grpc.pb.go file containing gRPC service definitions. +-func generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { ++// GenerateFile generates a _orb-grpc.pb.go file containing gRPC service definitions. ++func GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { + if len(file.Services) == 0 { + return nil + } +- filename := file.GeneratedFilenamePrefix + "_grpc.pb.go" ++ filename := file.GeneratedFilenamePrefix + "_orb-grpc.pb.go" + g := gen.NewGeneratedFile(filename, file.GoImportPath) + // Attach all comments associated with the syntax field. + genLeadingComments(g, file.Desc.SourceLocations().ByPath(protoreflect.SourcePath{fileDescriptorProtoSyntaxFieldNumber})) +- g.P("// Code generated by protoc-gen-go-grpc. DO NOT EDIT.") ++ g.P("// Code generated by protoc-gen-go-orb-grpc. DO NOT EDIT.") + g.P("// versions:") +- g.P("// - protoc-gen-go-grpc v", version) ++ g.P("// - protoc-gen-go-orb v", Version) + g.P("// - protoc ", protocVersion(gen)) + if file.Proto.GetOptions().GetDeprecated() { + g.P("// ", file.Desc.Path(), " is a deprecated file.") +@@ -178,7 +190,7 @@ + + g.P("// This is a compile-time assertion to ensure that this generated file") + g.P("// is compatible with the grpc package it is being compiled against.") +- if *useGenericStreams { ++ if *UseGenericStreams { + g.P("// Requires gRPC-Go v1.64.0 or later.") + g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion9")) + } else { +@@ -206,13 +218,6 @@ + // Full methods constants. + helper.genFullMethods(g, service) + +- // Client interface. +- clientName := service.GoName + "Client" +- +- g.P("// ", clientName, " is the client API for ", service.GoName, " service.") +- g.P("//") +- g.P("// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.") +- + // Copy comments from proto file. + genServiceComments(g, service) + +@@ -220,47 +225,9 @@ + g.P("//") + g.P(deprecationComment) + } +- g.AnnotateSymbol(clientName, protogen.Annotation{Location: service.Location}) +- g.P("type ", clientName, " interface {") +- for _, method := range service.Methods { +- g.AnnotateSymbol(clientName+"."+method.GoName, protogen.Annotation{Location: method.Location}) +- if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() { +- g.P(deprecationComment) +- } +- g.P(method.Comments.Leading, +- clientSignature(g, method)) +- } +- g.P("}") +- g.P() +- +- // Client structure. +- helper.generateClientStruct(g, clientName) +- +- // NewClient factory. +- if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { +- g.P(deprecationComment) +- } +- g.P("func New", clientName, " (cc ", grpcPackage.Ident("ClientConnInterface"), ") ", clientName, " {") +- helper.generateNewClientDefinitions(g, service, clientName) +- g.P("}") +- g.P() +- +- var methodIndex, streamIndex int +- // Client method implementations. +- for _, method := range service.Methods { +- if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { +- // Unary RPC method +- genClientMethod(gen, file, g, method, methodIndex) +- methodIndex++ +- } else { +- // Streaming RPC method +- genClientMethod(gen, file, g, method, streamIndex) +- streamIndex++ +- } +- } + + mustOrShould := "must" +- if !*requireUnimplemented { ++ if !*RequireUnimplemented { + mustOrShould = "should" + } + +@@ -287,7 +254,7 @@ + g.P(method.Comments.Leading, + serverSignature(g, method)) + } +- if *requireUnimplemented { ++ if *RequireUnimplemented { + g.P("mustEmbedUnimplemented", serverType, "()") + } + g.P("}") +@@ -309,7 +276,7 @@ + g.P(deprecationComment) + } + serviceDescVar := service.GoName + "_ServiceDesc" +- g.P("func Register", service.GoName, "Server(s ", grpcPackage.Ident("ServiceRegistrar"), ", srv ", serverType, ") {") ++ g.P("func register", service.GoName, "GRPCHandler(s ", grpcPackage.Ident("ServiceRegistrar"), ", srv ", serverType, ") {") + g.P("// If the following call panics, it indicates Unimplemented", serverType, " was") + g.P("// embedded by pointer and is nil. This will cause panics if an") + g.P("// unimplemented method is ever invoked, so we test this at initialization") +@@ -324,131 +291,6 @@ + helper.generateServerFunctions(gen, file, g, service, serverType, serviceDescVar) + } + +-func clientSignature(g *protogen.GeneratedFile, method *protogen.Method) string { +- s := method.GoName + "(ctx " + g.QualifiedGoIdent(contextPackage.Ident("Context")) +- if !method.Desc.IsStreamingClient() { +- s += ", in *" + g.QualifiedGoIdent(method.Input.GoIdent) +- } +- s += ", opts ..." + g.QualifiedGoIdent(grpcPackage.Ident("CallOption")) + ") (" +- if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { +- s += "*" + g.QualifiedGoIdent(method.Output.GoIdent) +- } else { +- if *useGenericStreams { +- s += clientStreamInterface(g, method) +- } else { +- s += method.Parent.GoName + "_" + method.GoName + "Client" +- } +- } +- s += ", error)" +- return s +-} +- +-func clientStreamInterface(g *protogen.GeneratedFile, method *protogen.Method) string { +- typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) +- if method.Desc.IsStreamingClient() && method.Desc.IsStreamingServer() { +- return g.QualifiedGoIdent(grpcPackage.Ident("BidiStreamingClient")) + "[" + typeParam + "]" +- } else if method.Desc.IsStreamingClient() { +- return g.QualifiedGoIdent(grpcPackage.Ident("ClientStreamingClient")) + "[" + typeParam + "]" +- } else { // i.e. if method.Desc.IsStreamingServer() +- return g.QualifiedGoIdent(grpcPackage.Ident("ServerStreamingClient")) + "[" + g.QualifiedGoIdent(method.Output.GoIdent) + "]" +- } +-} +- +-func genClientMethod(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, method *protogen.Method, index int) { +- service := method.Parent +- fmSymbol := helper.formatFullMethodSymbol(service, method) +- +- if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() { +- g.P(deprecationComment) +- } +- g.P("func (c *", unexport(service.GoName), "Client) ", clientSignature(g, method), "{") +- g.P("cOpts := append([]", grpcPackage.Ident("CallOption"), "{", grpcPackage.Ident("StaticMethod()"), "}, opts...)") +- if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { +- g.P("out := new(", method.Output.GoIdent, ")") +- g.P(`err := c.cc.Invoke(ctx, `, fmSymbol, `, in, out, cOpts...)`) +- g.P("if err != nil { return nil, err }") +- g.P("return out, nil") +- g.P("}") +- g.P() +- return +- } +- +- streamImpl := unexport(service.GoName) + method.GoName + "Client" +- if *useGenericStreams { +- typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) +- streamImpl = g.QualifiedGoIdent(grpcPackage.Ident("GenericClientStream")) + "[" + typeParam + "]" +- } +- +- serviceDescVar := service.GoName + "_ServiceDesc" +- g.P("stream, err := c.cc.NewStream(ctx, &", serviceDescVar, ".Streams[", index, `], `, fmSymbol, `, cOpts...)`) +- g.P("if err != nil { return nil, err }") +- g.P("x := &", streamImpl, "{ClientStream: stream}") +- if !method.Desc.IsStreamingClient() { +- g.P("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }") +- g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }") +- } +- g.P("return x, nil") +- g.P("}") +- g.P() +- +- // Auxiliary types aliases, for backwards compatibility. +- if *useGenericStreams { +- g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.") +- g.P("type ", service.GoName, "_", method.GoName, "Client = ", clientStreamInterface(g, method)) +- g.P() +- return +- } +- +- // Stream auxiliary types and methods, if we're not taking advantage of the +- // pre-implemented generic types and their methods. +- genSend := method.Desc.IsStreamingClient() +- genRecv := method.Desc.IsStreamingServer() +- genCloseAndRecv := !method.Desc.IsStreamingServer() +- +- g.P("type ", service.GoName, "_", method.GoName, "Client interface {") +- if genSend { +- g.P("Send(*", method.Input.GoIdent, ") error") +- } +- if genRecv { +- g.P("Recv() (*", method.Output.GoIdent, ", error)") +- } +- if genCloseAndRecv { +- g.P("CloseAndRecv() (*", method.Output.GoIdent, ", error)") +- } +- g.P(grpcPackage.Ident("ClientStream")) +- g.P("}") +- g.P() +- +- g.P("type ", streamImpl, " struct {") +- g.P(grpcPackage.Ident("ClientStream")) +- g.P("}") +- g.P() +- +- if genSend { +- g.P("func (x *", streamImpl, ") Send(m *", method.Input.GoIdent, ") error {") +- g.P("return x.ClientStream.SendMsg(m)") +- g.P("}") +- g.P() +- } +- if genRecv { +- g.P("func (x *", streamImpl, ") Recv() (*", method.Output.GoIdent, ", error) {") +- g.P("m := new(", method.Output.GoIdent, ")") +- g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }") +- g.P("return m, nil") +- g.P("}") +- g.P() +- } +- if genCloseAndRecv { +- g.P("func (x *", streamImpl, ") CloseAndRecv() (*", method.Output.GoIdent, ", error) {") +- g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }") +- g.P("m := new(", method.Output.GoIdent, ")") +- g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }") +- g.P("return m, nil") +- g.P("}") +- g.P() +- } +-} +- + func serverSignature(g *protogen.GeneratedFile, method *protogen.Method) string { + var reqArgs []string + ret := "error" +@@ -460,7 +302,7 @@ + reqArgs = append(reqArgs, "*"+g.QualifiedGoIdent(method.Input.GoIdent)) + } + if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { +- if *useGenericStreams { ++ if *UseGenericStreams { + reqArgs = append(reqArgs, serverStreamInterface(g, method)) + } else { + reqArgs = append(reqArgs, method.Parent.GoName+"_"+method.GoName+"Server") +@@ -521,7 +363,7 @@ + } + } + +-func genServerMethod(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, method *protogen.Method, hnameFuncNameFormatter func(string) string) string { ++func genServerMethod(_ *protogen.Plugin, _ *protogen.File, g *protogen.GeneratedFile, method *protogen.Method, hnameFuncNameFormatter func(string) string) string { + service := method.Parent + hname := fmt.Sprintf("_%s_%s_Handler", service.GoName, method.GoName) + +@@ -545,7 +387,7 @@ + } + + streamImpl := unexport(service.GoName) + method.GoName + "Server" +- if *useGenericStreams { ++ if *UseGenericStreams { + typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) + streamImpl = g.QualifiedGoIdent(grpcPackage.Ident("GenericServerStream")) + "[" + typeParam + "]" + } +@@ -562,7 +404,7 @@ + g.P() + + // Auxiliary types aliases, for backwards compatibility. +- if *useGenericStreams { ++ if *UseGenericStreams { + g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.") + g.P("type ", service.GoName, "_", method.GoName, "Server = ", serverStreamInterface(g, method)) + g.P() diff --git a/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.upstream b/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.upstream new file mode 100644 index 00000000..2321c8d9 --- /dev/null +++ b/server/cmd/protoc-gen-go-orb/orbgrpc/grpc.go.upstream @@ -0,0 +1,634 @@ +/* + * + * Copyright 2020 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package main + +import ( + "fmt" + "strconv" + "strings" + + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" +) + +const ( + contextPackage = protogen.GoImportPath("context") + grpcPackage = protogen.GoImportPath("google.golang.org/grpc") + codesPackage = protogen.GoImportPath("google.golang.org/grpc/codes") + statusPackage = protogen.GoImportPath("google.golang.org/grpc/status") +) + +type serviceGenerateHelperInterface interface { + formatFullMethodSymbol(service *protogen.Service, method *protogen.Method) string + genFullMethods(g *protogen.GeneratedFile, service *protogen.Service) + generateClientStruct(g *protogen.GeneratedFile, clientName string) + generateNewClientDefinitions(g *protogen.GeneratedFile, service *protogen.Service, clientName string) + generateUnimplementedServerType(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) + generateServerFunctions(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, serverType string, serviceDescVar string) + formatHandlerFuncName(service *protogen.Service, hname string) string +} + +type serviceGenerateHelper struct{} + +func (serviceGenerateHelper) formatFullMethodSymbol(service *protogen.Service, method *protogen.Method) string { + return fmt.Sprintf("%s_%s_FullMethodName", service.GoName, method.GoName) +} + +func (serviceGenerateHelper) genFullMethods(g *protogen.GeneratedFile, service *protogen.Service) { + if len(service.Methods) == 0 { + return + } + + g.P("const (") + for _, method := range service.Methods { + fmSymbol := helper.formatFullMethodSymbol(service, method) + fmName := fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name()) + g.P(fmSymbol, ` = "`, fmName, `"`) + } + g.P(")") + g.P() +} + +func (serviceGenerateHelper) generateClientStruct(g *protogen.GeneratedFile, clientName string) { + g.P("type ", unexport(clientName), " struct {") + g.P("cc ", grpcPackage.Ident("ClientConnInterface")) + g.P("}") + g.P() +} + +func (serviceGenerateHelper) generateNewClientDefinitions(g *protogen.GeneratedFile, service *protogen.Service, clientName string) { + g.P("return &", unexport(clientName), "{cc}") +} + +func (serviceGenerateHelper) generateUnimplementedServerType(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) { + serverType := service.GoName + "Server" + mustOrShould := "must" + if !*requireUnimplemented { + mustOrShould = "should" + } + // Server Unimplemented struct for forward compatibility. + g.P("// Unimplemented", serverType, " ", mustOrShould, " be embedded to have") + g.P("// forward compatible implementations.") + g.P("//") + g.P("// NOTE: this should be embedded by value instead of pointer to avoid a nil") + g.P("// pointer dereference when methods are called.") + g.P("type Unimplemented", serverType, " struct {}") + g.P() + for _, method := range service.Methods { + nilArg := "" + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + nilArg = "nil," + } + g.P("func (Unimplemented", serverType, ") ", serverSignature(g, method), "{") + g.P("return ", nilArg, statusPackage.Ident("Errorf"), "(", codesPackage.Ident("Unimplemented"), `, "method `, method.GoName, ` not implemented")`) + g.P("}") + } + if *requireUnimplemented { + g.P("func (Unimplemented", serverType, ") mustEmbedUnimplemented", serverType, "() {}") + } + g.P("func (Unimplemented", serverType, ") testEmbeddedByValue() {}") + g.P() +} + +func (serviceGenerateHelper) generateServerFunctions(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service, serverType string, serviceDescVar string) { + // Server handler implementations. + handlerNames := make([]string, 0, len(service.Methods)) + for _, method := range service.Methods { + hname := genServerMethod(gen, file, g, method, func(hname string) string { + return hname + }) + handlerNames = append(handlerNames, hname) + } + genServiceDesc(file, g, serviceDescVar, serverType, service, handlerNames) +} + +func (serviceGenerateHelper) formatHandlerFuncName(service *protogen.Service, hname string) string { + return hname +} + +var helper serviceGenerateHelperInterface = serviceGenerateHelper{} + +// FileDescriptorProto.package field number +const fileDescriptorProtoPackageFieldNumber = 2 + +// FileDescriptorProto.syntax field number +const fileDescriptorProtoSyntaxFieldNumber = 12 + +// generateFile generates a _grpc.pb.go file containing gRPC service definitions. +func generateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { + if len(file.Services) == 0 { + return nil + } + filename := file.GeneratedFilenamePrefix + "_grpc.pb.go" + g := gen.NewGeneratedFile(filename, file.GoImportPath) + // Attach all comments associated with the syntax field. + genLeadingComments(g, file.Desc.SourceLocations().ByPath(protoreflect.SourcePath{fileDescriptorProtoSyntaxFieldNumber})) + g.P("// Code generated by protoc-gen-go-grpc. DO NOT EDIT.") + g.P("// versions:") + g.P("// - protoc-gen-go-grpc v", version) + g.P("// - protoc ", protocVersion(gen)) + if file.Proto.GetOptions().GetDeprecated() { + g.P("// ", file.Desc.Path(), " is a deprecated file.") + } else { + g.P("// source: ", file.Desc.Path()) + } + g.P() + // Attach all comments associated with the package field. + genLeadingComments(g, file.Desc.SourceLocations().ByPath(protoreflect.SourcePath{fileDescriptorProtoPackageFieldNumber})) + g.P("package ", file.GoPackageName) + g.P() + generateFileContent(gen, file, g) + return g +} + +func protocVersion(gen *protogen.Plugin) string { + v := gen.Request.GetCompilerVersion() + if v == nil { + return "(unknown)" + } + var suffix string + if s := v.GetSuffix(); s != "" { + suffix = "-" + s + } + return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix) +} + +// generateFileContent generates the gRPC service definitions, excluding the package statement. +func generateFileContent(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) { + if len(file.Services) == 0 { + return + } + + g.P("// This is a compile-time assertion to ensure that this generated file") + g.P("// is compatible with the grpc package it is being compiled against.") + if *useGenericStreams { + g.P("// Requires gRPC-Go v1.64.0 or later.") + g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion9")) + } else { + g.P("// Requires gRPC-Go v1.62.0 or later.") + g.P("const _ = ", grpcPackage.Ident("SupportPackageIsVersion8")) // When changing, update version number above. + } + g.P() + for _, service := range file.Services { + genService(gen, file, g, service) + } +} + +// genServiceComments copies the comments from the RPC proto definitions +// to the corresponding generated interface file. +func genServiceComments(g *protogen.GeneratedFile, service *protogen.Service) { + if service.Comments.Leading != "" { + // Add empty comment line to attach this service's comments to + // the godoc comments previously output for all services. + g.P("//") + g.P(strings.TrimSpace(service.Comments.Leading.String())) + } +} + +func genService(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, service *protogen.Service) { + // Full methods constants. + helper.genFullMethods(g, service) + + // Client interface. + clientName := service.GoName + "Client" + + g.P("// ", clientName, " is the client API for ", service.GoName, " service.") + g.P("//") + g.P("// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.") + + // Copy comments from proto file. + genServiceComments(g, service) + + if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { + g.P("//") + g.P(deprecationComment) + } + g.AnnotateSymbol(clientName, protogen.Annotation{Location: service.Location}) + g.P("type ", clientName, " interface {") + for _, method := range service.Methods { + g.AnnotateSymbol(clientName+"."+method.GoName, protogen.Annotation{Location: method.Location}) + if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() { + g.P(deprecationComment) + } + g.P(method.Comments.Leading, + clientSignature(g, method)) + } + g.P("}") + g.P() + + // Client structure. + helper.generateClientStruct(g, clientName) + + // NewClient factory. + if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { + g.P(deprecationComment) + } + g.P("func New", clientName, " (cc ", grpcPackage.Ident("ClientConnInterface"), ") ", clientName, " {") + helper.generateNewClientDefinitions(g, service, clientName) + g.P("}") + g.P() + + var methodIndex, streamIndex int + // Client method implementations. + for _, method := range service.Methods { + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + // Unary RPC method + genClientMethod(gen, file, g, method, methodIndex) + methodIndex++ + } else { + // Streaming RPC method + genClientMethod(gen, file, g, method, streamIndex) + streamIndex++ + } + } + + mustOrShould := "must" + if !*requireUnimplemented { + mustOrShould = "should" + } + + // Server interface. + serverType := service.GoName + "Server" + g.P("// ", serverType, " is the server API for ", service.GoName, " service.") + g.P("// All implementations ", mustOrShould, " embed Unimplemented", serverType) + g.P("// for forward compatibility.") + + // Copy comments from proto file. + genServiceComments(g, service) + + if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { + g.P("//") + g.P(deprecationComment) + } + g.AnnotateSymbol(serverType, protogen.Annotation{Location: service.Location}) + g.P("type ", serverType, " interface {") + for _, method := range service.Methods { + g.AnnotateSymbol(serverType+"."+method.GoName, protogen.Annotation{Location: method.Location}) + if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() { + g.P(deprecationComment) + } + g.P(method.Comments.Leading, + serverSignature(g, method)) + } + if *requireUnimplemented { + g.P("mustEmbedUnimplemented", serverType, "()") + } + g.P("}") + g.P() + + // Server Unimplemented struct for forward compatibility. + helper.generateUnimplementedServerType(gen, file, g, service) + + // Unsafe Server interface to opt-out of forward compatibility. + g.P("// Unsafe", serverType, " may be embedded to opt out of forward compatibility for this service.") + g.P("// Use of this interface is not recommended, as added methods to ", serverType, " will") + g.P("// result in compilation errors.") + g.P("type Unsafe", serverType, " interface {") + g.P("mustEmbedUnimplemented", serverType, "()") + g.P("}") + + // Server registration. + if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { + g.P(deprecationComment) + } + serviceDescVar := service.GoName + "_ServiceDesc" + g.P("func Register", service.GoName, "Server(s ", grpcPackage.Ident("ServiceRegistrar"), ", srv ", serverType, ") {") + g.P("// If the following call panics, it indicates Unimplemented", serverType, " was") + g.P("// embedded by pointer and is nil. This will cause panics if an") + g.P("// unimplemented method is ever invoked, so we test this at initialization") + g.P("// time to prevent it from happening at runtime later due to I/O.") + g.P("if t, ok := srv.(interface { testEmbeddedByValue() }); ok {") + g.P("t.testEmbeddedByValue()") + g.P("}") + g.P("s.RegisterService(&", serviceDescVar, `, srv)`) + g.P("}") + g.P() + + helper.generateServerFunctions(gen, file, g, service, serverType, serviceDescVar) +} + +func clientSignature(g *protogen.GeneratedFile, method *protogen.Method) string { + s := method.GoName + "(ctx " + g.QualifiedGoIdent(contextPackage.Ident("Context")) + if !method.Desc.IsStreamingClient() { + s += ", in *" + g.QualifiedGoIdent(method.Input.GoIdent) + } + s += ", opts ..." + g.QualifiedGoIdent(grpcPackage.Ident("CallOption")) + ") (" + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + s += "*" + g.QualifiedGoIdent(method.Output.GoIdent) + } else { + if *useGenericStreams { + s += clientStreamInterface(g, method) + } else { + s += method.Parent.GoName + "_" + method.GoName + "Client" + } + } + s += ", error)" + return s +} + +func clientStreamInterface(g *protogen.GeneratedFile, method *protogen.Method) string { + typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) + if method.Desc.IsStreamingClient() && method.Desc.IsStreamingServer() { + return g.QualifiedGoIdent(grpcPackage.Ident("BidiStreamingClient")) + "[" + typeParam + "]" + } else if method.Desc.IsStreamingClient() { + return g.QualifiedGoIdent(grpcPackage.Ident("ClientStreamingClient")) + "[" + typeParam + "]" + } else { // i.e. if method.Desc.IsStreamingServer() + return g.QualifiedGoIdent(grpcPackage.Ident("ServerStreamingClient")) + "[" + g.QualifiedGoIdent(method.Output.GoIdent) + "]" + } +} + +func genClientMethod(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, method *protogen.Method, index int) { + service := method.Parent + fmSymbol := helper.formatFullMethodSymbol(service, method) + + if method.Desc.Options().(*descriptorpb.MethodOptions).GetDeprecated() { + g.P(deprecationComment) + } + g.P("func (c *", unexport(service.GoName), "Client) ", clientSignature(g, method), "{") + g.P("cOpts := append([]", grpcPackage.Ident("CallOption"), "{", grpcPackage.Ident("StaticMethod()"), "}, opts...)") + if !method.Desc.IsStreamingServer() && !method.Desc.IsStreamingClient() { + g.P("out := new(", method.Output.GoIdent, ")") + g.P(`err := c.cc.Invoke(ctx, `, fmSymbol, `, in, out, cOpts...)`) + g.P("if err != nil { return nil, err }") + g.P("return out, nil") + g.P("}") + g.P() + return + } + + streamImpl := unexport(service.GoName) + method.GoName + "Client" + if *useGenericStreams { + typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) + streamImpl = g.QualifiedGoIdent(grpcPackage.Ident("GenericClientStream")) + "[" + typeParam + "]" + } + + serviceDescVar := service.GoName + "_ServiceDesc" + g.P("stream, err := c.cc.NewStream(ctx, &", serviceDescVar, ".Streams[", index, `], `, fmSymbol, `, cOpts...)`) + g.P("if err != nil { return nil, err }") + g.P("x := &", streamImpl, "{ClientStream: stream}") + if !method.Desc.IsStreamingClient() { + g.P("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }") + g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }") + } + g.P("return x, nil") + g.P("}") + g.P() + + // Auxiliary types aliases, for backwards compatibility. + if *useGenericStreams { + g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.") + g.P("type ", service.GoName, "_", method.GoName, "Client = ", clientStreamInterface(g, method)) + g.P() + return + } + + // Stream auxiliary types and methods, if we're not taking advantage of the + // pre-implemented generic types and their methods. + genSend := method.Desc.IsStreamingClient() + genRecv := method.Desc.IsStreamingServer() + genCloseAndRecv := !method.Desc.IsStreamingServer() + + g.P("type ", service.GoName, "_", method.GoName, "Client interface {") + if genSend { + g.P("Send(*", method.Input.GoIdent, ") error") + } + if genRecv { + g.P("Recv() (*", method.Output.GoIdent, ", error)") + } + if genCloseAndRecv { + g.P("CloseAndRecv() (*", method.Output.GoIdent, ", error)") + } + g.P(grpcPackage.Ident("ClientStream")) + g.P("}") + g.P() + + g.P("type ", streamImpl, " struct {") + g.P(grpcPackage.Ident("ClientStream")) + g.P("}") + g.P() + + if genSend { + g.P("func (x *", streamImpl, ") Send(m *", method.Input.GoIdent, ") error {") + g.P("return x.ClientStream.SendMsg(m)") + g.P("}") + g.P() + } + if genRecv { + g.P("func (x *", streamImpl, ") Recv() (*", method.Output.GoIdent, ", error) {") + g.P("m := new(", method.Output.GoIdent, ")") + g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }") + g.P("return m, nil") + g.P("}") + g.P() + } + if genCloseAndRecv { + g.P("func (x *", streamImpl, ") CloseAndRecv() (*", method.Output.GoIdent, ", error) {") + g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }") + g.P("m := new(", method.Output.GoIdent, ")") + g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }") + g.P("return m, nil") + g.P("}") + g.P() + } +} + +func serverSignature(g *protogen.GeneratedFile, method *protogen.Method) string { + var reqArgs []string + ret := "error" + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + reqArgs = append(reqArgs, g.QualifiedGoIdent(contextPackage.Ident("Context"))) + ret = "(*" + g.QualifiedGoIdent(method.Output.GoIdent) + ", error)" + } + if !method.Desc.IsStreamingClient() { + reqArgs = append(reqArgs, "*"+g.QualifiedGoIdent(method.Input.GoIdent)) + } + if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { + if *useGenericStreams { + reqArgs = append(reqArgs, serverStreamInterface(g, method)) + } else { + reqArgs = append(reqArgs, method.Parent.GoName+"_"+method.GoName+"Server") + } + } + return method.GoName + "(" + strings.Join(reqArgs, ", ") + ") " + ret +} + +func genServiceDesc(file *protogen.File, g *protogen.GeneratedFile, serviceDescVar string, serverType string, service *protogen.Service, handlerNames []string) { + // Service descriptor. + g.P("// ", serviceDescVar, " is the ", grpcPackage.Ident("ServiceDesc"), " for ", service.GoName, " service.") + g.P("// It's only intended for direct use with ", grpcPackage.Ident("RegisterService"), ",") + g.P("// and not to be introspected or modified (even as a copy)") + g.P("var ", serviceDescVar, " = ", grpcPackage.Ident("ServiceDesc"), " {") + g.P("ServiceName: ", strconv.Quote(string(service.Desc.FullName())), ",") + g.P("HandlerType: (*", serverType, ")(nil),") + g.P("Methods: []", grpcPackage.Ident("MethodDesc"), "{") + for i, method := range service.Methods { + if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { + continue + } + g.P("{") + g.P("MethodName: ", strconv.Quote(string(method.Desc.Name())), ",") + g.P("Handler: ", handlerNames[i], ",") + g.P("},") + } + g.P("},") + g.P("Streams: []", grpcPackage.Ident("StreamDesc"), "{") + for i, method := range service.Methods { + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + continue + } + g.P("{") + g.P("StreamName: ", strconv.Quote(string(method.Desc.Name())), ",") + g.P("Handler: ", handlerNames[i], ",") + if method.Desc.IsStreamingServer() { + g.P("ServerStreams: true,") + } + if method.Desc.IsStreamingClient() { + g.P("ClientStreams: true,") + } + g.P("},") + } + g.P("},") + g.P("Metadata: \"", file.Desc.Path(), "\",") + g.P("}") + g.P() +} + +func serverStreamInterface(g *protogen.GeneratedFile, method *protogen.Method) string { + typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) + if method.Desc.IsStreamingClient() && method.Desc.IsStreamingServer() { + return g.QualifiedGoIdent(grpcPackage.Ident("BidiStreamingServer")) + "[" + typeParam + "]" + } else if method.Desc.IsStreamingClient() { + return g.QualifiedGoIdent(grpcPackage.Ident("ClientStreamingServer")) + "[" + typeParam + "]" + } else { // i.e. if method.Desc.IsStreamingServer() + return g.QualifiedGoIdent(grpcPackage.Ident("ServerStreamingServer")) + "[" + g.QualifiedGoIdent(method.Output.GoIdent) + "]" + } +} + +func genServerMethod(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile, method *protogen.Method, hnameFuncNameFormatter func(string) string) string { + service := method.Parent + hname := fmt.Sprintf("_%s_%s_Handler", service.GoName, method.GoName) + + if !method.Desc.IsStreamingClient() && !method.Desc.IsStreamingServer() { + g.P("func ", hnameFuncNameFormatter(hname), "(srv interface{}, ctx ", contextPackage.Ident("Context"), ", dec func(interface{}) error, interceptor ", grpcPackage.Ident("UnaryServerInterceptor"), ") (interface{}, error) {") + g.P("in := new(", method.Input.GoIdent, ")") + g.P("if err := dec(in); err != nil { return nil, err }") + g.P("if interceptor == nil { return srv.(", service.GoName, "Server).", method.GoName, "(ctx, in) }") + g.P("info := &", grpcPackage.Ident("UnaryServerInfo"), "{") + g.P("Server: srv,") + fmSymbol := helper.formatFullMethodSymbol(service, method) + g.P("FullMethod: ", fmSymbol, ",") + g.P("}") + g.P("handler := func(ctx ", contextPackage.Ident("Context"), ", req interface{}) (interface{}, error) {") + g.P("return srv.(", service.GoName, "Server).", method.GoName, "(ctx, req.(*", method.Input.GoIdent, "))") + g.P("}") + g.P("return interceptor(ctx, in, info, handler)") + g.P("}") + g.P() + return hname + } + + streamImpl := unexport(service.GoName) + method.GoName + "Server" + if *useGenericStreams { + typeParam := g.QualifiedGoIdent(method.Input.GoIdent) + ", " + g.QualifiedGoIdent(method.Output.GoIdent) + streamImpl = g.QualifiedGoIdent(grpcPackage.Ident("GenericServerStream")) + "[" + typeParam + "]" + } + + g.P("func ", hnameFuncNameFormatter(hname), "(srv interface{}, stream ", grpcPackage.Ident("ServerStream"), ") error {") + if !method.Desc.IsStreamingClient() { + g.P("m := new(", method.Input.GoIdent, ")") + g.P("if err := stream.RecvMsg(m); err != nil { return err }") + g.P("return srv.(", service.GoName, "Server).", method.GoName, "(m, &", streamImpl, "{ServerStream: stream})") + } else { + g.P("return srv.(", service.GoName, "Server).", method.GoName, "(&", streamImpl, "{ServerStream: stream})") + } + g.P("}") + g.P() + + // Auxiliary types aliases, for backwards compatibility. + if *useGenericStreams { + g.P("// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.") + g.P("type ", service.GoName, "_", method.GoName, "Server = ", serverStreamInterface(g, method)) + g.P() + return hname + } + + // Stream auxiliary types and methods, if we're not taking advantage of the + // pre-implemented generic types and their methods. + genSend := method.Desc.IsStreamingServer() + genSendAndClose := !method.Desc.IsStreamingServer() + genRecv := method.Desc.IsStreamingClient() + + g.P("type ", service.GoName, "_", method.GoName, "Server interface {") + if genSend { + g.P("Send(*", method.Output.GoIdent, ") error") + } + if genSendAndClose { + g.P("SendAndClose(*", method.Output.GoIdent, ") error") + } + if genRecv { + g.P("Recv() (*", method.Input.GoIdent, ", error)") + } + g.P(grpcPackage.Ident("ServerStream")) + g.P("}") + g.P() + + g.P("type ", streamImpl, " struct {") + g.P(grpcPackage.Ident("ServerStream")) + g.P("}") + g.P() + + if genSend { + g.P("func (x *", streamImpl, ") Send(m *", method.Output.GoIdent, ") error {") + g.P("return x.ServerStream.SendMsg(m)") + g.P("}") + g.P() + } + if genSendAndClose { + g.P("func (x *", streamImpl, ") SendAndClose(m *", method.Output.GoIdent, ") error {") + g.P("return x.ServerStream.SendMsg(m)") + g.P("}") + g.P() + } + if genRecv { + g.P("func (x *", streamImpl, ") Recv() (*", method.Input.GoIdent, ", error) {") + g.P("m := new(", method.Input.GoIdent, ")") + g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }") + g.P("return m, nil") + g.P("}") + g.P() + } + + return hname +} + +func genLeadingComments(g *protogen.GeneratedFile, loc protoreflect.SourceLocation) { + for _, s := range loc.LeadingDetachedComments { + g.P(protogen.Comments(s)) + g.P() + } + if s := loc.LeadingComments; s != "" { + g.P(protogen.Comments(s)) + g.P() + } +} + +const deprecationComment = "// Deprecated: Do not use." + +func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] } diff --git a/server/http/cmd/protoc-gen-go-micro-http/http.go b/server/http/cmd/protoc-gen-go-micro-http/http.go deleted file mode 100644 index b55a96d9..00000000 --- a/server/http/cmd/protoc-gen-go-micro-http/http.go +++ /dev/null @@ -1,379 +0,0 @@ -//notlint:nosnakecase,lll -package main - -import ( - "fmt" - "net/http" - "os" - "regexp" - "strings" - - "google.golang.org/protobuf/reflect/protoreflect" - - "google.golang.org/genproto/googleapis/api/annotations" - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/descriptorpb" -) - -var methodSets = make(map[string]int) //nolint:gochecknoglobals - -// generateFile generates a _http.micro.pb.go file containing kratos errors definitions. -func generateFile(gen *protogen.Plugin, file *protogen.File, omitempty bool) *protogen.GeneratedFile { - if len(file.Services) == 0 || (omitempty && !hasHTTPRule(file.Services)) { - return nil - } - - filename := file.GeneratedFilenamePrefix + "_http.micro.pb.go" - - g := gen.NewGeneratedFile(filename, file.GoImportPath) - - // Generate header message. - g.P("// Code generated by protoc-gen-go-micro-http. DO NOT EDIT.") - g.P("//") - g.P("// version:") - g.P("// - protoc-gen-go-micro-http " + release) - g.P("// - protoc ", protocVersion(gen)) - g.P("//") - - if file.Proto.GetOptions().GetDeprecated() { - g.P("// ", file.Desc.Path(), " is a deprecated file.") - } else { - g.P("// Proto source: ", file.Desc.Path()) - } - - // Generate package name. - g.P() - g.P("package ", file.GoPackageName) - g.P() - - // Generate service code. - if len(file.Services) > 0 { - g.P(protoFile{genServices(file, g, omitempty)}.Render()) - } - - return g -} - -// genServices generates a list of service definitions from a proto file. -func genServices(file *protogen.File, generated *protogen.GeneratedFile, omitempty bool) []serviceDesc { - var services []serviceDesc - - for _, service := range file.Services { - if service := genService(file, generated, service, omitempty); len(service.Methods) > 0 { - services = append(services, service) - } - } - - return services -} - -// genService will generate a single service description. -func genService(file *protogen.File, generated *protogen.GeneratedFile, service *protogen.Service, omitempty bool) serviceDesc { - if service.Desc.Options().(*descriptorpb.ServiceOptions).GetDeprecated() { - generated.P("//") - generated.P(deprecationComment) - } - - // HTTP Server. - serviceDescription := serviceDesc{ - Type: service.GoName, - Name: string(service.Desc.FullName()), - Metadata: file.Desc.Path(), - Methods: make([]methodDesc, 0, 1), - } - - for _, method := range service.Methods { - // gRPC streaming is not suppoerted at the moment - if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { - continue - } - - rule, ok := proto.GetExtension(method.Desc.Options(), annotations.E_Http).(*annotations.HttpRule) - if rule != nil && ok { - for _, bind := range rule.GetAdditionalBindings() { - serviceDescription.AddMethod(buildHTTPRule(generated, method, bind)) - } - - serviceDescription.AddMethod(buildHTTPRule(generated, method, rule)) - } else if !omitempty { - serviceDescription.AddMethod( - buildMethodDesc(generated, method, http.MethodPost, - fmt.Sprintf("/%s/%s", service.Desc.FullName(), method.Desc.Name()))) - } - } - - return serviceDescription -} - -func hasHTTPRule(services []*protogen.Service) bool { - for _, service := range services { - for _, method := range service.Methods { - if method.Desc.IsStreamingClient() || method.Desc.IsStreamingServer() { - continue - } - - rule, ok := proto.GetExtension(method.Desc.Options(), annotations.E_Http).(*annotations.HttpRule) - if rule != nil && ok { - return true - } - } - } - - return false -} - -func buildHTTPRule(generated *protogen.GeneratedFile, protogenMethod *protogen.Method, rule *annotations.HttpRule) methodDesc { - body := rule.GetBody() - responseBody := rule.GetResponseBody() - path, method := parsePattern(rule.GetPattern()) - - methodDescription := buildMethodDesc(generated, protogenMethod, method, path) - - if (method == http.MethodGet || method == http.MethodDelete) && body != "" { - errMessage("%s %s body should not be declared.", method, path) - } else if method != http.MethodGet && method != http.MethodDelete && body == "" { - errMessage("%s %s does not declare a body.", method, path) - } - - switch { - case body == "*": - methodDescription.HasBody = true - methodDescription.Body = "" - case body != "": - methodDescription.HasBody = true - methodDescription.Body = "." + camelCaseVars(body) - default: - methodDescription.HasBody = false - } - - if responseBody == "*" { - methodDescription.ResponseBody = "" - } else if responseBody != "" { - methodDescription.ResponseBody = "." + camelCaseVars(responseBody) - } - - return methodDescription -} - -func parsePattern(pattern any) (string, string) { - path, method := "", "" - - switch pattern := pattern.(type) { - case *annotations.HttpRule_Get: - path = pattern.Get - method = http.MethodGet - case *annotations.HttpRule_Put: - path = pattern.Put - method = http.MethodPut - case *annotations.HttpRule_Post: - path = pattern.Post - method = http.MethodPost - case *annotations.HttpRule_Delete: - path = pattern.Delete - method = http.MethodDelete - case *annotations.HttpRule_Patch: - path = pattern.Patch - method = http.MethodPatch - case *annotations.HttpRule_Custom: - path = pattern.Custom.GetPath() - method = pattern.Custom.GetKind() - } - - return path, method -} - -func buildMethodDesc(generated *protogen.GeneratedFile, protogenMethod *protogen.Method, method, path string) methodDesc { - defer func() { methodSets[protogenMethod.GoName]++ }() - - vars := buildPathVars(path) - - for v, s := range vars { - fields := protogenMethod.Input.Desc.Fields() - - if s != nil { - path = replacePath(v, *s, path) - } - - for _, field := range strings.Split(v, ".") { - if strings.TrimSpace(field) == "" { - continue - } - - if strings.Contains(field, ":") { - field = strings.Split(field, ":")[0] - } - - fd := fields.ByName(protoreflect.Name(field)) - if fd == nil { - errMessage("The corresponding field '%s' declaration in message could not be found in '%s'", v, path) - os.Exit(2) //nolint:gocritic - } - - switch { - case fd.IsMap(): - errMessage("The field in path:'%s' shouldn't be a map", v) - case fd.IsList(): - errMessage("The field in path:'%s' shouldn't be a list.", v) - case fd.Kind() == protoreflect.MessageKind || fd.Kind() == protoreflect.GroupKind: - fields = fd.Message().Fields() - } - } - } - - return methodDesc{ - Name: protogenMethod.GoName, - OriginalName: string(protogenMethod.Desc.Name()), - Num: methodSets[protogenMethod.GoName], - Request: generated.QualifiedGoIdent(protogenMethod.Input.GoIdent), - Reply: generated.QualifiedGoIdent(protogenMethod.Output.GoIdent), - Path: path, - Method: fMethod(method), - HasVars: len(vars) > 0, - } -} - -func buildPathVars(path string) (res map[string]*string) { - if strings.HasSuffix(path, "/") { - errMessage("Path %s should not end with \"/\"", path) - } - - pattern := regexp.MustCompile(`(?i){([a-z.0-9_\s]*)=?([^{}]*)}`) - matches := pattern.FindAllStringSubmatch(path, -1) - res = make(map[string]*string, len(matches)) - - for _, m := range matches { - name := strings.TrimSpace(m[1]) - if len(name) > 1 && len(m[2]) > 0 { - res[name] = &m[2] - } else { - res[name] = nil - } - } - - return -} - -func replacePath(name string, value string, path string) string { - pattern := regexp.MustCompile(fmt.Sprintf(`(?i){([\s]*%s[\s]*)=?([^{}]*)}`, name)) - - idx := pattern.FindStringIndex(path) - if len(idx) > 0 { - path = fmt.Sprintf("%s{%s:%s}%s", - path[:idx[0]], // The start of the match - name, - strings.ReplaceAll(value, "*", ".*"), - path[idx[1]:], - ) - } - - return path -} - -func camelCaseVars(s string) string { - subs := strings.Split(s, ".") - vars := make([]string, 0, len(subs)) - - for _, sub := range subs { - vars = append(vars, camelCase(sub)) - } - - return strings.Join(vars, ".") -} - -// camelCase returns the CamelCased name. -// If there is an interior underscore followed by a lower case letter, -// drop the underscore and convert the letter to upper case. -// There is a remote possibility of this rewrite causing a name collision, -// but it's so remote we're prepared to pretend it's nonexistent - since the -// C++ generator lowercase names, it's extremely unlikely to have two fields -// with different capitalization. -// In short, _my_field_name_2 becomes XMyFieldName_2. -func camelCase(input string) string { - if input == "" { - return "" - } - - output := make([]byte, 0, 32) - i := 0 - - if input[0] == '_' { - // Need a capital letter; drop the '_'. - output = append(output, 'X') - i++ - } - - // Invariant: if the next letter is lower case, it must be converted - // to upper case. - // That is, we process a word at a time, where words are marked by _ or - // upper case letter. Digits are treated as words. - for ; i < len(input); i++ { - c := input[i] - if c == '_' && i+1 < len(input) && isASCIILower(input[i+1]) { - continue // Skip the underscore in s. - } - - if isASCIIDigit(c) { - output = append(output, c) - continue - } - - // Assume we have a letter now - if not, it's a bogus identifier. - // The next word is a sequence of characters that must start upper case. - if isASCIILower(c) { - c ^= ' ' // Make it a capital letter. - } - - output = append(output, c) // Guaranteed not lower case. - - // Accept lower case sequence that follows. - for i+1 < len(input) && isASCIILower(input[i+1]) { - i++ - output = append(output, input[i]) - } - } - - return string(output) -} - -// Is c an ASCII lower-case letter? -func isASCIILower(c byte) bool { - return 'a' <= c && c <= 'z' -} - -// Is c an ASCII digit? -func isASCIIDigit(c byte) bool { - return '0' <= c && c <= '9' -} - -func protocVersion(gen *protogen.Plugin) string { - v := gen.Request.GetCompilerVersion() - if v == nil { - return "(unknown)" - } - - var suffix string - - if s := v.GetSuffix(); s != "" { - suffix = "-" + s - } - - return fmt.Sprintf("v%d.%d.%d%s", v.GetMajor(), v.GetMinor(), v.GetPatch(), suffix) -} - -func fMethod(s string) string { - if len(s) < 2 { - return s - } - - l := strings.ToLower(s) - - return strings.ToUpper(string(l[0])) + l[1:] -} - -func errMessage(msg string, args ...any) { - f := fmt.Sprintf("\u001B[31mWARN\u001B[m: %s\n", msg) - fmt.Fprintf(os.Stderr, f, args...) -} - -const deprecationComment = "// Deprecated: Do not use." diff --git a/server/http/cmd/protoc-gen-go-micro-http/http_test.go b/server/http/cmd/protoc-gen-go-micro-http/http_test.go deleted file mode 100644 index d0c16a93..00000000 --- a/server/http/cmd/protoc-gen-go-micro-http/http_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "reflect" - "testing" -) - -func TestNoParameters(t *testing.T) { - path := "/test/noparams" - m := buildPathVars(path) - if !reflect.DeepEqual(m, map[string]*string{}) { - t.Fatalf("Map should be empty") - } -} - -func TestSingleParam(t *testing.T) { - path := "/test/{message.id}" - m := buildPathVars(path) - if !reflect.DeepEqual(len(m), 1) { - t.Fatalf("len(m) not is 1") - } - if m["message.id"] != nil { - t.Fatalf(`m["message.id"] should be empty`) - } -} - -func TestTwoParametersReplacement(t *testing.T) { - //nolint:goconst - path := "/test/{message.id}/{message.name=messages/*}" - m := buildPathVars(path) - if len(m) != 2 { - t.Fatal("len(m) should be 2") - } - if m["message.id"] != nil { - t.Fatal(`m["message.id"] should be nil`) - } - if m["message.name"] == nil { - t.Fatal(`m["message.name"] should not be nil`) - } - if *m["message.name"] != "messages/*" { - t.Fatal(`m["message.name"] should be "messages/*"`) - } -} - -func TestNoReplacePath(t *testing.T) { - path := "/test/{message.id=test}" - if !reflect.DeepEqual(replacePath("message.id", "test", path), "/test/{message.id:test}") { - t.Fatal(`replacePath("message.id", "test", path) should be "/test/{message.id:test}"`) - } - path = "/test/{message.id=test/*}" - if !reflect.DeepEqual(replacePath("message.id", "test/*", path), "/test/{message.id:test/.*}") { - t.Fatal(`replacePath("message.id", "test/*", path) should be "/test/{message.id:test/.*}"`) - } -} - -func TestReplacePath(t *testing.T) { - path := "/test/{message.id}/{message.name=messages/*}" - newPath := replacePath("message.name", "messages/*", path) - if !reflect.DeepEqual("/test/{message.id}/{message.name:messages/.*}", newPath) { - t.Fatal(`replacePath("message.name", "messages/*", path) should be "/test/{message.id}/{message.name:messages/.*}"`) - } -} - -func TestIteration(t *testing.T) { - path := "/test/{message.id}/{message.name=messages/*}" - vars := buildPathVars(path) - for v, s := range vars { - if s != nil { - path = replacePath(v, *s, path) - } - } - if !reflect.DeepEqual("/test/{message.id}/{message.name:messages/.*}", path) { - t.Fatal(`replacePath("message.name", "messages/*", path) should be "/test/{message.id}/{message.name:messages/.*}"`) - } -} - -func TestIterationMiddle(t *testing.T) { - path := "/test/{message.name=messages/*}/books" - vars := buildPathVars(path) - for v, s := range vars { - if s != nil { - path = replacePath(v, *s, path) - } - } - if !reflect.DeepEqual("/test/{message.name:messages/.*}/books", path) { - t.Fatal(`replacePath("message.name", "messages/*", path) should be "/test/{message.name:messages/.*}/books"`) - } -} diff --git a/server/http/cmd/protoc-gen-go-micro-http/main.go b/server/http/cmd/protoc-gen-go-micro-http/main.go deleted file mode 100644 index 6eabf45e..00000000 --- a/server/http/cmd/protoc-gen-go-micro-http/main.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "os" - - "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/types/pluginpb" -) - -func main() { - showVersion := flag.Bool("version", false, "print the version and exit") - omitempty := flag.Bool("omitempty", true, "omit if google.api is empty") - flag.Parse() - - if *showVersion { - _, err := fmt.Fprintf(os.Stdout, "protoc-gen-go-micro-http %v\n", release) - if err != nil { - log.Fatal(err) - } - - return - } - - opts := protogen.Options{ParamFunc: flag.CommandLine.Set} - opts.Run(func(gen *protogen.Plugin) error { - gen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) - for _, f := range gen.Files { - if !f.Generate { - continue - } - - generateFile(gen, f, *omitempty) - } - - return nil - }) -} diff --git a/server/http/cmd/protoc-gen-go-micro-http/template.go b/server/http/cmd/protoc-gen-go-micro-http/template.go deleted file mode 100644 index b2732e47..00000000 --- a/server/http/cmd/protoc-gen-go-micro-http/template.go +++ /dev/null @@ -1,91 +0,0 @@ -package main - -import ( - "bytes" - "strings" - "text/template" -) - -//nolint:gochecknoglobals -var httpTemplate = ` -import ( - "google.golang.org/grpc" - - "github.com/go-orb/go-orb/server" - - mhttp "github.com/go-orb/plugins/server/http" -) -{{range .Services}} - -// Register{{.Type}}HTTPHandler registers the service to an HTTP server. -func Register{{.Type}}HTTPHandler(srv *mhttp.ServerHTTP, handler {{.Type}}Server ) { - r := srv.Router() - {{- range .Methods}} - r.{{.Method}}("{{.Path}}", mhttp.NewGRPCHandler(srv, handler.{{.Name}})) - {{- end}} -} - -// Register{{.Type}}Handler will return a registration function that can be -// provided to entrypoints as a handler registration. -func Register{{.Type}}Handler(handler {{.Type}}Server) server.RegistrationFunc { - return server.RegistrationFunc(func(s any) { - switch srv := any(s).(type) { - case *mhttp.ServerHTTP: - Register{{.Type}}HTTPHandler(srv, handler) - case grpc.ServiceRegistrar: - // RegisterStreamsgRPCHandler(srv, handler) - default: - // Maybe we should log here with slog global logger - } - }) -} -{{- end}} -` - -// protoFile is the object passed to the template. -type protoFile struct { - Services []serviceDesc -} - -// serviceDesc describes a service. -type serviceDesc struct { - Type string // Greeter - Name string // helloworld.Greeter - Metadata string // api/helloworld/helloworld.proto - Methods []methodDesc -} - -// methodDesc describes a service method. -type methodDesc struct { - Name string - OriginalName string // The parsed original name - Num int - Request string - Reply string - - // http_rule annotation properties. - Path string - Method string - HasVars bool - HasBody bool - Body string - ResponseBody string -} - -func (s *serviceDesc) AddMethod(method methodDesc) { - s.Methods = append(s.Methods, method) -} - -func (p protoFile) Render() string { - tmpl, err := template.New("http").Parse(strings.TrimSpace(httpTemplate)) - if err != nil { - panic(err) - } - - buf := new(bytes.Buffer) - if err := tmpl.Execute(buf, p); err != nil { - panic(err) - } - - return strings.Trim(buf.String(), "\r\n") -} diff --git a/server/http/cmd/protoc-gen-go-micro-http/version.go b/server/http/cmd/protoc-gen-go-micro-http/version.go deleted file mode 100644 index ee80a1ae..00000000 --- a/server/http/cmd/protoc-gen-go-micro-http/version.go +++ /dev/null @@ -1,4 +0,0 @@ -package main - -// release is the current protoc-gen-go-http version. -const release = "v1.0.0"