blob: 1d3d3ca8f19bb2d2865cb72bfa3be1705b745a44 [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001package gengateway
2
3import (
4 "bytes"
5 "errors"
6 "fmt"
7 "strings"
8 "text/template"
9
10 "github.com/golang/glog"
11 generator2 "github.com/golang/protobuf/protoc-gen-go/generator"
12 "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
13 "github.com/grpc-ecosystem/grpc-gateway/utilities"
14)
15
16type param struct {
17 *descriptor.File
18 Imports []descriptor.GoPackage
19 UseRequestContext bool
20 RegisterFuncSuffix string
21 AllowPatchFeature bool
22}
23
24type binding struct {
25 *descriptor.Binding
26 Registry *descriptor.Registry
27 AllowPatchFeature bool
28}
29
30// GetBodyFieldPath returns the binding body's fieldpath.
31func (b binding) GetBodyFieldPath() string {
32 if b.Body != nil && len(b.Body.FieldPath) != 0 {
33 return b.Body.FieldPath.String()
34 }
35 return "*"
36}
37
38// GetBodyFieldPath returns the binding body's struct field name.
39func (b binding) GetBodyFieldStructName() (string, error) {
40 if b.Body != nil && len(b.Body.FieldPath) != 0 {
41 return generator2.CamelCase(b.Body.FieldPath.String()), nil
42 }
43 return "", errors.New("No body field found")
44}
45
46// HasQueryParam determines if the binding needs parameters in query string.
47//
48// It sometimes returns true even though actually the binding does not need.
49// But it is not serious because it just results in a small amount of extra codes generated.
50func (b binding) HasQueryParam() bool {
51 if b.Body != nil && len(b.Body.FieldPath) == 0 {
52 return false
53 }
54 fields := make(map[string]bool)
55 for _, f := range b.Method.RequestType.Fields {
56 fields[f.GetName()] = true
57 }
58 if b.Body != nil {
59 delete(fields, b.Body.FieldPath.String())
60 }
61 for _, p := range b.PathParams {
62 delete(fields, p.FieldPath.String())
63 }
64 return len(fields) > 0
65}
66
67func (b binding) QueryParamFilter() queryParamFilter {
68 var seqs [][]string
69 if b.Body != nil {
70 seqs = append(seqs, strings.Split(b.Body.FieldPath.String(), "."))
71 }
72 for _, p := range b.PathParams {
73 seqs = append(seqs, strings.Split(p.FieldPath.String(), "."))
74 }
75 return queryParamFilter{utilities.NewDoubleArray(seqs)}
76}
77
78// HasEnumPathParam returns true if the path parameter slice contains a parameter
79// that maps to an enum proto field that is not repeated, if not false is returned.
80func (b binding) HasEnumPathParam() bool {
81 return b.hasEnumPathParam(false)
82}
83
84// HasRepeatedEnumPathParam returns true if the path parameter slice contains a parameter
85// that maps to a repeated enum proto field, if not false is returned.
86func (b binding) HasRepeatedEnumPathParam() bool {
87 return b.hasEnumPathParam(true)
88}
89
90// hasEnumPathParam returns true if the path parameter slice contains a parameter
91// that maps to a enum proto field and that the enum proto field is or isn't repeated
92// based on the provided 'repeated' parameter.
93func (b binding) hasEnumPathParam(repeated bool) bool {
94 for _, p := range b.PathParams {
95 if p.IsEnum() && p.IsRepeated() == repeated {
96 return true
97 }
98 }
99 return false
100}
101
102// LookupEnum looks up a enum type by path parameter.
103func (b binding) LookupEnum(p descriptor.Parameter) *descriptor.Enum {
104 e, err := b.Registry.LookupEnum("", p.Target.GetTypeName())
105 if err != nil {
106 return nil
107 }
108 return e
109}
110
111// FieldMaskField returns the golang-style name of the variable for a FieldMask, if there is exactly one of that type in
112// the message. Otherwise, it returns an empty string.
113func (b binding) FieldMaskField() string {
114 var fieldMaskField *descriptor.Field
115 for _, f := range b.Method.RequestType.Fields {
116 if f.GetTypeName() == ".google.protobuf.FieldMask" {
117 // if there is more than 1 FieldMask for this request, then return none
118 if fieldMaskField != nil {
119 return ""
120 }
121 fieldMaskField = f
122 }
123 }
124 if fieldMaskField != nil {
125 return generator2.CamelCase(fieldMaskField.GetName())
126 }
127 return ""
128}
129
130// queryParamFilter is a wrapper of utilities.DoubleArray which provides String() to output DoubleArray.Encoding in a stable and predictable format.
131type queryParamFilter struct {
132 *utilities.DoubleArray
133}
134
135func (f queryParamFilter) String() string {
136 encodings := make([]string, len(f.Encoding))
137 for str, enc := range f.Encoding {
138 encodings[enc] = fmt.Sprintf("%q: %d", str, enc)
139 }
140 e := strings.Join(encodings, ", ")
141 return fmt.Sprintf("&utilities.DoubleArray{Encoding: map[string]int{%s}, Base: %#v, Check: %#v}", e, f.Base, f.Check)
142}
143
144type trailerParams struct {
145 Services []*descriptor.Service
146 UseRequestContext bool
147 RegisterFuncSuffix string
148 AssumeColonVerb bool
149}
150
151func applyTemplate(p param, reg *descriptor.Registry) (string, error) {
152 w := bytes.NewBuffer(nil)
153 if err := headerTemplate.Execute(w, p); err != nil {
154 return "", err
155 }
156 var targetServices []*descriptor.Service
157
158 for _, msg := range p.Messages {
159 msgName := generator2.CamelCase(*msg.Name)
160 msg.Name = &msgName
161 }
162 for _, svc := range p.Services {
163 var methodWithBindingsSeen bool
164 svcName := generator2.CamelCase(*svc.Name)
165 svc.Name = &svcName
166 for _, meth := range svc.Methods {
167 glog.V(2).Infof("Processing %s.%s", svc.GetName(), meth.GetName())
168 methName := generator2.CamelCase(*meth.Name)
169 meth.Name = &methName
170 for _, b := range meth.Bindings {
171 methodWithBindingsSeen = true
172 if err := handlerTemplate.Execute(w, binding{
173 Binding: b,
174 Registry: reg,
175 AllowPatchFeature: p.AllowPatchFeature,
176 }); err != nil {
177 return "", err
178 }
179
180 // Local
181 if err := localHandlerTemplate.Execute(w, binding{
182 Binding: b,
183 Registry: reg,
184 AllowPatchFeature: p.AllowPatchFeature,
185 }); err != nil {
186 return "", err
187 }
188 }
189 }
190 if methodWithBindingsSeen {
191 targetServices = append(targetServices, svc)
192 }
193 }
194 if len(targetServices) == 0 {
195 return "", errNoTargetService
196 }
197
198 assumeColonVerb := true
199 if reg != nil {
200 assumeColonVerb = !reg.GetAllowColonFinalSegments()
201 }
202 tp := trailerParams{
203 Services: targetServices,
204 UseRequestContext: p.UseRequestContext,
205 RegisterFuncSuffix: p.RegisterFuncSuffix,
206 AssumeColonVerb: assumeColonVerb,
207 }
208 // Local
209 if err := localTrailerTemplate.Execute(w, tp); err != nil {
210 return "", err
211 }
212
213 if err := trailerTemplate.Execute(w, tp); err != nil {
214 return "", err
215 }
216 return w.String(), nil
217}
218
219var (
220 headerTemplate = template.Must(template.New("header").Parse(`
221// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
222// source: {{.GetName}}
223
224/*
225Package {{.GoPkg.Name}} is a reverse proxy.
226
227It translates gRPC into RESTful JSON APIs.
228*/
229package {{.GoPkg.Name}}
230import (
231 {{range $i := .Imports}}{{if $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}
232
233 {{range $i := .Imports}}{{if not $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}
234)
235
236// Suppress "imported and not used" errors
237var _ codes.Code
238var _ io.Reader
239var _ status.Status
240var _ = runtime.String
241var _ = utilities.NewDoubleArray
242var _ = descriptor.ForMessage
243`))
244
245 handlerTemplate = template.Must(template.New("handler").Parse(`
246{{if and .Method.GetClientStreaming .Method.GetServerStreaming}}
247{{template "bidi-streaming-request-func" .}}
248{{else if .Method.GetClientStreaming}}
249{{template "client-streaming-request-func" .}}
250{{else}}
251{{template "client-rpc-request-func" .}}
252{{end}}
253`))
254
255 _ = template.Must(handlerTemplate.New("request-func-signature").Parse(strings.Replace(`
256{{if .Method.GetServerStreaming}}
257func request_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}(ctx context.Context, marshaler runtime.Marshaler, client {{.Method.Service.GetName}}Client, req *http.Request, pathParams map[string]string) ({{.Method.Service.GetName}}_{{.Method.GetName}}Client, runtime.ServerMetadata, error)
258{{else}}
259func request_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}(ctx context.Context, marshaler runtime.Marshaler, client {{.Method.Service.GetName}}Client, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error)
260{{end}}`, "\n", "", -1)))
261
262 _ = template.Must(handlerTemplate.New("client-streaming-request-func").Parse(`
263{{template "request-func-signature" .}} {
264 var metadata runtime.ServerMetadata
265 stream, err := client.{{.Method.GetName}}(ctx)
266 if err != nil {
267 grpclog.Infof("Failed to start streaming: %v", err)
268 return nil, metadata, err
269 }
270 dec := marshaler.NewDecoder(req.Body)
271 for {
272 var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
273 err = dec.Decode(&protoReq)
274 if err == io.EOF {
275 break
276 }
277 if err != nil {
278 grpclog.Infof("Failed to decode request: %v", err)
279 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
280 }
281 if err = stream.Send(&protoReq); err != nil {
282 if err == io.EOF {
283 break
284 }
285 grpclog.Infof("Failed to send request: %v", err)
286 return nil, metadata, err
287 }
288 }
289
290 if err := stream.CloseSend(); err != nil {
291 grpclog.Infof("Failed to terminate client stream: %v", err)
292 return nil, metadata, err
293 }
294 header, err := stream.Header()
295 if err != nil {
296 grpclog.Infof("Failed to get header from client: %v", err)
297 return nil, metadata, err
298 }
299 metadata.HeaderMD = header
300{{if .Method.GetServerStreaming}}
301 return stream, metadata, nil
302{{else}}
303 msg, err := stream.CloseAndRecv()
304 metadata.TrailerMD = stream.Trailer()
305 return msg, metadata, err
306{{end}}
307}
308`))
309
310 _ = template.Must(handlerTemplate.New("client-rpc-request-func").Parse(`
311{{$AllowPatchFeature := .AllowPatchFeature}}
312{{if .HasQueryParam}}
313var (
314 filter_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}} = {{.QueryParamFilter}}
315)
316{{end}}
317{{template "request-func-signature" .}} {
318 var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
319 var metadata runtime.ServerMetadata
320{{if .Body}}
321 newReader, berr := utilities.IOReaderFactory(req.Body)
322 if berr != nil {
323 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
324 }
325 if err := marshaler.NewDecoder(newReader()).Decode(&{{.Body.AssignableExpr "protoReq"}}); err != nil && err != io.EOF {
326 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
327 }
328 {{- if and $AllowPatchFeature (eq (.HTTPMethod) "PATCH") (.FieldMaskField) (not (eq "*" .GetBodyFieldPath)) }}
329 if protoReq.{{.FieldMaskField}} == nil || len(protoReq.{{.FieldMaskField}}.GetPaths()) == 0 {
330 _, md := descriptor.ForMessage(protoReq.{{.GetBodyFieldStructName}})
331 if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), md); err != nil {
332 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
333 } else {
334 protoReq.{{.FieldMaskField}} = fieldMask
335 }
336 }
337 {{end}}
338{{end}}
339{{if .PathParams}}
340 var (
341 val string
342{{- if .HasEnumPathParam}}
343 e int32
344{{- end}}
345{{- if .HasRepeatedEnumPathParam}}
346 es []int32
347{{- end}}
348 ok bool
349 err error
350 _ = err
351 )
352 {{$binding := .}}
353 {{range $param := .PathParams}}
354 {{$enum := $binding.LookupEnum $param}}
355 val, ok = pathParams[{{$param | printf "%q"}}]
356 if !ok {
357 return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", {{$param | printf "%q"}})
358 }
359{{if $param.IsNestedProto3}}
360 err = runtime.PopulateFieldFromPath(&protoReq, {{$param | printf "%q"}}, val)
361 {{if $enum}}
362 e{{if $param.IsRepeated}}s{{end}}, err = {{$param.ConvertFuncExpr}}(val{{if $param.IsRepeated}}, {{$binding.Registry.GetRepeatedPathParamSeparator | printf "%c" | printf "%q"}}{{end}}, {{$enum.GoType $param.Target.Message.File.GoPkg.Path}}_value)
363 {{end}}
364{{else if $enum}}
365 e{{if $param.IsRepeated}}s{{end}}, err = {{$param.ConvertFuncExpr}}(val{{if $param.IsRepeated}}, {{$binding.Registry.GetRepeatedPathParamSeparator | printf "%c" | printf "%q"}}{{end}}, {{$enum.GoType $param.Target.Message.File.GoPkg.Path}}_value)
366{{else}}
367 {{$param.AssignableExpr "protoReq"}}, err = {{$param.ConvertFuncExpr}}(val{{if $param.IsRepeated}}, {{$binding.Registry.GetRepeatedPathParamSeparator | printf "%c" | printf "%q"}}{{end}})
368{{end}}
369 if err != nil {
370 return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", {{$param | printf "%q"}}, err)
371 }
372{{if and $enum $param.IsRepeated}}
373 s := make([]{{$enum.GoType $param.Target.Message.File.GoPkg.Path}}, len(es))
374 for i, v := range es {
375 s[i] = {{$enum.GoType $param.Target.Message.File.GoPkg.Path}}(v)
376 }
377 {{$param.AssignableExpr "protoReq"}} = s
378{{else if $enum}}
379 {{$param.AssignableExpr "protoReq"}} = {{$enum.GoType $param.Target.Message.File.GoPkg.Path}}(e)
380{{end}}
381 {{end}}
382{{end}}
383{{if .HasQueryParam}}
384 if err := req.ParseForm(); err != nil {
385 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
386 }
387 if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}); err != nil {
388 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
389 }
390{{end}}
391{{if .Method.GetServerStreaming}}
392 stream, err := client.{{.Method.GetName}}(ctx, &protoReq)
393 if err != nil {
394 return nil, metadata, err
395 }
396 header, err := stream.Header()
397 if err != nil {
398 return nil, metadata, err
399 }
400 metadata.HeaderMD = header
401 return stream, metadata, nil
402{{else}}
403 msg, err := client.{{.Method.GetName}}(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
404 return msg, metadata, err
405{{end}}
406}`))
407
408 _ = template.Must(handlerTemplate.New("bidi-streaming-request-func").Parse(`
409{{template "request-func-signature" .}} {
410 var metadata runtime.ServerMetadata
411 stream, err := client.{{.Method.GetName}}(ctx)
412 if err != nil {
413 grpclog.Infof("Failed to start streaming: %v", err)
414 return nil, metadata, err
415 }
416 dec := marshaler.NewDecoder(req.Body)
417 handleSend := func() error {
418 var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
419 err := dec.Decode(&protoReq)
420 if err == io.EOF {
421 return err
422 }
423 if err != nil {
424 grpclog.Infof("Failed to decode request: %v", err)
425 return err
426 }
427 if err := stream.Send(&protoReq); err != nil {
428 grpclog.Infof("Failed to send request: %v", err)
429 return err
430 }
431 return nil
432 }
433 if err := handleSend(); err != nil {
434 if cerr := stream.CloseSend(); cerr != nil {
435 grpclog.Infof("Failed to terminate client stream: %v", cerr)
436 }
437 if err == io.EOF {
438 return stream, metadata, nil
439 }
440 return nil, metadata, err
441 }
442 go func() {
443 for {
444 if err := handleSend(); err != nil {
445 break
446 }
447 }
448 if err := stream.CloseSend(); err != nil {
449 grpclog.Infof("Failed to terminate client stream: %v", err)
450 }
451 }()
452 header, err := stream.Header()
453 if err != nil {
454 grpclog.Infof("Failed to get header from client: %v", err)
455 return nil, metadata, err
456 }
457 metadata.HeaderMD = header
458 return stream, metadata, nil
459}
460`))
461
462 localHandlerTemplate = template.Must(template.New("local-handler").Parse(`
463{{if and .Method.GetClientStreaming .Method.GetServerStreaming}}
464{{else if .Method.GetClientStreaming}}
465{{else if .Method.GetServerStreaming}}
466{{else}}
467{{template "local-client-rpc-request-func" .}}
468{{end}}
469`))
470
471 _ = template.Must(localHandlerTemplate.New("local-request-func-signature").Parse(strings.Replace(`
472{{if .Method.GetServerStreaming}}
473{{else}}
474func local_request_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}(ctx context.Context, marshaler runtime.Marshaler, server {{.Method.Service.GetName}}Server, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error)
475{{end}}`, "\n", "", -1)))
476
477 _ = template.Must(localHandlerTemplate.New("local-client-rpc-request-func").Parse(`
478{{$AllowPatchFeature := .AllowPatchFeature}}
479{{template "local-request-func-signature" .}} {
480 var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
481 var metadata runtime.ServerMetadata
482{{if .Body}}
483 newReader, berr := utilities.IOReaderFactory(req.Body)
484 if berr != nil {
485 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
486 }
487 if err := marshaler.NewDecoder(newReader()).Decode(&{{.Body.AssignableExpr "protoReq"}}); err != nil && err != io.EOF {
488 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
489 }
490 {{- if and $AllowPatchFeature (eq (.HTTPMethod) "PATCH") (.FieldMaskField) (not (eq "*" .GetBodyFieldPath)) }}
491 if protoReq.{{.FieldMaskField}} == nil || len(protoReq.{{.FieldMaskField}}.GetPaths()) == 0 {
492 _, md := descriptor.ForMessage(protoReq.{{.GetBodyFieldStructName}})
493 if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), md); err != nil {
494 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
495 } else {
496 protoReq.{{.FieldMaskField}} = fieldMask
497 }
498 }
499 {{end}}
500{{end}}
501{{if .PathParams}}
502 var (
503 val string
504{{- if .HasEnumPathParam}}
505 e int32
506{{- end}}
507{{- if .HasRepeatedEnumPathParam}}
508 es []int32
509{{- end}}
510 ok bool
511 err error
512 _ = err
513 )
514 {{$binding := .}}
515 {{range $param := .PathParams}}
516 {{$enum := $binding.LookupEnum $param}}
517 val, ok = pathParams[{{$param | printf "%q"}}]
518 if !ok {
519 return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", {{$param | printf "%q"}})
520 }
521{{if $param.IsNestedProto3}}
522 err = runtime.PopulateFieldFromPath(&protoReq, {{$param | printf "%q"}}, val)
523 {{if $enum}}
524 e{{if $param.IsRepeated}}s{{end}}, err = {{$param.ConvertFuncExpr}}(val{{if $param.IsRepeated}}, {{$binding.Registry.GetRepeatedPathParamSeparator | printf "%c" | printf "%q"}}{{end}}, {{$enum.GoType $param.Target.Message.File.GoPkg.Path}}_value)
525 {{end}}
526{{else if $enum}}
527 e{{if $param.IsRepeated}}s{{end}}, err = {{$param.ConvertFuncExpr}}(val{{if $param.IsRepeated}}, {{$binding.Registry.GetRepeatedPathParamSeparator | printf "%c" | printf "%q"}}{{end}}, {{$enum.GoType $param.Target.Message.File.GoPkg.Path}}_value)
528{{else}}
529 {{$param.AssignableExpr "protoReq"}}, err = {{$param.ConvertFuncExpr}}(val{{if $param.IsRepeated}}, {{$binding.Registry.GetRepeatedPathParamSeparator | printf "%c" | printf "%q"}}{{end}})
530{{end}}
531 if err != nil {
532 return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", {{$param | printf "%q"}}, err)
533 }
534{{if and $enum $param.IsRepeated}}
535 s := make([]{{$enum.GoType $param.Target.Message.File.GoPkg.Path}}, len(es))
536 for i, v := range es {
537 s[i] = {{$enum.GoType $param.Target.Message.File.GoPkg.Path}}(v)
538 }
539 {{$param.AssignableExpr "protoReq"}} = s
540{{else if $enum}}
541 {{$param.AssignableExpr "protoReq"}} = {{$enum.GoType $param.Target.Message.File.GoPkg.Path}}(e)
542{{end}}
543 {{end}}
544{{end}}
545{{if .HasQueryParam}}
546 if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}); err != nil {
547 return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
548 }
549{{end}}
550{{if .Method.GetServerStreaming}}
551 // TODO
552{{else}}
553 msg, err := server.{{.Method.GetName}}(ctx, &protoReq)
554 return msg, metadata, err
555{{end}}
556}`))
557
558 localTrailerTemplate = template.Must(template.New("local-trailer").Parse(`
559{{$UseRequestContext := .UseRequestContext}}
560{{range $svc := .Services}}
561// Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}Server registers the http handlers for service {{$svc.GetName}} to "mux".
562// UnaryRPC :call {{$svc.GetName}}Server directly.
563// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
564func Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}Server(ctx context.Context, mux *runtime.ServeMux, server {{$svc.GetName}}Server) error {
565 {{range $m := $svc.Methods}}
566 {{range $b := $m.Bindings}}
567 {{if or $m.GetClientStreaming $m.GetServerStreaming}}
568 mux.Handle({{$b.HTTPMethod | printf "%q"}}, pattern_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
569 err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
570 _, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
571 runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
572 return
573 })
574 {{else}}
575 mux.Handle({{$b.HTTPMethod | printf "%q"}}, pattern_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
576 {{- if $UseRequestContext }}
577 ctx, cancel := context.WithCancel(req.Context())
578 {{- else -}}
579 ctx, cancel := context.WithCancel(ctx)
580 {{- end }}
581 defer cancel()
582 inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
583 rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
584 if err != nil {
585 runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
586 return
587 }
588 resp, md, err := local_request_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(rctx, inboundMarshaler, server, req, pathParams)
589 ctx = runtime.NewServerMetadataContext(ctx, md)
590 if err != nil {
591 runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
592 return
593 }
594
595 {{ if $b.ResponseBody }}
596 forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(ctx, mux, outboundMarshaler, w, req, response_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}{resp}, mux.GetForwardResponseOptions()...)
597 {{ else }}
598 forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
599 {{end}}
600 })
601 {{end}}
602 {{end}}
603 {{end}}
604 return nil
605}
606{{end}}`))
607
608 trailerTemplate = template.Must(template.New("trailer").Parse(`
609{{$UseRequestContext := .UseRequestContext}}
610{{range $svc := .Services}}
611// Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}FromEndpoint is same as Register{{$svc.GetName}}{{$.RegisterFuncSuffix}} but
612// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
613func Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}FromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
614 conn, err := grpc.Dial(endpoint, opts...)
615 if err != nil {
616 return err
617 }
618 defer func() {
619 if err != nil {
620 if cerr := conn.Close(); cerr != nil {
621 grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
622 }
623 return
624 }
625 go func() {
626 <-ctx.Done()
627 if cerr := conn.Close(); cerr != nil {
628 grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
629 }
630 }()
631 }()
632
633 return Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}(ctx, mux, conn)
634}
635
636// Register{{$svc.GetName}}{{$.RegisterFuncSuffix}} registers the http handlers for service {{$svc.GetName}} to "mux".
637// The handlers forward requests to the grpc endpoint over "conn".
638func Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
639 return Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}Client(ctx, mux, New{{$svc.GetName}}Client(conn))
640}
641
642// Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}Client registers the http handlers for service {{$svc.GetName}}
643// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "{{$svc.GetName}}Client".
644// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "{{$svc.GetName}}Client"
645// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
646// "{{$svc.GetName}}Client" to call the correct interceptors.
647func Register{{$svc.GetName}}{{$.RegisterFuncSuffix}}Client(ctx context.Context, mux *runtime.ServeMux, client {{$svc.GetName}}Client) error {
648 {{range $m := $svc.Methods}}
649 {{range $b := $m.Bindings}}
650 mux.Handle({{$b.HTTPMethod | printf "%q"}}, pattern_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
651 {{- if $UseRequestContext }}
652 ctx, cancel := context.WithCancel(req.Context())
653 {{- else -}}
654 ctx, cancel := context.WithCancel(ctx)
655 {{- end }}
656 defer cancel()
657 inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
658 rctx, err := runtime.AnnotateContext(ctx, mux, req)
659 if err != nil {
660 runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
661 return
662 }
663 resp, md, err := request_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(rctx, inboundMarshaler, client, req, pathParams)
664 ctx = runtime.NewServerMetadataContext(ctx, md)
665 if err != nil {
666 runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
667 return
668 }
669 {{if $m.GetServerStreaming}}
670 forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(ctx, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
671 {{else}}
672 {{ if $b.ResponseBody }}
673 forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(ctx, mux, outboundMarshaler, w, req, response_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}{resp}, mux.GetForwardResponseOptions()...)
674 {{ else }}
675 forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
676 {{end}}
677 {{end}}
678 })
679 {{end}}
680 {{end}}
681 return nil
682}
683
684{{range $m := $svc.Methods}}
685{{range $b := $m.Bindings}}
686{{if $b.ResponseBody}}
687type response_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} struct {
688 proto.Message
689}
690
691func (m response_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}) XXX_ResponseBody() interface{} {
692 response := m.Message.(*{{$m.ResponseType.GoType $m.Service.File.GoPkg.Path}})
693 return {{$b.ResponseBody.AssignableExpr "response"}}
694}
695{{end}}
696{{end}}
697{{end}}
698
699var (
700 {{range $m := $svc.Methods}}
701 {{range $b := $m.Bindings}}
702 pattern_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = runtime.MustPattern(runtime.NewPattern({{$b.PathTmpl.Version}}, {{$b.PathTmpl.OpCodes | printf "%#v"}}, {{$b.PathTmpl.Pool | printf "%#v"}}, {{$b.PathTmpl.Verb | printf "%q"}}, runtime.AssumeColonVerbOpt({{$.AssumeColonVerb}})))
703 {{end}}
704 {{end}}
705)
706
707var (
708 {{range $m := $svc.Methods}}
709 {{range $b := $m.Bindings}}
710 forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = {{if $m.GetServerStreaming}}runtime.ForwardResponseStream{{else}}runtime.ForwardResponseMessage{{end}}
711 {{end}}
712 {{end}}
713)
714{{end}}`))
715)