github.com/gogo/protobuf@v1.3.2/protoc-gen-gogo/grpc/grpc.go (about)

     1  // Go support for Protocol Buffers - Google's data interchange format
     2  //
     3  // Copyright 2015 The Go Authors.  All rights reserved.
     4  // https://github.com/golang/protobuf
     5  //
     6  // Redistribution and use in source and binary forms, with or without
     7  // modification, are permitted provided that the following conditions are
     8  // met:
     9  //
    10  //     * Redistributions of source code must retain the above copyright
    11  // notice, this list of conditions and the following disclaimer.
    12  //     * Redistributions in binary form must reproduce the above
    13  // copyright notice, this list of conditions and the following disclaimer
    14  // in the documentation and/or other materials provided with the
    15  // distribution.
    16  //     * Neither the name of Google Inc. nor the names of its
    17  // contributors may be used to endorse or promote products derived from
    18  // this software without specific prior written permission.
    19  //
    20  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    21  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    22  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    23  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    24  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    25  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    26  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    27  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    28  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    29  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    30  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    31  
    32  // Package grpc outputs gRPC service descriptions in Go code.
    33  // It runs as a plugin for the Go protocol buffer compiler plugin.
    34  // It is linked in to protoc-gen-go.
    35  package grpc
    36  
    37  import (
    38  	"fmt"
    39  	"strconv"
    40  	"strings"
    41  
    42  	pb "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
    43  	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
    44  )
    45  
    46  // generatedCodeVersion indicates a version of the generated code.
    47  // It is incremented whenever an incompatibility between the generated code and
    48  // the grpc package is introduced; the generated code references
    49  // a constant, grpc.SupportPackageIsVersionN (where N is generatedCodeVersion).
    50  const generatedCodeVersion = 4
    51  
    52  // Paths for packages used by code generated in this file,
    53  // relative to the import_prefix of the generator.Generator.
    54  const (
    55  	contextPkgPath = "context"
    56  	grpcPkgPath    = "google.golang.org/grpc"
    57  	codePkgPath    = "google.golang.org/grpc/codes"
    58  	statusPkgPath  = "google.golang.org/grpc/status"
    59  )
    60  
    61  func init() {
    62  	generator.RegisterPlugin(new(grpc))
    63  }
    64  
    65  // grpc is an implementation of the Go protocol buffer compiler's
    66  // plugin architecture.  It generates bindings for gRPC support.
    67  type grpc struct {
    68  	gen *generator.Generator
    69  }
    70  
    71  // Name returns the name of this plugin, "grpc".
    72  func (g *grpc) Name() string {
    73  	return "grpc"
    74  }
    75  
    76  // The names for packages imported in the generated code.
    77  // They may vary from the final path component of the import path
    78  // if the name is used by other packages.
    79  var (
    80  	contextPkg string
    81  	grpcPkg    string
    82  )
    83  
    84  // Init initializes the plugin.
    85  func (g *grpc) Init(gen *generator.Generator) {
    86  	g.gen = gen
    87  }
    88  
    89  // Given a type name defined in a .proto, return its object.
    90  // Also record that we're using it, to guarantee the associated import.
    91  func (g *grpc) objectNamed(name string) generator.Object {
    92  	g.gen.RecordTypeUse(name)
    93  	return g.gen.ObjectNamed(name)
    94  }
    95  
    96  // Given a type name defined in a .proto, return its name as we will print it.
    97  func (g *grpc) typeName(str string) string {
    98  	return g.gen.TypeName(g.objectNamed(str))
    99  }
   100  
   101  // P forwards to g.gen.P.
   102  func (g *grpc) P(args ...interface{}) { g.gen.P(args...) }
   103  
   104  // Generate generates code for the services in the given file.
   105  func (g *grpc) Generate(file *generator.FileDescriptor) {
   106  	if len(file.FileDescriptorProto.Service) == 0 {
   107  		return
   108  	}
   109  
   110  	contextPkg = string(g.gen.AddImport(contextPkgPath))
   111  	grpcPkg = string(g.gen.AddImport(grpcPkgPath))
   112  
   113  	g.P("// Reference imports to suppress errors if they are not otherwise used.")
   114  	g.P("var _ ", contextPkg, ".Context")
   115  	g.P("var _ ", grpcPkg, ".ClientConn")
   116  	g.P()
   117  
   118  	// Assert version compatibility.
   119  	g.P("// This is a compile-time assertion to ensure that this generated file")
   120  	g.P("// is compatible with the grpc package it is being compiled against.")
   121  	g.P("const _ = ", grpcPkg, ".SupportPackageIsVersion", generatedCodeVersion)
   122  	g.P()
   123  
   124  	for i, service := range file.FileDescriptorProto.Service {
   125  		g.generateService(file, service, i)
   126  	}
   127  }
   128  
   129  // GenerateImports generates the import declaration for this file.
   130  func (g *grpc) GenerateImports(file *generator.FileDescriptor) {}
   131  
   132  // reservedClientName records whether a client name is reserved on the client side.
   133  var reservedClientName = map[string]bool{
   134  	// TODO: do we need any in gRPC?
   135  }
   136  
   137  func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }
   138  
   139  // deprecationComment is the standard comment added to deprecated
   140  // messages, fields, enums, and enum values.
   141  var deprecationComment = "// Deprecated: Do not use."
   142  
   143  // generateService generates all the code for the named service.
   144  func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) {
   145  	path := fmt.Sprintf("6,%d", index) // 6 means service.
   146  
   147  	origServName := service.GetName()
   148  	fullServName := origServName
   149  	if pkg := file.GetPackage(); pkg != "" {
   150  		fullServName = pkg + "." + fullServName
   151  	}
   152  	servName := generator.CamelCase(origServName)
   153  	deprecated := service.GetOptions().GetDeprecated()
   154  
   155  	g.P()
   156  	g.P(fmt.Sprintf(`// %sClient is the client API for %s service.
   157  //
   158  // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.`, servName, servName))
   159  
   160  	// Client interface.
   161  	if deprecated {
   162  		g.P("//")
   163  		g.P(deprecationComment)
   164  	}
   165  	g.P("type ", servName, "Client interface {")
   166  	for i, method := range service.Method {
   167  		g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
   168  		g.P(g.generateClientSignature(servName, method))
   169  	}
   170  	g.P("}")
   171  	g.P()
   172  
   173  	// Client structure.
   174  	g.P("type ", unexport(servName), "Client struct {")
   175  	g.P("cc *", grpcPkg, ".ClientConn")
   176  	g.P("}")
   177  	g.P()
   178  
   179  	// NewClient factory.
   180  	if deprecated {
   181  		g.P(deprecationComment)
   182  	}
   183  	g.P("func New", servName, "Client (cc *", grpcPkg, ".ClientConn) ", servName, "Client {")
   184  	g.P("return &", unexport(servName), "Client{cc}")
   185  	g.P("}")
   186  	g.P()
   187  
   188  	var methodIndex, streamIndex int
   189  	serviceDescVar := "_" + servName + "_serviceDesc"
   190  	// Client method implementations.
   191  	for _, method := range service.Method {
   192  		var descExpr string
   193  		if !method.GetServerStreaming() && !method.GetClientStreaming() {
   194  			// Unary RPC method
   195  			descExpr = fmt.Sprintf("&%s.Methods[%d]", serviceDescVar, methodIndex)
   196  			methodIndex++
   197  		} else {
   198  			// Streaming RPC method
   199  			descExpr = fmt.Sprintf("&%s.Streams[%d]", serviceDescVar, streamIndex)
   200  			streamIndex++
   201  		}
   202  		g.generateClientMethod(servName, fullServName, serviceDescVar, method, descExpr)
   203  	}
   204  
   205  	// Server interface.
   206  	serverType := servName + "Server"
   207  	g.P("// ", serverType, " is the server API for ", servName, " service.")
   208  	if deprecated {
   209  		g.P("//")
   210  		g.P(deprecationComment)
   211  	}
   212  	g.P("type ", serverType, " interface {")
   213  	for i, method := range service.Method {
   214  		g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
   215  		g.P(g.generateServerSignature(servName, method))
   216  	}
   217  	g.P("}")
   218  	g.P()
   219  
   220  	// Server Unimplemented struct for forward compatability.
   221  	if deprecated {
   222  		g.P(deprecationComment)
   223  	}
   224  	g.generateUnimplementedServer(servName, service)
   225  
   226  	// Server registration.
   227  	if deprecated {
   228  		g.P(deprecationComment)
   229  	}
   230  	g.P("func Register", servName, "Server(s *", grpcPkg, ".Server, srv ", serverType, ") {")
   231  	g.P("s.RegisterService(&", serviceDescVar, `, srv)`)
   232  	g.P("}")
   233  	g.P()
   234  
   235  	// Server handler implementations.
   236  	var handlerNames []string
   237  	for _, method := range service.Method {
   238  		hname := g.generateServerMethod(servName, fullServName, method)
   239  		handlerNames = append(handlerNames, hname)
   240  	}
   241  
   242  	// Service descriptor.
   243  	g.P("var ", serviceDescVar, " = ", grpcPkg, ".ServiceDesc {")
   244  	g.P("ServiceName: ", strconv.Quote(fullServName), ",")
   245  	g.P("HandlerType: (*", serverType, ")(nil),")
   246  	g.P("Methods: []", grpcPkg, ".MethodDesc{")
   247  	for i, method := range service.Method {
   248  		if method.GetServerStreaming() || method.GetClientStreaming() {
   249  			continue
   250  		}
   251  		g.P("{")
   252  		g.P("MethodName: ", strconv.Quote(method.GetName()), ",")
   253  		g.P("Handler: ", handlerNames[i], ",")
   254  		g.P("},")
   255  	}
   256  	g.P("},")
   257  	g.P("Streams: []", grpcPkg, ".StreamDesc{")
   258  	for i, method := range service.Method {
   259  		if !method.GetServerStreaming() && !method.GetClientStreaming() {
   260  			continue
   261  		}
   262  		g.P("{")
   263  		g.P("StreamName: ", strconv.Quote(method.GetName()), ",")
   264  		g.P("Handler: ", handlerNames[i], ",")
   265  		if method.GetServerStreaming() {
   266  			g.P("ServerStreams: true,")
   267  		}
   268  		if method.GetClientStreaming() {
   269  			g.P("ClientStreams: true,")
   270  		}
   271  		g.P("},")
   272  	}
   273  	g.P("},")
   274  	g.P("Metadata: \"", file.GetName(), "\",")
   275  	g.P("}")
   276  	g.P()
   277  }
   278  
   279  // generateUnimplementedServer creates the unimplemented server struct
   280  func (g *grpc) generateUnimplementedServer(servName string, service *pb.ServiceDescriptorProto) {
   281  	serverType := servName + "Server"
   282  	g.P("// Unimplemented", serverType, " can be embedded to have forward compatible implementations.")
   283  	g.P("type Unimplemented", serverType, " struct {")
   284  	g.P("}")
   285  	g.P()
   286  	// Unimplemented<service_name>Server's concrete methods
   287  	for _, method := range service.Method {
   288  		g.generateServerMethodConcrete(servName, method)
   289  	}
   290  	g.P()
   291  }
   292  
   293  // generateServerMethodConcrete returns unimplemented methods which ensure forward compatibility
   294  func (g *grpc) generateServerMethodConcrete(servName string, method *pb.MethodDescriptorProto) {
   295  	header := g.generateServerSignatureWithParamNames(servName, method)
   296  	g.P("func (*Unimplemented", servName, "Server) ", header, " {")
   297  	var nilArg string
   298  	if !method.GetServerStreaming() && !method.GetClientStreaming() {
   299  		nilArg = "nil, "
   300  	}
   301  	methName := generator.CamelCase(method.GetName())
   302  	statusPkg := string(g.gen.AddImport(statusPkgPath))
   303  	codePkg := string(g.gen.AddImport(codePkgPath))
   304  	g.P("return ", nilArg, statusPkg, `.Errorf(`, codePkg, `.Unimplemented, "method `, methName, ` not implemented")`)
   305  	g.P("}")
   306  }
   307  
   308  // generateClientSignature returns the client-side signature for a method.
   309  func (g *grpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
   310  	origMethName := method.GetName()
   311  	methName := generator.CamelCase(origMethName)
   312  	if reservedClientName[methName] {
   313  		methName += "_"
   314  	}
   315  	reqArg := ", in *" + g.typeName(method.GetInputType())
   316  	if method.GetClientStreaming() {
   317  		reqArg = ""
   318  	}
   319  	respName := "*" + g.typeName(method.GetOutputType())
   320  	if method.GetServerStreaming() || method.GetClientStreaming() {
   321  		respName = servName + "_" + generator.CamelCase(origMethName) + "Client"
   322  	}
   323  	return fmt.Sprintf("%s(ctx %s.Context%s, opts ...%s.CallOption) (%s, error)", methName, contextPkg, reqArg, grpcPkg, respName)
   324  }
   325  
   326  func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) {
   327  	sname := fmt.Sprintf("/%s/%s", fullServName, method.GetName())
   328  	methName := generator.CamelCase(method.GetName())
   329  	inType := g.typeName(method.GetInputType())
   330  	outType := g.typeName(method.GetOutputType())
   331  
   332  	if method.GetOptions().GetDeprecated() {
   333  		g.P(deprecationComment)
   334  	}
   335  	g.P("func (c *", unexport(servName), "Client) ", g.generateClientSignature(servName, method), "{")
   336  	if !method.GetServerStreaming() && !method.GetClientStreaming() {
   337  		g.P("out := new(", outType, ")")
   338  		// TODO: Pass descExpr to Invoke.
   339  		g.P(`err := c.cc.Invoke(ctx, "`, sname, `", in, out, opts...)`)
   340  		g.P("if err != nil { return nil, err }")
   341  		g.P("return out, nil")
   342  		g.P("}")
   343  		g.P()
   344  		return
   345  	}
   346  	streamType := unexport(servName) + methName + "Client"
   347  	g.P("stream, err := c.cc.NewStream(ctx, ", descExpr, `, "`, sname, `", opts...)`)
   348  	g.P("if err != nil { return nil, err }")
   349  	g.P("x := &", streamType, "{stream}")
   350  	if !method.GetClientStreaming() {
   351  		g.P("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }")
   352  		g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
   353  	}
   354  	g.P("return x, nil")
   355  	g.P("}")
   356  	g.P()
   357  
   358  	genSend := method.GetClientStreaming()
   359  	genRecv := method.GetServerStreaming()
   360  	genCloseAndRecv := !method.GetServerStreaming()
   361  
   362  	// Stream auxiliary types and methods.
   363  	g.P("type ", servName, "_", methName, "Client interface {")
   364  	if genSend {
   365  		g.P("Send(*", inType, ") error")
   366  	}
   367  	if genRecv {
   368  		g.P("Recv() (*", outType, ", error)")
   369  	}
   370  	if genCloseAndRecv {
   371  		g.P("CloseAndRecv() (*", outType, ", error)")
   372  	}
   373  	g.P(grpcPkg, ".ClientStream")
   374  	g.P("}")
   375  	g.P()
   376  
   377  	g.P("type ", streamType, " struct {")
   378  	g.P(grpcPkg, ".ClientStream")
   379  	g.P("}")
   380  	g.P()
   381  
   382  	if genSend {
   383  		g.P("func (x *", streamType, ") Send(m *", inType, ") error {")
   384  		g.P("return x.ClientStream.SendMsg(m)")
   385  		g.P("}")
   386  		g.P()
   387  	}
   388  	if genRecv {
   389  		g.P("func (x *", streamType, ") Recv() (*", outType, ", error) {")
   390  		g.P("m := new(", outType, ")")
   391  		g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
   392  		g.P("return m, nil")
   393  		g.P("}")
   394  		g.P()
   395  	}
   396  	if genCloseAndRecv {
   397  		g.P("func (x *", streamType, ") CloseAndRecv() (*", outType, ", error) {")
   398  		g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
   399  		g.P("m := new(", outType, ")")
   400  		g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
   401  		g.P("return m, nil")
   402  		g.P("}")
   403  		g.P()
   404  	}
   405  }
   406  
   407  // generateServerSignatureWithParamNames returns the server-side signature for a method with parameter names.
   408  func (g *grpc) generateServerSignatureWithParamNames(servName string, method *pb.MethodDescriptorProto) string {
   409  	origMethName := method.GetName()
   410  	methName := generator.CamelCase(origMethName)
   411  	if reservedClientName[methName] {
   412  		methName += "_"
   413  	}
   414  
   415  	var reqArgs []string
   416  	ret := "error"
   417  	if !method.GetServerStreaming() && !method.GetClientStreaming() {
   418  		reqArgs = append(reqArgs, "ctx "+contextPkg+".Context")
   419  		ret = "(*" + g.typeName(method.GetOutputType()) + ", error)"
   420  	}
   421  	if !method.GetClientStreaming() {
   422  		reqArgs = append(reqArgs, "req *"+g.typeName(method.GetInputType()))
   423  	}
   424  	if method.GetServerStreaming() || method.GetClientStreaming() {
   425  		reqArgs = append(reqArgs, "srv "+servName+"_"+generator.CamelCase(origMethName)+"Server")
   426  	}
   427  
   428  	return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
   429  }
   430  
   431  // generateServerSignature returns the server-side signature for a method.
   432  func (g *grpc) generateServerSignature(servName string, method *pb.MethodDescriptorProto) string {
   433  	origMethName := method.GetName()
   434  	methName := generator.CamelCase(origMethName)
   435  	if reservedClientName[methName] {
   436  		methName += "_"
   437  	}
   438  
   439  	var reqArgs []string
   440  	ret := "error"
   441  	if !method.GetServerStreaming() && !method.GetClientStreaming() {
   442  		reqArgs = append(reqArgs, contextPkg+".Context")
   443  		ret = "(*" + g.typeName(method.GetOutputType()) + ", error)"
   444  	}
   445  	if !method.GetClientStreaming() {
   446  		reqArgs = append(reqArgs, "*"+g.typeName(method.GetInputType()))
   447  	}
   448  	if method.GetServerStreaming() || method.GetClientStreaming() {
   449  		reqArgs = append(reqArgs, servName+"_"+generator.CamelCase(origMethName)+"Server")
   450  	}
   451  
   452  	return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
   453  }
   454  
   455  func (g *grpc) generateServerMethod(servName, fullServName string, method *pb.MethodDescriptorProto) string {
   456  	methName := generator.CamelCase(method.GetName())
   457  	hname := fmt.Sprintf("_%s_%s_Handler", servName, methName)
   458  	inType := g.typeName(method.GetInputType())
   459  	outType := g.typeName(method.GetOutputType())
   460  
   461  	if !method.GetServerStreaming() && !method.GetClientStreaming() {
   462  		g.P("func ", hname, "(srv interface{}, ctx ", contextPkg, ".Context, dec func(interface{}) error, interceptor ", grpcPkg, ".UnaryServerInterceptor) (interface{}, error) {")
   463  		g.P("in := new(", inType, ")")
   464  		g.P("if err := dec(in); err != nil { return nil, err }")
   465  		g.P("if interceptor == nil { return srv.(", servName, "Server).", methName, "(ctx, in) }")
   466  		g.P("info := &", grpcPkg, ".UnaryServerInfo{")
   467  		g.P("Server: srv,")
   468  		g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", fullServName, methName)), ",")
   469  		g.P("}")
   470  		g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {")
   471  		g.P("return srv.(", servName, "Server).", methName, "(ctx, req.(*", inType, "))")
   472  		g.P("}")
   473  		g.P("return interceptor(ctx, in, info, handler)")
   474  		g.P("}")
   475  		g.P()
   476  		return hname
   477  	}
   478  	streamType := unexport(servName) + methName + "Server"
   479  	g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {")
   480  	if !method.GetClientStreaming() {
   481  		g.P("m := new(", inType, ")")
   482  		g.P("if err := stream.RecvMsg(m); err != nil { return err }")
   483  		g.P("return srv.(", servName, "Server).", methName, "(m, &", streamType, "{stream})")
   484  	} else {
   485  		g.P("return srv.(", servName, "Server).", methName, "(&", streamType, "{stream})")
   486  	}
   487  	g.P("}")
   488  	g.P()
   489  
   490  	genSend := method.GetServerStreaming()
   491  	genSendAndClose := !method.GetServerStreaming()
   492  	genRecv := method.GetClientStreaming()
   493  
   494  	// Stream auxiliary types and methods.
   495  	g.P("type ", servName, "_", methName, "Server interface {")
   496  	if genSend {
   497  		g.P("Send(*", outType, ") error")
   498  	}
   499  	if genSendAndClose {
   500  		g.P("SendAndClose(*", outType, ") error")
   501  	}
   502  	if genRecv {
   503  		g.P("Recv() (*", inType, ", error)")
   504  	}
   505  	g.P(grpcPkg, ".ServerStream")
   506  	g.P("}")
   507  	g.P()
   508  
   509  	g.P("type ", streamType, " struct {")
   510  	g.P(grpcPkg, ".ServerStream")
   511  	g.P("}")
   512  	g.P()
   513  
   514  	if genSend {
   515  		g.P("func (x *", streamType, ") Send(m *", outType, ") error {")
   516  		g.P("return x.ServerStream.SendMsg(m)")
   517  		g.P("}")
   518  		g.P()
   519  	}
   520  	if genSendAndClose {
   521  		g.P("func (x *", streamType, ") SendAndClose(m *", outType, ") error {")
   522  		g.P("return x.ServerStream.SendMsg(m)")
   523  		g.P("}")
   524  		g.P()
   525  	}
   526  	if genRecv {
   527  		g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {")
   528  		g.P("m := new(", inType, ")")
   529  		g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }")
   530  		g.P("return m, nil")
   531  		g.P("}")
   532  		g.P()
   533  	}
   534  
   535  	return hname
   536  }