├── .bazelversion ├── docs ├── .gitignore ├── _imgs │ └── gotemplates │ │ ├── postman.png │ │ └── swaggerui.png ├── Gemfile ├── _config.yaml ├── _docs │ ├── background.md │ ├── aws.md │ ├── features.md │ ├── httpbody.md │ ├── faq.md │ └── examples.md ├── run.sh └── index.md ├── examples └── internal │ ├── clients │ ├── abe │ │ ├── .gitignore │ │ ├── .swagger-codegen │ │ │ └── VERSION │ │ ├── .swagger-codegen-ignore │ │ ├── model_examplepb_body.go │ │ ├── model_sub_string_message.go │ │ ├── model_examplepb_update_v2_request.go │ │ ├── model_pathenum_path_enum.go │ │ ├── model_runtime_error.go │ │ ├── model_a_bit_of_everything_nested.go │ │ ├── model_nested_deep_enum.go │ │ ├── model_message_path_enum_nested_path_enum.go │ │ ├── model_examplepb_numeric_enum.go │ │ ├── model_examplepb_book.go │ │ ├── response.go │ │ ├── BUILD.bazel │ │ ├── model_examplepb_a_bit_of_everything_repeated.go │ │ ├── enum_helper.go │ │ └── configuration.go │ ├── echo │ │ ├── .gitignore │ │ ├── .swagger-codegen │ │ │ └── VERSION │ │ ├── .swagger-codegen-ignore │ │ ├── model_examplepb_embedded.go │ │ ├── model_runtime_error.go │ │ ├── BUILD.bazel │ │ ├── model_examplepb_simple_message.go │ │ ├── response.go │ │ └── configuration.go │ ├── unannotatedecho │ │ ├── .gitignore │ │ ├── .swagger-codegen │ │ │ └── VERSION │ │ ├── .swagger-codegen-ignore │ │ ├── BUILD.bazel │ │ ├── model_runtime_error.go │ │ ├── model_examplepb_unannotated_simple_message.go │ │ ├── response.go │ │ └── configuration.go │ ├── responsebody │ │ ├── .swagger-codegen │ │ │ └── VERSION │ │ ├── docs │ │ │ ├── ResponseResponseType.md │ │ │ ├── ExamplepbResponseBodyReq.md │ │ │ ├── ExamplepbResponseBodyOutResponse.md │ │ │ ├── ExamplepbRepeatedResponseStrings.md │ │ │ ├── ExamplepbResponseBodyMessageResponse.md │ │ │ ├── ExamplepbResponseBodyOut.md │ │ │ ├── ExamplepbRepeatedResponseBodyOut.md │ │ │ ├── ExamplepbRepeatedResponseBodyOutResponse.md │ │ │ ├── ExamplepbResponseBodyMessage.md │ │ │ ├── RuntimeError.md │ │ │ ├── StreamResultOfExamplepbResponseBodyOut.md │ │ │ ├── RuntimeStreamError.md │ │ │ └── ProtobufAny.md │ │ ├── .gitignore │ │ ├── model_examplepb_response_body_out_response.go │ │ ├── model_examplepb_repeated_response_strings.go │ │ ├── model_examplepb_response_body_out.go │ │ ├── model_examplepb_repeated_response_body_out.go │ │ ├── model_examplepb_repeated_response_body_out_response.go │ │ ├── model_stream_result_of_examplepb_response_body_out.go │ │ ├── model_runtime_error.go │ │ ├── model_response_response_type.go │ │ ├── model_runtime_stream_error.go │ │ ├── BUILD.bazel │ │ ├── .swagger-codegen-ignore │ │ ├── response.go │ │ └── configuration.go │ └── generateunboundmethods │ │ ├── .gitignore │ │ ├── .swagger-codegen │ │ └── VERSION │ │ ├── .swagger-codegen-ignore │ │ ├── docs │ │ ├── ProtobufAny.md │ │ ├── RuntimeError.md │ │ └── ExamplepbGenerateUnboundMethodsSimpleMessage.md │ │ ├── model_protobuf_any.go │ │ ├── BUILD.bazel │ │ ├── model_runtime_error.go │ │ ├── model_examplepb_generate_unbound_methods_simple_message.go │ │ ├── response.go │ │ └── configuration.go │ ├── browser │ ├── bin │ │ └── .gitignore │ ├── .gitignore │ ├── bower.json │ ├── README.md │ ├── index.html │ ├── package.json │ ├── echo_service.spec.js │ └── gulpfile.js │ ├── gateway │ ├── doc.go │ ├── BUILD.bazel │ ├── gateway.go │ ├── main.go │ └── handlers.go │ ├── proto │ ├── sub │ │ ├── message.proto │ │ └── BUILD.bazel │ ├── sub2 │ │ ├── message.proto │ │ └── BUILD.bazel │ ├── pathenum │ │ ├── path_enum.proto │ │ └── BUILD.bazel │ └── examplepb │ │ ├── unannotated_echo_service.yaml │ │ ├── generated_input.proto │ │ ├── stream.proto │ │ ├── swagger_merge_b.proto │ │ ├── unannotated_echo_service.proto │ │ ├── generate_unbound_methods.proto │ │ ├── response_body_service.proto │ │ ├── echo_service.proto │ │ ├── swagger_merge_a.proto │ │ ├── non_standard_names.proto │ │ └── wrappers.proto │ ├── cmd │ ├── example-gateway-server │ │ ├── BUILD.bazel │ │ └── main.go │ └── example-grpc-server │ │ ├── BUILD.bazel │ │ └── main.go │ ├── README.md │ ├── server │ ├── echo.go │ ├── unannotatedecho.go │ ├── fieldmask_helper.go │ ├── non_standard_names.go │ ├── BUILD.bazel │ ├── responsebody.go │ ├── flow_combination.go │ └── main.go │ ├── integration │ └── BUILD.bazel │ └── helloworld │ ├── helloworld.proto │ └── BUILD.bazel ├── bin └── .gitignore ├── utilities ├── doc.go ├── readerfactory.go ├── BUILD.bazel └── pattern.go ├── protoc-gen-swagger ├── genswagger │ ├── doc.go │ ├── helpers.go │ ├── helpers_go111_old.go │ └── BUILD.bazel ├── BUILD.bazel └── options │ ├── BUILD.bazel │ └── annotations.proto ├── codegenerator ├── doc.go ├── parse_req.go ├── BUILD.bazel └── parse_req_test.go ├── protoc-gen-grpc-gateway ├── internal │ └── gengateway │ │ ├── doc.go │ │ └── BUILD.bazel ├── httprule │ ├── fuzz.go │ ├── BUILD.bazel │ ├── types.go │ ├── types_test.go │ └── compile.go ├── generator │ ├── BUILD.bazel │ └── generator.go ├── descriptor │ ├── grpc_api_service.go │ ├── BUILD.bazel │ └── grpc_api_configuration.go └── BUILD.bazel ├── runtime ├── doc.go ├── internal │ └── examplepb │ │ └── BUILD.bazel ├── marshal_httpbodyproto_test.go ├── marshal_json.go ├── marshal_httpbodyproto.go ├── marshal_proto.go ├── marshaler.go ├── proto2_convert.go ├── marshal_proto_test.go └── fieldmask.go ├── .github ├── ISSUE_TEMPLATE │ ├── feature.md │ ├── documentation.md │ └── bug.md ├── stale.yml ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── internal ├── casing │ ├── README.md │ ├── BUILD.bazel │ ├── LICENSE.md │ └── camel.go ├── BUILD.bazel └── errors.proto ├── .gitignore ├── .circleci ├── README.md └── Dockerfile ├── third_party └── googleapis │ ├── README.grpc-gateway │ └── google │ └── api │ ├── annotations.proto │ └── httpbody.proto ├── go.mod ├── BUILD ├── .goreleaser.yml ├── .bazelci └── presubmit.yml ├── fuzzit.sh ├── .devcontainer └── devcontainer.json ├── ADOPTERS.md ├── renovate.json └── LICENSE.txt /.bazelversion: -------------------------------------------------------------------------------- 1 | 3.5.0 2 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/.gitignore: -------------------------------------------------------------------------------- 1 | /docs 2 | -------------------------------------------------------------------------------- /examples/internal/clients/echo/.gitignore: -------------------------------------------------------------------------------- 1 | /docs 2 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/.swagger-codegen/VERSION: -------------------------------------------------------------------------------- 1 | 2.4.8 -------------------------------------------------------------------------------- /examples/internal/clients/echo/.swagger-codegen/VERSION: -------------------------------------------------------------------------------- 1 | 2.4.8 -------------------------------------------------------------------------------- /examples/internal/clients/unannotatedecho/.gitignore: -------------------------------------------------------------------------------- 1 | /docs 2 | -------------------------------------------------------------------------------- /examples/internal/browser/bin/.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | !/.gitignore 3 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/.swagger-codegen-ignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/.swagger-codegen/VERSION: -------------------------------------------------------------------------------- 1 | 2.4.8 -------------------------------------------------------------------------------- /examples/internal/clients/echo/.swagger-codegen-ignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/.gitignore: -------------------------------------------------------------------------------- 1 | /docs 2 | -------------------------------------------------------------------------------- /examples/internal/clients/unannotatedecho/.swagger-codegen/VERSION: -------------------------------------------------------------------------------- 1 | 2.4.8 -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/.swagger-codegen/VERSION: -------------------------------------------------------------------------------- 1 | 2.4.8 -------------------------------------------------------------------------------- /examples/internal/clients/unannotatedecho/.swagger-codegen-ignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | /protoc-gen-go 2 | /protoc-gen-grpc-gateway 3 | /protoc-gen-swagger 4 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/.swagger-codegen-ignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | -------------------------------------------------------------------------------- /examples/internal/browser/.gitignore: -------------------------------------------------------------------------------- 1 | /bower_components 2 | /node_modules 3 | /package-lock.json 4 | -------------------------------------------------------------------------------- /examples/internal/gateway/doc.go: -------------------------------------------------------------------------------- 1 | // Package gateway is an example of grpc-gateway server 2 | package gateway 3 | -------------------------------------------------------------------------------- /utilities/doc.go: -------------------------------------------------------------------------------- 1 | // Package utilities provides members for internal use in grpc-gateway. 2 | package utilities 3 | -------------------------------------------------------------------------------- /protoc-gen-swagger/genswagger/doc.go: -------------------------------------------------------------------------------- 1 | // Package genswagger provides a code generator for swagger. 2 | package genswagger 3 | -------------------------------------------------------------------------------- /docs/_imgs/gotemplates/postman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tetratelabs/grpc-gateway/master/docs/_imgs/gotemplates/postman.png -------------------------------------------------------------------------------- /docs/_imgs/gotemplates/swaggerui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tetratelabs/grpc-gateway/master/docs/_imgs/gotemplates/swaggerui.png -------------------------------------------------------------------------------- /codegenerator/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package codegenerator contains reusable functions used by the code generators. 3 | */ 4 | package codegenerator 5 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/internal/gengateway/doc.go: -------------------------------------------------------------------------------- 1 | // Package gengateway provides a code generator for grpc gateway files. 2 | package gengateway 3 | -------------------------------------------------------------------------------- /runtime/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package runtime contains runtime helper functions used by 3 | servers which protoc-gen-grpc-gateway generates. 4 | */ 5 | package runtime 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature 3 | about: Submit a proposal/request for a new feature 4 | --- 5 | 6 | ## 🚀 Feature 7 | 8 | (A clear and concise description of what the feature is.) 9 | -------------------------------------------------------------------------------- /examples/internal/proto/sub/message.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | option go_package = "sub"; 3 | package grpc.gateway.examples.internal.sub; 4 | 5 | message StringMessage { 6 | required string value = 1; 7 | } 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 📚 Documentation 3 | about: Report an issue related to documentation 4 | --- 5 | 6 | ## 📚 Documentation 7 | 8 | (A clear and concise description of what the issue is.) 9 | -------------------------------------------------------------------------------- /protoc-gen-swagger/genswagger/helpers.go: -------------------------------------------------------------------------------- 1 | //+build go1.12 2 | 3 | package genswagger 4 | 5 | import "strings" 6 | 7 | func fieldName(k string) string { 8 | return strings.ReplaceAll(strings.Title(k), "-", "_") 9 | } 10 | -------------------------------------------------------------------------------- /examples/internal/proto/sub2/message.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/sub2"; 3 | package sub2; 4 | 5 | message IdMessage { 6 | string uuid = 1; 7 | } 8 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/httprule/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package httprule 4 | 5 | func Fuzz(data []byte) int { 6 | _, err := Parse(string(data)) 7 | if err != nil { 8 | return 0 9 | } 10 | return 0 11 | } 12 | -------------------------------------------------------------------------------- /protoc-gen-swagger/genswagger/helpers_go111_old.go: -------------------------------------------------------------------------------- 1 | //+build !go1.12 2 | 3 | package genswagger 4 | 5 | import "strings" 6 | 7 | func fieldName(k string) string { 8 | return strings.Replace(strings.Title(k), "-", "_", -1) 9 | } 10 | -------------------------------------------------------------------------------- /internal/casing/README.md: -------------------------------------------------------------------------------- 1 | # Case conversion 2 | 3 | This package contains a single function, copied from the 4 | `github.com/golang/protobuf/protoc-gen-go/generator` package. That 5 | modules LICENSE is referenced in its entirety in this package. 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _output/ 2 | .idea 3 | 4 | # Bazel. 5 | bazel-bin 6 | bazel-genfiles 7 | bazel-grpc-gateway 8 | bazel-out 9 | bazel-testlogs 10 | .bazelrc 11 | 12 | # Go vendor directory 13 | vendor 14 | 15 | # Generated travis files 16 | .travis.yml 17 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :development, :test do 4 | gem 'github-pages', '~> 207' 5 | gem 'jekyll', '~> 3.9.0' 6 | gem 'jekyll-redirect-from', '~> 0.15.0' 7 | gem 'jekyll-sitemap', '~> 1.4.0' 8 | gem 'jekyll-toc', '~> 0.5.1' 9 | end 10 | -------------------------------------------------------------------------------- /internal/casing/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = ["camel.go"], 6 | importpath = "github.com/grpc-ecosystem/grpc-gateway/internal/casing", 7 | visibility = ["//:__subpackages__"], 8 | ) 9 | -------------------------------------------------------------------------------- /docs/_config.yaml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect 2 | 3 | repository: grpc-ecosystem/grpc-gateway 4 | 5 | collections: 6 | docs: 7 | output: true 8 | 9 | defaults: 10 | - scope: 11 | path: "" 12 | values: 13 | layout: "default" 14 | 15 | plugins: 16 | - jekyll-toc 17 | 18 | exclude: 19 | - run.sh 20 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ResponseResponseType.md: -------------------------------------------------------------------------------- 1 | # ResponseResponseType 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | 7 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 8 | 9 | 10 | -------------------------------------------------------------------------------- /.circleci/README.md: -------------------------------------------------------------------------------- 1 | ## gRPC-Gateway CI testing setup 2 | 3 | Contained within is the CI test setup for the Gateway. It runs on Circle CI. 4 | 5 | ### Whats up with the Dockerfile? 6 | 7 | The `Dockerfile` in this folder is used as the build environment when regenerating the files (see CONTRIBUTING.md). 8 | The canonical repository for this Dockerfile is `docker.pkg.github.com/grpc-ecosystem/grpc-gateway/build-env`. 9 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ExamplepbResponseBodyReq.md: -------------------------------------------------------------------------------- 1 | # ExamplepbResponseBodyReq 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Data** | **string** | | [optional] [default to null] 7 | 8 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_examplepb_body.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | type ExamplepbBody struct { 14 | Name string `json:"name,omitempty"` 15 | } 16 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_sub_string_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | type SubStringMessage struct { 14 | Value string `json:"value,omitempty"` 15 | } 16 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ExamplepbResponseBodyOutResponse.md: -------------------------------------------------------------------------------- 1 | # ExamplepbResponseBodyOutResponse 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Data** | **string** | | [optional] [default to null] 7 | 8 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ExamplepbRepeatedResponseStrings.md: -------------------------------------------------------------------------------- 1 | # ExamplepbRepeatedResponseStrings 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Values** | **[]string** | | [optional] [default to null] 7 | 8 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ExamplepbResponseBodyMessageResponse.md: -------------------------------------------------------------------------------- 1 | # ExamplepbResponseBodyMessageResponse 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Data** | **string** | | [optional] [default to null] 7 | 8 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/docs/ProtobufAny.md: -------------------------------------------------------------------------------- 1 | # ProtobufAny 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **TypeUrl** | **string** | | [optional] [default to null] 7 | **Value** | **string** | | [optional] [default to null] 8 | 9 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 10 | 11 | 12 | -------------------------------------------------------------------------------- /utilities/readerfactory.go: -------------------------------------------------------------------------------- 1 | package utilities 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "io/ioutil" 7 | ) 8 | 9 | // IOReaderFactory takes in an io.Reader and returns a function that will allow you to create a new reader that begins 10 | // at the start of the stream 11 | func IOReaderFactory(r io.Reader) (func() io.Reader, error) { 12 | b, err := ioutil.ReadAll(r) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | return func() io.Reader { 18 | return bytes.NewReader(b) 19 | }, nil 20 | } 21 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/generator/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = ["generator.go"], 8 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/generator", 9 | deps = [ 10 | "//protoc-gen-grpc-gateway/descriptor:go_default_library", 11 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 12 | ], 13 | ) 14 | -------------------------------------------------------------------------------- /examples/internal/clients/echo/model_examplepb_embedded.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Echo Service 3 | * 4 | * Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package echo 11 | 12 | // Embedded represents a message embedded in SimpleMessage. 13 | type ExamplepbEmbedded struct { 14 | Progress string `json:"progress,omitempty"` 15 | Note string `json:"note,omitempty"` 16 | } 17 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ExamplepbResponseBodyOut.md: -------------------------------------------------------------------------------- 1 | # ExamplepbResponseBodyOut 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Response** | [***ExamplepbResponseBodyOutResponse**](examplepbResponseBodyOutResponse.md) | | [optional] [default to null] 7 | 8 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/model_examplepb_response_body_out_response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | type ExamplepbResponseBodyOutResponse struct { 13 | Data string `json:"data,omitempty"` 14 | } 15 | -------------------------------------------------------------------------------- /examples/internal/clients/echo/model_runtime_error.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Echo Service 3 | * 4 | * Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package echo 11 | 12 | type RuntimeError struct { 13 | Error_ string `json:"error,omitempty"` 14 | Code int32 `json:"code,omitempty"` 15 | Message string `json:"message,omitempty"` 16 | Details []ProtobufAny `json:"details,omitempty"` 17 | } 18 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/model_examplepb_repeated_response_strings.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | type ExamplepbRepeatedResponseStrings struct { 13 | Values []string `json:"values,omitempty"` 14 | } 15 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/generator/generator.go: -------------------------------------------------------------------------------- 1 | // Package generator provides an abstract interface to code generators. 2 | package generator 3 | 4 | import ( 5 | plugin "github.com/golang/protobuf/protoc-gen-go/plugin" 6 | "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" 7 | ) 8 | 9 | // Generator is an abstraction of code generators. 10 | type Generator interface { 11 | // Generate generates output files from input .proto files. 12 | Generate(targets []*descriptor.File) ([]*plugin.CodeGeneratorResponse_File, error) 13 | } 14 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOut.md: -------------------------------------------------------------------------------- 1 | # ExamplepbRepeatedResponseBodyOut 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Response** | [**[]ExamplepbRepeatedResponseBodyOutResponse**](examplepbRepeatedResponseBodyOutResponse.md) | | [optional] [default to null] 7 | 8 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/model_examplepb_response_body_out.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | type ExamplepbResponseBodyOut struct { 13 | Response *ExamplepbResponseBodyOutResponse `json:"response,omitempty"` 14 | } 15 | -------------------------------------------------------------------------------- /examples/internal/proto/pathenum/path_enum.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/pathenum"; 3 | package grpc.gateway.examples.internal.pathenum; 4 | 5 | enum PathEnum { 6 | ABC = 0; 7 | DEF = 1; 8 | } 9 | 10 | message MessagePathEnum { 11 | enum NestedPathEnum { 12 | GHI = 0; 13 | JKL = 1; 14 | } 15 | } 16 | 17 | message MessageWithPathEnum { 18 | PathEnum value = 1; 19 | } 20 | 21 | message MessageWithNestedPathEnum { 22 | MessagePathEnum.NestedPathEnum value = 1; 23 | } 24 | -------------------------------------------------------------------------------- /third_party/googleapis/README.grpc-gateway: -------------------------------------------------------------------------------- 1 | Google APIs 2 | ============ 3 | 4 | Project: Google APIs 5 | URL: https://github.com/google/googleapis 6 | Revision: 3544ab16c3342d790b00764251e348705991ea4b 7 | License: Apache License 2.0 8 | 9 | 10 | Imported Files 11 | --------------- 12 | 13 | - google/api/annotations.proto 14 | - google/api/http.proto 15 | - google/api/httpbody.proto 16 | 17 | 18 | Generated Files 19 | ---------------- 20 | 21 | They are generated from the .proto files by protoc-gen-go. 22 | - google/api/annotations.pb.go 23 | - google/api/http.pb.go 24 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_examplepb_update_v2_request.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | type ExamplepbUpdateV2Request struct { 14 | Abe *ExamplepbABitOfEverything `json:"abe,omitempty"` 15 | UpdateMask *ProtobufFieldMask `json:"update_mask,omitempty"` 16 | } 17 | -------------------------------------------------------------------------------- /utilities/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "doc.go", 9 | "pattern.go", 10 | "readerfactory.go", 11 | "trie.go", 12 | ], 13 | importpath = "github.com/grpc-ecosystem/grpc-gateway/utilities", 14 | ) 15 | 16 | go_test( 17 | name = "go_default_test", 18 | size = "small", 19 | srcs = ["trie_test.go"], 20 | embed = [":go_default_library"], 21 | ) 22 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_pathenum_path_enum.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | type PathenumPathEnum string 14 | 15 | // List of pathenumPathEnum 16 | const ( 17 | ABC_PathenumPathEnum PathenumPathEnum = "ABC" 18 | DEF_PathenumPathEnum PathenumPathEnum = "DEF" 19 | ) 20 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/grpc-ecosystem/grpc-gateway 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/antihax/optional v1.0.0 7 | github.com/ghodss/yaml v1.0.0 8 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b 9 | github.com/golang/protobuf v1.3.3 10 | github.com/rogpeppe/fastuuid v1.2.0 11 | golang.org/x/net v0.0.0-20191002035440-2ec189313ef0 // indirect 12 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d 13 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 14 | google.golang.org/grpc v1.32.0 15 | gopkg.in/yaml.v2 v2.2.3 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Submit a bug report to help us improve 4 | --- 5 | 6 | ## 🐛 Bug Report 7 | 8 | (A clear and concise description of what the bug is.) 9 | 10 | ## To Reproduce 11 | 12 | (Write your steps here:) 13 | 14 | 1. Step 1... 15 | 1. Step 2... 16 | 1. Step 3... 17 | 18 | ## Expected behavior 19 | 20 | (Write what you thought would happen.) 21 | 22 | ## Actual Behavior 23 | 24 | (Write what happened. Add screenshots, if applicable.) 25 | 26 | ## Your Environment 27 | 28 | (Environment name, version and operating system.) 29 | -------------------------------------------------------------------------------- /examples/internal/browser/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc-gateway-example-browser", 3 | "description": "Example use of grpc-gateway from browser", 4 | "main": "index.js", 5 | "authors": [ 6 | "Yuki Yugui Sonoda " 7 | ], 8 | "license": "SEE LICENSE IN LICENSE file", 9 | "homepage": "https://github.com/grpc-ecosystem/grpc-gateway", 10 | "private": true, 11 | "dependencies": { 12 | "swagger-js": "~> 2.1" 13 | }, 14 | "ignore": [ 15 | "**/.*", 16 | "node_modules", 17 | "bower_components", 18 | "test", 19 | "tests" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/model_examplepb_repeated_response_body_out.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | type ExamplepbRepeatedResponseBodyOut struct { 13 | Response []ExamplepbRepeatedResponseBodyOutResponse `json:"response,omitempty"` 14 | } 15 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOutResponse.md: -------------------------------------------------------------------------------- 1 | # ExamplepbRepeatedResponseBodyOutResponse 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Data** | **string** | | [optional] [default to null] 7 | **Type_** | [***ResponseResponseType**](ResponseResponseType.md) | | [optional] [default to null] 8 | 9 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_runtime_error.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | type RuntimeError struct { 14 | Error_ string `json:"error,omitempty"` 15 | Code int32 `json:"code,omitempty"` 16 | Message string `json:"message,omitempty"` 17 | Details []ProtobufAny `json:"details,omitempty"` 18 | } 19 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ExamplepbResponseBodyMessage.md: -------------------------------------------------------------------------------- 1 | # ExamplepbResponseBodyMessage 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Request** | **string** | | [optional] [default to null] 7 | **Response** | [**ExamplepbResponseBodyMessageResponse**](examplepbResponseBodyMessageResponse.md) | | [optional] [default to null] 8 | 9 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/model_examplepb_repeated_response_body_out_response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | type ExamplepbRepeatedResponseBodyOutResponse struct { 13 | Data string `json:"data,omitempty"` 14 | Type_ *ResponseResponseType `json:"type,omitempty"` 15 | } 16 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/unannotated_echo_service.yaml: -------------------------------------------------------------------------------- 1 | type: google.api.Service 2 | config_version: 3 3 | 4 | http: 5 | rules: 6 | - selector: grpc.gateway.examples.internal.examplepb.UnannotatedEchoService.Echo 7 | post: "/v1/example/echo/{id}" 8 | additional_bindings: 9 | - get: "/v1/example/echo/{id}/{num}" 10 | - selector: grpc.gateway.examples.internal.examplepb.UnannotatedEchoService.EchoBody 11 | post: "/v1/example/echo_body" 12 | body: "*" 13 | - selector: grpc.gateway.examples.internal.examplepb.UnannotatedEchoService.EchoDelete 14 | delete: "/v1/example/echo_delete" 15 | 16 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/model_stream_result_of_examplepb_response_body_out.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | type StreamResultOfExamplepbResponseBodyOut struct { 13 | Result *ExamplepbResponseBodyOutResponse `json:"result,omitempty"` 14 | Error_ *RuntimeStreamError `json:"error,omitempty"` 15 | } 16 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | load("@bazel_gazelle//:def.bzl", "gazelle") 2 | load("@com_github_bazelbuild_buildtools//buildifier:def.bzl", "buildifier") 3 | 4 | buildifier( 5 | name = "buildifier", 6 | ) 7 | 8 | buildifier( 9 | name = "buildifier_check", 10 | mode = "check", 11 | ) 12 | 13 | # gazelle:exclude third_party 14 | # gazelle:exclude vendor 15 | # gazelle:exclude _output 16 | # gazelle:prefix github.com/grpc-ecosystem/grpc-gateway 17 | 18 | gazelle(name = "gazelle") 19 | 20 | package_group( 21 | name = "generators", 22 | packages = [ 23 | "//protoc-gen-grpc-gateway/...", 24 | "//protoc-gen-swagger/...", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/RuntimeError.md: -------------------------------------------------------------------------------- 1 | # RuntimeError 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Error_** | **string** | | [optional] [default to null] 7 | **Code** | **int32** | | [optional] [default to null] 8 | **Message** | **string** | | [optional] [default to null] 9 | **Details** | [**[]ProtobufAny**](protobufAny.md) | | [optional] [default to null] 10 | 11 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/model_runtime_error.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | type RuntimeError struct { 13 | Error_ string `json:"error,omitempty"` 14 | Code int32 `json:"code,omitempty"` 15 | Message string `json:"message,omitempty"` 16 | Details []ProtobufAny `json:"details,omitempty"` 17 | } 18 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/StreamResultOfExamplepbResponseBodyOut.md: -------------------------------------------------------------------------------- 1 | # StreamResultOfExamplepbResponseBodyOut 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Result** | [***ExamplepbResponseBodyOutResponse**](examplepbResponseBodyOutResponse.md) | | [optional] [default to null] 7 | **Error_** | [***RuntimeStreamError**](runtimeStreamError.md) | | [optional] [default to null] 8 | 9 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/docs/RuntimeError.md: -------------------------------------------------------------------------------- 1 | # RuntimeError 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Error_** | **string** | | [optional] [default to null] 7 | **Code** | **int32** | | [optional] [default to null] 8 | **Message** | **string** | | [optional] [default to null] 9 | **Details** | [**[]ProtobufAny**](protobufAny.md) | | [optional] [default to null] 10 | 11 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/internal/browser/README.md: -------------------------------------------------------------------------------- 1 | # Browser example 2 | 3 | This directory contains an example use of grpc-gateway with web browsers. 4 | The following commands automatically runs integration tests with phantomjs. 5 | 6 | ```shell-session 7 | $ npm install -g gulp-cli 8 | $ npm install 9 | $ gulp 10 | ``` 11 | 12 | ## Other examples 13 | 14 | ### Very simple example 15 | Run 16 | ```shell-session 17 | $ gulp bower 18 | $ gulp backends 19 | ``` 20 | 21 | then, open `index.html`. 22 | 23 | 24 | ### Integration test with your browser 25 | 26 | Run 27 | ```shell-session 28 | $ gulp serve 29 | ``` 30 | 31 | then, open `http://localhost:8000` with your browser. 32 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_a_bit_of_everything_nested.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | // Nested is nested type. 14 | type ABitOfEverythingNested struct { 15 | // name is nested field. 16 | Name string `json:"name,omitempty"` 17 | Amount int64 `json:"amount,omitempty"` 18 | // DeepEnum description. 19 | Ok *NestedDeepEnum `json:"ok,omitempty"` 20 | } 21 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_nested_deep_enum.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | // NestedDeepEnum : DeepEnum is one or zero. - FALSE: FALSE is false. - TRUE: TRUE is true. 13 | type NestedDeepEnum string 14 | 15 | // List of NestedDeepEnum 16 | const ( 17 | FALSE_NestedDeepEnum NestedDeepEnum = "FALSE" 18 | TRUE_NestedDeepEnum NestedDeepEnum = "TRUE" 19 | ) 20 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/docs/ExamplepbGenerateUnboundMethodsSimpleMessage.md: -------------------------------------------------------------------------------- 1 | # ExamplepbGenerateUnboundMethodsSimpleMessage 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **Id** | **string** | Id represents the message identifier. | [optional] [default to null] 7 | **Num** | **string** | | [optional] [default to null] 8 | **Duration** | **string** | | [optional] [default to null] 9 | 10 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/internal/cmd/example-gateway-server/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = ["main.go"], 6 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/cmd/example-gateway-server", 7 | visibility = ["//visibility:private"], 8 | deps = [ 9 | "//examples/internal/gateway:go_default_library", 10 | "@com_github_golang_glog//:go_default_library", 11 | ], 12 | ) 13 | 14 | go_binary( 15 | name = "example-gateway-server", 16 | embed = [":go_default_library"], 17 | visibility = ["//visibility:public"], 18 | ) 19 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_message_path_enum_nested_path_enum.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | type MessagePathEnumNestedPathEnum string 14 | 15 | // List of MessagePathEnumNestedPathEnum 16 | const ( 17 | GHI_MessagePathEnumNestedPathEnum MessagePathEnumNestedPathEnum = "GHI" 18 | JKL_MessagePathEnumNestedPathEnum MessagePathEnumNestedPathEnum = "JKL" 19 | ) 20 | -------------------------------------------------------------------------------- /examples/internal/cmd/example-grpc-server/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 2 | 3 | package(default_visibility = ["//visibility:private"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = ["main.go"], 8 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/cmd/example-grpc-server", 9 | deps = [ 10 | "//examples/internal/server:go_default_library", 11 | "@com_github_golang_glog//:go_default_library", 12 | ], 13 | ) 14 | 15 | go_binary( 16 | name = "example-server", 17 | embed = [":go_default_library"], 18 | visibility = ["//visibility:public"], 19 | ) 20 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/model_protobuf_any.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/generate_unbound_methods.proto 3 | * 4 | * Generate Unannotated Methods Echo Service Similar to echo_service.proto but without annotations and without external configuration. Generate Unannotated Methods Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package generateunboundmethods 11 | 12 | type ProtobufAny struct { 13 | TypeUrl string `json:"type_url,omitempty"` 14 | Value string `json:"value,omitempty"` 15 | } 16 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/generated_input.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | package grpc.gateway.examples.internal.examplepb; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/protobuf/empty.proto"; 7 | import "examples/internal/proto/examplepb/a_bit_of_everything.proto"; 8 | 9 | // This file is run through a genrule. 10 | 11 | // Defines some more operations to be added to ABitOfEverythingService 12 | service GeneratedService { 13 | rpc Create(ABitOfEverything) returns (google.protobuf.Empty) { 14 | option (google.api.http) = { 15 | post: "/v1/example/a_bit_of_everything/generated_create" 16 | body: "*" 17 | }; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_examplepb_numeric_enum.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | // ExamplepbNumericEnum : NumericEnum is one or zero. - ZERO: ZERO means 0 - ONE: ONE means 1 13 | type ExamplepbNumericEnum string 14 | 15 | // List of examplepbNumericEnum 16 | const ( 17 | ZERO_ExamplepbNumericEnum ExamplepbNumericEnum = "ZERO" 18 | ONE_ExamplepbNumericEnum ExamplepbNumericEnum = "ONE" 19 | ) 20 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/model_response_response_type.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | type ResponseResponseType string 13 | 14 | // List of ResponseResponseType 15 | const ( 16 | UNKNOWN_ResponseResponseType ResponseResponseType = "UNKNOWN" 17 | A_ResponseResponseType ResponseResponseType = "A" 18 | B_ResponseResponseType ResponseResponseType = "B" 19 | ) 20 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = [ 6 | "api_generate_unbound_methods_echo_service.go", 7 | "client.go", 8 | "configuration.go", 9 | "model_examplepb_generate_unbound_methods_simple_message.go", 10 | "model_protobuf_any.go", 11 | "model_runtime_error.go", 12 | "response.go", 13 | ], 14 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/clients/generateunboundmethods", 15 | visibility = ["//examples:__subpackages__"], 16 | deps = ["@org_golang_x_oauth2//:go_default_library"], 17 | ) 18 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - main: ./protoc-gen-grpc-gateway/main.go 3 | id: protoc-gen-grpc-gateway 4 | binary: protoc-gen-grpc-gateway 5 | env: 6 | - CGO_ENABLED=0 7 | goos: 8 | - linux 9 | - darwin 10 | - windows 11 | goarch: 12 | - amd64 13 | - main: ./protoc-gen-swagger/main.go 14 | id: protoc-gen-swagger 15 | binary: protoc-gen-swagger 16 | env: 17 | - CGO_ENABLED=0 18 | goos: 19 | - linux 20 | - darwin 21 | - windows 22 | goarch: 23 | - amd64 24 | archives: 25 | - name_template: "{{ .Binary }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}" 26 | format: binary 27 | replacements: 28 | amd64: x86_64 29 | dist: _output 30 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/model_runtime_stream_error.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | type RuntimeStreamError struct { 13 | GrpcCode int32 `json:"grpc_code,omitempty"` 14 | HttpCode int32 `json:"http_code,omitempty"` 15 | Message string `json:"message,omitempty"` 16 | HttpStatus string `json:"http_status,omitempty"` 17 | Details []ProtobufAny `json:"details,omitempty"` 18 | } 19 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/RuntimeStreamError.md: -------------------------------------------------------------------------------- 1 | # RuntimeStreamError 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **GrpcCode** | **int32** | | [optional] [default to null] 7 | **HttpCode** | **int32** | | [optional] [default to null] 8 | **Message** | **string** | | [optional] [default to null] 9 | **HttpStatus** | **string** | | [optional] [default to null] 10 | **Details** | [**[]ProtobufAny**](protobufAny.md) | | [optional] [default to null] 11 | 12 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 13 | 14 | 15 | -------------------------------------------------------------------------------- /utilities/pattern.go: -------------------------------------------------------------------------------- 1 | package utilities 2 | 3 | // An OpCode is a opcode of compiled path patterns. 4 | type OpCode int 5 | 6 | // These constants are the valid values of OpCode. 7 | const ( 8 | // OpNop does nothing 9 | OpNop = OpCode(iota) 10 | // OpPush pushes a component to stack 11 | OpPush 12 | // OpLitPush pushes a component to stack if it matches to the literal 13 | OpLitPush 14 | // OpPushM concatenates the remaining components and pushes it to stack 15 | OpPushM 16 | // OpConcatN pops N items from stack, concatenates them and pushes it back to stack 17 | OpConcatN 18 | // OpCapture pops an item and binds it to the variable 19 | OpCapture 20 | // OpEnd is the least positive invalid opcode. 21 | OpEnd 22 | ) 23 | -------------------------------------------------------------------------------- /codegenerator/parse_req.go: -------------------------------------------------------------------------------- 1 | package codegenerator 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | 8 | "github.com/golang/protobuf/proto" 9 | plugin "github.com/golang/protobuf/protoc-gen-go/plugin" 10 | ) 11 | 12 | // ParseRequest parses a code generator request from a proto Message. 13 | func ParseRequest(r io.Reader) (*plugin.CodeGeneratorRequest, error) { 14 | input, err := ioutil.ReadAll(r) 15 | if err != nil { 16 | return nil, fmt.Errorf("failed to read code generator request: %v", err) 17 | } 18 | req := new(plugin.CodeGeneratorRequest) 19 | if err = proto.Unmarshal(input, req); err != nil { 20 | return nil, fmt.Errorf("failed to unmarshal code generator request: %v", err) 21 | } 22 | return req, nil 23 | } 24 | -------------------------------------------------------------------------------- /examples/internal/cmd/example-grpc-server/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Command example-grpc-server is an example grpc server 3 | to be called by example-gateway-server. 4 | */ 5 | package main 6 | 7 | import ( 8 | "context" 9 | "flag" 10 | 11 | "github.com/golang/glog" 12 | "github.com/grpc-ecosystem/grpc-gateway/examples/internal/server" 13 | ) 14 | 15 | var ( 16 | addr = flag.String("addr", ":9090", "endpoint of the gRPC service") 17 | network = flag.String("network", "tcp", "a valid network type which is consistent to -addr") 18 | ) 19 | 20 | func main() { 21 | flag.Parse() 22 | defer glog.Flush() 23 | 24 | ctx := context.Background() 25 | if err := server.Run(ctx, *network, *addr); err != nil { 26 | glog.Fatal(err) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/internal/proto/sub/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 4 | 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | proto_library( 8 | name = "sub_proto", 9 | srcs = ["message.proto"], 10 | ) 11 | 12 | go_proto_library( 13 | name = "sub_go_proto", 14 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/sub", 15 | proto = ":sub_proto", 16 | ) 17 | 18 | go_library( 19 | name = "go_default_library", 20 | embed = [":sub_go_proto"], 21 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/sub", 22 | ) 23 | -------------------------------------------------------------------------------- /examples/internal/gateway/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = [ 6 | "doc.go", 7 | "gateway.go", 8 | "handlers.go", 9 | "main.go", 10 | ], 11 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/gateway", 12 | visibility = ["//visibility:public"], 13 | deps = [ 14 | "//examples/internal/proto/examplepb:go_default_library", 15 | "//runtime:go_default_library", 16 | "@com_github_golang_glog//:go_default_library", 17 | "@org_golang_google_grpc//:go_default_library", 18 | "@org_golang_google_grpc//connectivity:go_default_library", 19 | ], 20 | ) 21 | -------------------------------------------------------------------------------- /examples/internal/proto/sub2/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 4 | 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | proto_library( 8 | name = "sub2_proto", 9 | srcs = ["message.proto"], 10 | ) 11 | 12 | go_proto_library( 13 | name = "sub2_go_proto", 14 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/sub2", 15 | proto = ":sub2_proto", 16 | ) 17 | 18 | go_library( 19 | name = "go_default_library", 20 | embed = [":sub2_go_proto"], 21 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/sub2", 22 | ) 23 | -------------------------------------------------------------------------------- /internal/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 4 | 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | proto_library( 8 | name = "internal_proto", 9 | srcs = ["errors.proto"], 10 | deps = ["@com_google_protobuf//:any_proto"], 11 | ) 12 | 13 | go_proto_library( 14 | name = "internal_go_proto", 15 | importpath = "github.com/grpc-ecosystem/grpc-gateway/internal", 16 | proto = ":internal_proto", 17 | ) 18 | 19 | go_library( 20 | name = "go_default_library", 21 | embed = [":internal_go_proto"], 22 | importpath = "github.com/grpc-ecosystem/grpc-gateway/internal", 23 | ) 24 | -------------------------------------------------------------------------------- /.bazelci/presubmit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | platforms: 3 | ubuntu1804: 4 | build_flags: 5 | - "--build_tag_filters=-nolinux" 6 | build_targets: 7 | - "..." 8 | test_flags: 9 | - "--features=race" 10 | - "--test_tag_filters=-nolinux" 11 | test_targets: 12 | - "..." 13 | ubuntu1604: 14 | build_flags: 15 | - "--build_tag_filters=-nolinux" 16 | build_targets: 17 | - "..." 18 | test_flags: 19 | - "--features=race" 20 | - "--test_tag_filters=-nolinux" 21 | test_targets: 22 | - "..." 23 | macos: 24 | build_flags: 25 | - "--build_tag_filters=-nomacos" 26 | build_targets: 27 | - "..." 28 | test_flags: 29 | - "--features=race" 30 | - "--test_tag_filters=-nomacos" 31 | test_targets: 32 | - "..." 33 | -------------------------------------------------------------------------------- /docs/_docs/background.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: documentation 3 | --- 4 | 5 | # Background 6 | 7 | gRPC is great -- it generates API clients and server stubs in many programming languages, it is fast, easy-to-use, bandwidth-efficient and its design is combat-proven by Google. 8 | However, you might still want to provide a traditional RESTful API as well. Reasons can range from maintaining backwards-compatibility, supporting languages or clients not well supported by gRPC to simply maintaining the aesthetics and tooling involved with a RESTful architecture. 9 | 10 | This project aims to provide that HTTP+JSON interface to your gRPC service. A small amount of configuration in your service to attach HTTP semantics is all that's needed to generate a reverse-proxy with this library. 11 | 12 | -------------------------------------------------------------------------------- /examples/internal/clients/echo/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "api_echo_service.go", 9 | "client.go", 10 | "configuration.go", 11 | "model_examplepb_embedded.go", 12 | "model_examplepb_simple_message.go", 13 | "model_protobuf_any.go", 14 | "model_runtime_error.go", 15 | "response.go", 16 | ], 17 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/clients/echo", 18 | deps = [ 19 | "@com_github_antihax_optional//:go_default_library", 20 | "@org_golang_x_oauth2//:go_default_library", 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - help wanted 8 | - enhancement 9 | - security 10 | # Label to use when marking an issue as stale 11 | staleLabel: wontfix 12 | # Comment to post when marking an issue as stale. Set to `false` to disable 13 | markComment: > 14 | This issue has been automatically marked as stale because it has not had 15 | recent activity. It will be closed if no further activity occurs. Thank you 16 | for your contributions. 17 | # Comment to post when closing a stale issue. Set to `false` to disable 18 | closeComment: false 19 | -------------------------------------------------------------------------------- /examples/internal/clients/unannotatedecho/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "api_unannotated_echo_service.go", 9 | "client.go", 10 | "configuration.go", 11 | "model_examplepb_unannotated_simple_message.go", 12 | "model_protobuf_any.go", 13 | "model_runtime_error.go", 14 | "response.go", 15 | ], 16 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/clients/unannotatedecho", 17 | deps = [ 18 | "@com_github_antihax_optional//:go_default_library", 19 | "@org_golang_x_oauth2//:go_default_library", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /examples/internal/proto/pathenum/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 4 | 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | proto_library( 8 | name = "pathenum_proto", 9 | srcs = ["path_enum.proto"], 10 | ) 11 | 12 | go_proto_library( 13 | name = "pathenum_go_proto", 14 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/pathenum", 15 | proto = ":pathenum_proto", 16 | ) 17 | 18 | go_library( 19 | name = "go_default_library", 20 | embed = [":pathenum_go_proto"], 21 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/pathenum", 22 | ) 23 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/model_runtime_error.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/generate_unbound_methods.proto 3 | * 4 | * Generate Unannotated Methods Echo Service Similar to echo_service.proto but without annotations and without external configuration. Generate Unannotated Methods Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package generateunboundmethods 11 | 12 | type RuntimeError struct { 13 | Error_ string `json:"error,omitempty"` 14 | Code int32 `json:"code,omitempty"` 15 | Message string `json:"message,omitempty"` 16 | Details []ProtobufAny `json:"details,omitempty"` 17 | } 18 | -------------------------------------------------------------------------------- /examples/internal/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/internal/clients/unannotatedecho/model_runtime_error.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/unannotated_echo_service.proto 3 | * 4 | * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format. Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package unannotatedecho 11 | 12 | type RuntimeError struct { 13 | Error_ string `json:"error,omitempty"` 14 | Code int32 `json:"code,omitempty"` 15 | Message string `json:"message,omitempty"` 16 | Details []ProtobufAny `json:"details,omitempty"` 17 | } 18 | -------------------------------------------------------------------------------- /examples/internal/clients/echo/model_examplepb_simple_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Echo Service 3 | * 4 | * Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package echo 11 | 12 | // SimpleMessage represents a simple message sent to the Echo service. 13 | type ExamplepbSimpleMessage struct { 14 | // Id represents the message identifier. 15 | Id string `json:"id,omitempty"` 16 | Num string `json:"num,omitempty"` 17 | LineNum string `json:"line_num,omitempty"` 18 | Lang string `json:"lang,omitempty"` 19 | Status *ExamplepbEmbedded `json:"status,omitempty"` 20 | En string `json:"en,omitempty"` 21 | No *ExamplepbEmbedded `json:"no,omitempty"` 22 | } 23 | -------------------------------------------------------------------------------- /codegenerator/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "doc.go", 9 | "parse_req.go", 10 | ], 11 | importpath = "github.com/grpc-ecosystem/grpc-gateway/codegenerator", 12 | deps = [ 13 | "@com_github_golang_protobuf//proto:go_default_library", 14 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 15 | ], 16 | ) 17 | 18 | go_test( 19 | name = "go_default_test", 20 | srcs = ["parse_req_test.go"], 21 | embed = [":go_default_library"], 22 | deps = [ 23 | "@com_github_golang_protobuf//proto:go_default_library", 24 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /fuzzit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | # We use fuzzit fork until go-fuzz will support go-modules 5 | mkdir -p /go/src/github.com/dvyukov 6 | cd /go/src/github.com/dvyukov 7 | git clone https://github.com/fuzzitdev/go-fuzz 8 | cd go-fuzz 9 | go get ./... 10 | go build ./... 11 | 12 | #go get -v -u ./protoc-gen-grpc-gateway/httprule 13 | cd /src/grpc-gateway 14 | go-fuzz-build -libfuzzer -o parse-http-rule.a ./protoc-gen-grpc-gateway/httprule 15 | clang-9 -fsanitize=fuzzer parse-http-rule.a -o parse-http-rule 16 | 17 | wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.29/fuzzit_Linux_x86_64 18 | chmod a+x fuzzit 19 | 20 | if [ -z "CIRCLE_PULL_REQUEST" ]; then 21 | TYPE="fuzzing" 22 | else 23 | TYPE="local-regression" 24 | fi 25 | ./fuzzit create job --type ${TYPE} grpc-gateway/parse-http-rule parse-http-rule 26 | -------------------------------------------------------------------------------- /examples/internal/browser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc-gateway-example", 3 | "version": "1.0.0", 4 | "description": "Example use of grpc-gateway from browser", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "SEE LICENSE IN LICENSE.txt", 10 | "devDependencies": { 11 | "bower": "^1.7.9", 12 | "gulp": "^3.9.1", 13 | "gulp-bower": "0.0.13", 14 | "gulp-exit": "0.0.2", 15 | "gulp-jasmine-browser": "^1.3.2", 16 | "gulp-process": "^0.1.2", 17 | "gulp-shell": "^0.5.2", 18 | "jasmine": "^2.4.1", 19 | "phantomjs": "^2.1.7", 20 | "swagger-client": "^2.1.28", 21 | "webpack-stream": "^3.2.0", 22 | "bower-logger": "^0.2.2", 23 | "mout": "^1.1.0", 24 | "bower-config": "^0.6.2", 25 | "configstore": "^4.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /internal/errors.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package grpc.gateway.runtime; 3 | option go_package = "internal"; 4 | 5 | import "google/protobuf/any.proto"; 6 | 7 | // Error is the generic error returned from unary RPCs. 8 | message Error { 9 | string error = 1; 10 | // This is to make the error more compatible with users that expect errors to be Status objects: 11 | // https://github.com/grpc/grpc/blob/master/src/proto/grpc/status/status.proto 12 | // It should be the exact same message as the Error field. 13 | int32 code = 2; 14 | string message = 3; 15 | repeated google.protobuf.Any details = 4; 16 | } 17 | 18 | // StreamError is a response type which is returned when 19 | // streaming rpc returns an error. 20 | message StreamError { 21 | int32 grpc_code = 1; 22 | int32 http_code = 2; 23 | string message = 3; 24 | string http_status = 4; 25 | repeated google.protobuf.Any details = 5; 26 | } 27 | -------------------------------------------------------------------------------- /runtime/internal/examplepb/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 4 | 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | proto_library( 8 | name = "examplepb_proto", 9 | srcs = [ 10 | "example.proto", 11 | ], 12 | deps = [ 13 | "@com_google_protobuf//:empty_proto", 14 | "@com_google_protobuf//:timestamp_proto", 15 | ], 16 | ) 17 | 18 | go_proto_library( 19 | name = "examplepb_go_proto", 20 | importpath = "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb", 21 | proto = ":examplepb_proto", 22 | ) 23 | 24 | go_library( 25 | name = "go_default_library", 26 | embed = [":examplepb_go_proto"], 27 | importpath = "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb", 28 | ) 29 | -------------------------------------------------------------------------------- /examples/internal/clients/unannotatedecho/model_examplepb_unannotated_simple_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/unannotated_echo_service.proto 3 | * 4 | * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format. Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package unannotatedecho 11 | 12 | // UnannotatedSimpleMessage represents a simple message sent to the unannotated Echo service. 13 | type ExamplepbUnannotatedSimpleMessage struct { 14 | // Id represents the message identifier. 15 | Id string `json:"id,omitempty"` 16 | Num string `json:"num,omitempty"` 17 | Duration string `json:"duration,omitempty"` 18 | } 19 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/httprule/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "compile.go", 9 | "parse.go", 10 | "types.go", 11 | ], 12 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule", 13 | deps = [ 14 | "//utilities:go_default_library", 15 | "@com_github_golang_glog//:go_default_library", 16 | ], 17 | ) 18 | 19 | go_test( 20 | name = "go_default_test", 21 | size = "small", 22 | srcs = [ 23 | "compile_test.go", 24 | "parse_test.go", 25 | "types_test.go", 26 | ], 27 | embed = [":go_default_library"], 28 | deps = [ 29 | "//utilities:go_default_library", 30 | "@com_github_golang_glog//:go_default_library", 31 | ], 32 | ) 33 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "dockerFile": "../.circleci/Dockerfile", 3 | "overrideCommand": true, 4 | // Set *default* container specific settings.json values on container create. 5 | "settings": { 6 | "editor.formatOnSave": true, 7 | "terminal.integrated.shell.linux": "/bin/bash", 8 | "go.useGoProxyToCheckForToolUpdates": true, 9 | "go.useLanguageServer": true, 10 | "go.gopath": "/go", 11 | "go.goroot": "/usr/local/go", 12 | "go.toolsGopath": "/go/bin", 13 | "bazel.buildifierExecutable": "/go/bin/buildifier", 14 | "bazel.buildifierFixOnFormat": true, 15 | "bazel.enableCodeLens": true, 16 | }, 17 | // Add the IDs of extensions you want installed when the container is created. 18 | "extensions": [ 19 | "golang.Go", 20 | "bazelbuild.vscode-bazel", 21 | ], 22 | "postCreateCommand": "cd $(mktemp -d) && GO111MODULE=on go get golang.org/x/tools/gopls@latest github.com/bazelbuild/buildtools/buildifier@latest && cd -", 23 | } 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | #### References to other Issues or PRs 13 | 14 | 19 | 20 | #### Have you read the [Contributing Guidelines](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/CONTRIBUTING.md)? 21 | 22 | #### Brief description of what is fixed or changed 23 | 24 | #### Other comments 25 | -------------------------------------------------------------------------------- /docs/_docs/aws.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: documentation 3 | --- 4 | 5 | # AWS 6 | 7 | ## Import swagger documentation into AWS API Gateway 8 | The AWS API gateway service allows importing of an OpenAPI specification to create a REST API. The process is very straightforward and can be found [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-import-api.html). 9 | Here are some tips to consider when importing the documentation: 10 | 11 | 1. Remove any circular dependencies (these aren't supported by the parser). 12 | 2. Remove security-related annotations (These annotations aren't well supported by the parser). 13 | 3. Max length of fields are reviewed by the parser but the errors aren't self-explanatory. Review the [specification](https://swagger.io/specification/v2/) to verify that the requirements are met. 14 | 4. API gateway errors aren't great, but you can use this [page](https://apidevtools.org/swagger-parser/online/) for structure validation. 15 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_examplepb_book.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | import ( 14 | "time" 15 | ) 16 | 17 | // An example resource type from AIP-123 used to test the behavior described in the CreateBookRequest message. See: https://google.aip.dev/123 18 | type ExamplepbBook struct { 19 | // The resource name of the book. Format: `publishers/{publisher}/books/{book}` Example: `publishers/1257894000000000000/books/my-book` 20 | Name string `json:"name,omitempty"` 21 | // Output only. The book's ID. 22 | Id string `json:"id,omitempty"` 23 | // Output only. Creation time of the book. 24 | CreateTime time.Time `json:"create_time,omitempty"` 25 | } 26 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/model_examplepb_generate_unbound_methods_simple_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/generate_unbound_methods.proto 3 | * 4 | * Generate Unannotated Methods Echo Service Similar to echo_service.proto but without annotations and without external configuration. Generate Unannotated Methods Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package generateunboundmethods 11 | 12 | // GenerateUnboundMethodsSimpleMessage represents a simple message sent to the unannotated GenerateUnboundMethodsEchoService service. 13 | type ExamplepbGenerateUnboundMethodsSimpleMessage struct { 14 | // Id represents the message identifier. 15 | Id string `json:"id,omitempty"` 16 | Num string `json:"num,omitempty"` 17 | Duration string `json:"duration,omitempty"` 18 | } 19 | -------------------------------------------------------------------------------- /docs/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | JEKYLL_VERSION=4 6 | BUNDLE_DIR="/tmp/grpc-gateway-bundle" 7 | 8 | if [ ! -d "${BUNDLE_DIR}" ]; then 9 | mkdir "${BUNDLE_DIR}" 10 | 11 | # Run this to update the Gemsfile.lock 12 | docker run --rm \ 13 | --volume="${PWD}:/srv/jekyll" \ 14 | -e "JEKYLL_UID=$(id -u)" \ 15 | -e "JEKYLL_GID=$(id -g)" \ 16 | --volume="/tmp/grpc-gateway-bundle:/usr/local/bundle" \ 17 | -it "jekyll/builder:${JEKYLL_VERSION}" \ 18 | bundle update 19 | fi 20 | 21 | if [[ ${JEKYLL_GITHUB_TOKEN} == "" ]]; then 22 | echo "Please set \$JEKYLL_GITHUB_TOKEN before running" 23 | exit 1 24 | fi 25 | 26 | docker run --rm \ 27 | --volume="${PWD}:/srv/jekyll" \ 28 | -p 35729:35729 -p 4000:4000 \ 29 | -e "JEKYLL_UID=$(id -u)" \ 30 | -e "JEKYLL_GID=$(id -g)" \ 31 | -e "JEKYLL_GITHUB_TOKEN=${JEKYLL_GITHUB_TOKEN}" \ 32 | --volume="/tmp/grpc-gateway-bundle:/usr/local/bundle" \ 33 | -it "jekyll/builder:${JEKYLL_VERSION}" \ 34 | jekyll serve 35 | -------------------------------------------------------------------------------- /examples/internal/README.md: -------------------------------------------------------------------------------- 1 | # One way to run the example 2 | 3 | ```bash 4 | # Handle dependencies 5 | $ dep init 6 | ``` 7 | 8 | Follow the guides from this [README.md](./browser/README.md) to run the server and gateway. 9 | ```bash 10 | # Make sure you are in the correct directory: 11 | # $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/examples 12 | $ cd examples/internal/browser 13 | $ pwd 14 | 15 | # Install gulp 16 | $ npm install -g gulp-cli 17 | $ npm install 18 | $ gulp 19 | 20 | # Run 21 | $ gulp bower 22 | $ gulp backends 23 | ``` 24 | 25 | Then you can use curl or a browser to test: 26 | 27 | ```bash 28 | # List all apis 29 | $ curl http://localhost:8080/swagger/echo_service.swagger.json 30 | 31 | # Visit the apis 32 | $ curl -XPOST http://localhost:8080/v1/example/echo/foo 33 | {"id":"foo"} 34 | 35 | $ curl http://localhost:8080/v1/example/echo/foo/123 36 | {"id":"foo","num":"123"} 37 | 38 | ``` 39 | 40 | So you have visited the apis by HTTP successfully. You can also try other apis. 41 | -------------------------------------------------------------------------------- /ADOPTERS.md: -------------------------------------------------------------------------------- 1 | # Adopters 2 | 3 | This is a list of organizations that have spoken publicly about their adoption or 4 | production users that have added themselves (in alphabetical order): 5 | 6 | * [Ad Hoc](http://adhocteam.us/) uses the gRPC-Gateway to serve millions of 7 | API requests per day. 8 | * [Chef](https://www.chef.io/) uses gRPC-Gateway to provide the user-facing 9 | API of [Chef Automate](https://automate.chef.io/). Furthermore, the generated 10 | OpenAPI data serves as the basis for its [API documentation](https://automate.chef.io/docs/api/). 11 | The code is Open Source, [see `github.com/chef/automate`](https://github.com/chef/automate). 12 | * [Scaleway](https://www.scaleway.com/en/) uses the gRPC-Gateway since 2018 to 13 | serve millions of API requests per day [1]. 14 | 15 | If you have adopted the gRPC-Gateway and would like to be included in this list, 16 | feel free to submit a PR. 17 | 18 | [1]: [The odyssey of an HTTP request in Scaleway](https://www.youtube.com/watch?v=eLxD-zIUraE&feature=youtu.be&t=480). 19 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | go_library( 4 | name = "go_default_library", 5 | srcs = [ 6 | "api_response_body_service.go", 7 | "client.go", 8 | "configuration.go", 9 | "model_examplepb_repeated_response_body_out.go", 10 | "model_examplepb_repeated_response_body_out_response.go", 11 | "model_examplepb_repeated_response_strings.go", 12 | "model_examplepb_response_body_out.go", 13 | "model_examplepb_response_body_out_response.go", 14 | "model_protobuf_any.go", 15 | "model_response_response_type.go", 16 | "model_runtime_error.go", 17 | "model_runtime_stream_error.go", 18 | "model_stream_result_of_examplepb_response_body_out.go", 19 | "response.go", 20 | ], 21 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/clients/responsebody", 22 | visibility = ["//visibility:public"], 23 | deps = ["@org_golang_x_oauth2//:go_default_library"], 24 | ) 25 | -------------------------------------------------------------------------------- /protoc-gen-swagger/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") 2 | 3 | package(default_visibility = ["//visibility:private"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = ["main.go"], 8 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", 9 | deps = [ 10 | "//codegenerator:go_default_library", 11 | "//protoc-gen-grpc-gateway/descriptor:go_default_library", 12 | "//protoc-gen-swagger/genswagger:go_default_library", 13 | "@com_github_golang_glog//:go_default_library", 14 | "@com_github_golang_protobuf//proto:go_default_library", 15 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 16 | ], 17 | ) 18 | 19 | go_binary( 20 | name = "protoc-gen-swagger", 21 | embed = [":go_default_library"], 22 | visibility = ["//visibility:public"], 23 | ) 24 | 25 | go_test( 26 | name = "go_default_test", 27 | size = "small", 28 | srcs = ["main_test.go"], 29 | embed = [":go_default_library"], 30 | ) 31 | -------------------------------------------------------------------------------- /examples/internal/cmd/example-gateway-server/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Command example-gateway-server is an example reverse-proxy implementation 3 | whose HTTP handler is generated by grpc-gateway. 4 | */ 5 | package main 6 | 7 | import ( 8 | "context" 9 | "flag" 10 | 11 | "github.com/golang/glog" 12 | "github.com/grpc-ecosystem/grpc-gateway/examples/internal/gateway" 13 | ) 14 | 15 | var ( 16 | endpoint = flag.String("endpoint", "localhost:9090", "endpoint of the gRPC service") 17 | network = flag.String("network", "tcp", `one of "tcp" or "unix". Must be consistent to -endpoint`) 18 | swaggerDir = flag.String("swagger_dir", "examples/internal/proto/examplepb", "path to the directory which contains swagger definitions") 19 | ) 20 | 21 | func main() { 22 | flag.Parse() 23 | defer glog.Flush() 24 | 25 | ctx := context.Background() 26 | opts := gateway.Options{ 27 | Addr: ":8080", 28 | GRPCServer: gateway.Endpoint{ 29 | Network: *network, 30 | Addr: *endpoint, 31 | }, 32 | SwaggerDir: *swaggerDir, 33 | } 34 | if err := gateway.Run(ctx, opts); err != nil { 35 | glog.Fatal(err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/stream.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | package grpc.gateway.examples.internal.examplepb; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/protobuf/empty.proto"; 7 | import "examples/internal/proto/examplepb/a_bit_of_everything.proto"; 8 | import "examples/internal/proto/sub/message.proto"; 9 | 10 | // Defines some more operations to be added to ABitOfEverythingService 11 | service StreamService { 12 | rpc BulkCreate(stream ABitOfEverything) returns (google.protobuf.Empty) { 13 | option (google.api.http) = { 14 | post: "/v1/example/a_bit_of_everything/bulk" 15 | body: "*" 16 | }; 17 | } 18 | rpc List(google.protobuf.Empty) returns (stream ABitOfEverything) { 19 | option (google.api.http) = { 20 | get: "/v1/example/a_bit_of_everything" 21 | }; 22 | } 23 | rpc BulkEcho(stream grpc.gateway.examples.internal.sub.StringMessage) returns (stream grpc.gateway.examples.internal.sub.StringMessage) { 24 | option (google.api.http) = { 25 | post: "/v1/example/a_bit_of_everything/echo" 26 | body: "*" 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /protoc-gen-swagger/options/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 4 | 5 | package(default_visibility = ["//visibility:public"]) 6 | 7 | filegroup( 8 | name = "options_proto_files", 9 | srcs = [ 10 | "annotations.proto", 11 | "openapiv2.proto", 12 | ], 13 | ) 14 | 15 | go_library( 16 | name = "go_default_library", 17 | embed = [":options_go_proto"], 18 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options", 19 | ) 20 | 21 | proto_library( 22 | name = "options_proto", 23 | srcs = [ 24 | "annotations.proto", 25 | "openapiv2.proto", 26 | ], 27 | deps = [ 28 | "@com_google_protobuf//:any_proto", 29 | "@com_google_protobuf//:descriptor_proto", 30 | "@com_google_protobuf//:struct_proto", 31 | ], 32 | ) 33 | 34 | go_proto_library( 35 | name = "options_go_proto", 36 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options", 37 | proto = ":options_proto", 38 | ) 39 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/.swagger-codegen-ignore: -------------------------------------------------------------------------------- 1 | # Swagger Codegen Ignore 2 | # Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen 3 | 4 | # Use this file to prevent files from being overwritten by the generator. 5 | # The patterns follow closely to .gitignore or .dockerignore. 6 | 7 | # As an example, the C# client generator defines ApiClient.cs. 8 | # You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: 9 | #ApiClient.cs 10 | 11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*): 12 | #foo/*/qux 13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux 14 | 15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): 16 | #foo/**/qux 17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux 18 | 19 | # You can also negate patterns with an exclamation (!). 20 | # For example, you can ignore all files in a docs folder with the file extension .md: 21 | #docs/*.md 22 | # Then explicitly reverse the ignore rule for a single file: 23 | #!docs/README.md 24 | -------------------------------------------------------------------------------- /examples/internal/server/echo.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/golang/glog" 7 | examples "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/examplepb" 8 | "google.golang.org/grpc" 9 | "google.golang.org/grpc/metadata" 10 | ) 11 | 12 | // Implements of EchoServiceServer 13 | 14 | type echoServer struct{} 15 | 16 | func newEchoServer() examples.EchoServiceServer { 17 | return new(echoServer) 18 | } 19 | 20 | func (s *echoServer) Echo(ctx context.Context, msg *examples.SimpleMessage) (*examples.SimpleMessage, error) { 21 | glog.Info(msg) 22 | return msg, nil 23 | } 24 | 25 | func (s *echoServer) EchoBody(ctx context.Context, msg *examples.SimpleMessage) (*examples.SimpleMessage, error) { 26 | glog.Info(msg) 27 | grpc.SendHeader(ctx, metadata.New(map[string]string{ 28 | "foo": "foo1", 29 | "bar": "bar1", 30 | })) 31 | grpc.SetTrailer(ctx, metadata.New(map[string]string{ 32 | "foo": "foo2", 33 | "bar": "bar2", 34 | })) 35 | return msg, nil 36 | } 37 | 38 | func (s *echoServer) EchoDelete(ctx context.Context, msg *examples.SimpleMessage) (*examples.SimpleMessage, error) { 39 | glog.Info(msg) 40 | return msg, nil 41 | } 42 | -------------------------------------------------------------------------------- /third_party/googleapis/google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/api/http.proto"; 20 | import "google/protobuf/descriptor.proto"; 21 | 22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 23 | option java_multiple_files = true; 24 | option java_outer_classname = "AnnotationsProto"; 25 | option java_package = "com.google.api"; 26 | option objc_class_prefix = "GAPI"; 27 | 28 | extend google.protobuf.MethodOptions { 29 | // See `HttpRule`. 30 | HttpRule http = 72295728; 31 | } 32 | -------------------------------------------------------------------------------- /docs/_docs/features.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: documentation 3 | --- 4 | 5 | # Features 6 | 7 | ## Supported 8 | * Generating JSON API handlers 9 | * Method parameters in request body 10 | * Method parameters in request path 11 | * Method parameters in query string 12 | * Enum fields in path parameter (including repeated enum fields). 13 | * Mapping streaming APIs to newline-delimited JSON streams 14 | * Mapping HTTP headers with `Grpc-Metadata-` prefix to gRPC metadata (prefixed with `grpcgateway-`) 15 | * Optionally emitting API definition for [Swagger](http://swagger.io). 16 | * Setting [gRPC timeouts](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md) through inbound HTTP `Grpc-Timeout` header. 17 | * Partial support for [gRPC API Configuration](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config) files as an alternative to annotation. 18 | 19 | ## Want to support 20 | But not yet. 21 | * Optionally generating the entrypoint. #8 22 | * `import_path` parameter 23 | 24 | ## No plan to support 25 | But patch is welcome. 26 | * Method parameters in HTTP headers 27 | * Handling trailer metadata 28 | * Encoding request/response body in XML 29 | * True bi-directional streaming. (Probably impossible?) 30 | 31 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/httprule/types.go: -------------------------------------------------------------------------------- 1 | package httprule 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type template struct { 9 | segments []segment 10 | verb string 11 | template string 12 | } 13 | 14 | type segment interface { 15 | fmt.Stringer 16 | compile() (ops []op) 17 | } 18 | 19 | type wildcard struct{} 20 | 21 | type deepWildcard struct{} 22 | 23 | type literal string 24 | 25 | type variable struct { 26 | path string 27 | segments []segment 28 | } 29 | 30 | func (wildcard) String() string { 31 | return "*" 32 | } 33 | 34 | func (deepWildcard) String() string { 35 | return "**" 36 | } 37 | 38 | func (l literal) String() string { 39 | return string(l) 40 | } 41 | 42 | func (v variable) String() string { 43 | var segs []string 44 | for _, s := range v.segments { 45 | segs = append(segs, s.String()) 46 | } 47 | return fmt.Sprintf("{%s=%s}", v.path, strings.Join(segs, "/")) 48 | } 49 | 50 | func (t template) String() string { 51 | var segs []string 52 | for _, s := range t.segments { 53 | segs = append(segs, s.String()) 54 | } 55 | str := strings.Join(segs, "/") 56 | if t.verb != "" { 57 | str = fmt.Sprintf("%s:%s", str, t.verb) 58 | } 59 | return "/" + str 60 | } 61 | -------------------------------------------------------------------------------- /examples/internal/browser/echo_service.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var SwaggerClient = require('swagger-client'); 4 | 5 | describe('EchoService', function() { 6 | var client; 7 | 8 | beforeEach(function(done) { 9 | new SwaggerClient({ 10 | url: "http://localhost:8080/swagger/echo_service.swagger.json", 11 | usePromise: true, 12 | }).then(function(c) { 13 | client = c; 14 | done(); 15 | }); 16 | }); 17 | 18 | describe('Echo', function() { 19 | it('should echo the request back', function(done) { 20 | client.EchoService.Echo( 21 | {id: "foo"}, 22 | {responseContentType: "application/json"} 23 | ).then(function(resp) { 24 | expect(resp.obj).toEqual({id: "foo"}); 25 | }).catch(function(err) { 26 | done.fail(err); 27 | }).then(done); 28 | }); 29 | }); 30 | 31 | describe('EchoBody', function() { 32 | it('should echo the request back', function(done) { 33 | client.EchoService.EchoBody( 34 | {body: {id: "foo"}}, 35 | {responseContentType: "application/json"} 36 | ).then(function(resp) { 37 | expect(resp.obj).toEqual({id: "foo"}); 38 | }).catch(function(err) { 39 | done.fail(err); 40 | }).then(done); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /runtime/marshal_httpbodyproto_test.go: -------------------------------------------------------------------------------- 1 | package runtime_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 8 | "google.golang.org/genproto/googleapis/api/httpbody" 9 | ) 10 | 11 | func TestHTTPBodyContentType(t *testing.T) { 12 | m := runtime.HTTPBodyMarshaler{ 13 | &runtime.JSONPb{ 14 | OrigName: true, 15 | }, 16 | } 17 | expected := "CustomContentType" 18 | message := &httpbody.HttpBody{ 19 | ContentType: expected, 20 | } 21 | res := m.ContentType() 22 | if res != "application/json" { 23 | t.Errorf("content type not equal (%q, %q)", res, expected) 24 | } 25 | res = m.ContentTypeFromMessage(message) 26 | if res != expected { 27 | t.Errorf("content type not equal (%q, %q)", res, expected) 28 | } 29 | } 30 | 31 | func TestHTTPBodyMarshal(t *testing.T) { 32 | m := runtime.HTTPBodyMarshaler{ 33 | &runtime.JSONPb{ 34 | OrigName: true, 35 | }, 36 | } 37 | expected := []byte("Some test") 38 | message := &httpbody.HttpBody{ 39 | Data: expected, 40 | } 41 | res, err := m.Marshal(message) 42 | if err != nil { 43 | t.Errorf("m.Marshal(%#v) failed with %v; want success", message, err) 44 | } 45 | if !bytes.Equal(res, expected) { 46 | t.Errorf("Marshalled data not equal (%q, %q)", res, expected) 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "baseBranches": [ 6 | "master", 7 | "v2" 8 | ], 9 | "postUpdateOptions": [ 10 | "gomodTidy" 11 | ], 12 | "packageRules": [ 13 | { 14 | "updateTypes": [ 15 | "minor", 16 | "patch", 17 | "pin", 18 | "digest" 19 | ], 20 | "automerge": true 21 | }, 22 | { 23 | "baseBranchList": [ 24 | "master" 25 | ], 26 | "packageNames": [ 27 | "github.com/golang/protobuf", 28 | "google.golang.org/genproto", 29 | "io_bazel_rules_go", 30 | "golang.org/x/oauth2" 31 | ], 32 | "enabled": false 33 | }, 34 | { 35 | "baseBranchList": [ 36 | "v2" 37 | ], 38 | "packageNames": [ 39 | "github.com/golang/protobuf", 40 | "google.golang.org/protobuf" 41 | ], 42 | "groupName": "golang/protobuf" 43 | }, 44 | { 45 | "packagePatterns": [ 46 | "jekyll-.*" 47 | ], 48 | "enabled": false 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /examples/internal/server/unannotatedecho.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/golang/glog" 7 | examples "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/examplepb" 8 | "google.golang.org/grpc" 9 | "google.golang.org/grpc/metadata" 10 | ) 11 | 12 | // Implements of UnannotatedEchoServiceServer 13 | 14 | type unannotatedEchoServer struct{} 15 | 16 | func newUnannotatedEchoServer() examples.UnannotatedEchoServiceServer { 17 | return new(unannotatedEchoServer) 18 | } 19 | 20 | func (s *unannotatedEchoServer) Echo(ctx context.Context, msg *examples.UnannotatedSimpleMessage) (*examples.UnannotatedSimpleMessage, error) { 21 | glog.Info(msg) 22 | return msg, nil 23 | } 24 | 25 | func (s *unannotatedEchoServer) EchoBody(ctx context.Context, msg *examples.UnannotatedSimpleMessage) (*examples.UnannotatedSimpleMessage, error) { 26 | glog.Info(msg) 27 | grpc.SendHeader(ctx, metadata.New(map[string]string{ 28 | "foo": "foo1", 29 | "bar": "bar1", 30 | })) 31 | grpc.SetTrailer(ctx, metadata.New(map[string]string{ 32 | "foo": "foo2", 33 | "bar": "bar2", 34 | })) 35 | return msg, nil 36 | } 37 | 38 | func (s *unannotatedEchoServer) EchoDelete(ctx context.Context, msg *examples.UnannotatedSimpleMessage) (*examples.UnannotatedSimpleMessage, error) { 39 | glog.Info(msg) 40 | return msg, nil 41 | } 42 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/swagger_merge_b.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | 4 | // Merging Services 5 | // 6 | // This is an example of merging two proto files. 7 | package grpc.gateway.examples.internal.examplepb; 8 | 9 | import "google/api/annotations.proto"; 10 | 11 | // InMessageB represents a message to ServiceB. 12 | message InMessageB { 13 | // Here is the explanation about InMessageB.values 14 | string value = 1; 15 | } 16 | 17 | // OutMessageB represents a message returned from ServiceB. 18 | message OutMessageB { 19 | // Here is the explanation about OutMessageB.value 20 | repeated string values = 1; 21 | } 22 | 23 | // ServiceB service responds to incoming merge requests. 24 | service ServiceB { 25 | // ServiceB.MethodOne receives InMessageB and returns OutMessageB 26 | // 27 | // Here is the detail explanation about ServiceB.MethodOne. 28 | rpc MethodOne(InMessageB) returns (OutMessageB) { 29 | option (google.api.http) = { 30 | post: "/v1/example/b/1" 31 | body: "*" 32 | }; 33 | } 34 | // ServiceB.MethodTwo receives OutMessageB and returns InMessageB 35 | // 36 | // Here is the detail explanation about ServiceB.MethodTwo. 37 | rpc MethodTwo(OutMessageB) returns (InMessageB) { 38 | option (google.api.http) = { 39 | post: "/v1/example/b/2" 40 | body: "*" 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/_docs/httpbody.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: documentation 3 | --- 4 | 5 | # HttpBody messages 6 | The [HTTP Body](https://github.com/googleapis/googleapis/blob/master/google/api/httpbody.proto) messages allows a response message to be specified with custom data content and a custom content type header. The values included in the HTTPBody response will be used verbatim in the returned message from the gateway. Make sure you format your response carefully! 7 | 8 | ## Example Usage 9 | 1. Create a mux and configure it to use the `HTTPBodyMarshaler`. 10 | 11 | ```protobuf 12 | mux := runtime.NewServeMux() 13 | runtime.SetHTTPBodyMarshaler(mux) 14 | ``` 15 | 2. Define your service in gRPC with an httpbody response message 16 | 17 | ```protobuf 18 | import "google/api/httpbody.proto"; 19 | import "google/api/annotations.proto"; 20 | import "google/protobuf/empty.proto"; 21 | 22 | service HttpBodyExampleService { 23 | rpc HelloWorld(google.protobuf.Empty) returns (google.api.HttpBody) { 24 | option (google.api.http) = { 25 | get: "/helloworld" 26 | }; 27 | } 28 | } 29 | ``` 30 | 3. Generate gRPC and reverse-proxy stubs and implement your service. 31 | 32 | ## Example service implementation 33 | 34 | ```go 35 | func (*HttpBodyExampleService) Helloworld(ctx context.Context, in *empty.Empty) (*httpbody.HttpBody, error) { 36 | return &httpbody.HttpBody{ 37 | ContentType: "text/html", 38 | Data: []byte("Hello World"), 39 | }, nil 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /examples/internal/clients/echo/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Echo Service 3 | * 4 | * Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package echo 11 | 12 | import ( 13 | "net/http" 14 | ) 15 | 16 | type APIResponse struct { 17 | *http.Response `json:"-"` 18 | Message string `json:"message,omitempty"` 19 | // Operation is the name of the swagger operation. 20 | Operation string `json:"operation,omitempty"` 21 | // RequestURL is the request URL. This value is always available, even if the 22 | // embedded *http.Response is nil. 23 | RequestURL string `json:"url,omitempty"` 24 | // Method is the HTTP method used for the request. This value is always 25 | // available, even if the embedded *http.Response is nil. 26 | Method string `json:"method,omitempty"` 27 | // Payload holds the contents of the response body (which may be nil or empty). 28 | // This is provided here as the raw response.Body() reader will have already 29 | // been drained. 30 | Payload []byte `json:"-"` 31 | } 32 | 33 | func NewAPIResponse(r *http.Response) *APIResponse { 34 | 35 | response := &APIResponse{Response: r} 36 | return response 37 | } 38 | 39 | func NewAPIResponseWithError(errorMessage string) *APIResponse { 40 | 41 | response := &APIResponse{Message: errorMessage} 42 | return response 43 | } 44 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 2 | 3 | package(default_visibility = ["//protoc-gen-grpc-gateway:__subpackages__"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "doc.go", 9 | "generator.go", 10 | "template.go", 11 | ], 12 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/internal/gengateway", 13 | deps = [ 14 | "//internal/casing:go_default_library", 15 | "//protoc-gen-grpc-gateway/descriptor:go_default_library", 16 | "//protoc-gen-grpc-gateway/generator:go_default_library", 17 | "//utilities:go_default_library", 18 | "@com_github_golang_glog//:go_default_library", 19 | "@com_github_golang_protobuf//proto:go_default_library", 20 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 21 | ], 22 | ) 23 | 24 | go_test( 25 | name = "go_default_test", 26 | size = "small", 27 | srcs = [ 28 | "generator_test.go", 29 | "template_test.go", 30 | ], 31 | embed = [":go_default_library"], 32 | deps = [ 33 | "//protoc-gen-grpc-gateway/descriptor:go_default_library", 34 | "//protoc-gen-grpc-gateway/httprule:go_default_library", 35 | "@com_github_golang_protobuf//proto:go_default_library", 36 | "@io_bazel_rules_go//proto/wkt:descriptor_go_proto", 37 | ], 38 | ) 39 | -------------------------------------------------------------------------------- /examples/internal/integration/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_test") 2 | 3 | go_test( 4 | name = "go_default_test", 5 | srcs = [ 6 | "client_test.go", 7 | "fieldmask_test.go", 8 | "integration_test.go", 9 | "main_test.go", 10 | "proto_error_test.go", 11 | ], 12 | deps = [ 13 | "//examples/internal/clients/abe:go_default_library", 14 | "//examples/internal/clients/echo:go_default_library", 15 | "//examples/internal/clients/unannotatedecho:go_default_library", 16 | "//examples/internal/gateway:go_default_library", 17 | "//examples/internal/proto/examplepb:go_default_library", 18 | "//examples/internal/proto/pathenum:go_default_library", 19 | "//examples/internal/proto/sub:go_default_library", 20 | "//examples/internal/server:go_default_library", 21 | "//runtime:go_default_library", 22 | "@com_github_golang_glog//:go_default_library", 23 | "@com_github_golang_protobuf//descriptor:go_default_library_gen", 24 | "@com_github_golang_protobuf//jsonpb:go_default_library_gen", 25 | "@com_github_golang_protobuf//proto:go_default_library", 26 | "@go_googleapis//google/rpc:status_go_proto", 27 | "@io_bazel_rules_go//proto/wkt:empty_go_proto", 28 | "@io_bazel_rules_go//proto/wkt:field_mask_go_proto", 29 | "@org_golang_google_grpc//codes:go_default_library", 30 | ], 31 | ) 32 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: gRPC-Gateway 3 | --- 4 | 5 | # grpc-gateway 6 | 7 | [![CircleCI](https://circleci.com/gh/grpc-ecosystem/grpc-gateway.svg?style=svg)](https://circleci.com/gh/grpc-ecosystem/grpc-gateway) 8 | 9 | grpc-gateway is a plugin of [protoc](https://github.com/protocolbuffers/protobuf). 10 | It reads a [gRPC](https://grpc.io) service definition, 11 | and generates a reverse-proxy server which translates a RESTful JSON API into gRPC. 12 | This server is generated according to [custom options](https://cloud.google.com/service-infrastructure/docs/service-management/reference/rpc/google.api#http) in your gRPC definition. 13 | 14 | It helps you to provide your APIs in both gRPC and RESTful style at the same time. 15 | 16 | ![architecture introduction diagram](https://docs.google.com/drawings/d/12hp4CPqrNPFhattL_cIoJptFvlAqm5wLQ0ggqI5mkCg/pub?w=749&h=370) 17 | 18 | To learn more about us check out our documentation on: 19 | 20 | - [Our background](_docs/background.md) 21 | - [Installation and usage](_docs/usage.md) 22 | - [Examples](_docs/examples.md) 23 | - [Features](_docs/features.md) 24 | - [AWS API Gateway tips](_docs/aws.md) 25 | 26 | # Contribution 27 | 28 | See [CONTRIBUTING.md](http://github.com/grpc-ecosystem/grpc-gateway/blob/master/CONTRIBUTING.md). 29 | 30 | # License 31 | 32 | grpc-gateway is licensed under the BSD 3-Clause License. 33 | See [LICENSE.txt](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt) for more details. 34 | -------------------------------------------------------------------------------- /runtime/marshal_json.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | // JSONBuiltin is a Marshaler which marshals/unmarshals into/from JSON 9 | // with the standard "encoding/json" package of Golang. 10 | // Although it is generally faster for simple proto messages than JSONPb, 11 | // it does not support advanced features of protobuf, e.g. map, oneof, .... 12 | // 13 | // The NewEncoder and NewDecoder types return *json.Encoder and 14 | // *json.Decoder respectively. 15 | type JSONBuiltin struct{} 16 | 17 | // ContentType always Returns "application/json". 18 | func (*JSONBuiltin) ContentType() string { 19 | return "application/json" 20 | } 21 | 22 | // Marshal marshals "v" into JSON 23 | func (j *JSONBuiltin) Marshal(v interface{}) ([]byte, error) { 24 | return json.Marshal(v) 25 | } 26 | 27 | // Unmarshal unmarshals JSON data into "v". 28 | func (j *JSONBuiltin) Unmarshal(data []byte, v interface{}) error { 29 | return json.Unmarshal(data, v) 30 | } 31 | 32 | // NewDecoder returns a Decoder which reads JSON stream from "r". 33 | func (j *JSONBuiltin) NewDecoder(r io.Reader) Decoder { 34 | return json.NewDecoder(r) 35 | } 36 | 37 | // NewEncoder returns an Encoder which writes JSON stream into "w". 38 | func (j *JSONBuiltin) NewEncoder(w io.Writer) Encoder { 39 | return json.NewEncoder(w) 40 | } 41 | 42 | // Delimiter for newline encoded JSON streams. 43 | func (j *JSONBuiltin) Delimiter() []byte { 44 | return []byte("\n") 45 | } 46 | -------------------------------------------------------------------------------- /examples/internal/server/fieldmask_helper.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "log" 5 | "reflect" 6 | "strings" 7 | 8 | "github.com/grpc-ecosystem/grpc-gateway/internal/casing" 9 | "google.golang.org/genproto/protobuf/field_mask" 10 | ) 11 | 12 | func applyFieldMask(patchee, patcher interface{}, mask *field_mask.FieldMask) { 13 | if mask == nil { 14 | return 15 | } 16 | 17 | for _, path := range mask.GetPaths() { 18 | val := getField(patcher, path) 19 | if val.IsValid() { 20 | setValue(patchee, val, path) 21 | } 22 | } 23 | } 24 | 25 | func getField(obj interface{}, path string) (val reflect.Value) { 26 | // this func is lazy -- if anything bad happens just return nil 27 | defer func() { 28 | if r := recover(); r != nil { 29 | log.Printf("failed to get field:\npath: %q\nobj: %#v\nerr: %v", path, obj, r) 30 | val = reflect.Value{} 31 | } 32 | }() 33 | 34 | v := reflect.ValueOf(obj) 35 | if len(path) == 0 { 36 | return v 37 | } 38 | 39 | for _, s := range strings.Split(path, ".") { 40 | if v.Kind() == reflect.Ptr { 41 | v = reflect.Indirect(v) 42 | } 43 | v = v.FieldByName(casing.Camel(s)) 44 | } 45 | 46 | return v 47 | } 48 | 49 | func setValue(obj interface{}, newValue reflect.Value, path string) { 50 | defer func() { 51 | if r := recover(); r != nil { 52 | log.Printf("failed to set value:\nnewValue: %#v\npath: %q\nobj: %#v\nerr: %v", newValue, path, obj, r) 53 | } 54 | }() 55 | getField(obj, path).Set(newValue) 56 | } 57 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | import ( 14 | "net/http" 15 | ) 16 | 17 | type APIResponse struct { 18 | *http.Response `json:"-"` 19 | Message string `json:"message,omitempty"` 20 | // Operation is the name of the swagger operation. 21 | Operation string `json:"operation,omitempty"` 22 | // RequestURL is the request URL. This value is always available, even if the 23 | // embedded *http.Response is nil. 24 | RequestURL string `json:"url,omitempty"` 25 | // Method is the HTTP method used for the request. This value is always 26 | // available, even if the embedded *http.Response is nil. 27 | Method string `json:"method,omitempty"` 28 | // Payload holds the contents of the response body (which may be nil or empty). 29 | // This is provided here as the raw response.Body() reader will have already 30 | // been drained. 31 | Payload []byte `json:"-"` 32 | } 33 | 34 | func NewAPIResponse(r *http.Response) *APIResponse { 35 | 36 | response := &APIResponse{Response: r} 37 | return response 38 | } 39 | 40 | func NewAPIResponseWithError(errorMessage string) *APIResponse { 41 | 42 | response := &APIResponse{Message: errorMessage} 43 | return response 44 | } 45 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/unannotated_echo_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | 4 | // Unannotated Echo Service 5 | // Similar to echo_service.proto but without annotations. See 6 | // unannotated_echo_service.yaml for the equivalent of the annotations in 7 | // gRPC API configuration format. 8 | // 9 | // Echo Service API consists of a single service which returns 10 | // a message. 11 | package grpc.gateway.examples.internal.examplepb; 12 | 13 | // Do not need annotations.proto, can still use well known types as usual 14 | import "google/protobuf/duration.proto"; 15 | 16 | // UnannotatedSimpleMessage represents a simple message sent to the unannotated Echo service. 17 | message UnannotatedSimpleMessage { 18 | // Id represents the message identifier. 19 | string id = 1; 20 | int64 num = 2; 21 | google.protobuf.Duration duration = 3; 22 | } 23 | 24 | // Echo service responds to incoming echo requests. 25 | service UnannotatedEchoService { 26 | // Echo method receives a simple message and returns it. 27 | // 28 | // The message posted as the id parameter will also be 29 | // returned. 30 | rpc Echo(UnannotatedSimpleMessage) returns (UnannotatedSimpleMessage); 31 | 32 | // EchoBody method receives a simple message and returns it. 33 | rpc EchoBody(UnannotatedSimpleMessage) returns (UnannotatedSimpleMessage); 34 | 35 | // EchoDelete method receives a simple message and returns it. 36 | rpc EchoDelete(UnannotatedSimpleMessage) returns (UnannotatedSimpleMessage); 37 | } 38 | -------------------------------------------------------------------------------- /examples/internal/server/non_standard_names.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/golang/glog" 7 | examples "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/examplepb" 8 | ) 9 | 10 | // Implements NonStandardServiceServer 11 | 12 | type nonStandardServer struct{} 13 | 14 | func newNonStandardServer() examples.NonStandardServiceServer { 15 | return new(nonStandardServer) 16 | } 17 | 18 | func (s *nonStandardServer) Update(ctx context.Context, msg *examples.NonStandardUpdateRequest) (*examples.NonStandardMessage, error) { 19 | glog.Info(msg) 20 | 21 | newMsg := &examples.NonStandardMessage{ 22 | Thing: &examples.NonStandardMessage_Thing{SubThing: &examples.NonStandardMessage_Thing_SubThing{}}, // The fieldmask_helper doesn't generate nested structs if they are nil 23 | } 24 | applyFieldMask(newMsg, msg.Body, msg.UpdateMask) 25 | 26 | glog.Info(newMsg) 27 | return newMsg, nil 28 | } 29 | 30 | func (s *nonStandardServer) UpdateWithJSONNames(ctx context.Context, msg *examples.NonStandardWithJSONNamesUpdateRequest) (*examples.NonStandardMessageWithJSONNames, error) { 31 | glog.Info(msg) 32 | 33 | newMsg := &examples.NonStandardMessageWithJSONNames{ 34 | Thing: &examples.NonStandardMessageWithJSONNames_Thing{SubThing: &examples.NonStandardMessageWithJSONNames_Thing_SubThing{}}, // The fieldmask_helper doesn't generate nested structs if they are nil 35 | } 36 | applyFieldMask(newMsg, msg.Body, msg.UpdateMask) 37 | 38 | glog.Info(newMsg) 39 | return newMsg, nil 40 | } 41 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | import ( 13 | "net/http" 14 | ) 15 | 16 | type APIResponse struct { 17 | *http.Response `json:"-"` 18 | Message string `json:"message,omitempty"` 19 | // Operation is the name of the swagger operation. 20 | Operation string `json:"operation,omitempty"` 21 | // RequestURL is the request URL. This value is always available, even if the 22 | // embedded *http.Response is nil. 23 | RequestURL string `json:"url,omitempty"` 24 | // Method is the HTTP method used for the request. This value is always 25 | // available, even if the embedded *http.Response is nil. 26 | Method string `json:"method,omitempty"` 27 | // Payload holds the contents of the response body (which may be nil or empty). 28 | // This is provided here as the raw response.Body() reader will have already 29 | // been drained. 30 | Payload []byte `json:"-"` 31 | } 32 | 33 | func NewAPIResponse(r *http.Response) *APIResponse { 34 | 35 | response := &APIResponse{Response: r} 36 | return response 37 | } 38 | 39 | func NewAPIResponseWithError(errorMessage string) *APIResponse { 40 | 41 | response := &APIResponse{Message: errorMessage} 42 | return response 43 | } 44 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "api_a_bit_of_everything_service.go", 9 | "api_camel_case_service_name.go", 10 | "api_echo_rpc.go", 11 | "client.go", 12 | "configuration.go", 13 | "enum_helper.go", 14 | "model_a_bit_of_everything_nested.go", 15 | "model_examplepb_a_bit_of_everything.go", 16 | "model_examplepb_a_bit_of_everything_repeated.go", 17 | "model_examplepb_body.go", 18 | "model_examplepb_book.go", 19 | "model_examplepb_numeric_enum.go", 20 | "model_examplepb_update_v2_request.go", 21 | "model_message_path_enum_nested_path_enum.go", 22 | "model_nested_deep_enum.go", 23 | "model_pathenum_path_enum.go", 24 | "model_protobuf_any.go", 25 | "model_protobuf_field_mask.go", 26 | "model_runtime_error.go", 27 | "model_sub_string_message.go", 28 | "response.go", 29 | ], 30 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/clients/abe", 31 | deps = [ 32 | "//examples/internal/proto/examplepb:go_default_library", 33 | "//examples/internal/proto/pathenum:go_default_library", 34 | "//runtime:go_default_library", 35 | "@com_github_antihax_optional//:go_default_library", 36 | "@org_golang_x_oauth2//:go_default_library", 37 | ], 38 | ) 39 | -------------------------------------------------------------------------------- /internal/casing/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2010 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Gengo, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Gengo, Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/generate_unbound_methods.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | 4 | // Generate Unannotated Methods Echo Service 5 | // Similar to echo_service.proto but without annotations and without external configuration. 6 | // 7 | // Generate Unannotated Methods Echo Service API consists of a single service which returns 8 | // a message. 9 | package grpc.gateway.examples.internal.examplepb; 10 | 11 | // Do not need annotations.proto, can still use well known types as usual 12 | import "google/protobuf/duration.proto"; 13 | 14 | // GenerateUnboundMethodsSimpleMessage represents a simple message sent to the unannotated GenerateUnboundMethodsEchoService service. 15 | message GenerateUnboundMethodsSimpleMessage { 16 | // Id represents the message identifier. 17 | string id = 1; 18 | int64 num = 2; 19 | google.protobuf.Duration duration = 3; 20 | } 21 | 22 | // GenerateUnboundMethodsEchoService service responds to incoming echo requests. 23 | service GenerateUnboundMethodsEchoService { 24 | // Echo method receives a simple message and returns it. 25 | // 26 | // The message posted as the id parameter will also be 27 | // returned. 28 | rpc Echo(GenerateUnboundMethodsSimpleMessage) returns (GenerateUnboundMethodsSimpleMessage); 29 | 30 | // EchoBody method receives a simple message and returns it. 31 | rpc EchoBody(GenerateUnboundMethodsSimpleMessage) returns (GenerateUnboundMethodsSimpleMessage); 32 | 33 | // EchoDelete method receives a simple message and returns it. 34 | rpc EchoDelete(GenerateUnboundMethodsSimpleMessage) returns (GenerateUnboundMethodsSimpleMessage); 35 | } 36 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/descriptor/grpc_api_service.go: -------------------------------------------------------------------------------- 1 | package descriptor 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | "google.golang.org/genproto/googleapis/api/annotations" 6 | ) 7 | 8 | // GrpcAPIService represents a stripped down version of google.api.Service . 9 | // Compare to https://github.com/googleapis/googleapis/blob/master/google/api/service.proto 10 | // The original imports 23 other protobuf files we are not interested in. If a significant 11 | // subset (>50%) of these start being reproduced in this file we should swap to using the 12 | // full generated version instead. 13 | // 14 | // For the purposes of the gateway generator we only consider a small subset of all 15 | // available features google supports in their service descriptions. Thanks to backwards 16 | // compatibility guarantees by protobuf it is safe for us to remove the other fields. 17 | // We also only implement the absolute minimum of protobuf generator boilerplate to use 18 | // our simplified version. These should be pretty stable too. 19 | type GrpcAPIService struct { 20 | // Http Rule. Named Http in the actual proto. Changed to suppress linter warning. 21 | HTTP *annotations.Http `protobuf:"bytes,9,opt,name=http" json:"http,omitempty"` 22 | } 23 | 24 | // ProtoMessage returns an empty GrpcAPIService element 25 | func (*GrpcAPIService) ProtoMessage() {} 26 | 27 | // Reset resets the GrpcAPIService 28 | func (m *GrpcAPIService) Reset() { *m = GrpcAPIService{} } 29 | 30 | // String returns the string representation of the GrpcAPIService 31 | func (m *GrpcAPIService) String() string { return proto.CompactTextString(m) } 32 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/response_body_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | 4 | package grpc.gateway.examples.internal.examplepb; 5 | 6 | import "google/api/annotations.proto"; 7 | 8 | message ResponseBodyIn { 9 | string data = 1; 10 | } 11 | 12 | message ResponseBodyOut { 13 | message Response { 14 | string data = 1; 15 | } 16 | Response response = 2; 17 | } 18 | 19 | message RepeatedResponseBodyOut { 20 | message Response { 21 | string data = 1; 22 | enum ResponseType { 23 | // UNKNOWN 24 | UNKNOWN = 0; 25 | // A is 1 26 | A = 1; 27 | // B is 2 28 | B = 2; 29 | } 30 | ResponseType type = 3; 31 | } 32 | repeated Response response = 2; 33 | } 34 | 35 | message RepeatedResponseStrings { 36 | repeated string values = 1; 37 | } 38 | 39 | service ResponseBodyService { 40 | rpc GetResponseBody(ResponseBodyIn) returns (ResponseBodyOut) { 41 | option (google.api.http) = { 42 | get : "/responsebody/{data}" 43 | response_body : "response" 44 | }; 45 | } 46 | rpc ListResponseBodies(ResponseBodyIn) returns (RepeatedResponseBodyOut) { 47 | option (google.api.http) = { 48 | get : "/responsebodies/{data}" 49 | response_body : "response" 50 | }; 51 | } 52 | rpc ListResponseStrings(ResponseBodyIn) returns (RepeatedResponseStrings) { 53 | option (google.api.http) = { 54 | get : "/responsestrings/{data}" 55 | response_body : "values" 56 | }; 57 | } 58 | 59 | rpc GetResponseBodyStream(ResponseBodyIn) returns (stream ResponseBodyOut) { 60 | option (google.api.http) = { 61 | get : "/responsebody/stream/{data}" 62 | response_body : "response" 63 | }; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.circleci/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.15.2 2 | 3 | # Warm apt cache and install dependencies 4 | # bzip2 is required by the node_tests (to extract its dependencies). 5 | # patch is required by bazel tests 6 | RUN apt-get update && \ 7 | apt-get install -y wget unzip \ 8 | openjdk-11-jre \ 9 | bzip2 \ 10 | patch 11 | 12 | # Install swagger-codegen 13 | ENV SWAGGER_CODEGEN_VERSION=2.4.8 14 | RUN wget https://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/${SWAGGER_CODEGEN_VERSION}/swagger-codegen-cli-${SWAGGER_CODEGEN_VERSION}.jar \ 15 | -O /usr/local/bin/swagger-codegen-cli.jar 16 | 17 | # Wrap the jar for swagger-codgen 18 | RUN echo -e '#!/bin/bash\njava -jar /usr/local/bin/swagger-codegen-cli.jar "$@"' > /usr/local/bin/swagger-codegen && \ 19 | chmod +x /usr/local/bin/swagger-codegen 20 | 21 | # Install protoc 22 | ENV PROTOC_VERSION=3.12.0 23 | RUN wget https://github.com/google/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip \ 24 | -O /protoc-${PROTOC_VERSION}-linux-x86_64.zip && \ 25 | unzip /protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /usr/local/ && \ 26 | rm -f /protoc-${PROTOC_VERSION}-linux-x86_64.zip 27 | 28 | # Install node, used by NVM 29 | ENV NODE_VERSION=v10.16.3 30 | ENV NVM_VERSION=v0.35.0 31 | RUN wget -qO- https://raw.githubusercontent.com/creationix/nvm/${NVM_VERSION}/install.sh | bash 32 | 33 | # Install Bazelisk as bazel to manage Bazel 34 | RUN go get github.com/bazelbuild/bazelisk && \ 35 | mv $(which bazelisk) /usr/local/bin/bazel 36 | 37 | # Clean up 38 | RUN apt-get autoremove -y && \ 39 | apt-get remove -y wget \ 40 | unzip && \ 41 | rm -rf /var/lib/apt/lists/* 42 | -------------------------------------------------------------------------------- /runtime/marshal_httpbodyproto.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "google.golang.org/genproto/googleapis/api/httpbody" 5 | ) 6 | 7 | // SetHTTPBodyMarshaler overwrite the default marshaler with the HTTPBodyMarshaler 8 | func SetHTTPBodyMarshaler(serveMux *ServeMux) { 9 | serveMux.marshalers.mimeMap[MIMEWildcard] = &HTTPBodyMarshaler{ 10 | Marshaler: &JSONPb{OrigName: true}, 11 | } 12 | } 13 | 14 | // HTTPBodyMarshaler is a Marshaler which supports marshaling of a 15 | // google.api.HttpBody message as the full response body if it is 16 | // the actual message used as the response. If not, then this will 17 | // simply fallback to the Marshaler specified as its default Marshaler. 18 | type HTTPBodyMarshaler struct { 19 | Marshaler 20 | } 21 | 22 | // ContentType implementation to keep backwards compatibility with marshal interface 23 | func (h *HTTPBodyMarshaler) ContentType() string { 24 | return h.ContentTypeFromMessage(nil) 25 | } 26 | 27 | // ContentTypeFromMessage in case v is a google.api.HttpBody message it returns 28 | // its specified content type otherwise fall back to the default Marshaler. 29 | func (h *HTTPBodyMarshaler) ContentTypeFromMessage(v interface{}) string { 30 | if httpBody, ok := v.(*httpbody.HttpBody); ok { 31 | return httpBody.GetContentType() 32 | } 33 | return h.Marshaler.ContentType() 34 | } 35 | 36 | // Marshal marshals "v" by returning the body bytes if v is a 37 | // google.api.HttpBody message, otherwise it falls back to the default Marshaler. 38 | func (h *HTTPBodyMarshaler) Marshal(v interface{}) ([]byte, error) { 39 | if httpBody, ok := v.(*httpbody.HttpBody); ok { 40 | return httpBody.Data, nil 41 | } 42 | return h.Marshaler.Marshal(v) 43 | } 44 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/generate_unbound_methods.proto 3 | * 4 | * Generate Unannotated Methods Echo Service Similar to echo_service.proto but without annotations and without external configuration. Generate Unannotated Methods Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package generateunboundmethods 11 | 12 | import ( 13 | "net/http" 14 | ) 15 | 16 | type APIResponse struct { 17 | *http.Response `json:"-"` 18 | Message string `json:"message,omitempty"` 19 | // Operation is the name of the swagger operation. 20 | Operation string `json:"operation,omitempty"` 21 | // RequestURL is the request URL. This value is always available, even if the 22 | // embedded *http.Response is nil. 23 | RequestURL string `json:"url,omitempty"` 24 | // Method is the HTTP method used for the request. This value is always 25 | // available, even if the embedded *http.Response is nil. 26 | Method string `json:"method,omitempty"` 27 | // Payload holds the contents of the response body (which may be nil or empty). 28 | // This is provided here as the raw response.Body() reader will have already 29 | // been drained. 30 | Payload []byte `json:"-"` 31 | } 32 | 33 | func NewAPIResponse(r *http.Response) *APIResponse { 34 | 35 | response := &APIResponse{Response: r} 36 | return response 37 | } 38 | 39 | func NewAPIResponseWithError(errorMessage string) *APIResponse { 40 | 41 | response := &APIResponse{Message: errorMessage} 42 | return response 43 | } 44 | -------------------------------------------------------------------------------- /examples/internal/clients/unannotatedecho/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/unannotated_echo_service.proto 3 | * 4 | * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format. Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package unannotatedecho 11 | 12 | import ( 13 | "net/http" 14 | ) 15 | 16 | type APIResponse struct { 17 | *http.Response `json:"-"` 18 | Message string `json:"message,omitempty"` 19 | // Operation is the name of the swagger operation. 20 | Operation string `json:"operation,omitempty"` 21 | // RequestURL is the request URL. This value is always available, even if the 22 | // embedded *http.Response is nil. 23 | RequestURL string `json:"url,omitempty"` 24 | // Method is the HTTP method used for the request. This value is always 25 | // available, even if the embedded *http.Response is nil. 26 | Method string `json:"method,omitempty"` 27 | // Payload holds the contents of the response body (which may be nil or empty). 28 | // This is provided here as the raw response.Body() reader will have already 29 | // been drained. 30 | Payload []byte `json:"-"` 31 | } 32 | 33 | func NewAPIResponse(r *http.Response) *APIResponse { 34 | 35 | response := &APIResponse{Response: r} 36 | return response 37 | } 38 | 39 | func NewAPIResponseWithError(errorMessage string) *APIResponse { 40 | 41 | response := &APIResponse{Message: errorMessage} 42 | return response 43 | } 44 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/descriptor/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "grpc_api_configuration.go", 9 | "grpc_api_service.go", 10 | "registry.go", 11 | "services.go", 12 | "types.go", 13 | ], 14 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor", 15 | deps = [ 16 | "//internal/casing:go_default_library", 17 | "//protoc-gen-grpc-gateway/httprule:go_default_library", 18 | "@com_github_ghodss_yaml//:go_default_library", 19 | "@com_github_golang_glog//:go_default_library", 20 | "@com_github_golang_protobuf//jsonpb:go_default_library_gen", 21 | "@com_github_golang_protobuf//proto:go_default_library", 22 | "@go_googleapis//google/api:annotations_go_proto", 23 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 24 | "@io_bazel_rules_go//proto/wkt:descriptor_go_proto", 25 | ], 26 | ) 27 | 28 | go_test( 29 | name = "go_default_test", 30 | size = "small", 31 | srcs = [ 32 | "grpc_api_configuration_test.go", 33 | "registry_test.go", 34 | "services_test.go", 35 | "types_test.go", 36 | ], 37 | embed = [":go_default_library"], 38 | deps = [ 39 | "//protoc-gen-grpc-gateway/httprule:go_default_library", 40 | "@com_github_golang_protobuf//proto:go_default_library", 41 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 42 | "@io_bazel_rules_go//proto/wkt:descriptor_go_proto", 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /examples/internal/helloworld/helloworld.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package helloworld; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/protobuf/wrappers.proto"; 7 | 8 | service Greeter { 9 | rpc SayHello (HelloRequest) returns (HelloReply) { 10 | option (google.api.http) = { 11 | get: "/say/{name}" 12 | additional_bindings: { 13 | get: "/say/strval/{strVal}", 14 | } 15 | additional_bindings: { 16 | get: "/say/floatval/{floatVal}", 17 | } 18 | additional_bindings: { 19 | get: "/say/doubleval/{doubleVal}", 20 | } 21 | additional_bindings: { 22 | get: "/say/boolval/{boolVal}", 23 | } 24 | additional_bindings: { 25 | get: "/say/bytesval/{bytesVal}", 26 | } 27 | additional_bindings: { 28 | get: "/say/int32val/{int32Val}", 29 | } 30 | additional_bindings: { 31 | get: "/say/uint32val/{uint32Val}", 32 | } 33 | additional_bindings: { 34 | get: "/say/int64val/{int64Val}", 35 | } 36 | additional_bindings: { 37 | get: "/say/uint64val/{uint64Val}", 38 | } 39 | }; 40 | } 41 | } 42 | 43 | message HelloRequest { 44 | string name = 1; 45 | google.protobuf.StringValue strVal = 2; 46 | google.protobuf.FloatValue floatVal = 3; 47 | google.protobuf.DoubleValue doubleVal = 4; 48 | google.protobuf.BoolValue boolVal = 5; 49 | google.protobuf.BytesValue bytesVal = 6; 50 | google.protobuf.Int32Value int32Val = 7; 51 | google.protobuf.UInt32Value uint32Val = 8; 52 | google.protobuf.Int64Value int64Val = 9; 53 | google.protobuf.UInt64Value uint64Val = 10; 54 | } 55 | 56 | message HelloReply { 57 | string message = 1; 58 | } 59 | -------------------------------------------------------------------------------- /examples/internal/helloworld/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_proto//proto:defs.bzl", "proto_library") 2 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 3 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 4 | 5 | proto_library( 6 | name = "helloworld_proto", 7 | srcs = ["helloworld.proto"], 8 | visibility = ["//visibility:public"], 9 | deps = [ 10 | "@com_google_protobuf//:wrappers_proto", 11 | "@go_googleapis//google/api:annotations_proto", 12 | ], 13 | ) 14 | 15 | go_proto_library( 16 | name = "helloworld_go_proto", 17 | compilers = ["@io_bazel_rules_go//proto:go_grpc"], 18 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/helloworld", 19 | proto = ":helloworld_proto", 20 | visibility = ["//visibility:public"], 21 | deps = ["@go_googleapis//google/api:annotations_go_proto"], 22 | ) 23 | 24 | go_library( 25 | name = "go_default_library", 26 | srcs = ["helloworld.pb.gw.go"], 27 | embed = [":helloworld_go_proto"], 28 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/helloworld", 29 | visibility = ["//visibility:public"], 30 | deps = [ 31 | "//runtime:go_default_library", 32 | "//utilities:go_default_library", 33 | "@com_github_golang_protobuf//descriptor:go_default_library_gen", 34 | "@com_github_golang_protobuf//proto:go_default_library", 35 | "@org_golang_google_grpc//:go_default_library", 36 | "@org_golang_google_grpc//codes:go_default_library", 37 | "@org_golang_google_grpc//grpclog:go_default_library", 38 | "@org_golang_google_grpc//metadata:go_default_library", 39 | "@org_golang_google_grpc//status:go_default_library", 40 | ], 41 | ) 42 | -------------------------------------------------------------------------------- /codegenerator/parse_req_test.go: -------------------------------------------------------------------------------- 1 | package codegenerator_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "reflect" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/golang/protobuf/proto" 12 | plugin "github.com/golang/protobuf/protoc-gen-go/plugin" 13 | "github.com/grpc-ecosystem/grpc-gateway/codegenerator" 14 | ) 15 | 16 | var parseReqTests = []struct { 17 | name string 18 | in io.Reader 19 | out *plugin.CodeGeneratorRequest 20 | err error 21 | }{ 22 | { 23 | "Empty input should produce empty output", 24 | mustGetReader(&plugin.CodeGeneratorRequest{}), 25 | &plugin.CodeGeneratorRequest{}, 26 | nil, 27 | }, 28 | { 29 | "Invalid reader should produce error", 30 | &invalidReader{}, 31 | nil, 32 | fmt.Errorf("failed to read code generator request: invalid reader"), 33 | }, 34 | { 35 | "Invalid proto message should produce error", 36 | strings.NewReader("{}"), 37 | nil, 38 | fmt.Errorf("failed to unmarshal code generator request: unexpected EOF"), 39 | }, 40 | } 41 | 42 | func TestParseRequest(t *testing.T) { 43 | for _, tt := range parseReqTests { 44 | t.Run(tt.name, func(t *testing.T) { 45 | out, err := codegenerator.ParseRequest(tt.in) 46 | if !reflect.DeepEqual(err, tt.err) { 47 | t.Errorf("got %v, want %v", err, tt.err) 48 | } 49 | if err == nil && !reflect.DeepEqual(*out, *tt.out) { 50 | t.Errorf("got %v, want %v", *out, *tt.out) 51 | } 52 | }) 53 | } 54 | } 55 | 56 | func mustGetReader(pb proto.Message) io.Reader { 57 | b, err := proto.Marshal(pb) 58 | if err != nil { 59 | panic(err) 60 | } 61 | return bytes.NewBuffer(b) 62 | } 63 | 64 | type invalidReader struct { 65 | } 66 | 67 | func (*invalidReader) Read(p []byte) (int, error) { 68 | return 0, fmt.Errorf("invalid reader") 69 | } 70 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/model_examplepb_a_bit_of_everything_repeated.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | type ExamplepbABitOfEverythingRepeated struct { 14 | PathRepeatedFloatValue []float32 `json:"path_repeated_float_value,omitempty"` 15 | PathRepeatedDoubleValue []float64 `json:"path_repeated_double_value,omitempty"` 16 | PathRepeatedInt64Value []string `json:"path_repeated_int64_value,omitempty"` 17 | PathRepeatedUint64Value []string `json:"path_repeated_uint64_value,omitempty"` 18 | PathRepeatedInt32Value []int32 `json:"path_repeated_int32_value,omitempty"` 19 | PathRepeatedFixed64Value []string `json:"path_repeated_fixed64_value,omitempty"` 20 | PathRepeatedFixed32Value []int64 `json:"path_repeated_fixed32_value,omitempty"` 21 | PathRepeatedBoolValue []bool `json:"path_repeated_bool_value,omitempty"` 22 | PathRepeatedStringValue []string `json:"path_repeated_string_value,omitempty"` 23 | PathRepeatedBytesValue []string `json:"path_repeated_bytes_value,omitempty"` 24 | PathRepeatedUint32Value []int64 `json:"path_repeated_uint32_value,omitempty"` 25 | PathRepeatedEnumValue []ExamplepbNumericEnum `json:"path_repeated_enum_value,omitempty"` 26 | PathRepeatedSfixed32Value []int32 `json:"path_repeated_sfixed32_value,omitempty"` 27 | PathRepeatedSfixed64Value []string `json:"path_repeated_sfixed64_value,omitempty"` 28 | PathRepeatedSint32Value []int32 `json:"path_repeated_sint32_value,omitempty"` 29 | PathRepeatedSint64Value []string `json:"path_repeated_sint64_value,omitempty"` 30 | } 31 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/enum_helper.go: -------------------------------------------------------------------------------- 1 | package abe 2 | 3 | import ( 4 | pbexamplepb "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/examplepb" 5 | pbpathenum "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/pathenum" 6 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 7 | ) 8 | 9 | // String returns a string representation of "NumericEnum" 10 | func (e ExamplepbNumericEnum) String() string { 11 | return pbexamplepb.NumericEnum_ONE.String() 12 | } 13 | 14 | // UnmarshalJSON does a no-op unmarshal to ExamplepbNumericEnum. 15 | // It just validates that the input is sane. 16 | func (e ExamplepbNumericEnum) UnmarshalJSON(b []byte) error { 17 | return unmarshalJSONEnum(b, pbexamplepb.NumericEnum_value) 18 | } 19 | 20 | // String returns a string representation of "MessagePathEnum" 21 | func (e MessagePathEnumNestedPathEnum) String() string { 22 | return pbpathenum.MessagePathEnum_JKL.String() 23 | } 24 | 25 | // UnmarshalJSON does a no-op unmarshal to MessagePathEnumNestedPathEnum. 26 | // It just validates that the input is sane. 27 | func (e MessagePathEnumNestedPathEnum) UnmarshalJSON(b []byte) error { 28 | return unmarshalJSONEnum(b, pbpathenum.MessagePathEnum_NestedPathEnum_value) 29 | } 30 | 31 | // String returns a string representation of "PathEnum" 32 | func (e PathenumPathEnum) String() string { 33 | return pbpathenum.PathEnum_DEF.String() 34 | } 35 | 36 | // UnmarshalJSON does a no-op unmarshal to PathenumPathEnum. 37 | // It just validates that the input is sane. 38 | func (e PathenumPathEnum) UnmarshalJSON(b []byte) error { 39 | return unmarshalJSONEnum(b, pbpathenum.PathEnum_value) 40 | } 41 | 42 | func unmarshalJSONEnum(b []byte, enumValMap map[string]int32) error { 43 | val := string(b[1 : len(b)-1]) 44 | _, err := runtime.Enum(val, enumValMap) 45 | return err 46 | } 47 | -------------------------------------------------------------------------------- /runtime/marshal_proto.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "io" 5 | 6 | "errors" 7 | "github.com/golang/protobuf/proto" 8 | "io/ioutil" 9 | ) 10 | 11 | // ProtoMarshaller is a Marshaller which marshals/unmarshals into/from serialize proto bytes 12 | type ProtoMarshaller struct{} 13 | 14 | // ContentType always returns "application/octet-stream". 15 | func (*ProtoMarshaller) ContentType() string { 16 | return "application/octet-stream" 17 | } 18 | 19 | // Marshal marshals "value" into Proto 20 | func (*ProtoMarshaller) Marshal(value interface{}) ([]byte, error) { 21 | message, ok := value.(proto.Message) 22 | if !ok { 23 | return nil, errors.New("unable to marshal non proto field") 24 | } 25 | return proto.Marshal(message) 26 | } 27 | 28 | // Unmarshal unmarshals proto "data" into "value" 29 | func (*ProtoMarshaller) Unmarshal(data []byte, value interface{}) error { 30 | message, ok := value.(proto.Message) 31 | if !ok { 32 | return errors.New("unable to unmarshal non proto field") 33 | } 34 | return proto.Unmarshal(data, message) 35 | } 36 | 37 | // NewDecoder returns a Decoder which reads proto stream from "reader". 38 | func (marshaller *ProtoMarshaller) NewDecoder(reader io.Reader) Decoder { 39 | return DecoderFunc(func(value interface{}) error { 40 | buffer, err := ioutil.ReadAll(reader) 41 | if err != nil { 42 | return err 43 | } 44 | return marshaller.Unmarshal(buffer, value) 45 | }) 46 | } 47 | 48 | // NewEncoder returns an Encoder which writes proto stream into "writer". 49 | func (marshaller *ProtoMarshaller) NewEncoder(writer io.Writer) Encoder { 50 | return EncoderFunc(func(value interface{}) error { 51 | buffer, err := marshaller.Marshal(value) 52 | if err != nil { 53 | return err 54 | } 55 | _, err = writer.Write(buffer) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | return nil 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/httprule/types_test.go: -------------------------------------------------------------------------------- 1 | package httprule 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestTemplateStringer(t *testing.T) { 9 | for _, spec := range []struct { 10 | segs []segment 11 | want string 12 | }{ 13 | { 14 | segs: []segment{ 15 | literal("v1"), 16 | }, 17 | want: "/v1", 18 | }, 19 | { 20 | segs: []segment{ 21 | wildcard{}, 22 | }, 23 | want: "/*", 24 | }, 25 | { 26 | segs: []segment{ 27 | deepWildcard{}, 28 | }, 29 | want: "/**", 30 | }, 31 | { 32 | segs: []segment{ 33 | variable{ 34 | path: "name", 35 | segments: []segment{ 36 | literal("a"), 37 | }, 38 | }, 39 | }, 40 | want: "/{name=a}", 41 | }, 42 | { 43 | segs: []segment{ 44 | variable{ 45 | path: "name", 46 | segments: []segment{ 47 | literal("a"), 48 | wildcard{}, 49 | literal("b"), 50 | }, 51 | }, 52 | }, 53 | want: "/{name=a/*/b}", 54 | }, 55 | { 56 | segs: []segment{ 57 | literal("v1"), 58 | variable{ 59 | path: "name", 60 | segments: []segment{ 61 | literal("a"), 62 | wildcard{}, 63 | literal("b"), 64 | }, 65 | }, 66 | literal("c"), 67 | variable{ 68 | path: "field.nested", 69 | segments: []segment{ 70 | wildcard{}, 71 | literal("d"), 72 | }, 73 | }, 74 | wildcard{}, 75 | literal("e"), 76 | deepWildcard{}, 77 | }, 78 | want: "/v1/{name=a/*/b}/c/{field.nested=*/d}/*/e/**", 79 | }, 80 | } { 81 | tmpl := template{segments: spec.segs} 82 | if got, want := tmpl.String(), spec.want; got != want { 83 | t.Errorf("%#v.String() = %q; want %q", tmpl, got, want) 84 | } 85 | 86 | tmpl.verb = "LOCK" 87 | if got, want := tmpl.String(), fmt.Sprintf("%s:LOCK", spec.want); got != want { 88 | t.Errorf("%#v.String() = %q; want %q", tmpl, got, want) 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/docs/ProtobufAny.md: -------------------------------------------------------------------------------- 1 | # ProtobufAny 2 | 3 | ## Properties 4 | Name | Type | Description | Notes 5 | ------------ | ------------- | ------------- | ------------- 6 | **TypeUrl** | **string** | A URL/resource name that uniquely identifies the type of the serialized protocol buffer message. This string must contain at least one \"/\" character. The last segment of the URL's path must represent the fully qualified name of the type (as in `path/google.protobuf.Duration`). The name should be in a canonical form (e.g., leading \".\" is not accepted). In practice, teams usually precompile into the binary all types that they expect it to use in the context of Any. However, for URLs which use the scheme `http`, `https`, or no scheme, one can optionally set up a type server that maps type URLs to message definitions as follows: * If no scheme is provided, `https` is assumed. * An HTTP GET on the URL must yield a [google.protobuf.Type][] value in binary format, or produce an error. * Applications are allowed to cache lookup results based on the URL, or have them precompiled into a binary to avoid any lookup. Therefore, binary compatibility needs to be preserved on changes to types. (Use versioned type names to manage breaking changes.) Note: this functionality is not currently available in the official protobuf release, and it is not used for type URLs beginning with type.googleapis.com. Schemes other than `http`, `https` (or the empty scheme) might be used with implementation specific semantics. | [optional] [default to null] 7 | **Value** | **string** | Must be a valid serialized protocol buffer of the above specified type. | [optional] [default to null] 8 | 9 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 10 | 11 | 12 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") 2 | load("@io_bazel_rules_go//proto:compiler.bzl", "go_proto_compiler") 3 | 4 | package(default_visibility = ["//visibility:private"]) 5 | 6 | go_library( 7 | name = "go_default_library", 8 | srcs = ["main.go"], 9 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", 10 | deps = [ 11 | "//codegenerator:go_default_library", 12 | "//protoc-gen-grpc-gateway/descriptor:go_default_library", 13 | "//protoc-gen-grpc-gateway/internal/gengateway:go_default_library", 14 | "@com_github_golang_glog//:go_default_library", 15 | "@com_github_golang_protobuf//proto:go_default_library", 16 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 17 | ], 18 | ) 19 | 20 | go_binary( 21 | name = "protoc-gen-grpc-gateway", 22 | embed = [":go_default_library"], 23 | visibility = ["//visibility:public"], 24 | ) 25 | 26 | go_proto_compiler( 27 | name = "go_gen_grpc_gateway", 28 | options = [ 29 | "logtostderr=true", 30 | "allow_repeated_fields_in_body=true", 31 | ], 32 | plugin = ":protoc-gen-grpc-gateway", 33 | suffix = ".pb.gw.go", 34 | visibility = ["//visibility:public"], 35 | deps = [ 36 | "//runtime:go_default_library", 37 | "//utilities:go_default_library", 38 | "@com_github_golang_protobuf//descriptor:go_default_library_gen", 39 | "@com_github_golang_protobuf//proto:go_default_library", 40 | "@org_golang_google_grpc//:go_default_library", 41 | "@org_golang_google_grpc//codes:go_default_library", 42 | "@org_golang_google_grpc//grpclog:go_default_library", 43 | "@org_golang_google_grpc//metadata:go_default_library", 44 | "@org_golang_google_grpc//status:go_default_library", 45 | "@org_golang_x_net//context:go_default_library", 46 | ], 47 | ) 48 | -------------------------------------------------------------------------------- /protoc-gen-swagger/options/annotations.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package grpc.gateway.protoc_gen_swagger.options; 4 | 5 | option go_package = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"; 6 | 7 | import "google/protobuf/descriptor.proto"; 8 | import "protoc-gen-swagger/options/openapiv2.proto"; 9 | 10 | extend google.protobuf.FileOptions { 11 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 12 | // 13 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 14 | // different descriptor messages. 15 | Swagger openapiv2_swagger = 1042; 16 | } 17 | extend google.protobuf.MethodOptions { 18 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 19 | // 20 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 21 | // different descriptor messages. 22 | Operation openapiv2_operation = 1042; 23 | } 24 | extend google.protobuf.MessageOptions { 25 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 26 | // 27 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 28 | // different descriptor messages. 29 | Schema openapiv2_schema = 1042; 30 | } 31 | extend google.protobuf.ServiceOptions { 32 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 33 | // 34 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 35 | // different descriptor messages. 36 | Tag openapiv2_tag = 1042; 37 | } 38 | extend google.protobuf.FieldOptions { 39 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 40 | // 41 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 42 | // different descriptor messages. 43 | JSONSchema openapiv2_field = 1042; 44 | } 45 | -------------------------------------------------------------------------------- /examples/internal/server/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "a_bit_of_everything.go", 9 | "echo.go", 10 | "fieldmask_helper.go", 11 | "flow_combination.go", 12 | "main.go", 13 | "non_standard_names.go", 14 | "responsebody.go", 15 | "unannotatedecho.go", 16 | ], 17 | importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/server", 18 | deps = [ 19 | "//examples/internal/proto/examplepb:go_default_library", 20 | "//examples/internal/proto/pathenum:go_default_library", 21 | "//examples/internal/proto/sub:go_default_library", 22 | "//examples/internal/proto/sub2:go_default_library", 23 | "//internal/casing:go_default_library", 24 | "//runtime:go_default_library", 25 | "@com_github_golang_glog//:go_default_library", 26 | "@com_github_golang_protobuf//proto:go_default_library", 27 | "@com_github_rogpeppe_fastuuid//:go_default_library", 28 | "@go_googleapis//google/rpc:errdetails_go_proto", 29 | "@io_bazel_rules_go//proto/wkt:duration_go_proto", 30 | "@io_bazel_rules_go//proto/wkt:empty_go_proto", 31 | "@io_bazel_rules_go//proto/wkt:field_mask_go_proto", 32 | "@io_bazel_rules_go//proto/wkt:wrappers_go_proto", 33 | "@org_golang_google_grpc//:go_default_library", 34 | "@org_golang_google_grpc//codes:go_default_library", 35 | "@org_golang_google_grpc//metadata:go_default_library", 36 | "@org_golang_google_grpc//status:go_default_library", 37 | ], 38 | ) 39 | 40 | go_test( 41 | name = "go_default_test", 42 | srcs = ["fieldmask_helper_test.go"], 43 | embed = [":go_default_library"], 44 | deps = ["@io_bazel_rules_go//proto/wkt:field_mask_go_proto"], 45 | ) 46 | -------------------------------------------------------------------------------- /examples/internal/server/responsebody.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | examples "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/examplepb" 8 | ) 9 | 10 | // Implements of ResponseBodyServiceServer 11 | 12 | type responseBodyServer struct{} 13 | 14 | func newResponseBodyServer() examples.ResponseBodyServiceServer { 15 | return new(responseBodyServer) 16 | } 17 | 18 | func (s *responseBodyServer) GetResponseBody(ctx context.Context, req *examples.ResponseBodyIn) (*examples.ResponseBodyOut, error) { 19 | return &examples.ResponseBodyOut{ 20 | Response: &examples.ResponseBodyOut_Response{ 21 | Data: req.Data, 22 | }, 23 | }, nil 24 | } 25 | 26 | func (s *responseBodyServer) ListResponseBodies(ctx context.Context, req *examples.ResponseBodyIn) (*examples.RepeatedResponseBodyOut, error) { 27 | return &examples.RepeatedResponseBodyOut{ 28 | Response: []*examples.RepeatedResponseBodyOut_Response{ 29 | &examples.RepeatedResponseBodyOut_Response{ 30 | Data: req.Data, 31 | }, 32 | }, 33 | }, nil 34 | } 35 | 36 | func (s *responseBodyServer) ListResponseStrings(ctx context.Context, req *examples.ResponseBodyIn) (*examples.RepeatedResponseStrings, error) { 37 | if req.Data == "empty" { 38 | return &examples.RepeatedResponseStrings{ 39 | Values: []string{}, 40 | }, nil 41 | } 42 | return &examples.RepeatedResponseStrings{ 43 | Values: []string{"hello", req.Data}, 44 | }, nil 45 | } 46 | 47 | func (s *responseBodyServer) GetResponseBodyStream(req *examples.ResponseBodyIn, stream examples.ResponseBodyService_GetResponseBodyStreamServer) error { 48 | if err := stream.Send(&examples.ResponseBodyOut{ 49 | Response: &examples.ResponseBodyOut_Response{ 50 | Data: fmt.Sprintf("first %s", req.Data), 51 | }, 52 | }); err != nil { 53 | return err 54 | } 55 | 56 | return stream.Send(&examples.ResponseBodyOut{ 57 | Response: &examples.ResponseBodyOut_Response{ 58 | Data: fmt.Sprintf("second %s", req.Data), 59 | }, 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /protoc-gen-swagger/genswagger/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") 2 | 3 | package(default_visibility = ["//protoc-gen-swagger:__subpackages__"]) 4 | 5 | go_library( 6 | name = "go_default_library", 7 | srcs = [ 8 | "doc.go", 9 | "generator.go", 10 | "helpers.go", 11 | "helpers_go111_old.go", 12 | "template.go", 13 | "types.go", 14 | ], 15 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger", 16 | deps = [ 17 | "//internal:go_default_library", 18 | "//internal/casing:go_default_library", 19 | "//protoc-gen-grpc-gateway/descriptor:go_default_library", 20 | "//protoc-gen-grpc-gateway/generator:go_default_library", 21 | "//protoc-gen-swagger/options:go_default_library", 22 | "@com_github_golang_glog//:go_default_library", 23 | "@com_github_golang_protobuf//descriptor:go_default_library_gen", 24 | "@com_github_golang_protobuf//jsonpb:go_default_library_gen", 25 | "@com_github_golang_protobuf//proto:go_default_library", 26 | "@io_bazel_rules_go//proto/wkt:any_go_proto", 27 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 28 | "@io_bazel_rules_go//proto/wkt:descriptor_go_proto", 29 | "@io_bazel_rules_go//proto/wkt:struct_go_proto", 30 | ], 31 | ) 32 | 33 | go_test( 34 | name = "go_default_test", 35 | size = "small", 36 | srcs = ["template_test.go"], 37 | embed = [":go_default_library"], 38 | deps = [ 39 | "//protoc-gen-grpc-gateway/descriptor:go_default_library", 40 | "//protoc-gen-grpc-gateway/httprule:go_default_library", 41 | "//protoc-gen-swagger/options:go_default_library", 42 | "@com_github_golang_protobuf//proto:go_default_library", 43 | "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto", 44 | "@io_bazel_rules_go//proto/wkt:descriptor_go_proto", 45 | "@io_bazel_rules_go//proto/wkt:struct_go_proto", 46 | ], 47 | ) 48 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/echo_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | 4 | // Echo Service 5 | // 6 | // Echo Service API consists of a single service which returns 7 | // a message. 8 | package grpc.gateway.examples.internal.examplepb; 9 | 10 | import "google/api/annotations.proto"; 11 | 12 | // Embedded represents a message embedded in SimpleMessage. 13 | message Embedded { 14 | oneof mark { 15 | int64 progress = 1; 16 | string note = 2; 17 | } 18 | } 19 | 20 | // SimpleMessage represents a simple message sent to the Echo service. 21 | message SimpleMessage { 22 | // Id represents the message identifier. 23 | string id = 1; 24 | int64 num = 2; 25 | oneof code { 26 | int64 line_num = 3; 27 | string lang = 4; 28 | } 29 | Embedded status = 5; 30 | oneof ext { 31 | int64 en = 6; 32 | Embedded no = 7; 33 | } 34 | } 35 | 36 | // Echo service responds to incoming echo requests. 37 | service EchoService { 38 | // Echo method receives a simple message and returns it. 39 | // 40 | // The message posted as the id parameter will also be 41 | // returned. 42 | rpc Echo(SimpleMessage) returns (SimpleMessage) { 43 | option (google.api.http) = { 44 | post: "/v1/example/echo/{id}" 45 | additional_bindings { 46 | get: "/v1/example/echo/{id}/{num}" 47 | } 48 | additional_bindings { 49 | get: "/v1/example/echo/{id}/{num}/{lang}" 50 | } 51 | additional_bindings { 52 | get: "/v1/example/echo1/{id}/{line_num}/{status.note}" 53 | } 54 | additional_bindings { 55 | get: "/v1/example/echo2/{no.note}" 56 | } 57 | }; 58 | } 59 | // EchoBody method receives a simple message and returns it. 60 | rpc EchoBody(SimpleMessage) returns (SimpleMessage) { 61 | option (google.api.http) = { 62 | post: "/v1/example/echo_body" 63 | body: "*" 64 | }; 65 | } 66 | // EchoDelete method receives a simple message and returns it. 67 | rpc EchoDelete(SimpleMessage) returns (SimpleMessage) { 68 | option (google.api.http) = { 69 | delete: "/v1/example/echo_delete" 70 | }; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /runtime/marshaler.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // Marshaler defines a conversion between byte sequence and gRPC payloads / fields. 8 | type Marshaler interface { 9 | // Marshal marshals "v" into byte sequence. 10 | Marshal(v interface{}) ([]byte, error) 11 | // Unmarshal unmarshals "data" into "v". 12 | // "v" must be a pointer value. 13 | Unmarshal(data []byte, v interface{}) error 14 | // NewDecoder returns a Decoder which reads byte sequence from "r". 15 | NewDecoder(r io.Reader) Decoder 16 | // NewEncoder returns an Encoder which writes bytes sequence into "w". 17 | NewEncoder(w io.Writer) Encoder 18 | // ContentType returns the Content-Type which this marshaler is responsible for. 19 | ContentType() string 20 | } 21 | 22 | // Marshalers that implement contentTypeMarshaler will have their ContentTypeFromMessage method called 23 | // to set the Content-Type header on the response 24 | type contentTypeMarshaler interface { 25 | // ContentTypeFromMessage returns the Content-Type this marshaler produces from the provided message 26 | ContentTypeFromMessage(v interface{}) string 27 | } 28 | 29 | // Decoder decodes a byte sequence 30 | type Decoder interface { 31 | Decode(v interface{}) error 32 | } 33 | 34 | // Encoder encodes gRPC payloads / fields into byte sequence. 35 | type Encoder interface { 36 | Encode(v interface{}) error 37 | } 38 | 39 | // DecoderFunc adapts an decoder function into Decoder. 40 | type DecoderFunc func(v interface{}) error 41 | 42 | // Decode delegates invocations to the underlying function itself. 43 | func (f DecoderFunc) Decode(v interface{}) error { return f(v) } 44 | 45 | // EncoderFunc adapts an encoder function into Encoder 46 | type EncoderFunc func(v interface{}) error 47 | 48 | // Encode delegates invocations to the underlying function itself. 49 | func (f EncoderFunc) Encode(v interface{}) error { return f(v) } 50 | 51 | // Delimited defines the streaming delimiter. 52 | type Delimited interface { 53 | // Delimiter returns the record separator for the stream. 54 | Delimiter() []byte 55 | } 56 | -------------------------------------------------------------------------------- /examples/internal/gateway/gateway.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "net/http" 8 | "time" 9 | 10 | "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/examplepb" 11 | gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime" 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | // newGateway returns a new gateway server which translates HTTP into gRPC. 16 | func newGateway(ctx context.Context, conn *grpc.ClientConn, opts []gwruntime.ServeMuxOption) (http.Handler, error) { 17 | 18 | mux := gwruntime.NewServeMux(opts...) 19 | 20 | for _, f := range []func(context.Context, *gwruntime.ServeMux, *grpc.ClientConn) error{ 21 | examplepb.RegisterEchoServiceHandler, 22 | examplepb.RegisterStreamServiceHandler, 23 | examplepb.RegisterABitOfEverythingServiceHandler, 24 | examplepb.RegisterFlowCombinationHandler, 25 | examplepb.RegisterNonStandardServiceHandler, 26 | examplepb.RegisterResponseBodyServiceHandler, 27 | } { 28 | if err := f(ctx, mux, conn); err != nil { 29 | return nil, err 30 | } 31 | } 32 | return mux, nil 33 | } 34 | 35 | func dial(ctx context.Context, network, addr string) (*grpc.ClientConn, error) { 36 | switch network { 37 | case "tcp": 38 | return dialTCP(ctx, addr) 39 | case "unix": 40 | return dialUnix(ctx, addr) 41 | default: 42 | return nil, fmt.Errorf("unsupported network type %q", network) 43 | } 44 | } 45 | 46 | // dialTCP creates a client connection via TCP. 47 | // "addr" must be a valid TCP address with a port number. 48 | func dialTCP(ctx context.Context, addr string) (*grpc.ClientConn, error) { 49 | return grpc.DialContext(ctx, addr, grpc.WithInsecure()) 50 | } 51 | 52 | // dialUnix creates a client connection via a unix domain socket. 53 | // "addr" must be a valid path to the socket. 54 | func dialUnix(ctx context.Context, addr string) (*grpc.ClientConn, error) { 55 | d := func(addr string, timeout time.Duration) (net.Conn, error) { 56 | return net.DialTimeout("unix", addr, timeout) 57 | } 58 | return grpc.DialContext(ctx, addr, grpc.WithInsecure(), grpc.WithDialer(d)) 59 | } 60 | -------------------------------------------------------------------------------- /internal/casing/camel.go: -------------------------------------------------------------------------------- 1 | package casing 2 | 3 | // Camel returns the CamelCased name. 4 | // 5 | // This was moved from the now deprecated github.com/golang/protobuf/protoc-gen-go/generator package 6 | // 7 | // If there is an interior underscore followed by a lower case letter, 8 | // drop the underscore and convert the letter to upper case. 9 | // There is a remote possibility of this rewrite causing a name collision, 10 | // but it's so remote we're prepared to pretend it's nonexistent - since the 11 | // C++ generator lowercases names, it's extremely unlikely to have two fields 12 | // with different capitalizations. 13 | // In short, _my_field_name_2 becomes XMyFieldName_2. 14 | func Camel(s string) string { 15 | if s == "" { 16 | return "" 17 | } 18 | t := make([]byte, 0, 32) 19 | i := 0 20 | if s[0] == '_' { 21 | // Need a capital letter; drop the '_'. 22 | t = append(t, 'X') 23 | i++ 24 | } 25 | // Invariant: if the next letter is lower case, it must be converted 26 | // to upper case. 27 | // That is, we process a word at a time, where words are marked by _ or 28 | // upper case letter. Digits are treated as words. 29 | for ; i < len(s); i++ { 30 | c := s[i] 31 | if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) { 32 | continue // Skip the underscore in s. 33 | } 34 | if isASCIIDigit(c) { 35 | t = append(t, c) 36 | continue 37 | } 38 | // Assume we have a letter now - if not, it's a bogus identifier. 39 | // The next word is a sequence of characters that must start upper case. 40 | if isASCIILower(c) { 41 | c ^= ' ' // Make it a capital letter. 42 | } 43 | t = append(t, c) // Guaranteed not lower case. 44 | // Accept lower case sequence that follows. 45 | for i+1 < len(s) && isASCIILower(s[i+1]) { 46 | i++ 47 | t = append(t, s[i]) 48 | } 49 | } 50 | return string(t) 51 | } 52 | 53 | // And now lots of helper functions. 54 | 55 | // Is c an ASCII lower-case letter? 56 | func isASCIILower(c byte) bool { 57 | return 'a' <= c && c <= 'z' 58 | } 59 | 60 | // Is c an ASCII digit? 61 | func isASCIIDigit(c byte) bool { 62 | return '0' <= c && c <= '9' 63 | } 64 | -------------------------------------------------------------------------------- /examples/internal/gateway/main.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/golang/glog" 8 | gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime" 9 | ) 10 | 11 | // Endpoint describes a gRPC endpoint 12 | type Endpoint struct { 13 | Network, Addr string 14 | } 15 | 16 | // Options is a set of options to be passed to Run 17 | type Options struct { 18 | // Addr is the address to listen 19 | Addr string 20 | 21 | // GRPCServer defines an endpoint of a gRPC service 22 | GRPCServer Endpoint 23 | 24 | // SwaggerDir is a path to a directory from which the server 25 | // serves swagger specs. 26 | SwaggerDir string 27 | 28 | // Mux is a list of options to be passed to the grpc-gateway multiplexer 29 | Mux []gwruntime.ServeMuxOption 30 | } 31 | 32 | // Run starts a HTTP server and blocks while running if successful. 33 | // The server will be shutdown when "ctx" is canceled. 34 | func Run(ctx context.Context, opts Options) error { 35 | ctx, cancel := context.WithCancel(ctx) 36 | defer cancel() 37 | 38 | conn, err := dial(ctx, opts.GRPCServer.Network, opts.GRPCServer.Addr) 39 | if err != nil { 40 | return err 41 | } 42 | go func() { 43 | <-ctx.Done() 44 | if err := conn.Close(); err != nil { 45 | glog.Errorf("Failed to close a client connection to the gRPC server: %v", err) 46 | } 47 | }() 48 | 49 | mux := http.NewServeMux() 50 | mux.HandleFunc("/swagger/", swaggerServer(opts.SwaggerDir)) 51 | mux.HandleFunc("/healthz", healthzServer(conn)) 52 | 53 | gw, err := newGateway(ctx, conn, opts.Mux) 54 | if err != nil { 55 | return err 56 | } 57 | mux.Handle("/", gw) 58 | 59 | s := &http.Server{ 60 | Addr: opts.Addr, 61 | Handler: allowCORS(mux), 62 | } 63 | go func() { 64 | <-ctx.Done() 65 | glog.Infof("Shutting down the http server") 66 | if err := s.Shutdown(context.Background()); err != nil { 67 | glog.Errorf("Failed to shutdown http server: %v", err) 68 | } 69 | }() 70 | 71 | glog.Infof("Starting listening at %s", opts.Addr) 72 | if err := s.ListenAndServe(); err != http.ErrServerClosed { 73 | glog.Errorf("Failed to listen and serve: %v", err) 74 | return err 75 | } 76 | return nil 77 | } 78 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/swagger_merge_a.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | 4 | // Merging Services 5 | // 6 | // This is an example of merging two proto files. 7 | package grpc.gateway.examples.internal.examplepb; 8 | 9 | import "google/api/annotations.proto"; 10 | 11 | // InMessageA represents a message to ServiceA and ServiceC. 12 | message InMessageA { 13 | // Here is the explanation about InMessageA.values 14 | repeated string values = 1; 15 | } 16 | 17 | // OutMessageA represents a message returned from ServiceA. 18 | message OutMessageA { 19 | // Here is the explanation about OutMessageA.value 20 | string value = 1; 21 | } 22 | 23 | // OutMessageC represents a message returned from ServiceC. 24 | message OutMessageC { 25 | // Here is the explanation about OutMessageC.value 26 | string value = 1; 27 | } 28 | 29 | // ServiceA provices MethodOne and MethodTwo 30 | service ServiceA { 31 | // ServiceA.MethodOne receives InMessageA and returns OutMessageA 32 | // 33 | // Here is the detail explanation about ServiceA.MethodOne. 34 | rpc MethodOne(InMessageA) returns (OutMessageA) { 35 | option (google.api.http) = { 36 | post: "/v1/example/a/1" 37 | body: "*" 38 | }; 39 | } 40 | // ServiceA.MethodTwo receives OutMessageA and returns InMessageA 41 | // 42 | // Here is the detail explanation about ServiceA.MethodTwo. 43 | rpc MethodTwo(OutMessageA) returns (InMessageA) { 44 | option (google.api.http) = { 45 | post: "/v1/example/a/2" 46 | body: "*" 47 | }; 48 | } 49 | } 50 | 51 | // ServiceC service responds to incoming merge requests. 52 | service ServiceC { 53 | // ServiceC.MethodOne receives InMessageA and returns OutMessageC 54 | // 55 | // Here is the detail explanation about ServiceC.MethodOne. 56 | rpc MethodOne(InMessageA) returns (OutMessageC) { 57 | option (google.api.http) = { 58 | post: "/v1/example/c/1" 59 | body: "*" 60 | }; 61 | } 62 | // ServiceC.MethodTwo receives OutMessageA and returns InMessageA 63 | // 64 | // Here is the detail explanation about ServiceC.MethodTwo. 65 | rpc MethodTwo(OutMessageA) returns (InMessageA) { 66 | option (google.api.http) = { 67 | post: "/v1/example/c/2" 68 | body: "*" 69 | }; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # The gRPC-Gateway project is maintained by volunteers in their spare time. Please follow these troubleshooting steps before submitting an issue. 2 | 3 | - [ ] Check if your issue has already been reported (https://github.com/grpc-ecosystem/grpc-gateway/issues). 4 | - [ ] Update your protoc to the [latest version](https://github.com/google/protobuf/releases). 5 | - [ ] Update your copy of the `grpc-gateway` library to the latest version from github: 6 | ```sh 7 | go get -u github.com/grpc-ecosystem/grpc-gateway 8 | ``` 9 | - [ ] Delete the `protoc-gen-grpc-gateway` and `protoc-gen-swagger` binary from your `PATH`, 10 | and reinstall the latest versions: 11 | ```sh 12 | go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway 13 | go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger 14 | ``` 15 | 16 | ## I still have a problem! 17 | 18 | Please consider reaching out for help on a chat forum, such as 19 | [Gophers Slack](https://invite.slack.golangbridge.org/) (channel #grpc-gateway). 20 | It's much easier to help with common debugging steps in a chat, and some of 21 | the maintainers are reading the channel regularly. If you 22 | submit an issue which is clearly an environment setup problem, or it's obvious 23 | you haven't tried seeking help somewhere else first, we may close your issue. 24 | 25 | ## I still have a problem! 26 | 27 | Please follow these steps to submit a bug report: 28 | 29 | ### Bug reports: 30 | 31 | Fill in the following sections with explanations of what's gone wrong. 32 | 33 | ### Steps you follow to reproduce the error: 34 | 35 | ```html 36 | 42 | ``` 43 | 44 | Your steps here. 45 | 46 | ### What did you expect to happen instead: 47 | 48 | ```html 49 | 53 | ``` 54 | 55 | Your answer here. 56 | 57 | ### What's your theory on why it isn't working: 58 | 59 | ```html 60 | 64 | ``` 65 | 66 | Your theory here. 67 | -------------------------------------------------------------------------------- /examples/internal/clients/echo/configuration.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Echo Service 3 | * 4 | * Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package echo 11 | 12 | import ( 13 | "net/http" 14 | ) 15 | 16 | // contextKeys are used to identify the type of value in the context. 17 | // Since these are string, it is possible to get a short description of the 18 | // context key for logging and debugging using key.String(). 19 | 20 | type contextKey string 21 | 22 | func (c contextKey) String() string { 23 | return "auth " + string(c) 24 | } 25 | 26 | var ( 27 | // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. 28 | ContextOAuth2 = contextKey("token") 29 | 30 | // ContextBasicAuth takes BasicAuth as authentication for the request. 31 | ContextBasicAuth = contextKey("basic") 32 | 33 | // ContextAccessToken takes a string oauth2 access token as authentication for the request. 34 | ContextAccessToken = contextKey("accesstoken") 35 | 36 | // ContextAPIKey takes an APIKey as authentication for the request 37 | ContextAPIKey = contextKey("apikey") 38 | ) 39 | 40 | // BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth 41 | type BasicAuth struct { 42 | UserName string `json:"userName,omitempty"` 43 | Password string `json:"password,omitempty"` 44 | } 45 | 46 | // APIKey provides API key based authentication to a request passed via context using ContextAPIKey 47 | type APIKey struct { 48 | Key string 49 | Prefix string 50 | } 51 | 52 | type Configuration struct { 53 | BasePath string `json:"basePath,omitempty"` 54 | Host string `json:"host,omitempty"` 55 | Scheme string `json:"scheme,omitempty"` 56 | DefaultHeader map[string]string `json:"defaultHeader,omitempty"` 57 | UserAgent string `json:"userAgent,omitempty"` 58 | HTTPClient *http.Client 59 | } 60 | 61 | func NewConfiguration() *Configuration { 62 | cfg := &Configuration{ 63 | BasePath: "https://localhost", 64 | DefaultHeader: make(map[string]string), 65 | UserAgent: "Swagger-Codegen/1.0.0/go", 66 | } 67 | return cfg 68 | } 69 | 70 | func (c *Configuration) AddDefaultHeader(key string, value string) { 71 | c.DefaultHeader[key] = value 72 | } 73 | -------------------------------------------------------------------------------- /examples/internal/gateway/handlers.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "path" 7 | "strings" 8 | 9 | "github.com/golang/glog" 10 | "google.golang.org/grpc" 11 | "google.golang.org/grpc/connectivity" 12 | ) 13 | 14 | // swaggerServer returns swagger specification files located under "/swagger/" 15 | func swaggerServer(dir string) http.HandlerFunc { 16 | return func(w http.ResponseWriter, r *http.Request) { 17 | if !strings.HasSuffix(r.URL.Path, ".swagger.json") { 18 | glog.Errorf("Not Found: %s", r.URL.Path) 19 | http.NotFound(w, r) 20 | return 21 | } 22 | 23 | glog.Infof("Serving %s", r.URL.Path) 24 | p := strings.TrimPrefix(r.URL.Path, "/swagger/") 25 | p = path.Join(dir, p) 26 | http.ServeFile(w, r, p) 27 | } 28 | } 29 | 30 | // allowCORS allows Cross Origin Resoruce Sharing from any origin. 31 | // Don't do this without consideration in production systems. 32 | func allowCORS(h http.Handler) http.Handler { 33 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 34 | if origin := r.Header.Get("Origin"); origin != "" { 35 | w.Header().Set("Access-Control-Allow-Origin", origin) 36 | if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" { 37 | preflightHandler(w, r) 38 | return 39 | } 40 | } 41 | h.ServeHTTP(w, r) 42 | }) 43 | } 44 | 45 | // preflightHandler adds the necessary headers in order to serve 46 | // CORS from any origin using the methods "GET", "HEAD", "POST", "PUT", "DELETE" 47 | // We insist, don't do this without consideration in production systems. 48 | func preflightHandler(w http.ResponseWriter, r *http.Request) { 49 | headers := []string{"Content-Type", "Accept", "Authorization"} 50 | w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ",")) 51 | methods := []string{"GET", "HEAD", "POST", "PUT", "DELETE"} 52 | w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ",")) 53 | glog.Infof("preflight request for %s", r.URL.Path) 54 | } 55 | 56 | // healthzServer returns a simple health handler which returns ok. 57 | func healthzServer(conn *grpc.ClientConn) http.HandlerFunc { 58 | return func(w http.ResponseWriter, r *http.Request) { 59 | w.Header().Set("Content-Type", "text/plain") 60 | if s := conn.GetState(); s != connectivity.Ready { 61 | http.Error(w, fmt.Sprintf("grpc server is %s", s), http.StatusBadGateway) 62 | return 63 | } 64 | fmt.Fprintln(w, "ok") 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /examples/internal/clients/abe/configuration.go: -------------------------------------------------------------------------------- 1 | /* 2 | * A Bit of Everything 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: 1.0 7 | * Contact: none@example.com 8 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 9 | */ 10 | 11 | package abe 12 | 13 | import ( 14 | "net/http" 15 | ) 16 | 17 | // contextKeys are used to identify the type of value in the context. 18 | // Since these are string, it is possible to get a short description of the 19 | // context key for logging and debugging using key.String(). 20 | 21 | type contextKey string 22 | 23 | func (c contextKey) String() string { 24 | return "auth " + string(c) 25 | } 26 | 27 | var ( 28 | // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. 29 | ContextOAuth2 = contextKey("token") 30 | 31 | // ContextBasicAuth takes BasicAuth as authentication for the request. 32 | ContextBasicAuth = contextKey("basic") 33 | 34 | // ContextAccessToken takes a string oauth2 access token as authentication for the request. 35 | ContextAccessToken = contextKey("accesstoken") 36 | 37 | // ContextAPIKey takes an APIKey as authentication for the request 38 | ContextAPIKey = contextKey("apikey") 39 | ) 40 | 41 | // BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth 42 | type BasicAuth struct { 43 | UserName string `json:"userName,omitempty"` 44 | Password string `json:"password,omitempty"` 45 | } 46 | 47 | // APIKey provides API key based authentication to a request passed via context using ContextAPIKey 48 | type APIKey struct { 49 | Key string 50 | Prefix string 51 | } 52 | 53 | type Configuration struct { 54 | BasePath string `json:"basePath,omitempty"` 55 | Host string `json:"host,omitempty"` 56 | Scheme string `json:"scheme,omitempty"` 57 | DefaultHeader map[string]string `json:"defaultHeader,omitempty"` 58 | UserAgent string `json:"userAgent,omitempty"` 59 | HTTPClient *http.Client 60 | } 61 | 62 | func NewConfiguration() *Configuration { 63 | cfg := &Configuration{ 64 | BasePath: "http://localhost", 65 | DefaultHeader: make(map[string]string), 66 | UserAgent: "Swagger-Codegen/1.0.0/go", 67 | } 68 | return cfg 69 | } 70 | 71 | func (c *Configuration) AddDefaultHeader(key string, value string) { 72 | c.DefaultHeader[key] = value 73 | } 74 | -------------------------------------------------------------------------------- /examples/internal/clients/responsebody/configuration.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/response_body_service.proto 3 | * 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package responsebody 11 | 12 | import ( 13 | "net/http" 14 | ) 15 | 16 | // contextKeys are used to identify the type of value in the context. 17 | // Since these are string, it is possible to get a short description of the 18 | // context key for logging and debugging using key.String(). 19 | 20 | type contextKey string 21 | 22 | func (c contextKey) String() string { 23 | return "auth " + string(c) 24 | } 25 | 26 | var ( 27 | // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. 28 | ContextOAuth2 = contextKey("token") 29 | 30 | // ContextBasicAuth takes BasicAuth as authentication for the request. 31 | ContextBasicAuth = contextKey("basic") 32 | 33 | // ContextAccessToken takes a string oauth2 access token as authentication for the request. 34 | ContextAccessToken = contextKey("accesstoken") 35 | 36 | // ContextAPIKey takes an APIKey as authentication for the request 37 | ContextAPIKey = contextKey("apikey") 38 | ) 39 | 40 | // BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth 41 | type BasicAuth struct { 42 | UserName string `json:"userName,omitempty"` 43 | Password string `json:"password,omitempty"` 44 | } 45 | 46 | // APIKey provides API key based authentication to a request passed via context using ContextAPIKey 47 | type APIKey struct { 48 | Key string 49 | Prefix string 50 | } 51 | 52 | type Configuration struct { 53 | BasePath string `json:"basePath,omitempty"` 54 | Host string `json:"host,omitempty"` 55 | Scheme string `json:"scheme,omitempty"` 56 | DefaultHeader map[string]string `json:"defaultHeader,omitempty"` 57 | UserAgent string `json:"userAgent,omitempty"` 58 | HTTPClient *http.Client 59 | } 60 | 61 | func NewConfiguration() *Configuration { 62 | cfg := &Configuration{ 63 | BasePath: "https://localhost", 64 | DefaultHeader: make(map[string]string), 65 | UserAgent: "Swagger-Codegen/1.0.0/go", 66 | } 67 | return cfg 68 | } 69 | 70 | func (c *Configuration) AddDefaultHeader(key string, value string) { 71 | c.DefaultHeader[key] = value 72 | } 73 | -------------------------------------------------------------------------------- /examples/internal/server/flow_combination.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "io" 6 | 7 | examples "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/examplepb" 8 | ) 9 | 10 | type flowCombinationServer struct{} 11 | 12 | func newFlowCombinationServer() examples.FlowCombinationServer { 13 | return &flowCombinationServer{} 14 | } 15 | 16 | func (s flowCombinationServer) RpcEmptyRpc(ctx context.Context, req *examples.EmptyProto) (*examples.EmptyProto, error) { 17 | return req, nil 18 | } 19 | 20 | func (s flowCombinationServer) RpcEmptyStream(req *examples.EmptyProto, stream examples.FlowCombination_RpcEmptyStreamServer) error { 21 | return stream.Send(req) 22 | } 23 | 24 | func (s flowCombinationServer) StreamEmptyRpc(stream examples.FlowCombination_StreamEmptyRpcServer) error { 25 | for { 26 | _, err := stream.Recv() 27 | if err == io.EOF { 28 | break 29 | } 30 | if err != nil { 31 | return err 32 | } 33 | } 34 | return stream.SendAndClose(new(examples.EmptyProto)) 35 | } 36 | 37 | func (s flowCombinationServer) StreamEmptyStream(stream examples.FlowCombination_StreamEmptyStreamServer) error { 38 | for { 39 | _, err := stream.Recv() 40 | if err == io.EOF { 41 | break 42 | } 43 | if err != nil { 44 | return err 45 | } 46 | } 47 | return stream.Send(new(examples.EmptyProto)) 48 | } 49 | 50 | func (s flowCombinationServer) RpcBodyRpc(ctx context.Context, req *examples.NonEmptyProto) (*examples.EmptyProto, error) { 51 | return new(examples.EmptyProto), nil 52 | } 53 | 54 | func (s flowCombinationServer) RpcPathSingleNestedRpc(ctx context.Context, req *examples.SingleNestedProto) (*examples.EmptyProto, error) { 55 | return new(examples.EmptyProto), nil 56 | } 57 | 58 | func (s flowCombinationServer) RpcPathNestedRpc(ctx context.Context, req *examples.NestedProto) (*examples.EmptyProto, error) { 59 | return new(examples.EmptyProto), nil 60 | } 61 | 62 | func (s flowCombinationServer) RpcBodyStream(req *examples.NonEmptyProto, stream examples.FlowCombination_RpcBodyStreamServer) error { 63 | return stream.Send(new(examples.EmptyProto)) 64 | } 65 | 66 | func (s flowCombinationServer) RpcPathSingleNestedStream(req *examples.SingleNestedProto, stream examples.FlowCombination_RpcPathSingleNestedStreamServer) error { 67 | return stream.Send(new(examples.EmptyProto)) 68 | } 69 | 70 | func (s flowCombinationServer) RpcPathNestedStream(req *examples.NestedProto, stream examples.FlowCombination_RpcPathNestedStreamServer) error { 71 | return stream.Send(new(examples.EmptyProto)) 72 | } 73 | -------------------------------------------------------------------------------- /runtime/proto2_convert.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | ) 6 | 7 | // StringP returns a pointer to a string whose pointee is same as the given string value. 8 | func StringP(val string) (*string, error) { 9 | return proto.String(val), nil 10 | } 11 | 12 | // BoolP parses the given string representation of a boolean value, 13 | // and returns a pointer to a bool whose value is same as the parsed value. 14 | func BoolP(val string) (*bool, error) { 15 | b, err := Bool(val) 16 | if err != nil { 17 | return nil, err 18 | } 19 | return proto.Bool(b), nil 20 | } 21 | 22 | // Float64P parses the given string representation of a floating point number, 23 | // and returns a pointer to a float64 whose value is same as the parsed number. 24 | func Float64P(val string) (*float64, error) { 25 | f, err := Float64(val) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return proto.Float64(f), nil 30 | } 31 | 32 | // Float32P parses the given string representation of a floating point number, 33 | // and returns a pointer to a float32 whose value is same as the parsed number. 34 | func Float32P(val string) (*float32, error) { 35 | f, err := Float32(val) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return proto.Float32(f), nil 40 | } 41 | 42 | // Int64P parses the given string representation of an integer 43 | // and returns a pointer to a int64 whose value is same as the parsed integer. 44 | func Int64P(val string) (*int64, error) { 45 | i, err := Int64(val) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return proto.Int64(i), nil 50 | } 51 | 52 | // Int32P parses the given string representation of an integer 53 | // and returns a pointer to a int32 whose value is same as the parsed integer. 54 | func Int32P(val string) (*int32, error) { 55 | i, err := Int32(val) 56 | if err != nil { 57 | return nil, err 58 | } 59 | return proto.Int32(i), err 60 | } 61 | 62 | // Uint64P parses the given string representation of an integer 63 | // and returns a pointer to a uint64 whose value is same as the parsed integer. 64 | func Uint64P(val string) (*uint64, error) { 65 | i, err := Uint64(val) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return proto.Uint64(i), err 70 | } 71 | 72 | // Uint32P parses the given string representation of an integer 73 | // and returns a pointer to a uint32 whose value is same as the parsed integer. 74 | func Uint32P(val string) (*uint32, error) { 75 | i, err := Uint32(val) 76 | if err != nil { 77 | return nil, err 78 | } 79 | return proto.Uint32(i), err 80 | } 81 | -------------------------------------------------------------------------------- /examples/internal/server/main.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | 8 | "github.com/golang/glog" 9 | examples "github.com/grpc-ecosystem/grpc-gateway/examples/internal/proto/examplepb" 10 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 11 | "google.golang.org/grpc" 12 | ) 13 | 14 | // Run starts the example gRPC service. 15 | // "network" and "address" are passed to net.Listen. 16 | func Run(ctx context.Context, network, address string) error { 17 | l, err := net.Listen(network, address) 18 | if err != nil { 19 | return err 20 | } 21 | defer func() { 22 | if err := l.Close(); err != nil { 23 | glog.Errorf("Failed to close %s %s: %v", network, address, err) 24 | } 25 | }() 26 | 27 | s := grpc.NewServer() 28 | examples.RegisterEchoServiceServer(s, newEchoServer()) 29 | examples.RegisterFlowCombinationServer(s, newFlowCombinationServer()) 30 | examples.RegisterNonStandardServiceServer(s, newNonStandardServer()) 31 | 32 | abe := newABitOfEverythingServer() 33 | examples.RegisterABitOfEverythingServiceServer(s, abe) 34 | examples.RegisterStreamServiceServer(s, abe) 35 | examples.RegisterResponseBodyServiceServer(s, newResponseBodyServer()) 36 | 37 | go func() { 38 | defer s.GracefulStop() 39 | <-ctx.Done() 40 | }() 41 | return s.Serve(l) 42 | } 43 | 44 | // RunInProcessGateway starts the invoke in process http gateway. 45 | func RunInProcessGateway(ctx context.Context, addr string, opts ...runtime.ServeMuxOption) error { 46 | mux := runtime.NewServeMux(opts...) 47 | 48 | examples.RegisterEchoServiceHandlerServer(ctx, mux, newEchoServer()) 49 | examples.RegisterFlowCombinationHandlerServer(ctx, mux, newFlowCombinationServer()) 50 | examples.RegisterNonStandardServiceHandlerServer(ctx, mux, newNonStandardServer()) 51 | 52 | abe := newABitOfEverythingServer() 53 | examples.RegisterABitOfEverythingServiceHandlerServer(ctx, mux, abe) 54 | examples.RegisterStreamServiceHandlerServer(ctx, mux, abe) 55 | examples.RegisterResponseBodyServiceHandlerServer(ctx, mux, newResponseBodyServer()) 56 | 57 | s := &http.Server{ 58 | Addr: addr, 59 | Handler: mux, 60 | } 61 | 62 | go func() { 63 | <-ctx.Done() 64 | glog.Infof("Shutting down the http gateway server") 65 | if err := s.Shutdown(context.Background()); err != nil { 66 | glog.Errorf("Failed to shutdown http gateway server: %v", err) 67 | } 68 | }() 69 | 70 | if err := s.ListenAndServe(); err != http.ErrServerClosed { 71 | glog.Errorf("Failed to listen and serve: %v", err) 72 | return err 73 | } 74 | return nil 75 | 76 | } 77 | -------------------------------------------------------------------------------- /examples/internal/browser/gulpfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var path = require('path'); 6 | 7 | var bower = require('gulp-bower'); 8 | var exit = require('gulp-exit'); 9 | var gprocess = require('gulp-process'); 10 | var shell = require('gulp-shell'); 11 | var jasmineBrowser = require('gulp-jasmine-browser'); 12 | var webpack = require('webpack-stream'); 13 | 14 | gulp.task('bower', function () { 15 | return bower(); 16 | }); 17 | 18 | gulp.task('server', shell.task([ 19 | 'go build -o bin/example-server github.com/grpc-ecosystem/grpc-gateway/examples/internal/cmd/example-grpc-server', 20 | ])); 21 | 22 | gulp.task('gateway', shell.task([ 23 | 'go build -o bin/example-gw github.com/grpc-ecosystem/grpc-gateway/examples/internal/cmd/example-gateway-server', 24 | ])); 25 | 26 | gulp.task('serve-server', ['server'], function () { 27 | gprocess.start('server-server', 'bin/example-server', [ 28 | '--logtostderr', 29 | ]); 30 | gulp.watch('bin/example-server', ['serve-server']); 31 | }); 32 | 33 | gulp.task('serve-gateway', ['gateway', 'serve-server'], function () { 34 | gprocess.start('gateway-server', 'bin/example-gw', [ 35 | '--logtostderr', '--swagger_dir', path.join(__dirname, "../proto/examplepb"), 36 | ]); 37 | gulp.watch('bin/example-gw', ['serve-gateway']); 38 | }); 39 | 40 | gulp.task('backends', ['serve-gateway', 'serve-server']); 41 | 42 | var specFiles = ['*.spec.js']; 43 | gulp.task('test', ['backends'], function (done) { 44 | return gulp.src(specFiles) 45 | .pipe(webpack({ output: { filename: 'spec.js' } })) 46 | .pipe(jasmineBrowser.specRunner({ 47 | console: true, 48 | sourceMappedStacktrace: true, 49 | })) 50 | .pipe(jasmineBrowser.headless({ 51 | findOpenPort: true, 52 | catch: true, 53 | throwFailures: true, 54 | })) 55 | .on('error', function (err) { 56 | done(err); 57 | process.exit(1); 58 | }) 59 | .pipe(exit()); 60 | }); 61 | 62 | gulp.task('serve', ['backends'], function (done) { 63 | var JasminePlugin = require('gulp-jasmine-browser/webpack/jasmine-plugin'); 64 | var plugin = new JasminePlugin(); 65 | 66 | return gulp.src(specFiles) 67 | .pipe(webpack({ 68 | output: { filename: 'spec.js' }, 69 | watch: true, 70 | plugins: [plugin], 71 | })) 72 | .pipe(jasmineBrowser.specRunner({ 73 | sourceMappedStacktrace: true, 74 | })) 75 | .pipe(jasmineBrowser.server({ 76 | port: 8000, 77 | whenReady: plugin.whenReady, 78 | })); 79 | }); 80 | 81 | gulp.task('default', ['test']); 82 | -------------------------------------------------------------------------------- /runtime/marshal_proto_test.go: -------------------------------------------------------------------------------- 1 | package runtime_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/golang/protobuf/proto" 8 | "github.com/golang/protobuf/ptypes/timestamp" 9 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 10 | "github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb" 11 | ) 12 | 13 | var message = &examplepb.ABitOfEverything{ 14 | SingleNested: &examplepb.ABitOfEverything_Nested{}, 15 | RepeatedStringValue: nil, 16 | MappedStringValue: nil, 17 | MappedNestedValue: nil, 18 | RepeatedEnumValue: nil, 19 | TimestampValue: ×tamp.Timestamp{}, 20 | Uuid: "6EC2446F-7E89-4127-B3E6-5C05E6BECBA7", 21 | Nested: []*examplepb.ABitOfEverything_Nested{ 22 | { 23 | Name: "foo", 24 | Amount: 12345, 25 | }, 26 | }, 27 | Uint64Value: 0xFFFFFFFFFFFFFFFF, 28 | EnumValue: examplepb.NumericEnum_ONE, 29 | OneofValue: &examplepb.ABitOfEverything_OneofString{ 30 | OneofString: "bar", 31 | }, 32 | MapValue: map[string]examplepb.NumericEnum{ 33 | "a": examplepb.NumericEnum_ONE, 34 | "b": examplepb.NumericEnum_ZERO, 35 | }, 36 | } 37 | 38 | func TestProtoMarshalUnmarshal(t *testing.T) { 39 | marshaller := runtime.ProtoMarshaller{} 40 | 41 | // Marshal 42 | buffer, err := marshaller.Marshal(message) 43 | if err != nil { 44 | t.Fatalf("Marshalling returned error: %s", err.Error()) 45 | } 46 | 47 | // Unmarshal 48 | unmarshalled := &examplepb.ABitOfEverything{} 49 | err = marshaller.Unmarshal(buffer, unmarshalled) 50 | if err != nil { 51 | t.Fatalf("Unmarshalling returned error: %s", err.Error()) 52 | } 53 | 54 | if !proto.Equal(unmarshalled, message) { 55 | t.Errorf( 56 | "Unmarshalled didn't match original message: (original = %v) != (unmarshalled = %v)", 57 | unmarshalled, 58 | message, 59 | ) 60 | } 61 | } 62 | 63 | func TestProtoEncoderDecodert(t *testing.T) { 64 | marshaller := runtime.ProtoMarshaller{} 65 | 66 | var buf bytes.Buffer 67 | 68 | encoder := marshaller.NewEncoder(&buf) 69 | decoder := marshaller.NewDecoder(&buf) 70 | 71 | // Encode 72 | err := encoder.Encode(message) 73 | if err != nil { 74 | t.Fatalf("Encoding returned error: %s", err.Error()) 75 | } 76 | 77 | // Decode 78 | unencoded := &examplepb.ABitOfEverything{} 79 | err = decoder.Decode(unencoded) 80 | if err != nil { 81 | t.Fatalf("Unmarshalling returned error: %s", err.Error()) 82 | } 83 | 84 | if !proto.Equal(unencoded, message) { 85 | t.Errorf( 86 | "Unencoded didn't match original message: (original = %v) != (unencoded = %v)", 87 | unencoded, 88 | message, 89 | ) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /docs/_docs/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: documentation 3 | title: FAQ 4 | --- 5 | 6 | # FAQ 7 | 8 | ## How can I write the annotations which grpc-gateway requires? 9 | The gRPC-Gateway follows the spec of 10 | [`google.api.HttpRule`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto), 11 | so first check out the documentation if it is feasible in the spec. 12 | 13 | See also [a past discussion](https://groups.google.com/d/msg/grpc-io/Xqx80hG0D44/VNCDHjeE6pUJ) 14 | in the grpc-io mailing list. 15 | 16 | ## I want to support a certain style of HTTP request but the code generated by grpc-gateway does not. How can I support this style? 17 | See the question above at first. 18 | 19 | The gRPC-Gateway is intended to cover 80% of use cases without forcing you to write comprehensive but 20 | complicated annotations. So the gateway itself does not always cover all the use cases you 21 | have by design. In other words, the gateway automates typical boring boilerplate mapping 22 | between gRPC and HTTP/1 communication, but it does not do arbitrarily complex custom mappings for you. 23 | 24 | On the other hand, you can still add whatever you want as a middleware which wraps 25 | [`runtime.ServeMux`](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#ServeMux). 26 | Since `runtime.ServeMux` is just a standard [`http.Handler`](http://golang.org/pkg/http#Handler), 27 | you can easily write a custom wrapper of `runtime.ServeMux`, leveraged with existing third-party 28 | libraries in Go. 29 | e.g. https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/gateway/main.go 30 | 31 | ## My gRPC server is written in (Scala|C++|Ruby|Haskell|....). Is there a (Scala|C++|Ruby|Haskell|....) version of grpc-gateway? 32 | 33 | AFAIK, no. But it should not be a big issue because the reverse-proxy which grpc-gateway generates 34 | usually works as an independent process and communicates with your gRPC server over TCP or a unix-domain socket. 35 | 36 | ## Why are the models in the swagger specification prefixed with the last part of the proto package name? 37 | 38 | The reason to generate the prefixes is that we don't have a guaranteed unique namespace. 39 | If two packages produce different Foo messages then we will have trouble. 40 | 41 | ## Why not strip the prefix? 42 | 43 | When a message is added which happens to conflict with another message 44 | (e.g. by importing a message with the same name from a different package) 45 | it will break code that is very far away from the code that changed. 46 | This is in an effort to adhere to the 47 | [principle of least astonishment](https://en.wikipedia.org/wiki/Principle_of_least_astonishment). 48 | -------------------------------------------------------------------------------- /examples/internal/clients/generateunboundmethods/configuration.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/generate_unbound_methods.proto 3 | * 4 | * Generate Unannotated Methods Echo Service Similar to echo_service.proto but without annotations and without external configuration. Generate Unannotated Methods Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package generateunboundmethods 11 | 12 | import ( 13 | "net/http" 14 | ) 15 | 16 | // contextKeys are used to identify the type of value in the context. 17 | // Since these are string, it is possible to get a short description of the 18 | // context key for logging and debugging using key.String(). 19 | 20 | type contextKey string 21 | 22 | func (c contextKey) String() string { 23 | return "auth " + string(c) 24 | } 25 | 26 | var ( 27 | // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. 28 | ContextOAuth2 = contextKey("token") 29 | 30 | // ContextBasicAuth takes BasicAuth as authentication for the request. 31 | ContextBasicAuth = contextKey("basic") 32 | 33 | // ContextAccessToken takes a string oauth2 access token as authentication for the request. 34 | ContextAccessToken = contextKey("accesstoken") 35 | 36 | // ContextAPIKey takes an APIKey as authentication for the request 37 | ContextAPIKey = contextKey("apikey") 38 | ) 39 | 40 | // BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth 41 | type BasicAuth struct { 42 | UserName string `json:"userName,omitempty"` 43 | Password string `json:"password,omitempty"` 44 | } 45 | 46 | // APIKey provides API key based authentication to a request passed via context using ContextAPIKey 47 | type APIKey struct { 48 | Key string 49 | Prefix string 50 | } 51 | 52 | type Configuration struct { 53 | BasePath string `json:"basePath,omitempty"` 54 | Host string `json:"host,omitempty"` 55 | Scheme string `json:"scheme,omitempty"` 56 | DefaultHeader map[string]string `json:"defaultHeader,omitempty"` 57 | UserAgent string `json:"userAgent,omitempty"` 58 | HTTPClient *http.Client 59 | } 60 | 61 | func NewConfiguration() *Configuration { 62 | cfg := &Configuration{ 63 | BasePath: "https://localhost", 64 | DefaultHeader: make(map[string]string), 65 | UserAgent: "Swagger-Codegen/1.0.0/go", 66 | } 67 | return cfg 68 | } 69 | 70 | func (c *Configuration) AddDefaultHeader(key string, value string) { 71 | c.DefaultHeader[key] = value 72 | } 73 | -------------------------------------------------------------------------------- /examples/internal/clients/unannotatedecho/configuration.go: -------------------------------------------------------------------------------- 1 | /* 2 | * examples/internal/proto/examplepb/unannotated_echo_service.proto 3 | * 4 | * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format. Echo Service API consists of a single service which returns a message. 5 | * 6 | * API version: version not set 7 | * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) 8 | */ 9 | 10 | package unannotatedecho 11 | 12 | import ( 13 | "net/http" 14 | ) 15 | 16 | // contextKeys are used to identify the type of value in the context. 17 | // Since these are string, it is possible to get a short description of the 18 | // context key for logging and debugging using key.String(). 19 | 20 | type contextKey string 21 | 22 | func (c contextKey) String() string { 23 | return "auth " + string(c) 24 | } 25 | 26 | var ( 27 | // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. 28 | ContextOAuth2 = contextKey("token") 29 | 30 | // ContextBasicAuth takes BasicAuth as authentication for the request. 31 | ContextBasicAuth = contextKey("basic") 32 | 33 | // ContextAccessToken takes a string oauth2 access token as authentication for the request. 34 | ContextAccessToken = contextKey("accesstoken") 35 | 36 | // ContextAPIKey takes an APIKey as authentication for the request 37 | ContextAPIKey = contextKey("apikey") 38 | ) 39 | 40 | // BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth 41 | type BasicAuth struct { 42 | UserName string `json:"userName,omitempty"` 43 | Password string `json:"password,omitempty"` 44 | } 45 | 46 | // APIKey provides API key based authentication to a request passed via context using ContextAPIKey 47 | type APIKey struct { 48 | Key string 49 | Prefix string 50 | } 51 | 52 | type Configuration struct { 53 | BasePath string `json:"basePath,omitempty"` 54 | Host string `json:"host,omitempty"` 55 | Scheme string `json:"scheme,omitempty"` 56 | DefaultHeader map[string]string `json:"defaultHeader,omitempty"` 57 | UserAgent string `json:"userAgent,omitempty"` 58 | HTTPClient *http.Client 59 | } 60 | 61 | func NewConfiguration() *Configuration { 62 | cfg := &Configuration{ 63 | BasePath: "https://localhost", 64 | DefaultHeader: make(map[string]string), 65 | UserAgent: "Swagger-Codegen/1.0.0/go", 66 | } 67 | return cfg 68 | } 69 | 70 | func (c *Configuration) AddDefaultHeader(key string, value string) { 71 | c.DefaultHeader[key] = value 72 | } 73 | -------------------------------------------------------------------------------- /docs/_docs/examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | category: documentation 3 | --- 4 | 5 | # Examples 6 | 7 | Examples are available under `examples/internal` directory. 8 | * [`proto/examplepb/echo_service.proto`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/echo_service.proto), 9 | [`proto/examplepb/a_bit_of_everything.proto`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/a_bit_of_everything.proto), 10 | [`proto/examplepb/unannotated_echo_service.proto`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/unannotated_echo_service.proto): 11 | protobuf service definitions. 12 | * [`proto/examplepb/echo_service.pb.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/echo_service.pb.go), 13 | [`proto/examplepb/a_bit_of_everything.pb.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/a_bit_of_everything.pb.go), 14 | [`proto/examplepb/unannotated_echo_service.pb.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/unannotated_echo_service.pb.go): 15 | generated Go service stubs and types. 16 | * [`proto/examplepb/echo_service.pb.gw.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/echo_service.pb.gw.go), 17 | [`proto/examplepb/a_bit_of_everything.pb.gw.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/a_bit_of_everything.pb.gw.go), 18 | [`proto/examplepb/uannotated_echo_service.pb.gw.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/uannotated_echo_service.pb.gw.go): 19 | generated gRPC-gateway clients. 20 | * [`proto/examplepb/unannotated_echo_service.yaml`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/uannotated_echo_service.yaml): 21 | gRPC API Configuration for `unannotated_echo_service.proto`. 22 | * [`server/main.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/server/main.go): 23 | service implementation. 24 | * [`main.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/gateway/main.go): 25 | entrypoint of the generated reverse proxy. 26 | 27 | To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), 28 | gRPC-gateway, and a gRPC server, see 29 | [this code example by CoreOS](https://github.com/philips/grpc-gateway-example/blob/master/cmd/serve.go) 30 | (and its accompanying 31 | [blog post](https://coreos.com/blog/grpc-protobufs-swagger.html)) 32 | 33 | 34 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/descriptor/grpc_api_configuration.go: -------------------------------------------------------------------------------- 1 | package descriptor 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | 9 | "github.com/ghodss/yaml" 10 | "github.com/golang/protobuf/jsonpb" 11 | ) 12 | 13 | func loadGrpcAPIServiceFromYAML(yamlFileContents []byte, yamlSourceLogName string) (*GrpcAPIService, error) { 14 | jsonContents, err := yaml.YAMLToJSON(yamlFileContents) 15 | if err != nil { 16 | return nil, fmt.Errorf("Failed to convert gRPC API Configuration from YAML in '%v' to JSON: %v", yamlSourceLogName, err) 17 | } 18 | 19 | // As our GrpcAPIService is incomplete accept unknown fields. 20 | unmarshaler := jsonpb.Unmarshaler{ 21 | AllowUnknownFields: true, 22 | } 23 | 24 | serviceConfiguration := GrpcAPIService{} 25 | if err := unmarshaler.Unmarshal(bytes.NewReader(jsonContents), &serviceConfiguration); err != nil { 26 | return nil, fmt.Errorf("Failed to parse gRPC API Configuration from YAML in '%v': %v", yamlSourceLogName, err) 27 | } 28 | 29 | return &serviceConfiguration, nil 30 | } 31 | 32 | func registerHTTPRulesFromGrpcAPIService(registry *Registry, service *GrpcAPIService, sourceLogName string) error { 33 | if service.HTTP == nil { 34 | // Nothing to do 35 | return nil 36 | } 37 | 38 | for _, rule := range service.HTTP.GetRules() { 39 | selector := "." + strings.Trim(rule.GetSelector(), " ") 40 | if strings.ContainsAny(selector, "*, ") { 41 | return fmt.Errorf("Selector '%v' in %v must specify a single service method without wildcards", rule.GetSelector(), sourceLogName) 42 | } 43 | 44 | registry.AddExternalHTTPRule(selector, rule) 45 | } 46 | 47 | return nil 48 | } 49 | 50 | // LoadGrpcAPIServiceFromYAML loads a gRPC API Configuration from the given YAML file 51 | // and registers the HttpRule descriptions contained in it as externalHTTPRules in 52 | // the given registry. This must be done before loading the proto file. 53 | // 54 | // You can learn more about gRPC API Service descriptions from google's documentation 55 | // at https://cloud.google.com/endpoints/docs/grpc/grpc-service-config 56 | // 57 | // Note that for the purposes of the gateway generator we only consider a subset of all 58 | // available features google supports in their service descriptions. 59 | func (r *Registry) LoadGrpcAPIServiceFromYAML(yamlFile string) error { 60 | yamlFileContents, err := ioutil.ReadFile(yamlFile) 61 | if err != nil { 62 | return fmt.Errorf("Failed to read gRPC API Configuration description from '%v': %v", yamlFile, err) 63 | } 64 | 65 | service, err := loadGrpcAPIServiceFromYAML(yamlFileContents, yamlFile) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | return registerHTTPRulesFromGrpcAPIService(r, service, yamlFile) 71 | } 72 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/non_standard_names.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | package grpc.gateway.examples.internal.examplepb; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/protobuf/field_mask.proto"; 7 | 8 | // NonStandardMessage has oddly named fields. 9 | message NonStandardMessage { 10 | // Id represents the message identifier. 11 | string id = 1; 12 | int64 Num = 2; 13 | int64 line_num = 3; 14 | string langIdent = 4; 15 | string STATUS = 5; 16 | int64 en_GB = 6; 17 | string no = 7; 18 | 19 | message Thing { 20 | message SubThing { 21 | string sub_value = 1; 22 | } 23 | SubThing subThing = 1; 24 | } 25 | Thing thing = 8; 26 | } 27 | 28 | message NonStandardUpdateRequest { 29 | NonStandardMessage body = 1; 30 | google.protobuf.FieldMask update_mask = 2; 31 | } 32 | 33 | // NonStandardMessageWithJSONNames maps odd field names to odd JSON names for maximum confusion. 34 | message NonStandardMessageWithJSONNames { 35 | // Id represents the message identifier. 36 | string id = 1 [json_name="ID"]; 37 | int64 Num = 2 [json_name="Num"]; 38 | int64 line_num = 3 [json_name="LineNum"]; 39 | string langIdent = 4 [json_name="langIdent"]; 40 | string STATUS = 5 [json_name="status"]; 41 | int64 en_GB = 6 [json_name="En_GB"]; 42 | string no = 7 [json_name="yes"]; 43 | 44 | message Thing { 45 | message SubThing { 46 | string sub_value = 1 [json_name="sub_Value"]; 47 | } 48 | SubThing subThing = 1 [json_name="SubThing"]; 49 | } 50 | Thing thing = 8 [json_name="Thingy"]; 51 | } 52 | 53 | message NonStandardWithJSONNamesUpdateRequest { 54 | NonStandardMessageWithJSONNames body = 1; 55 | google.protobuf.FieldMask update_mask = 2; 56 | } 57 | 58 | // NonStandardService responds to incoming messages, applies a field mask and returns the masked response. 59 | service NonStandardService { 60 | // Apply field mask to empty NonStandardMessage and return result. 61 | rpc Update(NonStandardUpdateRequest) returns (NonStandardMessage) { 62 | option (google.api.http) = { 63 | patch: "/v1/example/non_standard/update" 64 | body: "body" 65 | }; 66 | } 67 | 68 | // Apply field mask to empty NonStandardMessageWithJSONNames and return result. 69 | rpc UpdateWithJSONNames(NonStandardWithJSONNamesUpdateRequest) returns (NonStandardMessageWithJSONNames) { 70 | option (google.api.http) = { 71 | patch: "/v1/example/non_standard/update_with_json_names" 72 | body: "body" 73 | }; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/internal/proto/examplepb/wrappers.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "examplepb"; 3 | package grpc.gateway.examples.internal.examplepb; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/protobuf/wrappers.proto"; 7 | import "google/protobuf/empty.proto"; 8 | 9 | message Wrappers { 10 | google.protobuf.StringValue string_value = 1; 11 | google.protobuf.Int32Value int32_value = 2; 12 | google.protobuf.Int64Value int64_value = 3; 13 | google.protobuf.FloatValue float_value = 4; 14 | google.protobuf.DoubleValue double_value = 5; 15 | google.protobuf.BoolValue bool_value = 6; 16 | google.protobuf.UInt32Value uint32_value = 7; 17 | google.protobuf.UInt64Value uint64_value = 8; 18 | google.protobuf.BytesValue bytes_value = 9; 19 | } 20 | 21 | service WrappersService { 22 | rpc Create(Wrappers) returns (Wrappers) { 23 | option (google.api.http) = { 24 | post: "/v1/example/wrappers" 25 | body: "*" 26 | }; 27 | } 28 | 29 | rpc CreateStringValue(google.protobuf.StringValue) returns (google.protobuf.StringValue) { 30 | option (google.api.http) = { 31 | post: "/v1/testString" 32 | body: "*" 33 | }; 34 | } 35 | rpc CreateInt32Value(google.protobuf.Int32Value) returns (google.protobuf.Int32Value) { 36 | option (google.api.http) = { 37 | post: "/v1/testInt32" 38 | body: "*" 39 | }; 40 | } 41 | rpc CreateInt64Value(google.protobuf.Int64Value) returns (google.protobuf.Int64Value){ 42 | option (google.api.http) = { 43 | post: "/v1/testInt64" 44 | body: "*" 45 | }; 46 | } 47 | rpc CreateFloatValue(google.protobuf.FloatValue) returns (google.protobuf.FloatValue){ 48 | option (google.api.http) = { 49 | post: "/v1/testFloat" 50 | body: "*" 51 | }; 52 | } 53 | rpc CreateDoubleValue(google.protobuf.DoubleValue) returns (google.protobuf.DoubleValue){ 54 | option (google.api.http) = { 55 | post: "/v1/testDouble" 56 | body: "*" 57 | }; 58 | } 59 | rpc CreateBoolValue(google.protobuf.BoolValue) returns (google.protobuf.BoolValue){ 60 | option (google.api.http) = { 61 | post: "/v1/testBool" 62 | body: "*" 63 | }; 64 | } 65 | rpc CreateUInt32Value(google.protobuf.UInt32Value) returns (google.protobuf.UInt32Value){ 66 | option (google.api.http) = { 67 | post: "/v1/testUint32" 68 | body: "*" 69 | }; 70 | } 71 | rpc CreateUInt64Value(google.protobuf.UInt64Value) returns (google.protobuf.UInt64Value){ 72 | option (google.api.http) = { 73 | post: "/v1/testUint64" 74 | body: "*" 75 | }; 76 | } 77 | rpc CreateBytesValue(google.protobuf.BytesValue) returns (google.protobuf.BytesValue){ 78 | option (google.api.http) = { 79 | post: "/v1/testBytes" 80 | body: "*" 81 | }; 82 | } 83 | rpc CreateEmpty(google.protobuf.Empty) returns (google.protobuf.Empty){ 84 | option (google.api.http) = { 85 | post: "/v1/testEmpty" 86 | body: "*" 87 | }; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /runtime/fieldmask.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "strings" 7 | 8 | descriptor2 "github.com/golang/protobuf/descriptor" 9 | "github.com/golang/protobuf/protoc-gen-go/descriptor" 10 | "google.golang.org/genproto/protobuf/field_mask" 11 | ) 12 | 13 | func translateName(name string, md *descriptor.DescriptorProto) (string, *descriptor.DescriptorProto) { 14 | // TODO - should really gate this with a test that the marshaller has used json names 15 | if md != nil { 16 | for _, f := range md.Field { 17 | if f.JsonName != nil && f.Name != nil && *f.JsonName == name { 18 | var subType *descriptor.DescriptorProto 19 | 20 | // If the field has a TypeName then we retrieve the nested type for translating the embedded message names. 21 | if f.TypeName != nil { 22 | typeSplit := strings.Split(*f.TypeName, ".") 23 | typeName := typeSplit[len(typeSplit)-1] 24 | for _, t := range md.NestedType { 25 | if typeName == *t.Name { 26 | subType = t 27 | } 28 | } 29 | } 30 | return *f.Name, subType 31 | } 32 | } 33 | } 34 | return name, nil 35 | } 36 | 37 | // FieldMaskFromRequestBody creates a FieldMask printing all complete paths from the JSON body. 38 | func FieldMaskFromRequestBody(r io.Reader, md *descriptor.DescriptorProto) (*field_mask.FieldMask, error) { 39 | fm := &field_mask.FieldMask{} 40 | var root interface{} 41 | if err := json.NewDecoder(r).Decode(&root); err != nil { 42 | if err == io.EOF { 43 | return fm, nil 44 | } 45 | return nil, err 46 | } 47 | 48 | queue := []fieldMaskPathItem{{node: root, md: md}} 49 | for len(queue) > 0 { 50 | // dequeue an item 51 | item := queue[0] 52 | queue = queue[1:] 53 | 54 | if m, ok := item.node.(map[string]interface{}); ok { 55 | // if the item is an object, then enqueue all of its children 56 | for k, v := range m { 57 | protoName, subMd := translateName(k, item.md) 58 | if subMsg, ok := v.(descriptor2.Message); ok { 59 | _, subMd = descriptor2.ForMessage(subMsg) 60 | } 61 | 62 | var path string 63 | if item.path == "" { 64 | path = protoName 65 | } else { 66 | path = item.path + "." + protoName 67 | } 68 | queue = append(queue, fieldMaskPathItem{path: path, node: v, md: subMd}) 69 | } 70 | } else if len(item.path) > 0 { 71 | // otherwise, it's a leaf node so print its path 72 | fm.Paths = append(fm.Paths, item.path) 73 | } 74 | } 75 | 76 | return fm, nil 77 | } 78 | 79 | // fieldMaskPathItem stores a in-progress deconstruction of a path for a fieldmask 80 | type fieldMaskPathItem struct { 81 | // the list of prior fields leading up to node connected by dots 82 | path string 83 | 84 | // a generic decoded json object the current item to inspect for further path extraction 85 | node interface{} 86 | 87 | // descriptor for parent message 88 | md *descriptor.DescriptorProto 89 | } 90 | -------------------------------------------------------------------------------- /protoc-gen-grpc-gateway/httprule/compile.go: -------------------------------------------------------------------------------- 1 | package httprule 2 | 3 | import ( 4 | "github.com/grpc-ecosystem/grpc-gateway/utilities" 5 | ) 6 | 7 | const ( 8 | opcodeVersion = 1 9 | ) 10 | 11 | // Template is a compiled representation of path templates. 12 | type Template struct { 13 | // Version is the version number of the format. 14 | Version int 15 | // OpCodes is a sequence of operations. 16 | OpCodes []int 17 | // Pool is a constant pool 18 | Pool []string 19 | // Verb is a VERB part in the template. 20 | Verb string 21 | // Fields is a list of field paths bound in this template. 22 | Fields []string 23 | // Original template (example: /v1/a_bit_of_everything) 24 | Template string 25 | } 26 | 27 | // Compiler compiles utilities representation of path templates into marshallable operations. 28 | // They can be unmarshalled by runtime.NewPattern. 29 | type Compiler interface { 30 | Compile() Template 31 | } 32 | 33 | type op struct { 34 | // code is the opcode of the operation 35 | code utilities.OpCode 36 | 37 | // str is a string operand of the code. 38 | // num is ignored if str is not empty. 39 | str string 40 | 41 | // num is a numeric operand of the code. 42 | num int 43 | } 44 | 45 | func (w wildcard) compile() []op { 46 | return []op{ 47 | {code: utilities.OpPush}, 48 | } 49 | } 50 | 51 | func (w deepWildcard) compile() []op { 52 | return []op{ 53 | {code: utilities.OpPushM}, 54 | } 55 | } 56 | 57 | func (l literal) compile() []op { 58 | return []op{ 59 | { 60 | code: utilities.OpLitPush, 61 | str: string(l), 62 | }, 63 | } 64 | } 65 | 66 | func (v variable) compile() []op { 67 | var ops []op 68 | for _, s := range v.segments { 69 | ops = append(ops, s.compile()...) 70 | } 71 | ops = append(ops, op{ 72 | code: utilities.OpConcatN, 73 | num: len(v.segments), 74 | }, op{ 75 | code: utilities.OpCapture, 76 | str: v.path, 77 | }) 78 | 79 | return ops 80 | } 81 | 82 | func (t template) Compile() Template { 83 | var rawOps []op 84 | for _, s := range t.segments { 85 | rawOps = append(rawOps, s.compile()...) 86 | } 87 | 88 | var ( 89 | ops []int 90 | pool []string 91 | fields []string 92 | ) 93 | consts := make(map[string]int) 94 | for _, op := range rawOps { 95 | ops = append(ops, int(op.code)) 96 | if op.str == "" { 97 | ops = append(ops, op.num) 98 | } else { 99 | if _, ok := consts[op.str]; !ok { 100 | consts[op.str] = len(pool) 101 | pool = append(pool, op.str) 102 | } 103 | ops = append(ops, consts[op.str]) 104 | } 105 | if op.code == utilities.OpCapture { 106 | fields = append(fields, op.str) 107 | } 108 | } 109 | return Template{ 110 | Version: opcodeVersion, 111 | OpCodes: ops, 112 | Pool: pool, 113 | Verb: t.verb, 114 | Fields: fields, 115 | Template: t.template, 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /third_party/googleapis/google/api/httpbody.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | 16 | syntax = "proto3"; 17 | 18 | package google.api; 19 | 20 | import "google/protobuf/any.proto"; 21 | 22 | option cc_enable_arenas = true; 23 | option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; 24 | option java_multiple_files = true; 25 | option java_outer_classname = "HttpBodyProto"; 26 | option java_package = "com.google.api"; 27 | option objc_class_prefix = "GAPI"; 28 | 29 | // Message that represents an arbitrary HTTP body. It should only be used for 30 | // payload formats that can't be represented as JSON, such as raw binary or 31 | // an HTML page. 32 | // 33 | // 34 | // This message can be used both in streaming and non-streaming API methods in 35 | // the request as well as the response. 36 | // 37 | // It can be used as a top-level request field, which is convenient if one 38 | // wants to extract parameters from either the URL or HTTP template into the 39 | // request fields and also want access to the raw HTTP body. 40 | // 41 | // Example: 42 | // 43 | // message GetResourceRequest { 44 | // // A unique request id. 45 | // string request_id = 1; 46 | // 47 | // // The raw HTTP body is bound to this field. 48 | // google.api.HttpBody http_body = 2; 49 | // } 50 | // 51 | // service ResourceService { 52 | // rpc GetResource(GetResourceRequest) returns (google.api.HttpBody); 53 | // rpc UpdateResource(google.api.HttpBody) returns 54 | // (google.protobuf.Empty); 55 | // } 56 | // 57 | // Example with streaming methods: 58 | // 59 | // service CaldavService { 60 | // rpc GetCalendar(stream google.api.HttpBody) 61 | // returns (stream google.api.HttpBody); 62 | // rpc UpdateCalendar(stream google.api.HttpBody) 63 | // returns (stream google.api.HttpBody); 64 | // } 65 | // 66 | // Use of this type only changes how the request and response bodies are 67 | // handled, all other features will continue to work unchanged. 68 | message HttpBody { 69 | // The HTTP Content-Type header value specifying the content type of the body. 70 | string content_type = 1; 71 | 72 | // The HTTP request/response body as raw binary. 73 | bytes data = 2; 74 | 75 | // Application specific response metadata. Must be set in the first response 76 | // for streaming APIs. 77 | repeated google.protobuf.Any extensions = 3; 78 | } --------------------------------------------------------------------------------