Add vendor folder to git

This commit is contained in:
Lucas Käldström 2017-06-26 19:23:05 +03:00
parent 66cf5eaafb
commit 183585f56f
No known key found for this signature in database
GPG key ID: 600FEFBBD0D40D21
6916 changed files with 2629581 additions and 1 deletions

View file

@ -0,0 +1,198 @@
# Created by .ignore support plugin (hsz.mobi)
coverage.txt
### Go template
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
### Windows template
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Kate template
# Swap Files #
.*.kate-swp
.swp.*
### SublimeText template
# cache files for sublime text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
# sftp configuration file
sftp-config.json
### Linux template
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea
.idea/tasks.xml
.idea/dictionaries
.idea/vcs.xml
.idea/jsLibraryMappings.xml
# Sensitive or high-churn files:
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
.idea/libraries
# Mongo Explorer plugin:
.idea/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Xcode template
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
### Eclipse template
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/

View file

@ -0,0 +1,18 @@
sudo: false
language: go
go:
- 1.6.x
- 1.7.x
- 1.8.x
install:
- go get github.com/prometheus/client_golang/prometheus
- go get google.golang.org/grpc
- go get golang.org/x/net/context
- go get github.com/stretchr/testify
script:
- ./test_all.sh
after_success:
- bash <(curl -s https://codecov.io/bash)

View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,247 @@
# Go gRPC Interceptors for Prometheus monitoring
[![Travis Build](https://travis-ci.org/grpc-ecosystem/go-grpc-prometheus.svg)](https://travis-ci.org/grpc-ecosystem/go-grpc-prometheus)
[![Go Report Card](https://goreportcard.com/badge/github.com/grpc-ecosystem/go-grpc-prometheus)](http://goreportcard.com/report/grpc-ecosystem/go-grpc-prometheus)
[![GoDoc](http://img.shields.io/badge/GoDoc-Reference-blue.svg)](https://godoc.org/github.com/grpc-ecosystem/go-grpc-prometheus)
[![SourceGraph](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-prometheus/-/badge.svg)](https://sourcegraph.com/github.com/grpc-ecosystem/go-grpc-prometheus/?badge)
[![codecov](https://codecov.io/gh/grpc-ecosystem/go-grpc-prometheus/branch/master/graph/badge.svg)](https://codecov.io/gh/grpc-ecosystem/go-grpc-prometheus)
[![Apache 2.0 License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
[Prometheus](https://prometheus.io/) monitoring for your [gRPC Go](https://github.com/grpc/grpc-go) servers and clients.
A sister implementation for [gRPC Java](https://github.com/grpc/grpc-java) (same metrics, same semantics) is in [grpc-ecosystem/java-grpc-prometheus](https://github.com/grpc-ecosystem/java-grpc-prometheus).
## Interceptors
[gRPC Go](https://github.com/grpc/grpc-go) recently acquired support for Interceptors, i.e. middleware that is executed
by a gRPC Server before the request is passed onto the user's application logic. It is a perfect way to implement
common patterns: auth, logging and... monitoring.
To use Interceptors in chains, please see [`go-grpc-middleware`](https://github.com/mwitkow/go-grpc-middleware).
## Usage
There are two types of interceptors: client-side and server-side. This package provides monitoring Interceptors for both.
### Server-side
```go
import "github.com/grpc-ecosystem/go-grpc-prometheus"
...
// Initialize your gRPC server's interceptor.
myServer := grpc.NewServer(
grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
)
// Register your gRPC service implementations.
myservice.RegisterMyServiceServer(s.server, &myServiceImpl{})
// After all your registrations, make sure all of the Prometheus metrics are initialized.
grpc_prometheus.Register(myServer)
// Register Prometheus metrics handler.
http.Handle("/metrics", prometheus.Handler())
...
```
### Client-side
```go
import "github.com/grpc-ecosystem/go-grpc-prometheus"
...
clientConn, err = grpc.Dial(
address,
grpc.WithUnaryInterceptor(UnaryClientInterceptor),
grpc.WithStreamInterceptor(StreamClientInterceptor)
)
client = pb_testproto.NewTestServiceClient(clientConn)
resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"})
...
```
# Metrics
## Labels
All server-side metrics start with `grpc_server` as Prometheus subsystem name. All client-side metrics start with `grpc_client`. Both of them have mirror-concepts. Similarly all methods
contain the same rich labels:
* `grpc_service` - the [gRPC service](http://www.grpc.io/docs/#defining-a-service) name, which is the combination of protobuf `package` and
the `grpc_service` section name. E.g. for `package = mwitkow.testproto` and
`service TestService` the label will be `grpc_service="mwitkow.testproto.TestService"`
* `grpc_method` - the name of the method called on the gRPC service. E.g.
`grpc_method="Ping"`
* `grpc_type` - the gRPC [type of request](http://www.grpc.io/docs/guides/concepts.html#rpc-life-cycle).
Differentiating between the two is important especially for latency measurements.
- `unary` is single request, single response RPC
- `client_stream` is a multi-request, single response RPC
- `server_stream` is a single request, multi-response RPC
- `bidi_stream` is a multi-request, multi-response RPC
Additionally for completed RPCs, the following labels are used:
* `grpc_code` - the human-readable [gRPC status code](https://github.com/grpc/grpc-go/blob/master/codes/codes.go).
The list of all statuses is to long, but here are some common ones:
- `OK` - means the RPC was successful
- `IllegalArgument` - RPC contained bad values
- `Internal` - server-side error not disclosed to the clients
## Counters
The counters and their up to date documentation is in [server_reporter.go](server_reporter.go) and [client_reporter.go](client_reporter.go)
the respective Prometheus handler (usually `/metrics`).
For the purpose of this documentation we will only discuss `grpc_server` metrics. The `grpc_client` ones contain mirror concepts.
For simplicity, let's assume we're tracking a single server-side RPC call of [`mwitkow.testproto.TestService`](examples/testproto/test.proto),
calling the method `PingList`. The call succeeds and returns 20 messages in the stream.
First, immediately after the server receives the call it will increment the
`grpc_server_started_total` and start the handling time clock (if histograms are enabled).
```jsoniq
grpc_server_started_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1
```
Then the user logic gets invoked. It receives one message from the client containing the request
(it's a `server_stream`):
```jsoniq
grpc_server_msg_received_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1
```
The user logic may return an error, or send multiple messages back to the client. In this case, on
each of the 20 messages sent back, a counter will be incremented:
```jsoniq
grpc_server_msg_sent_total{grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 20
```
After the call completes, it's status (`OK` or other [gRPC status code](https://github.com/grpc/grpc-go/blob/master/codes/codes.go))
and the relevant call labels increment the `grpc_server_handled_total` counter.
```jsoniq
grpc_server_handled_total{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1
```
## Histograms
[Prometheus histograms](https://prometheus.io/docs/concepts/metric_types/#histogram) are a great way
to measure latency distributions of your RPCs. However since it is bad practice to have metrics
of [high cardinality](https://prometheus.io/docs/practices/instrumentation/#do-not-overuse-labels))
the latency monitoring metrics are disabled by default. To enable them please call the following
in your server initialization code:
```jsoniq
grpc_prometheus.EnableHandlingTimeHistogram()
```
After the call completes, it's handling time will be recorded in a [Prometheus histogram](https://prometheus.io/docs/concepts/metric_types/#histogram)
variable `grpc_server_handling_seconds`. It contains three sub-metrics:
* `grpc_server_handling_seconds_count` - the count of all completed RPCs by status and method
* `grpc_server_handling_seconds_sum` - cumulative time of RPCs by status and method, useful for
calculating average handling times
* `grpc_server_handling_seconds_bucket` - contains the counts of RPCs by status and method in respective
handling-time buckets. These buckets can be used by Prometheus to estimate SLAs (see [here](https://prometheus.io/docs/practices/histograms/))
The counter values will look as follows:
```jsoniq
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.005"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.01"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.025"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.05"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.1"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.25"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="0.5"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="1"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="2.5"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="5"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="10"} 1
grpc_server_handling_seconds_bucket{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream",le="+Inf"} 1
grpc_server_handling_seconds_sum{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 0.0003866430000000001
grpc_server_handling_seconds_count{grpc_code="OK",grpc_method="PingList",grpc_service="mwitkow.testproto.TestService",grpc_type="server_stream"} 1
```
## Useful query examples
Prometheus philosophy is to provide the most detailed metrics possible to the monitoring system, and
let the aggregations be handled there. The verbosity of above metrics make it possible to have that
flexibility. Here's a couple of useful monitoring queries:
### request inbound rate
```jsoniq
sum(rate(grpc_server_started_total{job="foo"}[1m])) by (grpc_service)
```
For `job="foo"` (common label to differentiate between Prometheus monitoring targets), calculate the
rate of requests per second (1 minute window) for each gRPC `grpc_service` that the job has. Please note
how the `grpc_method` is being omitted here: all methods of a given gRPC service will be summed together.
### unary request error rate
```jsoniq
sum(rate(grpc_server_handled_total{job="foo",grpc_type="unary",grpc_code!="OK"}[1m])) by (grpc_service)
```
For `job="foo"`, calculate the per-`grpc_service` rate of `unary` (1:1) RPCs that failed, i.e. the
ones that didn't finish with `OK` code.
### unary request error percentage
```jsoniq
sum(rate(grpc_server_handled_total{job="foo",grpc_type="unary",grpc_code!="OK"}[1m])) by (grpc_service)
/
sum(rate(grpc_server_started_total{job="foo",grpc_type="unary"}[1m])) by (grpc_service)
* 100.0
```
For `job="foo"`, calculate the percentage of failed requests by service. It's easy to notice that
this is a combination of the two above examples. This is an example of a query you would like to
[alert on](https://prometheus.io/docs/alerting/rules/) in your system for SLA violations, e.g.
"no more than 1% requests should fail".
### average response stream size
```jsoniq
sum(rate(grpc_server_msg_sent_total{job="foo",grpc_type="server_stream"}[10m])) by (grpc_service)
/
sum(rate(grpc_server_started_total{job="foo",grpc_type="server_stream"}[10m])) by (grpc_service)
```
For `job="foo"` what is the `grpc_service`-wide `10m` average of messages returned for all `
server_stream` RPCs. This allows you to track the stream sizes returned by your system, e.g. allows
you to track when clients started to send "wide" queries that ret
Note the divisor is the number of started RPCs, in order to account for in-flight requests.
### 99%-tile latency of unary requests
```jsoniq
histogram_quantile(0.99,
sum(rate(grpc_server_handling_seconds_bucket{job="foo",grpc_type="unary"}[5m])) by (grpc_service,le)
)
```
For `job="foo"`, returns an 99%-tile [quantile estimation](https://prometheus.io/docs/practices/histograms/#quantiles)
of the handling time of RPCs per service. Please note the `5m` rate, this means that the quantile
estimation will take samples in a rolling `5m` window. When combined with other quantiles
(e.g. 50%, 90%), this query gives you tremendous insight into the responsiveness of your system
(e.g. impact of caching).
### percentage of slow unary queries (>250ms)
```jsoniq
100.0 - (
sum(rate(grpc_server_handling_seconds_bucket{job="foo",grpc_type="unary",le="0.25"}[5m])) by (grpc_service)
/
sum(rate(grpc_server_handling_seconds_count{job="foo",grpc_type="unary"}[5m])) by (grpc_service)
) * 100.0
```
For `job="foo"` calculate the by-`grpc_service` fraction of slow requests that took longer than `0.25`
seconds. This query is relatively complex, since the Prometheus aggregations use `le` (less or equal)
buckets, meaning that counting "fast" requests fractions is easier. However, simple maths helps.
This is an example of a query you would like to alert on in your system for SLA violations,
e.g. "less than 1% of requests are slower than 250ms".
## Status
This code has been used since August 2015 as the basis for monitoring of *production* gRPC micro services at [Improbable](https://improbable.io).
## License
`go-grpc-prometheus` is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.

View file

@ -0,0 +1,72 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
// gRPC Prometheus monitoring interceptors for client-side gRPC.
package grpc_prometheus
import (
"io"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
// UnaryClientInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Unary RPCs.
func UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
monitor := newClientReporter(Unary, method)
monitor.SentMessage()
err := invoker(ctx, method, req, reply, cc, opts...)
if err != nil {
monitor.ReceivedMessage()
}
monitor.Handled(grpc.Code(err))
return err
}
// StreamServerInterceptor is a gRPC client-side interceptor that provides Prometheus monitoring for Streaming RPCs.
func StreamClientInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
monitor := newClientReporter(clientStreamType(desc), method)
clientStream, err := streamer(ctx, desc, cc, method, opts...)
if err != nil {
monitor.Handled(grpc.Code(err))
return nil, err
}
return &monitoredClientStream{clientStream, monitor}, nil
}
func clientStreamType(desc *grpc.StreamDesc) grpcType {
if desc.ClientStreams && !desc.ServerStreams {
return ClientStream
} else if !desc.ClientStreams && desc.ServerStreams {
return ServerStream
}
return BidiStream
}
// monitoredClientStream wraps grpc.ClientStream allowing each Sent/Recv of message to increment counters.
type monitoredClientStream struct {
grpc.ClientStream
monitor *clientReporter
}
func (s *monitoredClientStream) SendMsg(m interface{}) error {
err := s.ClientStream.SendMsg(m)
if err == nil {
s.monitor.SentMessage()
}
return err
}
func (s *monitoredClientStream) RecvMsg(m interface{}) error {
err := s.ClientStream.RecvMsg(m)
if err == nil {
s.monitor.ReceivedMessage()
} else if err == io.EOF {
s.monitor.Handled(codes.OK)
} else {
s.monitor.Handled(grpc.Code(err))
}
return err
}

View file

@ -0,0 +1,111 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package grpc_prometheus
import (
"time"
"google.golang.org/grpc/codes"
prom "github.com/prometheus/client_golang/prometheus"
)
var (
clientStartedCounter = prom.NewCounterVec(
prom.CounterOpts{
Namespace: "grpc",
Subsystem: "client",
Name: "started_total",
Help: "Total number of RPCs started on the client.",
}, []string{"grpc_type", "grpc_service", "grpc_method"})
clientHandledCounter = prom.NewCounterVec(
prom.CounterOpts{
Namespace: "grpc",
Subsystem: "client",
Name: "handled_total",
Help: "Total number of RPCs completed by the client, regardless of success or failure.",
}, []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"})
clientStreamMsgReceived = prom.NewCounterVec(
prom.CounterOpts{
Namespace: "grpc",
Subsystem: "client",
Name: "msg_received_total",
Help: "Total number of RPC stream messages received by the client.",
}, []string{"grpc_type", "grpc_service", "grpc_method"})
clientStreamMsgSent = prom.NewCounterVec(
prom.CounterOpts{
Namespace: "grpc",
Subsystem: "client",
Name: "msg_sent_total",
Help: "Total number of gRPC stream messages sent by the client.",
}, []string{"grpc_type", "grpc_service", "grpc_method"})
clientHandledHistogramEnabled = false
clientHandledHistogramOpts = prom.HistogramOpts{
Namespace: "grpc",
Subsystem: "client",
Name: "handling_seconds",
Help: "Histogram of response latency (seconds) of the gRPC until it is finished by the application.",
Buckets: prom.DefBuckets,
}
clientHandledHistogram *prom.HistogramVec
)
func init() {
prom.MustRegister(clientStartedCounter)
prom.MustRegister(clientHandledCounter)
prom.MustRegister(clientStreamMsgReceived)
prom.MustRegister(clientStreamMsgSent)
}
// EnableClientHandlingTimeHistogram turns on recording of handling time of RPCs.
// Histogram metrics can be very expensive for Prometheus to retain and query.
func EnableClientHandlingTimeHistogram(opts ...HistogramOption) {
for _, o := range opts {
o(&clientHandledHistogramOpts)
}
if !clientHandledHistogramEnabled {
clientHandledHistogram = prom.NewHistogramVec(
clientHandledHistogramOpts,
[]string{"grpc_type", "grpc_service", "grpc_method"},
)
prom.Register(clientHandledHistogram)
}
clientHandledHistogramEnabled = true
}
type clientReporter struct {
rpcType grpcType
serviceName string
methodName string
startTime time.Time
}
func newClientReporter(rpcType grpcType, fullMethod string) *clientReporter {
r := &clientReporter{rpcType: rpcType}
if clientHandledHistogramEnabled {
r.startTime = time.Now()
}
r.serviceName, r.methodName = splitMethodName(fullMethod)
clientStartedCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
return r
}
func (r *clientReporter) ReceivedMessage() {
clientStreamMsgReceived.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
}
func (r *clientReporter) SentMessage() {
clientStreamMsgSent.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
}
func (r *clientReporter) Handled(code codes.Code) {
clientHandledCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName, code.String()).Inc()
if clientHandledHistogramEnabled {
clientHandledHistogram.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Observe(time.Since(r.startTime).Seconds())
}
}

View file

@ -0,0 +1,212 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package grpc_prometheus
import (
"net"
"testing"
"time"
"io"
pb_testproto "github.com/grpc-ecosystem/go-grpc-prometheus/examples/testproto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
func TestClientInterceptorSuite(t *testing.T) {
suite.Run(t, &ClientInterceptorTestSuite{})
}
type ClientInterceptorTestSuite struct {
suite.Suite
serverListener net.Listener
server *grpc.Server
clientConn *grpc.ClientConn
testClient pb_testproto.TestServiceClient
ctx context.Context
}
func (s *ClientInterceptorTestSuite) SetupSuite() {
var err error
EnableClientHandlingTimeHistogram()
s.serverListener, err = net.Listen("tcp", "127.0.0.1:0")
require.NoError(s.T(), err, "must be able to allocate a port for serverListener")
// This is the point where we hook up the interceptor
s.server = grpc.NewServer()
pb_testproto.RegisterTestServiceServer(s.server, &testService{t: s.T()})
go func() {
s.server.Serve(s.serverListener)
}()
s.clientConn, err = grpc.Dial(
s.serverListener.Addr().String(),
grpc.WithInsecure(),
grpc.WithBlock(),
grpc.WithUnaryInterceptor(UnaryClientInterceptor),
grpc.WithStreamInterceptor(StreamClientInterceptor),
grpc.WithTimeout(2*time.Second))
require.NoError(s.T(), err, "must not error on client Dial")
s.testClient = pb_testproto.NewTestServiceClient(s.clientConn)
}
func (s *ClientInterceptorTestSuite) SetupTest() {
// Make all RPC calls last at most 2 sec, meaning all async issues or deadlock will not kill tests.
s.ctx, _ = context.WithTimeout(context.TODO(), 2*time.Second)
}
func (s *ClientInterceptorTestSuite) TearDownSuite() {
if s.serverListener != nil {
s.server.Stop()
s.T().Logf("stopped grpc.Server at: %v", s.serverListener.Addr().String())
s.serverListener.Close()
}
if s.clientConn != nil {
s.clientConn.Close()
}
}
func (s *ClientInterceptorTestSuite) TestUnaryIncrementsStarted() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_started_total", "PingEmpty", "unary")
s.testClient.PingEmpty(s.ctx, &pb_testproto.Empty{})
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_started_total", "PingEmpty", "unary")
assert.EqualValues(s.T(), before+1, after, "grpc_client_started_total should be incremented for PingEmpty")
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_started_total", "PingError", "unary")
s.testClient.PingError(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.Unavailable)})
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_started_total", "PingError", "unary")
assert.EqualValues(s.T(), before+1, after, "grpc_client_started_total should be incremented for PingError")
}
func (s *ClientInterceptorTestSuite) TestUnaryIncrementsHandled() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_handled_total", "PingEmpty", "unary", "OK")
s.testClient.PingEmpty(s.ctx, &pb_testproto.Empty{}) // should return with code=OK
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_handled_total", "PingEmpty", "unary", "OK")
assert.EqualValues(s.T(), before+1, after, "grpc_client_handled_count should be incremented for PingEmpty")
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_handled_total", "PingError", "unary", "FailedPrecondition")
s.testClient.PingError(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.FailedPrecondition)}) // should return with code=FailedPrecondition
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_handled_total", "PingError", "unary", "FailedPrecondition")
assert.EqualValues(s.T(), before+1, after, "grpc_client_handled_total should be incremented for PingError")
}
func (s *ClientInterceptorTestSuite) TestUnaryIncrementsHistograms() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_handling_seconds_count", "PingEmpty", "unary")
s.testClient.PingEmpty(s.ctx, &pb_testproto.Empty{}) // should return with code=OK
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_handling_seconds_count", "PingEmpty", "unary")
assert.EqualValues(s.T(), before+1, after, "grpc_client_handled_count should be incremented for PingEmpty")
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_handling_seconds_count", "PingError", "unary")
s.testClient.PingError(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.FailedPrecondition)}) // should return with code=FailedPrecondition
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_handling_seconds_count", "PingError", "unary")
assert.EqualValues(s.T(), before+1, after, "grpc_client_handling_seconds_count should be incremented for PingError")
}
func (s *ClientInterceptorTestSuite) TestStreamingIncrementsStarted() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_started_total", "PingList", "server_stream")
s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{})
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_started_total", "PingList", "server_stream")
assert.EqualValues(s.T(), before+1, after, "grpc_client_started_total should be incremented for PingList")
}
func (s *ClientInterceptorTestSuite) TestStreamingIncrementsHistograms() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_handling_seconds_count", "PingList", "server_stream")
ss, _ := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{}) // should return with code=OK
// Do a read, just for kicks.
for {
_, err := ss.Recv()
if err == io.EOF {
break
}
require.NoError(s.T(), err, "reading pingList shouldn't fail")
}
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_handling_seconds_count", "PingList", "server_stream")
assert.EqualValues(s.T(), before+1, after, "grpc_client_handling_seconds_count should be incremented for PingList OK")
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_handling_seconds_count", "PingList", "server_stream")
ss, err := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.FailedPrecondition)}) // should return with code=FailedPrecondition
require.NoError(s.T(), err, "PingList must not fail immedietely")
// Do a read, just to progate errors.
_, err = ss.Recv()
require.Equal(s.T(), codes.FailedPrecondition, grpc.Code(err), "Recv must return FailedPrecondition, otherwise the test is wrong")
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_handling_seconds_count", "PingList", "server_stream")
assert.EqualValues(s.T(), before+1, after, "grpc_client_handling_seconds_count should be incremented for PingList FailedPrecondition")
}
func (s *ClientInterceptorTestSuite) TestStreamingIncrementsHandled() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_handled_total", "PingList", "server_stream", "OK")
ss, _ := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{}) // should return with code=OK
// Do a read, just for kicks.
for {
_, err := ss.Recv()
if err == io.EOF {
break
}
require.NoError(s.T(), err, "reading pingList shouldn't fail")
}
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_handled_total", "PingList", "server_stream", "OK")
assert.EqualValues(s.T(), before+1, after, "grpc_client_handled_total should be incremented for PingList OK")
before = sumCountersForMetricAndLabels(s.T(), "grpc_client_handled_total", "PingList", "server_stream", "FailedPrecondition")
ss, err := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.FailedPrecondition)}) // should return with code=FailedPrecondition
require.NoError(s.T(), err, "PingList must not fail immedietely")
// Do a read, just to progate errors.
_, err = ss.Recv()
require.Equal(s.T(), codes.FailedPrecondition, grpc.Code(err), "Recv must return FailedPrecondition, otherwise the test is wrong")
after = sumCountersForMetricAndLabels(s.T(), "grpc_client_handled_total", "PingList", "server_stream", "FailedPrecondition")
assert.EqualValues(s.T(), before+1, after, "grpc_client_handled_total should be incremented for PingList FailedPrecondition")
}
func (s *ClientInterceptorTestSuite) TestStreamingIncrementsMessageCounts() {
beforeRecv := sumCountersForMetricAndLabels(s.T(), "grpc_client_msg_received_total", "PingList", "server_stream")
beforeSent := sumCountersForMetricAndLabels(s.T(), "grpc_client_msg_sent_total", "PingList", "server_stream")
ss, _ := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{}) // should return with code=OK
// Do a read, just for kicks.
count := 0
for {
_, err := ss.Recv()
if err == io.EOF {
break
}
require.NoError(s.T(), err, "reading pingList shouldn't fail")
count += 1
}
require.EqualValues(s.T(), countListResponses, count, "Number of received msg on the wire must match")
afterSent := sumCountersForMetricAndLabels(s.T(), "grpc_client_msg_sent_total", "PingList", "server_stream")
afterRecv := sumCountersForMetricAndLabels(s.T(), "grpc_client_msg_received_total", "PingList", "server_stream")
assert.EqualValues(s.T(), beforeSent+1, afterSent, "grpc_client_msg_sent_total should be incremented 20 times for PingList")
assert.EqualValues(s.T(), beforeRecv+countListResponses, afterRecv, "grpc_client_msg_sent_total should be incremented ones for PingList ")
}

View file

@ -0,0 +1,10 @@
all: test_go
test_go: test.proto
PATH="${GOPATH}/bin:${PATH}" protoc \
-I. \
-I${GOPATH}/src \
--go_out=plugins=grpc:. \
test.proto

View file

@ -0,0 +1,329 @@
// Code generated by protoc-gen-go.
// source: test.proto
// DO NOT EDIT!
/*
Package mwitkow_testproto is a generated protocol buffer package.
It is generated from these files:
test.proto
It has these top-level messages:
Empty
PingRequest
PingResponse
*/
package mwitkow_testproto
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Empty struct {
}
func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type PingRequest struct {
Value string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
SleepTimeMs int32 `protobuf:"varint,2,opt,name=sleep_time_ms,json=sleepTimeMs" json:"sleep_time_ms,omitempty"`
ErrorCodeReturned uint32 `protobuf:"varint,3,opt,name=error_code_returned,json=errorCodeReturned" json:"error_code_returned,omitempty"`
}
func (m *PingRequest) Reset() { *m = PingRequest{} }
func (m *PingRequest) String() string { return proto.CompactTextString(m) }
func (*PingRequest) ProtoMessage() {}
func (*PingRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *PingRequest) GetValue() string {
if m != nil {
return m.Value
}
return ""
}
func (m *PingRequest) GetSleepTimeMs() int32 {
if m != nil {
return m.SleepTimeMs
}
return 0
}
func (m *PingRequest) GetErrorCodeReturned() uint32 {
if m != nil {
return m.ErrorCodeReturned
}
return 0
}
type PingResponse struct {
Value string `protobuf:"bytes,1,opt,name=Value,json=value" json:"Value,omitempty"`
Counter int32 `protobuf:"varint,2,opt,name=counter" json:"counter,omitempty"`
}
func (m *PingResponse) Reset() { *m = PingResponse{} }
func (m *PingResponse) String() string { return proto.CompactTextString(m) }
func (*PingResponse) ProtoMessage() {}
func (*PingResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *PingResponse) GetValue() string {
if m != nil {
return m.Value
}
return ""
}
func (m *PingResponse) GetCounter() int32 {
if m != nil {
return m.Counter
}
return 0
}
func init() {
proto.RegisterType((*Empty)(nil), "mwitkow.testproto.Empty")
proto.RegisterType((*PingRequest)(nil), "mwitkow.testproto.PingRequest")
proto.RegisterType((*PingResponse)(nil), "mwitkow.testproto.PingResponse")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for TestService service
type TestServiceClient interface {
PingEmpty(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PingResponse, error)
Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error)
PingError(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*Empty, error)
PingList(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (TestService_PingListClient, error)
}
type testServiceClient struct {
cc *grpc.ClientConn
}
func NewTestServiceClient(cc *grpc.ClientConn) TestServiceClient {
return &testServiceClient{cc}
}
func (c *testServiceClient) PingEmpty(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PingResponse, error) {
out := new(PingResponse)
err := grpc.Invoke(ctx, "/mwitkow.testproto.TestService/PingEmpty", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testServiceClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) {
out := new(PingResponse)
err := grpc.Invoke(ctx, "/mwitkow.testproto.TestService/Ping", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testServiceClient) PingError(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := grpc.Invoke(ctx, "/mwitkow.testproto.TestService/PingError", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testServiceClient) PingList(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (TestService_PingListClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[0], c.cc, "/mwitkow.testproto.TestService/PingList", opts...)
if err != nil {
return nil, err
}
x := &testServicePingListClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type TestService_PingListClient interface {
Recv() (*PingResponse, error)
grpc.ClientStream
}
type testServicePingListClient struct {
grpc.ClientStream
}
func (x *testServicePingListClient) Recv() (*PingResponse, error) {
m := new(PingResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for TestService service
type TestServiceServer interface {
PingEmpty(context.Context, *Empty) (*PingResponse, error)
Ping(context.Context, *PingRequest) (*PingResponse, error)
PingError(context.Context, *PingRequest) (*Empty, error)
PingList(*PingRequest, TestService_PingListServer) error
}
func RegisterTestServiceServer(s *grpc.Server, srv TestServiceServer) {
s.RegisterService(&_TestService_serviceDesc, srv)
}
func _TestService_PingEmpty_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TestServiceServer).PingEmpty(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/mwitkow.testproto.TestService/PingEmpty",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TestServiceServer).PingEmpty(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _TestService_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PingRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TestServiceServer).Ping(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/mwitkow.testproto.TestService/Ping",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TestServiceServer).Ping(ctx, req.(*PingRequest))
}
return interceptor(ctx, in, info, handler)
}
func _TestService_PingError_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PingRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TestServiceServer).PingError(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/mwitkow.testproto.TestService/PingError",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TestServiceServer).PingError(ctx, req.(*PingRequest))
}
return interceptor(ctx, in, info, handler)
}
func _TestService_PingList_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(PingRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(TestServiceServer).PingList(m, &testServicePingListServer{stream})
}
type TestService_PingListServer interface {
Send(*PingResponse) error
grpc.ServerStream
}
type testServicePingListServer struct {
grpc.ServerStream
}
func (x *testServicePingListServer) Send(m *PingResponse) error {
return x.ServerStream.SendMsg(m)
}
var _TestService_serviceDesc = grpc.ServiceDesc{
ServiceName: "mwitkow.testproto.TestService",
HandlerType: (*TestServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "PingEmpty",
Handler: _TestService_PingEmpty_Handler,
},
{
MethodName: "Ping",
Handler: _TestService_Ping_Handler,
},
{
MethodName: "PingError",
Handler: _TestService_PingError_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "PingList",
Handler: _TestService_PingList_Handler,
ServerStreams: true,
},
},
Metadata: "test.proto",
}
func init() { proto.RegisterFile("test.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 273 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x90, 0xcf, 0x4b, 0xc3, 0x30,
0x14, 0xc7, 0xd7, 0x69, 0x9d, 0x7b, 0x75, 0x87, 0x45, 0x0f, 0xc5, 0x83, 0x96, 0x9c, 0x7a, 0x0a,
0xa2, 0x77, 0x2f, 0x22, 0x2a, 0x28, 0x4a, 0x1c, 0x5e, 0x8b, 0xb6, 0x0f, 0x09, 0x2e, 0x4d, 0x4d,
0x5e, 0x57, 0xfc, 0xdf, 0xfc, 0xe3, 0x24, 0x59, 0x05, 0x61, 0x0e, 0x3d, 0xec, 0x98, 0xcf, 0xf7,
0xf1, 0xfd, 0x11, 0x00, 0x42, 0x47, 0xa2, 0xb1, 0x86, 0x0c, 0x9b, 0xea, 0x4e, 0xd1, 0x9b, 0xe9,
0x84, 0x67, 0x01, 0xf1, 0x11, 0xc4, 0x97, 0xba, 0xa1, 0x0f, 0xde, 0x41, 0xf2, 0xa0, 0xea, 0x57,
0x89, 0xef, 0x2d, 0x3a, 0x62, 0x07, 0x10, 0x2f, 0x9e, 0xe7, 0x2d, 0xa6, 0x51, 0x16, 0xe5, 0x63,
0xb9, 0x7c, 0x30, 0x0e, 0x13, 0x37, 0x47, 0x6c, 0x0a, 0x52, 0x1a, 0x0b, 0xed, 0xd2, 0x61, 0x16,
0xe5, 0xb1, 0x4c, 0x02, 0x9c, 0x29, 0x8d, 0x77, 0x8e, 0x09, 0xd8, 0x47, 0x6b, 0x8d, 0x2d, 0x4a,
0x53, 0x61, 0x61, 0x91, 0x5a, 0x5b, 0x63, 0x95, 0x6e, 0x65, 0x51, 0x3e, 0x91, 0xd3, 0x20, 0x5d,
0x98, 0x0a, 0x65, 0x2f, 0xf0, 0x73, 0xd8, 0x5b, 0x06, 0xbb, 0xc6, 0xd4, 0x0e, 0x7d, 0xf2, 0xd3,
0x6a, 0x72, 0x0a, 0xa3, 0xd2, 0xb4, 0x35, 0xa1, 0xed, 0x33, 0xbf, 0x9f, 0xa7, 0x9f, 0x43, 0x48,
0x66, 0xe8, 0xe8, 0x11, 0xed, 0x42, 0x95, 0xc8, 0xae, 0x61, 0xec, 0xfd, 0xc2, 0x2a, 0x96, 0x8a,
0x95, 0xc9, 0x22, 0x28, 0x87, 0xc7, 0xbf, 0x28, 0x3f, 0x7b, 0xf0, 0x01, 0xbb, 0x81, 0x6d, 0x4f,
0xd8, 0xd1, 0xda, 0xd3, 0xf0, 0x57, 0xff, 0xb1, 0xba, 0xea, 0x4b, 0xf9, 0xf5, 0x7f, 0xfa, 0xad,
0x2d, 0xcd, 0x07, 0xec, 0x1e, 0x76, 0xfd, 0xe9, 0xad, 0x72, 0xb4, 0x81, 0x5e, 0x27, 0xd1, 0xcb,
0x4e, 0xe0, 0x67, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x38, 0x3e, 0x02, 0xe9, 0x28, 0x02, 0x00,
0x00,
}

View file

@ -0,0 +1,28 @@
syntax = "proto3";
package mwitkow.testproto;
message Empty {
}
message PingRequest {
string value = 1;
int32 sleep_time_ms = 2;
uint32 error_code_returned = 3;
}
message PingResponse {
string Value = 1;
int32 counter = 2;
}
service TestService {
rpc PingEmpty(Empty) returns (PingResponse) {}
rpc Ping(PingRequest) returns (PingResponse) {}
rpc PingError(PingRequest) returns (Empty) {}
rpc PingList(PingRequest) returns (stream PingResponse) {}
}

View file

@ -0,0 +1,74 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
// gRPC Prometheus monitoring interceptors for server-side gRPC.
package grpc_prometheus
import (
"golang.org/x/net/context"
"google.golang.org/grpc"
)
// PreregisterServices takes a gRPC server and pre-initializes all counters to 0.
// This allows for easier monitoring in Prometheus (no missing metrics), and should be called *after* all services have
// been registered with the server.
func Register(server *grpc.Server) {
serviceInfo := server.GetServiceInfo()
for serviceName, info := range serviceInfo {
for _, mInfo := range info.Methods {
preRegisterMethod(serviceName, &mInfo)
}
}
}
// UnaryServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Unary RPCs.
func UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
monitor := newServerReporter(Unary, info.FullMethod)
monitor.ReceivedMessage()
resp, err := handler(ctx, req)
monitor.Handled(grpc.Code(err))
if err == nil {
monitor.SentMessage()
}
return resp, err
}
// StreamServerInterceptor is a gRPC server-side interceptor that provides Prometheus monitoring for Streaming RPCs.
func StreamServerInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
monitor := newServerReporter(streamRpcType(info), info.FullMethod)
err := handler(srv, &monitoredServerStream{ss, monitor})
monitor.Handled(grpc.Code(err))
return err
}
func streamRpcType(info *grpc.StreamServerInfo) grpcType {
if info.IsClientStream && !info.IsServerStream {
return ClientStream
} else if !info.IsClientStream && info.IsServerStream {
return ServerStream
}
return BidiStream
}
// monitoredStream wraps grpc.ServerStream allowing each Sent/Recv of message to increment counters.
type monitoredServerStream struct {
grpc.ServerStream
monitor *serverReporter
}
func (s *monitoredServerStream) SendMsg(m interface{}) error {
err := s.ServerStream.SendMsg(m)
if err == nil {
s.monitor.SentMessage()
}
return err
}
func (s *monitoredServerStream) RecvMsg(m interface{}) error {
err := s.ServerStream.RecvMsg(m)
if err == nil {
s.monitor.ReceivedMessage()
}
return err
}

View file

@ -0,0 +1,157 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package grpc_prometheus
import (
"time"
"google.golang.org/grpc/codes"
prom "github.com/prometheus/client_golang/prometheus"
"google.golang.org/grpc"
)
type grpcType string
const (
Unary grpcType = "unary"
ClientStream grpcType = "client_stream"
ServerStream grpcType = "server_stream"
BidiStream grpcType = "bidi_stream"
)
var (
serverStartedCounter = prom.NewCounterVec(
prom.CounterOpts{
Namespace: "grpc",
Subsystem: "server",
Name: "started_total",
Help: "Total number of RPCs started on the server.",
}, []string{"grpc_type", "grpc_service", "grpc_method"})
serverHandledCounter = prom.NewCounterVec(
prom.CounterOpts{
Namespace: "grpc",
Subsystem: "server",
Name: "handled_total",
Help: "Total number of RPCs completed on the server, regardless of success or failure.",
}, []string{"grpc_type", "grpc_service", "grpc_method", "grpc_code"})
serverStreamMsgReceived = prom.NewCounterVec(
prom.CounterOpts{
Namespace: "grpc",
Subsystem: "server",
Name: "msg_received_total",
Help: "Total number of RPC stream messages received on the server.",
}, []string{"grpc_type", "grpc_service", "grpc_method"})
serverStreamMsgSent = prom.NewCounterVec(
prom.CounterOpts{
Namespace: "grpc",
Subsystem: "server",
Name: "msg_sent_total",
Help: "Total number of gRPC stream messages sent by the server.",
}, []string{"grpc_type", "grpc_service", "grpc_method"})
serverHandledHistogramEnabled = false
serverHandledHistogramOpts = prom.HistogramOpts{
Namespace: "grpc",
Subsystem: "server",
Name: "handling_seconds",
Help: "Histogram of response latency (seconds) of gRPC that had been application-level handled by the server.",
Buckets: prom.DefBuckets,
}
serverHandledHistogram *prom.HistogramVec
)
func init() {
prom.MustRegister(serverStartedCounter)
prom.MustRegister(serverHandledCounter)
prom.MustRegister(serverStreamMsgReceived)
prom.MustRegister(serverStreamMsgSent)
}
type HistogramOption func(*prom.HistogramOpts)
// WithHistogramBuckets allows you to specify custom bucket ranges for histograms if EnableHandlingTimeHistogram is on.
func WithHistogramBuckets(buckets []float64) HistogramOption {
return func(o *prom.HistogramOpts) { o.Buckets = buckets }
}
// EnableHandlingTimeHistogram turns on recording of handling time of RPCs for server-side interceptors.
// Histogram metrics can be very expensive for Prometheus to retain and query.
func EnableHandlingTimeHistogram(opts ...HistogramOption) {
for _, o := range opts {
o(&serverHandledHistogramOpts)
}
if !serverHandledHistogramEnabled {
serverHandledHistogram = prom.NewHistogramVec(
serverHandledHistogramOpts,
[]string{"grpc_type", "grpc_service", "grpc_method"},
)
prom.Register(serverHandledHistogram)
}
serverHandledHistogramEnabled = true
}
type serverReporter struct {
rpcType grpcType
serviceName string
methodName string
startTime time.Time
}
func newServerReporter(rpcType grpcType, fullMethod string) *serverReporter {
r := &serverReporter{rpcType: rpcType}
if serverHandledHistogramEnabled {
r.startTime = time.Now()
}
r.serviceName, r.methodName = splitMethodName(fullMethod)
serverStartedCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
return r
}
func (r *serverReporter) ReceivedMessage() {
serverStreamMsgReceived.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
}
func (r *serverReporter) SentMessage() {
serverStreamMsgSent.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Inc()
}
func (r *serverReporter) Handled(code codes.Code) {
serverHandledCounter.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName, code.String()).Inc()
if serverHandledHistogramEnabled {
serverHandledHistogram.WithLabelValues(string(r.rpcType), r.serviceName, r.methodName).Observe(time.Since(r.startTime).Seconds())
}
}
// preRegisterMethod is invoked on Register of a Server, allowing all gRPC services labels to be pre-populated.
func preRegisterMethod(serviceName string, mInfo *grpc.MethodInfo) {
methodName := mInfo.Name
methodType := string(typeFromMethodInfo(mInfo))
// These are just references (no increments), as just referencing will create the labels but not set values.
serverStartedCounter.GetMetricWithLabelValues(methodType, serviceName, methodName)
serverStreamMsgReceived.GetMetricWithLabelValues(methodType, serviceName, methodName)
serverStreamMsgSent.GetMetricWithLabelValues(methodType, serviceName, methodName)
if serverHandledHistogramEnabled {
serverHandledHistogram.GetMetricWithLabelValues(methodType, serviceName, methodName)
}
for _, code := range allCodes {
serverHandledCounter.GetMetricWithLabelValues(methodType, serviceName, methodName, code.String())
}
}
func typeFromMethodInfo(mInfo *grpc.MethodInfo) grpcType {
if mInfo.IsClientStream == false && mInfo.IsServerStream == false {
return Unary
}
if mInfo.IsClientStream == true && mInfo.IsServerStream == false {
return ClientStream
}
if mInfo.IsClientStream == false && mInfo.IsServerStream == true {
return ServerStream
}
return BidiStream
}

View file

@ -0,0 +1,307 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package grpc_prometheus
import (
"bufio"
"io"
"net"
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"
"time"
pb_testproto "github.com/grpc-ecosystem/go-grpc-prometheus/examples/testproto"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
const (
pingDefaultValue = "I like kittens."
countListResponses = 20
)
func TestServerInterceptorSuite(t *testing.T) {
suite.Run(t, &ServerInterceptorTestSuite{})
}
type ServerInterceptorTestSuite struct {
suite.Suite
serverListener net.Listener
server *grpc.Server
clientConn *grpc.ClientConn
testClient pb_testproto.TestServiceClient
ctx context.Context
}
func (s *ServerInterceptorTestSuite) SetupSuite() {
var err error
EnableHandlingTimeHistogram()
s.serverListener, err = net.Listen("tcp", "127.0.0.1:0")
require.NoError(s.T(), err, "must be able to allocate a port for serverListener")
// This is the point where we hook up the interceptor
s.server = grpc.NewServer(
grpc.StreamInterceptor(StreamServerInterceptor),
grpc.UnaryInterceptor(UnaryServerInterceptor),
)
pb_testproto.RegisterTestServiceServer(s.server, &testService{t: s.T()})
go func() {
s.server.Serve(s.serverListener)
}()
s.clientConn, err = grpc.Dial(s.serverListener.Addr().String(), grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(2*time.Second))
require.NoError(s.T(), err, "must not error on client Dial")
s.testClient = pb_testproto.NewTestServiceClient(s.clientConn)
// Important! Pre-register stuff here.
Register(s.server)
}
func (s *ServerInterceptorTestSuite) SetupTest() {
// Make all RPC calls last at most 2 sec, meaning all async issues or deadlock will not kill tests.
s.ctx, _ = context.WithTimeout(context.TODO(), 2*time.Second)
}
func (s *ServerInterceptorTestSuite) TearDownSuite() {
if s.serverListener != nil {
s.server.Stop()
s.T().Logf("stopped grpc.Server at: %v", s.serverListener.Addr().String())
s.serverListener.Close()
}
if s.clientConn != nil {
s.clientConn.Close()
}
}
func (s *ServerInterceptorTestSuite) TestRegisterPresetsStuff() {
for testId, testCase := range []struct {
metricName string
existingLabels []string
}{
{"grpc_server_started_total", []string{"mwitkow.testproto.TestService", "PingEmpty", "unary"}},
{"grpc_server_started_total", []string{"mwitkow.testproto.TestService", "PingList", "server_stream"}},
{"grpc_server_msg_received_total", []string{"mwitkow.testproto.TestService", "PingList", "server_stream"}},
{"grpc_server_msg_sent_total", []string{"mwitkow.testproto.TestService", "PingEmpty", "unary"}},
{"grpc_server_handling_seconds_sum", []string{"mwitkow.testproto.TestService", "PingEmpty", "unary"}},
{"grpc_server_handling_seconds_count", []string{"mwitkow.testproto.TestService", "PingList", "server_stream"}},
{"grpc_server_handled_total", []string{"mwitkow.testproto.TestService", "PingList", "server_stream", "OutOfRange"}},
{"grpc_server_handled_total", []string{"mwitkow.testproto.TestService", "PingList", "server_stream", "Aborted"}},
{"grpc_server_handled_total", []string{"mwitkow.testproto.TestService", "PingEmpty", "unary", "FailedPrecondition"}},
{"grpc_server_handled_total", []string{"mwitkow.testproto.TestService", "PingEmpty", "unary", "ResourceExhausted"}},
} {
lineCount := len(fetchPrometheusLines(s.T(), testCase.metricName, testCase.existingLabels...))
assert.NotEqual(s.T(), 0, lineCount, "metrics must exist for test case %d", testId)
}
}
func (s *ServerInterceptorTestSuite) TestUnaryIncrementsStarted() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_started_total", "PingEmpty", "unary")
s.testClient.PingEmpty(s.ctx, &pb_testproto.Empty{})
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_started_total", "PingEmpty", "unary")
assert.EqualValues(s.T(), before+1, after, "grpc_server_started_total should be incremented for PingEmpty")
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_started_total", "PingError", "unary")
s.testClient.PingError(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.Unavailable)})
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_started_total", "PingError", "unary")
assert.EqualValues(s.T(), before+1, after, "grpc_server_started_total should be incremented for PingError")
}
func (s *ServerInterceptorTestSuite) TestUnaryIncrementsHandled() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_handled_total", "PingEmpty", "unary", "OK")
s.testClient.PingEmpty(s.ctx, &pb_testproto.Empty{}) // should return with code=OK
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_handled_total", "PingEmpty", "unary", "OK")
assert.EqualValues(s.T(), before+1, after, "grpc_server_handled_count should be incremented for PingEmpty")
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_handled_total", "PingError", "unary", "FailedPrecondition")
s.testClient.PingError(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.FailedPrecondition)}) // should return with code=FailedPrecondition
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_handled_total", "PingError", "unary", "FailedPrecondition")
assert.EqualValues(s.T(), before+1, after, "grpc_server_handled_total should be incremented for PingError")
}
func (s *ServerInterceptorTestSuite) TestUnaryIncrementsHistograms() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_handling_seconds_count", "PingEmpty", "unary")
s.testClient.PingEmpty(s.ctx, &pb_testproto.Empty{}) // should return with code=OK
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_handling_seconds_count", "PingEmpty", "unary")
assert.EqualValues(s.T(), before+1, after, "grpc_server_handled_count should be incremented for PingEmpty")
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_handling_seconds_count", "PingError", "unary")
s.testClient.PingError(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.FailedPrecondition)}) // should return with code=FailedPrecondition
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_handling_seconds_count", "PingError", "unary")
assert.EqualValues(s.T(), before+1, after, "grpc_server_handling_seconds_count should be incremented for PingError")
}
func (s *ServerInterceptorTestSuite) TestStreamingIncrementsStarted() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_started_total", "PingList", "server_stream")
s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{})
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_started_total", "PingList", "server_stream")
assert.EqualValues(s.T(), before+1, after, "grpc_server_started_total should be incremented for PingList")
}
func (s *ServerInterceptorTestSuite) TestStreamingIncrementsHistograms() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_handling_seconds_count", "PingList", "server_stream")
ss, _ := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{}) // should return with code=OK
// Do a read, just for kicks.
for {
_, err := ss.Recv()
if err == io.EOF {
break
}
require.NoError(s.T(), err, "reading pingList shouldn't fail")
}
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_handling_seconds_count", "PingList", "server_stream")
assert.EqualValues(s.T(), before+1, after, "grpc_server_handling_seconds_count should be incremented for PingList OK")
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_handling_seconds_count", "PingList", "server_stream")
_, err := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.FailedPrecondition)}) // should return with code=FailedPrecondition
require.NoError(s.T(), err, "PingList must not fail immedietely")
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_handling_seconds_count", "PingList", "server_stream")
assert.EqualValues(s.T(), before+1, after, "grpc_server_handling_seconds_count should be incremented for PingList FailedPrecondition")
}
func (s *ServerInterceptorTestSuite) TestStreamingIncrementsHandled() {
var before int
var after int
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_handled_total", "PingList", "server_stream", "OK")
ss, _ := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{}) // should return with code=OK
// Do a read, just for kicks.
for {
_, err := ss.Recv()
if err == io.EOF {
break
}
require.NoError(s.T(), err, "reading pingList shouldn't fail")
}
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_handled_total", "PingList", "server_stream", "OK")
assert.EqualValues(s.T(), before+1, after, "grpc_server_handled_total should be incremented for PingList OK")
before = sumCountersForMetricAndLabels(s.T(), "grpc_server_handled_total", "PingList", "server_stream", "FailedPrecondition")
_, err := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{ErrorCodeReturned: uint32(codes.FailedPrecondition)}) // should return with code=FailedPrecondition
require.NoError(s.T(), err, "PingList must not fail immedietely")
after = sumCountersForMetricAndLabels(s.T(), "grpc_server_handled_total", "PingList", "server_stream", "FailedPrecondition")
assert.EqualValues(s.T(), before+1, after, "grpc_server_handled_total should be incremented for PingList FailedPrecondition")
}
func (s *ServerInterceptorTestSuite) TestStreamingIncrementsMessageCounts() {
beforeRecv := sumCountersForMetricAndLabels(s.T(), "grpc_server_msg_received_total", "PingList", "server_stream")
beforeSent := sumCountersForMetricAndLabels(s.T(), "grpc_server_msg_sent_total", "PingList", "server_stream")
ss, _ := s.testClient.PingList(s.ctx, &pb_testproto.PingRequest{}) // should return with code=OK
// Do a read, just for kicks.
count := 0
for {
_, err := ss.Recv()
if err == io.EOF {
break
}
require.NoError(s.T(), err, "reading pingList shouldn't fail")
count += 1
}
require.EqualValues(s.T(), countListResponses, count, "Number of received msg on the wire must match")
afterSent := sumCountersForMetricAndLabels(s.T(), "grpc_server_msg_sent_total", "PingList", "server_stream")
afterRecv := sumCountersForMetricAndLabels(s.T(), "grpc_server_msg_received_total", "PingList", "server_stream")
assert.EqualValues(s.T(), beforeSent+countListResponses, afterSent, "grpc_server_msg_sent_total should be incremented 20 times for PingList")
assert.EqualValues(s.T(), beforeRecv+1, afterRecv, "grpc_server_msg_sent_total should be incremented ones for PingList ")
}
func fetchPrometheusLines(t *testing.T, metricName string, matchingLabelValues ...string) []string {
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
require.NoError(t, err, "failed creating request for Prometheus handler")
prometheus.Handler().ServeHTTP(resp, req)
reader := bufio.NewReader(resp.Body)
ret := []string{}
for {
line, err := reader.ReadString('\n')
if err == io.EOF {
break
} else {
require.NoError(t, err, "error reading stuff")
}
if !strings.HasPrefix(line, metricName) {
continue
}
matches := true
for _, labelValue := range matchingLabelValues {
if !strings.Contains(line, `"`+labelValue+`"`) {
matches = false
}
}
if matches {
ret = append(ret, line)
}
}
return ret
}
func sumCountersForMetricAndLabels(t *testing.T, metricName string, matchingLabelValues ...string) int {
count := 0
for _, line := range fetchPrometheusLines(t, metricName, matchingLabelValues...) {
valueString := line[strings.LastIndex(line, " ")+1 : len(line)-1]
valueFloat, err := strconv.ParseFloat(valueString, 32)
require.NoError(t, err, "failed parsing value for line: %v", line)
count += int(valueFloat)
}
return count
}
type testService struct {
t *testing.T
}
func (s *testService) PingEmpty(ctx context.Context, _ *pb_testproto.Empty) (*pb_testproto.PingResponse, error) {
return &pb_testproto.PingResponse{Value: pingDefaultValue, Counter: 42}, nil
}
func (s *testService) Ping(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.PingResponse, error) {
// Send user trailers and headers.
return &pb_testproto.PingResponse{Value: ping.Value, Counter: 42}, nil
}
func (s *testService) PingError(ctx context.Context, ping *pb_testproto.PingRequest) (*pb_testproto.Empty, error) {
code := codes.Code(ping.ErrorCodeReturned)
return nil, grpc.Errorf(code, "Userspace error.")
}
func (s *testService) PingList(ping *pb_testproto.PingRequest, stream pb_testproto.TestService_PingListServer) error {
if ping.ErrorCodeReturned != 0 {
return grpc.Errorf(codes.Code(ping.ErrorCodeReturned), "foobar")
}
// Send user trailers and headers.
for i := 0; i < countListResponses; i++ {
stream.Send(&pb_testproto.PingResponse{Value: ping.Value, Counter: int32(i)})
}
return nil
}

View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -e
echo "" > coverage.txt
for d in $(go list ./... | grep -v vendor); do
echo -e "TESTS FOR: for \033[0;35m${d}\033[0m"
go test -race -v -coverprofile=profile.coverage.out -covermode=atomic $d
if [ -f profile.coverage.out ]; then
cat profile.coverage.out >> coverage.txt
rm profile.coverage.out
fi
echo ""
done

View file

@ -0,0 +1,27 @@
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.
package grpc_prometheus
import (
"strings"
"google.golang.org/grpc/codes"
)
var (
allCodes = []codes.Code{
codes.OK, codes.Canceled, codes.Unknown, codes.InvalidArgument, codes.DeadlineExceeded, codes.NotFound,
codes.AlreadyExists, codes.PermissionDenied, codes.Unauthenticated, codes.ResourceExhausted,
codes.FailedPrecondition, codes.Aborted, codes.OutOfRange, codes.Unimplemented, codes.Internal,
codes.Unavailable, codes.DataLoss,
}
)
func splitMethodName(fullMethodName string) (string, string) {
fullMethodName = strings.TrimPrefix(fullMethodName, "/") // remove leading slash
if i := strings.Index(fullMethodName, "/"); i >= 0 {
return fullMethodName[:i], fullMethodName[i+1:]
}
return "unknown", "unknown"
}

View file

@ -0,0 +1 @@
_output/

View file

@ -0,0 +1,32 @@
language: go
sudo: false
go:
- 1.7.3
- tip
go_import_path: github.com/grpc-ecosystem/grpc-gateway
cache:
directories:
- $HOME/local
- ${TRAVIS_BUILD_DIR}/examples/browser/node_modules
before_install:
- ./.travis/install-protoc.sh 3.1.0
- ./.travis/install-swagger-codegen.sh 2.1.6
- nvm install v6.1 && nvm use v6.1 && node --version
- go get github.com/golang/lint/golint
- go get github.com/dghubble/sling
install:
- go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
- go get github.com/grpc-ecosystem/grpc-gateway/runtime
- go get github.com/grpc-ecosystem/grpc-gateway/examples
- go get github.com/grpc-ecosystem/grpc-gateway/examples/server
before_script:
- sh -c 'cd examples/browser && npm install'
script:
- make realclean && make examples SWAGGER_CODEGEN="java -jar $HOME/local/swagger-codegen-cli.jar"
- if ! go version | grep devel; then test -z "$(git status --porcelain)" || (git status; git diff; exit 1); fi
- env GLOG_logtostderr=1 go test -race -v github.com/grpc-ecosystem/grpc-gateway/...
- make lint
- sh -c 'cd examples/browser && gulp'
env:
global:
- "PATH=$PATH:$HOME/local/bin"

View file

@ -0,0 +1,19 @@
#!/bin/sh -eu
protoc_version=$1
if test -z "${protoc_version}"; then
echo "Usage: .travis/install-protoc.sh protoc-version"
exit 1
fi
if [ "`$HOME/local/bin/protoc-${protoc_version} --version 2>/dev/null | cut -d' ' -f 2`" != ${protoc_version} ]; then
rm -rf $HOME/local/bin $HOME/local/include
mkdir -p $HOME/tmp $HOME/local
cd $HOME/tmp
wget https://github.com/google/protobuf/releases/download/v${protoc_version}/protoc-${protoc_version}-linux-x86_64.zip
unzip protoc-${protoc_version}-linux-x86_64.zip
mv bin $HOME/local/bin
mv include $HOME/local/include
fi
echo \$ $HOME/local/bin/protoc --version
$HOME/local/bin/protoc --version

View file

@ -0,0 +1,9 @@
#!/bin/sh -eu
codegen_version=$1
if test -z "${codegen_version}"; then
echo "Usage: .travis/install-swagger-codegen.sh codegen-version"
exit 1
fi
wget http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/${codegen_version}/swagger-codegen-cli-${codegen_version}.jar \
-O $HOME/local/swagger-codegen-cli.jar

View file

@ -0,0 +1,20 @@
# How to contribute
Thank you for your contribution to grpc-gateway.
Here's the recommended process of contribution.
1. `go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway`
2. `cd $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway`
3. hack, hack, hack...
4. Make sure that your change follows best practices in Go
* [Effective Go](https://golang.org/doc/effective_go.html)
* [Go Code Review Comments](https://golang.org/wiki/CodeReviewComments)
5. Make sure that `make test` passes.
6. Sign [a Contributor License Agreement](https://cla.developers.google.com/clas)
7. Open a pull request in Github
When you work on a larger contribution, it is also recommended that you get in touch
with us through the issue tracker.
### Code reviews
All submissions, including submissions by project members, require review.

View file

@ -0,0 +1,27 @@
Copyright (c) 2015, Gengo, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Gengo, Inc. nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

143
vendor/github.com/grpc-ecosystem/grpc-gateway/Makefile generated vendored Normal file
View file

@ -0,0 +1,143 @@
# This is a Makefile which maintains files automatically generated but to be
# shipped together with other files.
# You don't have to rebuild these targets by yourself unless you develop
# grpc-gateway itself.
PKG=github.com/grpc-ecosystem/grpc-gateway
GO_PLUGIN=bin/protoc-gen-go
GO_PLUGIN_PKG=github.com/golang/protobuf/protoc-gen-go
SWAGGER_PLUGIN=bin/protoc-gen-swagger
SWAGGER_PLUGIN_SRC= utilities/doc.go \
utilities/pattern.go \
utilities/trie.go \
protoc-gen-swagger/genswagger/generator.go \
protoc-gen-swagger/genswagger/template.go \
protoc-gen-swagger/main.go
SWAGGER_PLUGIN_PKG=$(PKG)/protoc-gen-swagger
GATEWAY_PLUGIN=bin/protoc-gen-grpc-gateway
GATEWAY_PLUGIN_PKG=$(PKG)/protoc-gen-grpc-gateway
GATEWAY_PLUGIN_SRC= utilities/doc.go \
utilities/pattern.go \
utilities/trie.go \
protoc-gen-grpc-gateway \
protoc-gen-grpc-gateway/descriptor \
protoc-gen-grpc-gateway/descriptor/registry.go \
protoc-gen-grpc-gateway/descriptor/services.go \
protoc-gen-grpc-gateway/descriptor/types.go \
protoc-gen-grpc-gateway/generator \
protoc-gen-grpc-gateway/generator/generator.go \
protoc-gen-grpc-gateway/gengateway \
protoc-gen-grpc-gateway/gengateway/doc.go \
protoc-gen-grpc-gateway/gengateway/generator.go \
protoc-gen-grpc-gateway/gengateway/template.go \
protoc-gen-grpc-gateway/httprule \
protoc-gen-grpc-gateway/httprule/compile.go \
protoc-gen-grpc-gateway/httprule/parse.go \
protoc-gen-grpc-gateway/httprule/types.go \
protoc-gen-grpc-gateway/main.go
GOOGLEAPIS_DIR=third_party/googleapis
OPTIONS_PROTO=$(GOOGLEAPIS_DIR)/google/api/annotations.proto $(GOOGLEAPIS_DIR)/google/api/http.proto
OPTIONS_GO=$(OPTIONS_PROTO:.proto=.pb.go)
OUTPUT_DIR=_output
RUNTIME_PROTO=runtime/internal/stream_chunk.proto
RUNTIME_GO=$(RUNTIME_PROTO:.proto=.pb.go)
PKGMAP=Mgoogle/protobuf/descriptor.proto=$(GO_PLUGIN_PKG)/descriptor,Mgoogle/api/annotations.proto=$(PKG)/$(GOOGLEAPIS_DIR)/google/api,Mexamples/sub/message.proto=$(PKG)/examples/sub
SWAGGER_EXAMPLES=examples/examplepb/echo_service.proto \
examples/examplepb/a_bit_of_everything.proto
EXAMPLES=examples/examplepb/echo_service.proto \
examples/examplepb/a_bit_of_everything.proto \
examples/examplepb/stream.proto \
examples/examplepb/flow_combination.proto
EXAMPLE_SVCSRCS=$(EXAMPLES:.proto=.pb.go)
EXAMPLE_GWSRCS=$(EXAMPLES:.proto=.pb.gw.go)
EXAMPLE_SWAGGERSRCS=$(EXAMPLES:.proto=.swagger.json)
EXAMPLE_DEPS=examples/sub/message.proto examples/sub2/message.proto
EXAMPLE_DEPSRCS=$(EXAMPLE_DEPS:.proto=.pb.go)
EXAMPLE_CLIENT_DIR=examples/clients
ECHO_EXAMPLE_SPEC=examples/examplepb/echo_service.swagger.json
ECHO_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/echo/EchoServiceApi.go \
$(EXAMPLE_CLIENT_DIR)/echo/ExamplepbSimpleMessage.go
ABE_EXAMPLE_SPEC=examples/examplepb/a_bit_of_everything.swagger.json
ABE_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/abe/ABitOfEverythingServiceApi.go \
$(EXAMPLE_CLIENT_DIR)/abe/ABitOfEverythingNested.go \
$(EXAMPLE_CLIENT_DIR)/abe/ExamplepbABitOfEverything.go \
$(EXAMPLE_CLIENT_DIR)/abe/ExamplepbNumericEnum.go \
$(EXAMPLE_CLIENT_DIR)/abe/ExamplepbIdMessage.go \
$(EXAMPLE_CLIENT_DIR)/abe/NestedDeepEnum.go \
$(EXAMPLE_CLIENT_DIR)/abe/ProtobufEmpty.go \
$(EXAMPLE_CLIENT_DIR)/abe/Sub2IdMessage.go \
$(EXAMPLE_CLIENT_DIR)/abe/SubStringMessage.go
EXAMPLE_CLIENT_SRCS=$(ECHO_EXAMPLE_SRCS) $(ABE_EXAMPLE_SRCS)
SWAGGER_CODEGEN=swagger-codegen
PROTOC_INC_PATH=$(dir $(shell which protoc))/../include
generate: $(OPTIONS_GO) $(RUNTIME_GO)
.SUFFIXES: .go .proto
$(GO_PLUGIN):
go get $(GO_PLUGIN_PKG)
go build -o $@ $(GO_PLUGIN_PKG)
$(OPTIONS_GO): $(OPTIONS_PROTO) $(GO_PLUGIN)
protoc -I $(PROTOC_INC_PATH) -I$(GOOGLEAPIS_DIR) --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP):$(GOOGLEAPIS_DIR) $(OPTIONS_PROTO)
$(RUNTIME_GO): $(RUNTIME_PROTO) $(GO_PLUGIN)
protoc -I $(PROTOC_INC_PATH) --plugin=$(GO_PLUGIN) -I. --go_out=$(PKGMAP):. $(RUNTIME_PROTO)
$(GATEWAY_PLUGIN): $(OPTIONS_GO) $(RUNTIME_GO) $(GATEWAY_PLUGIN_SRC)
go build -o $@ $(GATEWAY_PLUGIN_PKG)
$(SWAGGER_PLUGIN): $(OPTIONS_GO) $(SWAGGER_PLUGIN_SRC)
go build -o $@ $(SWAGGER_PLUGIN_PKG)
$(EXAMPLE_SVCSRCS): $(GO_PLUGIN) $(EXAMPLES)
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP),plugins=grpc:. $(EXAMPLES)
$(EXAMPLE_DEPSRCS): $(GO_PLUGIN) $(EXAMPLE_DEPS)
mkdir -p $(OUTPUT_DIR)
protoc -I $(PROTOC_INC_PATH) -I. --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP),plugins=grpc:$(OUTPUT_DIR) $(@:.pb.go=.proto)
cp $(OUTPUT_DIR)/$(PKG)/$@ $@ || cp $(OUTPUT_DIR)/$@ $@
$(EXAMPLE_GWSRCS): $(GATEWAY_PLUGIN) $(EXAMPLES)
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GATEWAY_PLUGIN) --grpc-gateway_out=logtostderr=true,$(PKGMAP):. $(EXAMPLES)
$(EXAMPLE_SWAGGERSRCS): $(SWAGGER_PLUGIN) $(SWAGGER_EXAMPLES)
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(SWAGGER_PLUGIN) --swagger_out=logtostderr=true,$(PKGMAP):. $(SWAGGER_EXAMPLES)
$(ECHO_EXAMPLE_SRCS): $(ECHO_EXAMPLE_SPEC)
$(SWAGGER_CODEGEN) generate -i $(ECHO_EXAMPLE_SPEC) \
-l go -o examples/clients --additional-properties packageName=echo
@rm -f $(EXAMPLE_CLIENT_DIR)/README.md $(EXAMPLE_CLIENT_DIR)/git_push.sh $(EXAMPLE_CLIENT_DIR)/.gitignore
$(ABE_EXAMPLE_SRCS): $(ABE_EXAMPLE_SPEC)
$(SWAGGER_CODEGEN) generate -i $(ABE_EXAMPLE_SPEC) \
-l go -o examples/clients --additional-properties packageName=abe
@rm -f $(EXAMPLE_CLIENT_DIR)/README.md $(EXAMPLE_CLIENT_DIR)/git_push.sh $(EXAMPLE_CLIENT_DIR)/.gitignore
examples: $(EXAMPLE_SVCSRCS) $(EXAMPLE_GWSRCS) $(EXAMPLE_DEPSRCS) $(EXAMPLE_SWAGGERSRCS) $(EXAMPLE_CLIENT_SRCS)
test: examples
go test -race $(PKG)/...
lint:
golint --set_exit_status $(PKG)/runtime
golint --set_exit_status $(PKG)/utilities/...
golint --set_exit_status $(PKG)/protoc-gen-grpc-gateway/...
golint --set_exit_status $(PKG)/protoc-gen-swagger/...
go vet $(PKG)/runtime || true
go vet $(PKG)/utilities/...
go vet $(PKG)/protoc-gen-grpc-gateway/...
go vet $(PKG)/protoc-gen-swagger/...
clean distclean:
rm -f $(GATEWAY_PLUGIN)
realclean: distclean
rm -f $(OPTIONS_GO)
rm -f $(EXAMPLE_SVCSRCS) $(EXAMPLE_DEPSRCS)
rm -f $(EXAMPLE_GWSRCS)
rm -f $(EXAMPLE_SWAGGERSRCS)
rm -f $(GO_PLUGIN)
rm -f $(SWAGGER_PLUGIN)
rm -f $(EXAMPLE_CLIENT_SRCS)
.PHONY: generate examples test lint clean distclean realclean

238
vendor/github.com/grpc-ecosystem/grpc-gateway/README.md generated vendored Normal file
View file

@ -0,0 +1,238 @@
# grpc-gateway
[![Build Status](https://travis-ci.org/grpc-ecosystem/grpc-gateway.svg?branch=master)](https://travis-ci.org/grpc-ecosystem/grpc-gateway)
grpc-gateway is a plugin of [protoc](http://github.com/google/protobuf).
It reads [gRPC](http://github.com/grpc/grpc-common) service definition,
and generates a reverse-proxy server which translates a RESTful JSON API into gRPC.
This server is generated according to [custom options](https://cloud.google.com/service-management/reference/rpc/google.api#http) in your gRPC definition.
It helps you to provide your APIs in both gRPC and RESTful style at the same time.
![architecture introduction diagram](https://docs.google.com/drawings/d/12hp4CPqrNPFhattL_cIoJptFvlAqm5wLQ0ggqI5mkCg/pub?w=749&amp;h=370)
## Background
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.
However, you might still want to provide classic RESTful APIs too for some reasons --
compatibility with languages not supported by gRPC, API backward-compatibility or aesthetics
of RESTful architecture.
That's what grpc-gateway helps you to do. You just need to implement your gRPC service with a small amount of custom options.
Then the reverse-proxy generated by grpc-gateway serves RESTful API on top of the gRPC service.
## Installation
First you need to install ProtocolBuffers 3.0.0-beta-3 or later.
```sh
mkdir tmp
cd tmp
git clone https://github.com/google/protobuf
cd protobuf
./autogen.sh
./configure
make
make check
sudo make install
```
Then, `go get -u` as usual.
```sh
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
go get -u github.com/golang/protobuf/protoc-gen-go
```
## Usage
Make sure that your `$GOPATH/bin` is in your `$PATH`.
1. Define your service in gRPC
your_service.proto:
```protobuf
syntax = "proto3";
package example;
message StringMessage {
string value = 1;
}
service YourService {
rpc Echo(StringMessage) returns (StringMessage) {}
}
```
2. Add a [custom option](https://cloud.google.com/service-management/reference/rpc/google.api#http) to the .proto file
your_service.proto:
```diff
syntax = "proto3";
package example;
+
+import "google/api/annotations.proto";
+
message StringMessage {
string value = 1;
}
service YourService {
- rpc Echo(StringMessage) returns (StringMessage) {}
+ rpc Echo(StringMessage) returns (StringMessage) {
+ option (google.api.http) = {
+ post: "/v1/example/echo"
+ body: "*"
+ };
+ }
}
```
3. Generate gRPC stub
```sh
protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--go_out=Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api,plugins=grpc:. \
path/to/your_service.proto
```
It will generate a stub file `path/to/your_service.pb.go`.
4. Implement your service in gRPC as usual
1. (Optional) Generate gRPC stub in the language you want.
e.g.
```sh
protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--ruby_out=. \
path/to/your/service_proto
protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--plugin=protoc-gen-grpc-ruby=grpc_ruby_plugin \
--grpc-ruby_out=. \
path/to/your/service.proto
```
2. Implement your service
5. Generate reverse-proxy
```sh
protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. \
path/to/your_service.proto
```
It will generate a reverse proxy `path/to/your_service.pb.gw.go`.
6. Write an entrypoint
Now you need to write an entrypoint of the proxy server.
```go
package main
import (
"flag"
"net/http"
"github.com/golang/glog"
"golang.org/x/net/context"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"google.golang.org/grpc"
gw "path/to/your_service_package"
)
var (
echoEndpoint = flag.String("echo_endpoint", "localhost:9090", "endpoint of YourService")
)
func run() error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts)
if err != nil {
return err
}
http.ListenAndServe(":8080", mux)
return nil
}
func main() {
flag.Parse()
defer glog.Flush()
if err := run(); err != nil {
glog.Fatal(err)
}
}
```
7. (Optional) Generate swagger definitions
```sh
protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--swagger_out=logtostderr=true:. \
path/to/your_service.proto
```
## Parameters and flags
`protoc-gen-grpc-gateway` supports custom mapping from Protobuf `import` to Golang import path.
They are compatible to [the parameters with same names in `protoc-gen-go`](https://github.com/golang/protobuf#parameters).
`protoc-gen-grpc-gateway` also supports some more command line flags to control logging. You can give these flags together with parameters above. Run `protoc-gen-grpc-gateway --help` for more details about the flags.
## More Examples
More examples are available under `examples` directory.
* `examplepb/echo_service.proto`, `examplepb/a_bit_of_everything.proto`: service definition
* `examplepb/echo_service.pb.go`, `examplepb/a_bit_of_everything.pb.go`: [generated] stub of the service
* `examplepb/echo_service.pb.gw.go`, `examplepb/a_bit_of_everything.pb.gw.go`: [generated] reverse proxy for the service
* `server/main.go`: service implementation
* `main.go`: entrypoint of the generated reverse proxy
To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), gRPC-gateway, and a gRPC server, see [this code example by CoreOS](https://github.com/philips/grpc-gateway-example/blob/master/cmd/serve.go) (and its accompanying [blog post](https://coreos.com/blog/gRPC-protobufs-swagger.html))
## Features
### Supported
* Generating JSON API handlers
* Method parameters in request body
* Method parameters in request path
* Method parameters in query string
* Mapping streaming APIs to JSON streams
* Mapping HTTP headers with `Grpc-Metadata-` prefix to gRPC metadata
* Optionally emitting API definition for [Swagger](http://swagger.io).
* Setting [gRPC timeouts](http://www.grpc.io/docs/guides/wire.html) through inbound HTTP `Grpc-Timeout` header.
### Want to support
But not yet.
* bytes and enum fields in path parameter. #5
* Optionally generating the entrypoint. #8
* `import_path` parameter
### No plan to support
But patch is welcome.
* Method parameters in HTTP headers
* Handling trailer metadata
* Encoding request/response body in XML
* True bi-directional streaming. (Probably impossible?)
# Mapping gRPC to HTTP
* [How gRPC error codes map to HTTP status codes in the response](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/runtime/errors.go#L15)
* HTTP request source IP is added as `X-Forwarded-For` gRPC request header
* HTTP request host is added as `X-Forwarded-Host` gRPC request header
* HTTP `Authorization` header is added as `authorization` gRPC request header
* Remaining HTTP header keys are prefixed with `Grpc-Metadata-` and added with their values to gRPC request header
# Contribution
See [CONTRIBUTING.md](http://github.com/grpc-ecosystem/grpc-gateway/blob/master/CONTRIBUTING.md).
# License
grpc-gateway is licensed under the BSD 3-Clause License.
See [LICENSE.txt](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt) for more details.

View file

@ -0,0 +1,3 @@
/protoc-gen-go
/protoc-gen-grpc-gateway
/protoc-gen-swagger

View file

@ -0,0 +1,2 @@
/bower_components
/node_modules

View file

@ -0,0 +1,31 @@
# Browser example
This directory contains an example use of grpc-gateway with web browsers.
The following commands automatically runs integration tests with phantomjs.
```shell-session
$ npm install -g gulp-cli
$ npm install
$ gulp
```
## Other examples
### Very simple example
Run
```shell-session
$ gulp bower
$ gulp backends
```
then, open `index.html`.
### Integration test with your browser
Run
```shell-session
$ gulp serve
```
then, open `http://localhost:8000` with your browser.

View file

@ -0,0 +1,185 @@
'use strict';
var SwaggerClient = require('swagger-client');
describe('ABitOfEverythingService', function() {
var client;
beforeEach(function(done) {
new SwaggerClient({
url: "http://localhost:8080/swagger/a_bit_of_everything.swagger.json",
usePromise: true,
}).then(function(c) {
client = c;
}).catch(function(err) {
done.fail(err);
}).then(done);
});
describe('Create', function() {
var created;
var expected = {
float_value: 1.5,
double_value: 2.5,
int64_value: "4294967296",
uint64_value: "9223372036854775807",
int32_value: -2147483648,
fixed64_value: "9223372036854775807",
fixed32_value: 4294967295,
bool_value: true,
string_value: "strprefix/foo",
uint32_value: 4294967295,
sfixed32_value: 2147483647,
sfixed64_value: "-4611686018427387904",
sint32_value: 2147483647,
sint64_value: "4611686018427387903",
nonConventionalNameValue: "camelCase",
};
beforeEach(function(done) {
client.ABitOfEverythingService.Create(expected).then(function(resp) {
created = resp.obj;
}).catch(function(err) {
done.fail(err);
}).then(done);
});
it('should assign id', function() {
expect(created.uuid).not.toBe("");
});
it('should echo the request back', function() {
delete created.uuid;
expect(created).toEqual(expected);
});
});
describe('CreateBody', function() {
var created;
var expected = {
float_value: 1.5,
double_value: 2.5,
int64_value: "4294967296",
uint64_value: "9223372036854775807",
int32_value: -2147483648,
fixed64_value: "9223372036854775807",
fixed32_value: 4294967295,
bool_value: true,
string_value: "strprefix/foo",
uint32_value: 4294967295,
sfixed32_value: 2147483647,
sfixed64_value: "-4611686018427387904",
sint32_value: 2147483647,
sint64_value: "4611686018427387903",
nonConventionalNameValue: "camelCase",
nested: [
{ name: "bar", amount: 10 },
{ name: "baz", amount: 20 },
],
repeated_string_value: ["a", "b", "c"],
oneof_string: "x",
// TODO(yugui) Support enum by name
map_value: { a: 1, b: 2 },
mapped_string_value: { a: "x", b: "y" },
mapped_nested_value: {
a: { name: "x", amount: 1 },
b: { name: "y", amount: 2 },
},
};
beforeEach(function(done) {
client.ABitOfEverythingService.CreateBody({
body: expected,
}).then(function(resp) {
created = resp.obj;
}).catch(function(err) {
done.fail(err);
}).then(done);
});
it('should assign id', function() {
expect(created.uuid).not.toBe("");
});
it('should echo the request back', function() {
delete created.uuid;
expect(created).toEqual(expected);
});
});
describe('lookup', function() {
var created;
var expected = {
bool_value: true,
string_value: "strprefix/foo",
};
beforeEach(function(done) {
client.ABitOfEverythingService.CreateBody({
body: expected,
}).then(function(resp) {
created = resp.obj;
}).catch(function(err) {
fail(err);
}).finally(done);
});
it('should look up an object by uuid', function(done) {
client.ABitOfEverythingService.Lookup({
uuid: created.uuid
}).then(function(resp) {
expect(resp.obj).toEqual(created);
}).catch(function(err) {
fail(err.errObj);
}).finally(done);
});
it('should fail if no such object', function(done) {
client.ABitOfEverythingService.Lookup({
uuid: 'not_exist',
}).then(function(resp) {
fail('expected failure but succeeded');
}).catch(function(err) {
expect(err.status).toBe(404);
}).finally(done);
});
});
describe('Delete', function() {
var created;
var expected = {
bool_value: true,
string_value: "strprefix/foo",
};
beforeEach(function(done) {
client.ABitOfEverythingService.CreateBody({
body: expected,
}).then(function(resp) {
created = resp.obj;
}).catch(function(err) {
fail(err);
}).finally(done);
});
it('should delete an object by id', function(done) {
client.ABitOfEverythingService.Delete({
uuid: created.uuid
}).then(function(resp) {
expect(resp.obj).toEqual({});
}).catch(function(err) {
fail(err.errObj);
}).then(function() {
return client.ABitOfEverythingService.Lookup({
uuid: created.uuid
});
}).then(function(resp) {
fail('expected failure but succeeded');
}). catch(function(err) {
expect(err.status).toBe(404);
}).finally(done);
});
});
});

View file

@ -0,0 +1,2 @@
/*
!/.gitignore

View file

@ -0,0 +1,21 @@
{
"name": "grpc-gateway-example-browser",
"description": "Example use of grpc-gateway from browser",
"main": "index.js",
"authors": [
"Yuki Yugui Sonoda <yugui@gengo.com>"
],
"license": "SEE LICENSE IN LICENSE file",
"homepage": "https://github.com/grpc-ecosystem/grpc-gateway",
"private": true,
"dependencies": {
"swagger-js": "~> 2.1"
},
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}

View file

@ -0,0 +1,43 @@
'use strict';
var SwaggerClient = require('swagger-client');
describe('EchoService', function() {
var client;
beforeEach(function(done) {
new SwaggerClient({
url: "http://localhost:8080/swagger/echo_service.swagger.json",
usePromise: true,
}).then(function(c) {
client = c;
done();
});
});
describe('Echo', function() {
it('should echo the request back', function(done) {
client.EchoService.Echo(
{id: "foo"},
{responseContentType: "application/json"}
).then(function(resp) {
expect(resp.obj).toEqual({id: "foo"});
}).catch(function(err) {
done.fail(err);
}).then(done);
});
});
describe('EchoBody', function() {
it('should echo the request back', function(done) {
client.EchoService.EchoBody(
{body: {id: "foo"}},
{responseContentType: "application/json"}
).then(function(resp) {
expect(resp.obj).toEqual({id: "foo"});
}).catch(function(err) {
done.fail(err);
}).then(done);
});
});
});

View file

@ -0,0 +1,81 @@
"use strict";
var gulp = require('gulp');
var path = require('path');
var bower = require('gulp-bower');
var exit = require('gulp-exit');
var gprocess = require('gulp-process');
var shell = require('gulp-shell');
var jasmineBrowser = require('gulp-jasmine-browser');
var webpack = require('webpack-stream');
gulp.task('bower', function(){
return bower();
});
gulp.task('server', shell.task([
'go build -o bin/example-server github.com/grpc-ecosystem/grpc-gateway/examples/server/cmd/example-server',
]));
gulp.task('gateway', shell.task([
'go build -o bin/example-gw github.com/grpc-ecosystem/grpc-gateway/examples',
]));
gulp.task('serve-server', ['server'], function(){
gprocess.start('server-server', 'bin/example-server', [
'--logtostderr',
]);
gulp.watch('bin/example-server', ['serve-server']);
});
gulp.task('serve-gateway', ['gateway', 'serve-server'], function(){
gprocess.start('gateway-server', 'bin/example-gw', [
'--logtostderr', '--swagger_dir', path.join(__dirname, "../examplepb"),
]);
gulp.watch('bin/example-gateway', ['serve-gateway']);
});
gulp.task('backends', ['serve-gateway', 'serve-server']);
var specFiles = ['*.spec.js'];
gulp.task('test', ['backends'], function(done) {
return gulp.src(specFiles)
.pipe(webpack({output: {filename: 'spec.js'}}))
.pipe(jasmineBrowser.specRunner({
console: true,
sourceMappedStacktrace: true,
}))
.pipe(jasmineBrowser.headless({
findOpenPort: true,
catch: true,
throwFailures: true,
}))
.on('error', function(err) {
done(err);
process.exit(1);
})
.pipe(exit());
});
gulp.task('serve', ['backends'], function(done) {
var JasminePlugin = require('gulp-jasmine-browser/webpack/jasmine-plugin');
var plugin = new JasminePlugin();
return gulp.src(specFiles)
.pipe(webpack({
output: {filename: 'spec.js'},
watch: true,
plugins: [plugin],
}))
.pipe(jasmineBrowser.specRunner({
sourceMappedStacktrace: true,
}))
.pipe(jasmineBrowser.server({
port: 8000,
whenReady: plugin.whenReady,
}));
});
gulp.task('default', ['test']);

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<script type="application/javascript" src="bower_components/swagger-js/browser/swagger-client.min.js"></script>
<script type="application/javascript">
window.client = new SwaggerClient({
url: "http://localhost:8080/swagger/echo_service.swagger.json",
success: function() {
client.EchoService.Echo(
{id: "foo"},
{responseContentType: "application/json"},
function(data) {
document.getElementById("echoBack").innerHTML = data.obj.id;
});
}
})
</script>
</head>
<body>
<div id="echoBack"></div>
</body>
</html>

View file

@ -0,0 +1,23 @@
{
"name": "grpc-gateway-example",
"version": "1.0.0",
"description": "Example use of grpc-gateway from browser",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "SEE LICENSE IN LICENSE.txt",
"devDependencies": {
"bower": "^1.7.9",
"gulp": "^3.9.1",
"gulp-bower": "0.0.13",
"gulp-exit": "0.0.2",
"gulp-jasmine-browser": "^1.3.2",
"gulp-process": "^0.1.2",
"gulp-shell": "^0.5.2",
"jasmine": "^2.4.1",
"phantomjs": "^2.1.7",
"swagger-client": "^2.1.14",
"webpack-stream": "^3.2.0"
}
}

View file

@ -0,0 +1,162 @@
package main
import (
"reflect"
"testing"
"github.com/grpc-ecosystem/grpc-gateway/examples/clients/abe"
"github.com/grpc-ecosystem/grpc-gateway/examples/clients/echo"
)
func TestClientIntegration(t *testing.T) {
}
func TestEchoClient(t *testing.T) {
if testing.Short() {
t.Skip()
return
}
cl := echo.NewEchoServiceApiWithBasePath("http://localhost:8080")
resp, err := cl.Echo("foo")
if err != nil {
t.Errorf(`cl.Echo("foo") failed with %v; want success`, err)
}
if got, want := resp.Id, "foo"; got != want {
t.Errorf("resp.Id = %q; want %q", got, want)
}
}
func TestEchoBodyClient(t *testing.T) {
if testing.Short() {
t.Skip()
return
}
cl := echo.NewEchoServiceApiWithBasePath("http://localhost:8080")
req := echo.ExamplepbSimpleMessage{Id: "foo"}
resp, err := cl.EchoBody(req)
if err != nil {
t.Errorf("cl.EchoBody(%#v) failed with %v; want success", req, err)
}
if got, want := resp.Id, "foo"; got != want {
t.Errorf("resp.Id = %q; want %q", got, want)
}
}
func TestAbitOfEverythingClient(t *testing.T) {
if testing.Short() {
t.Skip()
return
}
cl := abe.NewABitOfEverythingServiceApiWithBasePath("http://localhost:8080")
testABEClientCreate(t, cl)
testABEClientCreateBody(t, cl)
}
func testABEClientCreate(t *testing.T, cl *abe.ABitOfEverythingServiceApi) {
want := abe.ExamplepbABitOfEverything{
FloatValue: 1.5,
DoubleValue: 2.5,
Int64Value: "4294967296",
Uint64Value: "9223372036854775807",
Int32Value: -2147483648,
Fixed64Value: "9223372036854775807",
Fixed32Value: 4294967295,
BoolValue: true,
StringValue: "strprefix/foo",
Uint32Value: 4294967295,
Sfixed32Value: 2147483647,
Sfixed64Value: "-4611686018427387904",
Sint32Value: 2147483647,
Sint64Value: "4611686018427387903",
NonConventionalNameValue: "camelCase",
}
resp, err := cl.Create(
want.FloatValue,
want.DoubleValue,
want.Int64Value,
want.Uint64Value,
want.Int32Value,
want.Fixed64Value,
want.Fixed32Value,
want.BoolValue,
want.StringValue,
want.Uint32Value,
want.Sfixed32Value,
want.Sfixed64Value,
want.Sint32Value,
want.Sint64Value,
want.NonConventionalNameValue,
)
if err != nil {
t.Errorf("cl.Create(%#v) failed with %v; want success", want, err)
}
if resp.Uuid == "" {
t.Errorf("resp.Uuid is empty; want not empty")
}
resp.Uuid = ""
if got := resp; !reflect.DeepEqual(got, want) {
t.Errorf("resp = %#v; want %#v", got, want)
}
}
func testABEClientCreateBody(t *testing.T, cl *abe.ABitOfEverythingServiceApi) {
t.Log("TODO: support enum")
return
want := abe.ExamplepbABitOfEverything{
FloatValue: 1.5,
DoubleValue: 2.5,
Int64Value: "4294967296",
Uint64Value: "9223372036854775807",
Int32Value: -2147483648,
Fixed64Value: "9223372036854775807",
Fixed32Value: 4294967295,
BoolValue: true,
StringValue: "strprefix/foo",
Uint32Value: 4294967295,
Sfixed32Value: 2147483647,
Sfixed64Value: "-4611686018427387904",
Sint32Value: 2147483647,
Sint64Value: "4611686018427387903",
NonConventionalNameValue: "camelCase",
Nested: []abe.ABitOfEverythingNested{
{
Name: "bar",
Amount: 10,
},
{
Name: "baz",
Amount: 20,
},
},
RepeatedStringValue: []string{"a", "b", "c"},
OneofString: "x",
MapValue: map[string]abe.ExamplepbNumericEnum{
// "a": abe.ExamplepbNumericEnum_ONE,
// "b": abe.ExamplepbNumericEnum_ZERO,
},
MappedStringValue: map[string]string{
"a": "x",
"b": "y",
},
MappedNestedValue: map[string]abe.ABitOfEverythingNested{
"a": {Name: "x", Amount: 1},
"b": {Name: "y", Amount: 2},
},
}
resp, err := cl.CreateBody(want)
if err != nil {
t.Errorf("cl.CreateBody(%#v) failed with %v; want success", want, err)
}
if resp.Uuid == "" {
t.Errorf("resp.Uuid is empty; want not empty")
}
resp.Uuid = ""
if got := resp; !reflect.DeepEqual(got, want) {
t.Errorf("resp = %#v; want %#v", got, want)
}
}

View file

@ -0,0 +1,11 @@
package abe
import (
)
type ABitOfEverythingNested struct {
Amount int64 `json:"amount,omitempty"`
Name string `json:"name,omitempty"`
Ok NestedDeepEnum `json:"ok,omitempty"`
}

View file

@ -0,0 +1,648 @@
package abe
import (
"strings"
"fmt"
"encoding/json"
"errors"
"github.com/dghubble/sling"
)
type ABitOfEverythingServiceApi struct {
basePath string
}
func NewABitOfEverythingServiceApi() *ABitOfEverythingServiceApi{
return &ABitOfEverythingServiceApi {
basePath: "http://localhost",
}
}
func NewABitOfEverythingServiceApiWithBasePath(basePath string) *ABitOfEverythingServiceApi{
return &ABitOfEverythingServiceApi {
basePath: basePath,
}
}
/**
*
*
* @param floatValue
* @param doubleValue
* @param int64Value
* @param uint64Value
* @param int32Value
* @param fixed64Value
* @param fixed32Value
* @param boolValue
* @param stringValue
* @param uint32Value
* @param sfixed32Value
* @param sfixed64Value
* @param sint32Value
* @param sint64Value
* @param nonConventionalNameValue
* @return ExamplepbABitOfEverything
*/
//func (a ABitOfEverythingServiceApi) Create (floatValue float32, doubleValue float64, int64Value string, uint64Value string, int32Value int32, fixed64Value string, fixed32Value int64, boolValue bool, stringValue string, uint32Value int64, sfixed32Value int32, sfixed64Value string, sint32Value int32, sint64Value string, nonConventionalNameValue string) (ExamplepbABitOfEverything, error) {
func (a ABitOfEverythingServiceApi) Create (floatValue float32, doubleValue float64, int64Value string, uint64Value string, int32Value int32, fixed64Value string, fixed32Value int64, boolValue bool, stringValue string, uint32Value int64, sfixed32Value int32, sfixed64Value string, sint32Value int32, sint64Value string, nonConventionalNameValue string) (ExamplepbABitOfEverything, error) {
_sling := sling.New().Post(a.basePath)
// create path and map variables
path := "/v1/example/a_bit_of_everything/{float_value}/{double_value}/{int64_value}/separator/{uint64_value}/{int32_value}/{fixed64_value}/{fixed32_value}/{bool_value}/{string_value}/{uint32_value}/{sfixed32_value}/{sfixed64_value}/{sint32_value}/{sint64_value}/{nonConventionalNameValue}"
path = strings.Replace(path, "{" + "float_value" + "}", fmt.Sprintf("%v", floatValue), -1)
path = strings.Replace(path, "{" + "double_value" + "}", fmt.Sprintf("%v", doubleValue), -1)
path = strings.Replace(path, "{" + "int64_value" + "}", fmt.Sprintf("%v", int64Value), -1)
path = strings.Replace(path, "{" + "uint64_value" + "}", fmt.Sprintf("%v", uint64Value), -1)
path = strings.Replace(path, "{" + "int32_value" + "}", fmt.Sprintf("%v", int32Value), -1)
path = strings.Replace(path, "{" + "fixed64_value" + "}", fmt.Sprintf("%v", fixed64Value), -1)
path = strings.Replace(path, "{" + "fixed32_value" + "}", fmt.Sprintf("%v", fixed32Value), -1)
path = strings.Replace(path, "{" + "bool_value" + "}", fmt.Sprintf("%v", boolValue), -1)
path = strings.Replace(path, "{" + "string_value" + "}", fmt.Sprintf("%v", stringValue), -1)
path = strings.Replace(path, "{" + "uint32_value" + "}", fmt.Sprintf("%v", uint32Value), -1)
path = strings.Replace(path, "{" + "sfixed32_value" + "}", fmt.Sprintf("%v", sfixed32Value), -1)
path = strings.Replace(path, "{" + "sfixed64_value" + "}", fmt.Sprintf("%v", sfixed64Value), -1)
path = strings.Replace(path, "{" + "sint32_value" + "}", fmt.Sprintf("%v", sint32Value), -1)
path = strings.Replace(path, "{" + "sint64_value" + "}", fmt.Sprintf("%v", sint64Value), -1)
path = strings.Replace(path, "{" + "nonConventionalNameValue" + "}", fmt.Sprintf("%v", nonConventionalNameValue), -1)
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
var successPayload = new(ExamplepbABitOfEverything)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
*
*
* @param body
* @return ExamplepbABitOfEverything
*/
//func (a ABitOfEverythingServiceApi) CreateBody (body ExamplepbABitOfEverything) (ExamplepbABitOfEverything, error) {
func (a ABitOfEverythingServiceApi) CreateBody (body ExamplepbABitOfEverything) (ExamplepbABitOfEverything, error) {
_sling := sling.New().Post(a.basePath)
// create path and map variables
path := "/v1/example/a_bit_of_everything"
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
// body params
_sling = _sling.BodyJSON(body)
var successPayload = new(ExamplepbABitOfEverything)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
*
*
* @param singleNestedName
* @param body
* @return ExamplepbABitOfEverything
*/
//func (a ABitOfEverythingServiceApi) DeepPathEcho (singleNestedName string, body ExamplepbABitOfEverything) (ExamplepbABitOfEverything, error) {
func (a ABitOfEverythingServiceApi) DeepPathEcho (singleNestedName string, body ExamplepbABitOfEverything) (ExamplepbABitOfEverything, error) {
_sling := sling.New().Post(a.basePath)
// create path and map variables
path := "/v1/example/a_bit_of_everything/{single_nested.name}"
path = strings.Replace(path, "{" + "single_nested.name" + "}", fmt.Sprintf("%v", singleNestedName), -1)
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
// body params
_sling = _sling.BodyJSON(body)
var successPayload = new(ExamplepbABitOfEverything)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
*
*
* @param uuid
* @return ProtobufEmpty
*/
//func (a ABitOfEverythingServiceApi) Delete (uuid string) (ProtobufEmpty, error) {
func (a ABitOfEverythingServiceApi) Delete (uuid string) (ProtobufEmpty, error) {
_sling := sling.New().Delete(a.basePath)
// create path and map variables
path := "/v1/example/a_bit_of_everything/{uuid}"
path = strings.Replace(path, "{" + "uuid" + "}", fmt.Sprintf("%v", uuid), -1)
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
var successPayload = new(ProtobufEmpty)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
*
*
* @param value
* @return SubStringMessage
*/
//func (a ABitOfEverythingServiceApi) Echo (value string) (SubStringMessage, error) {
func (a ABitOfEverythingServiceApi) Echo (value string) (SubStringMessage, error) {
_sling := sling.New().Get(a.basePath)
// create path and map variables
path := "/v1/example/a_bit_of_everything/echo/{value}"
path = strings.Replace(path, "{" + "value" + "}", fmt.Sprintf("%v", value), -1)
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
var successPayload = new(SubStringMessage)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
*
*
* @return SubStringMessage
*/
//func (a ABitOfEverythingServiceApi) Echo_1 () (SubStringMessage, error) {
func (a ABitOfEverythingServiceApi) Echo_1 () (SubStringMessage, error) {
_sling := sling.New().Get(a.basePath)
// create path and map variables
path := "/v2/example/echo"
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
var successPayload = new(SubStringMessage)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
*
*
* @param body
* @return SubStringMessage
*/
//func (a ABitOfEverythingServiceApi) Echo_2 (body string) (SubStringMessage, error) {
func (a ABitOfEverythingServiceApi) Echo_2 (body string) (SubStringMessage, error) {
_sling := sling.New().Post(a.basePath)
// create path and map variables
path := "/v2/example/echo"
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
// body params
_sling = _sling.BodyJSON(body)
var successPayload = new(SubStringMessage)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
*
*
* @param uuid
* @return ExamplepbABitOfEverything
*/
//func (a ABitOfEverythingServiceApi) Lookup (uuid string) (ExamplepbABitOfEverything, error) {
func (a ABitOfEverythingServiceApi) Lookup (uuid string) (ExamplepbABitOfEverything, error) {
_sling := sling.New().Get(a.basePath)
// create path and map variables
path := "/v1/example/a_bit_of_everything/{uuid}"
path = strings.Replace(path, "{" + "uuid" + "}", fmt.Sprintf("%v", uuid), -1)
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
var successPayload = new(ExamplepbABitOfEverything)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
*
*
* @return ProtobufEmpty
*/
//func (a ABitOfEverythingServiceApi) Timeout () (ProtobufEmpty, error) {
func (a ABitOfEverythingServiceApi) Timeout () (ProtobufEmpty, error) {
_sling := sling.New().Get(a.basePath)
// create path and map variables
path := "/v2/example/timeout"
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
var successPayload = new(ProtobufEmpty)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
*
*
* @param uuid
* @param body
* @return ProtobufEmpty
*/
//func (a ABitOfEverythingServiceApi) Update (uuid string, body ExamplepbABitOfEverything) (ProtobufEmpty, error) {
func (a ABitOfEverythingServiceApi) Update (uuid string, body ExamplepbABitOfEverything) (ProtobufEmpty, error) {
_sling := sling.New().Put(a.basePath)
// create path and map variables
path := "/v1/example/a_bit_of_everything/{uuid}"
path = strings.Replace(path, "{" + "uuid" + "}", fmt.Sprintf("%v", uuid), -1)
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
// body params
_sling = _sling.BodyJSON(body)
var successPayload = new(ProtobufEmpty)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}

View file

@ -0,0 +1,35 @@
package abe
import (
"time"
)
type ExamplepbABitOfEverything struct {
BoolValue bool `json:"bool_value,omitempty"`
DoubleValue float64 `json:"double_value,omitempty"`
EnumValue ExamplepbNumericEnum `json:"enum_value,omitempty"`
Fixed32Value int64 `json:"fixed32_value,omitempty"`
Fixed64Value string `json:"fixed64_value,omitempty"`
FloatValue float32 `json:"float_value,omitempty"`
Int32Value int32 `json:"int32_value,omitempty"`
Int64Value string `json:"int64_value,omitempty"`
MapValue map[string]ExamplepbNumericEnum `json:"map_value,omitempty"`
MappedNestedValue map[string]ABitOfEverythingNested `json:"mapped_nested_value,omitempty"`
MappedStringValue map[string]string `json:"mapped_string_value,omitempty"`
Nested []ABitOfEverythingNested `json:"nested,omitempty"`
NonConventionalNameValue string `json:"nonConventionalNameValue,omitempty"`
OneofEmpty ProtobufEmpty `json:"oneof_empty,omitempty"`
OneofString string `json:"oneof_string,omitempty"`
RepeatedStringValue []string `json:"repeated_string_value,omitempty"`
Sfixed32Value int32 `json:"sfixed32_value,omitempty"`
Sfixed64Value string `json:"sfixed64_value,omitempty"`
SingleNested ABitOfEverythingNested `json:"single_nested,omitempty"`
Sint32Value int32 `json:"sint32_value,omitempty"`
Sint64Value string `json:"sint64_value,omitempty"`
StringValue string `json:"string_value,omitempty"`
TimestampValue time.Time `json:"timestamp_value,omitempty"`
Uint32Value int64 `json:"uint32_value,omitempty"`
Uint64Value string `json:"uint64_value,omitempty"`
Uuid string `json:"uuid,omitempty"`
}

View file

@ -0,0 +1,8 @@
package abe
import (
)
type ExamplepbNumericEnum struct {
}

View file

@ -0,0 +1,8 @@
package abe
import (
)
type NestedDeepEnum struct {
}

View file

@ -0,0 +1,8 @@
package abe
import (
)
type ProtobufEmpty struct {
}

View file

@ -0,0 +1,9 @@
package abe
import (
)
type Sub2IdMessage struct {
Uuid string `json:"uuid,omitempty"`
}

View file

@ -0,0 +1,9 @@
package abe
import (
)
type SubStringMessage struct {
Value string `json:"value,omitempty"`
}

View file

@ -0,0 +1,145 @@
package echo
import (
"strings"
"fmt"
"encoding/json"
"errors"
"github.com/dghubble/sling"
)
type EchoServiceApi struct {
basePath string
}
func NewEchoServiceApi() *EchoServiceApi{
return &EchoServiceApi {
basePath: "http://localhost",
}
}
func NewEchoServiceApiWithBasePath(basePath string) *EchoServiceApi{
return &EchoServiceApi {
basePath: basePath,
}
}
/**
* Echo method receives a simple message and returns it.
* The message posted as the id parameter will also be\nreturned.
* @param id
* @return ExamplepbSimpleMessage
*/
//func (a EchoServiceApi) Echo (id string) (ExamplepbSimpleMessage, error) {
func (a EchoServiceApi) Echo (id string) (ExamplepbSimpleMessage, error) {
_sling := sling.New().Post(a.basePath)
// create path and map variables
path := "/v1/example/echo/{id}"
path = strings.Replace(path, "{" + "id" + "}", fmt.Sprintf("%v", id), -1)
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
var successPayload = new(ExamplepbSimpleMessage)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}
/**
* EchoBody method receives a simple message and returns it.
*
* @param body
* @return ExamplepbSimpleMessage
*/
//func (a EchoServiceApi) EchoBody (body ExamplepbSimpleMessage) (ExamplepbSimpleMessage, error) {
func (a EchoServiceApi) EchoBody (body ExamplepbSimpleMessage) (ExamplepbSimpleMessage, error) {
_sling := sling.New().Post(a.basePath)
// create path and map variables
path := "/v1/example/echo_body"
_sling = _sling.Path(path)
// accept header
accepts := []string { "application/json" }
for key := range accepts {
_sling = _sling.Set("Accept", accepts[key])
break // only use the first Accept
}
// body params
_sling = _sling.BodyJSON(body)
var successPayload = new(ExamplepbSimpleMessage)
// We use this map (below) so that any arbitrary error JSON can be handled.
// FIXME: This is in the absence of this Go generator honoring the non-2xx
// response (error) models, which needs to be implemented at some point.
var failurePayload map[string]interface{}
httpResponse, err := _sling.Receive(successPayload, &failurePayload)
if err == nil {
// err == nil only means that there wasn't a sub-application-layer error (e.g. no network error)
if failurePayload != nil {
// If the failurePayload is present, there likely was some kind of non-2xx status
// returned (and a JSON payload error present)
var str []byte
str, err = json.Marshal(failurePayload)
if err == nil { // For safety, check for an error marshalling... probably superfluous
// This will return the JSON error body as a string
err = errors.New(string(str))
}
} else {
// So, there was no network-type error, and nothing in the failure payload,
// but we should still check the status code
if httpResponse == nil {
// This should never happen...
err = errors.New("No HTTP Response received.")
} else if code := httpResponse.StatusCode; 200 > code || code > 299 {
err = errors.New("HTTP Error: " + string(httpResponse.StatusCode))
}
}
}
return *successPayload, err
}

View file

@ -0,0 +1,9 @@
package echo
import (
)
type ExamplepbSimpleMessage struct {
Id string `json:"id,omitempty"`
}

View file

@ -0,0 +1,664 @@
// Code generated by protoc-gen-go.
// source: examples/examplepb/a_bit_of_everything.proto
// DO NOT EDIT!
package examplepb
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"
import google_protobuf1 "github.com/golang/protobuf/ptypes/empty"
import grpc_gateway_examples_sub "github.com/grpc-ecosystem/grpc-gateway/examples/sub"
import sub2 "github.com/grpc-ecosystem/grpc-gateway/examples/sub2"
import google_protobuf2 "github.com/golang/protobuf/ptypes/timestamp"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// NumericEnum is one or zero.
type NumericEnum int32
const (
// ZERO means 0
NumericEnum_ZERO NumericEnum = 0
// ONE means 1
NumericEnum_ONE NumericEnum = 1
)
var NumericEnum_name = map[int32]string{
0: "ZERO",
1: "ONE",
}
var NumericEnum_value = map[string]int32{
"ZERO": 0,
"ONE": 1,
}
func (x NumericEnum) String() string {
return proto.EnumName(NumericEnum_name, int32(x))
}
func (NumericEnum) EnumDescriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
// DeepEnum is one or zero.
type ABitOfEverything_Nested_DeepEnum int32
const (
// FALSE is false.
ABitOfEverything_Nested_FALSE ABitOfEverything_Nested_DeepEnum = 0
// TRUE is true.
ABitOfEverything_Nested_TRUE ABitOfEverything_Nested_DeepEnum = 1
)
var ABitOfEverything_Nested_DeepEnum_name = map[int32]string{
0: "FALSE",
1: "TRUE",
}
var ABitOfEverything_Nested_DeepEnum_value = map[string]int32{
"FALSE": 0,
"TRUE": 1,
}
func (x ABitOfEverything_Nested_DeepEnum) String() string {
return proto.EnumName(ABitOfEverything_Nested_DeepEnum_name, int32(x))
}
func (ABitOfEverything_Nested_DeepEnum) EnumDescriptor() ([]byte, []int) {
return fileDescriptor1, []int{0, 0, 0}
}
// Intentionaly complicated message type to cover much features of Protobuf.
// NEXT ID: 27
type ABitOfEverything struct {
SingleNested *ABitOfEverything_Nested `protobuf:"bytes,25,opt,name=single_nested,json=singleNested" json:"single_nested,omitempty"`
Uuid string `protobuf:"bytes,1,opt,name=uuid" json:"uuid,omitempty"`
Nested []*ABitOfEverything_Nested `protobuf:"bytes,2,rep,name=nested" json:"nested,omitempty"`
FloatValue float32 `protobuf:"fixed32,3,opt,name=float_value,json=floatValue" json:"float_value,omitempty"`
DoubleValue float64 `protobuf:"fixed64,4,opt,name=double_value,json=doubleValue" json:"double_value,omitempty"`
Int64Value int64 `protobuf:"varint,5,opt,name=int64_value,json=int64Value" json:"int64_value,omitempty"`
Uint64Value uint64 `protobuf:"varint,6,opt,name=uint64_value,json=uint64Value" json:"uint64_value,omitempty"`
Int32Value int32 `protobuf:"varint,7,opt,name=int32_value,json=int32Value" json:"int32_value,omitempty"`
Fixed64Value uint64 `protobuf:"fixed64,8,opt,name=fixed64_value,json=fixed64Value" json:"fixed64_value,omitempty"`
Fixed32Value uint32 `protobuf:"fixed32,9,opt,name=fixed32_value,json=fixed32Value" json:"fixed32_value,omitempty"`
BoolValue bool `protobuf:"varint,10,opt,name=bool_value,json=boolValue" json:"bool_value,omitempty"`
StringValue string `protobuf:"bytes,11,opt,name=string_value,json=stringValue" json:"string_value,omitempty"`
// TODO(yugui) add bytes_value
Uint32Value uint32 `protobuf:"varint,13,opt,name=uint32_value,json=uint32Value" json:"uint32_value,omitempty"`
EnumValue NumericEnum `protobuf:"varint,14,opt,name=enum_value,json=enumValue,enum=grpc.gateway.examples.examplepb.NumericEnum" json:"enum_value,omitempty"`
Sfixed32Value int32 `protobuf:"fixed32,15,opt,name=sfixed32_value,json=sfixed32Value" json:"sfixed32_value,omitempty"`
Sfixed64Value int64 `protobuf:"fixed64,16,opt,name=sfixed64_value,json=sfixed64Value" json:"sfixed64_value,omitempty"`
Sint32Value int32 `protobuf:"zigzag32,17,opt,name=sint32_value,json=sint32Value" json:"sint32_value,omitempty"`
Sint64Value int64 `protobuf:"zigzag64,18,opt,name=sint64_value,json=sint64Value" json:"sint64_value,omitempty"`
RepeatedStringValue []string `protobuf:"bytes,19,rep,name=repeated_string_value,json=repeatedStringValue" json:"repeated_string_value,omitempty"`
// Types that are valid to be assigned to OneofValue:
// *ABitOfEverything_OneofEmpty
// *ABitOfEverything_OneofString
OneofValue isABitOfEverything_OneofValue `protobuf_oneof:"oneof_value"`
MapValue map[string]NumericEnum `protobuf:"bytes,22,rep,name=map_value,json=mapValue" json:"map_value,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value,enum=grpc.gateway.examples.examplepb.NumericEnum"`
MappedStringValue map[string]string `protobuf:"bytes,23,rep,name=mapped_string_value,json=mappedStringValue" json:"mapped_string_value,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
MappedNestedValue map[string]*ABitOfEverything_Nested `protobuf:"bytes,24,rep,name=mapped_nested_value,json=mappedNestedValue" json:"mapped_nested_value,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
NonConventionalNameValue string `protobuf:"bytes,26,opt,name=nonConventionalNameValue" json:"nonConventionalNameValue,omitempty"`
TimestampValue *google_protobuf2.Timestamp `protobuf:"bytes,27,opt,name=timestamp_value,json=timestampValue" json:"timestamp_value,omitempty"`
}
func (m *ABitOfEverything) Reset() { *m = ABitOfEverything{} }
func (m *ABitOfEverything) String() string { return proto.CompactTextString(m) }
func (*ABitOfEverything) ProtoMessage() {}
func (*ABitOfEverything) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
type isABitOfEverything_OneofValue interface {
isABitOfEverything_OneofValue()
}
type ABitOfEverything_OneofEmpty struct {
OneofEmpty *google_protobuf1.Empty `protobuf:"bytes,20,opt,name=oneof_empty,json=oneofEmpty,oneof"`
}
type ABitOfEverything_OneofString struct {
OneofString string `protobuf:"bytes,21,opt,name=oneof_string,json=oneofString,oneof"`
}
func (*ABitOfEverything_OneofEmpty) isABitOfEverything_OneofValue() {}
func (*ABitOfEverything_OneofString) isABitOfEverything_OneofValue() {}
func (m *ABitOfEverything) GetOneofValue() isABitOfEverything_OneofValue {
if m != nil {
return m.OneofValue
}
return nil
}
func (m *ABitOfEverything) GetSingleNested() *ABitOfEverything_Nested {
if m != nil {
return m.SingleNested
}
return nil
}
func (m *ABitOfEverything) GetNested() []*ABitOfEverything_Nested {
if m != nil {
return m.Nested
}
return nil
}
func (m *ABitOfEverything) GetOneofEmpty() *google_protobuf1.Empty {
if x, ok := m.GetOneofValue().(*ABitOfEverything_OneofEmpty); ok {
return x.OneofEmpty
}
return nil
}
func (m *ABitOfEverything) GetOneofString() string {
if x, ok := m.GetOneofValue().(*ABitOfEverything_OneofString); ok {
return x.OneofString
}
return ""
}
func (m *ABitOfEverything) GetMapValue() map[string]NumericEnum {
if m != nil {
return m.MapValue
}
return nil
}
func (m *ABitOfEverything) GetMappedStringValue() map[string]string {
if m != nil {
return m.MappedStringValue
}
return nil
}
func (m *ABitOfEverything) GetMappedNestedValue() map[string]*ABitOfEverything_Nested {
if m != nil {
return m.MappedNestedValue
}
return nil
}
func (m *ABitOfEverything) GetTimestampValue() *google_protobuf2.Timestamp {
if m != nil {
return m.TimestampValue
}
return nil
}
// XXX_OneofFuncs is for the internal use of the proto package.
func (*ABitOfEverything) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
return _ABitOfEverything_OneofMarshaler, _ABitOfEverything_OneofUnmarshaler, _ABitOfEverything_OneofSizer, []interface{}{
(*ABitOfEverything_OneofEmpty)(nil),
(*ABitOfEverything_OneofString)(nil),
}
}
func _ABitOfEverything_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
m := msg.(*ABitOfEverything)
// oneof_value
switch x := m.OneofValue.(type) {
case *ABitOfEverything_OneofEmpty:
b.EncodeVarint(20<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.OneofEmpty); err != nil {
return err
}
case *ABitOfEverything_OneofString:
b.EncodeVarint(21<<3 | proto.WireBytes)
b.EncodeStringBytes(x.OneofString)
case nil:
default:
return fmt.Errorf("ABitOfEverything.OneofValue has unexpected type %T", x)
}
return nil
}
func _ABitOfEverything_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
m := msg.(*ABitOfEverything)
switch tag {
case 20: // oneof_value.oneof_empty
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
msg := new(google_protobuf1.Empty)
err := b.DecodeMessage(msg)
m.OneofValue = &ABitOfEverything_OneofEmpty{msg}
return true, err
case 21: // oneof_value.oneof_string
if wire != proto.WireBytes {
return true, proto.ErrInternalBadWireType
}
x, err := b.DecodeStringBytes()
m.OneofValue = &ABitOfEverything_OneofString{x}
return true, err
default:
return false, nil
}
}
func _ABitOfEverything_OneofSizer(msg proto.Message) (n int) {
m := msg.(*ABitOfEverything)
// oneof_value
switch x := m.OneofValue.(type) {
case *ABitOfEverything_OneofEmpty:
s := proto.Size(x.OneofEmpty)
n += proto.SizeVarint(20<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s))
n += s
case *ABitOfEverything_OneofString:
n += proto.SizeVarint(21<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(len(x.OneofString)))
n += len(x.OneofString)
case nil:
default:
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
}
return n
}
// Nested is nested type.
type ABitOfEverything_Nested struct {
// name is nested field.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Amount uint32 `protobuf:"varint,2,opt,name=amount" json:"amount,omitempty"`
Ok ABitOfEverything_Nested_DeepEnum `protobuf:"varint,3,opt,name=ok,enum=grpc.gateway.examples.examplepb.ABitOfEverything_Nested_DeepEnum" json:"ok,omitempty"`
}
func (m *ABitOfEverything_Nested) Reset() { *m = ABitOfEverything_Nested{} }
func (m *ABitOfEverything_Nested) String() string { return proto.CompactTextString(m) }
func (*ABitOfEverything_Nested) ProtoMessage() {}
func (*ABitOfEverything_Nested) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0, 0} }
func init() {
proto.RegisterType((*ABitOfEverything)(nil), "grpc.gateway.examples.examplepb.ABitOfEverything")
proto.RegisterType((*ABitOfEverything_Nested)(nil), "grpc.gateway.examples.examplepb.ABitOfEverything.Nested")
proto.RegisterEnum("grpc.gateway.examples.examplepb.NumericEnum", NumericEnum_name, NumericEnum_value)
proto.RegisterEnum("grpc.gateway.examples.examplepb.ABitOfEverything_Nested_DeepEnum", ABitOfEverything_Nested_DeepEnum_name, ABitOfEverything_Nested_DeepEnum_value)
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for ABitOfEverythingService service
type ABitOfEverythingServiceClient interface {
Create(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*ABitOfEverything, error)
CreateBody(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*ABitOfEverything, error)
Lookup(ctx context.Context, in *sub2.IdMessage, opts ...grpc.CallOption) (*ABitOfEverything, error)
Update(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*google_protobuf1.Empty, error)
Delete(ctx context.Context, in *sub2.IdMessage, opts ...grpc.CallOption) (*google_protobuf1.Empty, error)
Echo(ctx context.Context, in *grpc_gateway_examples_sub.StringMessage, opts ...grpc.CallOption) (*grpc_gateway_examples_sub.StringMessage, error)
DeepPathEcho(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*ABitOfEverything, error)
Timeout(ctx context.Context, in *google_protobuf1.Empty, opts ...grpc.CallOption) (*google_protobuf1.Empty, error)
}
type aBitOfEverythingServiceClient struct {
cc *grpc.ClientConn
}
func NewABitOfEverythingServiceClient(cc *grpc.ClientConn) ABitOfEverythingServiceClient {
return &aBitOfEverythingServiceClient{cc}
}
func (c *aBitOfEverythingServiceClient) Create(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*ABitOfEverything, error) {
out := new(ABitOfEverything)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Create", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *aBitOfEverythingServiceClient) CreateBody(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*ABitOfEverything, error) {
out := new(ABitOfEverything)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/CreateBody", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *aBitOfEverythingServiceClient) Lookup(ctx context.Context, in *sub2.IdMessage, opts ...grpc.CallOption) (*ABitOfEverything, error) {
out := new(ABitOfEverything)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Lookup", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *aBitOfEverythingServiceClient) Update(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) {
out := new(google_protobuf1.Empty)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Update", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *aBitOfEverythingServiceClient) Delete(ctx context.Context, in *sub2.IdMessage, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) {
out := new(google_protobuf1.Empty)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Delete", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *aBitOfEverythingServiceClient) Echo(ctx context.Context, in *grpc_gateway_examples_sub.StringMessage, opts ...grpc.CallOption) (*grpc_gateway_examples_sub.StringMessage, error) {
out := new(grpc_gateway_examples_sub.StringMessage)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Echo", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *aBitOfEverythingServiceClient) DeepPathEcho(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*ABitOfEverything, error) {
out := new(ABitOfEverything)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/DeepPathEcho", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *aBitOfEverythingServiceClient) Timeout(ctx context.Context, in *google_protobuf1.Empty, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) {
out := new(google_protobuf1.Empty)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Timeout", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for ABitOfEverythingService service
type ABitOfEverythingServiceServer interface {
Create(context.Context, *ABitOfEverything) (*ABitOfEverything, error)
CreateBody(context.Context, *ABitOfEverything) (*ABitOfEverything, error)
Lookup(context.Context, *sub2.IdMessage) (*ABitOfEverything, error)
Update(context.Context, *ABitOfEverything) (*google_protobuf1.Empty, error)
Delete(context.Context, *sub2.IdMessage) (*google_protobuf1.Empty, error)
Echo(context.Context, *grpc_gateway_examples_sub.StringMessage) (*grpc_gateway_examples_sub.StringMessage, error)
DeepPathEcho(context.Context, *ABitOfEverything) (*ABitOfEverything, error)
Timeout(context.Context, *google_protobuf1.Empty) (*google_protobuf1.Empty, error)
}
func RegisterABitOfEverythingServiceServer(s *grpc.Server, srv ABitOfEverythingServiceServer) {
s.RegisterService(&_ABitOfEverythingService_serviceDesc, srv)
}
func _ABitOfEverythingService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ABitOfEverything)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ABitOfEverythingServiceServer).Create(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Create",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ABitOfEverythingServiceServer).Create(ctx, req.(*ABitOfEverything))
}
return interceptor(ctx, in, info, handler)
}
func _ABitOfEverythingService_CreateBody_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ABitOfEverything)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ABitOfEverythingServiceServer).CreateBody(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.ABitOfEverythingService/CreateBody",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ABitOfEverythingServiceServer).CreateBody(ctx, req.(*ABitOfEverything))
}
return interceptor(ctx, in, info, handler)
}
func _ABitOfEverythingService_Lookup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(sub2.IdMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ABitOfEverythingServiceServer).Lookup(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Lookup",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ABitOfEverythingServiceServer).Lookup(ctx, req.(*sub2.IdMessage))
}
return interceptor(ctx, in, info, handler)
}
func _ABitOfEverythingService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ABitOfEverything)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ABitOfEverythingServiceServer).Update(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Update",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ABitOfEverythingServiceServer).Update(ctx, req.(*ABitOfEverything))
}
return interceptor(ctx, in, info, handler)
}
func _ABitOfEverythingService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(sub2.IdMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ABitOfEverythingServiceServer).Delete(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Delete",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ABitOfEverythingServiceServer).Delete(ctx, req.(*sub2.IdMessage))
}
return interceptor(ctx, in, info, handler)
}
func _ABitOfEverythingService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(grpc_gateway_examples_sub.StringMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ABitOfEverythingServiceServer).Echo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Echo",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ABitOfEverythingServiceServer).Echo(ctx, req.(*grpc_gateway_examples_sub.StringMessage))
}
return interceptor(ctx, in, info, handler)
}
func _ABitOfEverythingService_DeepPathEcho_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ABitOfEverything)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ABitOfEverythingServiceServer).DeepPathEcho(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.ABitOfEverythingService/DeepPathEcho",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ABitOfEverythingServiceServer).DeepPathEcho(ctx, req.(*ABitOfEverything))
}
return interceptor(ctx, in, info, handler)
}
func _ABitOfEverythingService_Timeout_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(google_protobuf1.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ABitOfEverythingServiceServer).Timeout(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Timeout",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ABitOfEverythingServiceServer).Timeout(ctx, req.(*google_protobuf1.Empty))
}
return interceptor(ctx, in, info, handler)
}
var _ABitOfEverythingService_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.gateway.examples.examplepb.ABitOfEverythingService",
HandlerType: (*ABitOfEverythingServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Create",
Handler: _ABitOfEverythingService_Create_Handler,
},
{
MethodName: "CreateBody",
Handler: _ABitOfEverythingService_CreateBody_Handler,
},
{
MethodName: "Lookup",
Handler: _ABitOfEverythingService_Lookup_Handler,
},
{
MethodName: "Update",
Handler: _ABitOfEverythingService_Update_Handler,
},
{
MethodName: "Delete",
Handler: _ABitOfEverythingService_Delete_Handler,
},
{
MethodName: "Echo",
Handler: _ABitOfEverythingService_Echo_Handler,
},
{
MethodName: "DeepPathEcho",
Handler: _ABitOfEverythingService_DeepPathEcho_Handler,
},
{
MethodName: "Timeout",
Handler: _ABitOfEverythingService_Timeout_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "examples/examplepb/a_bit_of_everything.proto",
}
func init() { proto.RegisterFile("examples/examplepb/a_bit_of_everything.proto", fileDescriptor1) }
var fileDescriptor1 = []byte{
// 1198 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x57, 0xcb, 0x6e, 0xdb, 0x46,
0x17, 0xf6, 0x48, 0xb6, 0x6c, 0x1d, 0x5a, 0xb2, 0x3c, 0x8e, 0x1d, 0x45, 0xfe, 0x7f, 0x88, 0x55,
0xda, 0x82, 0x70, 0x0d, 0x12, 0x96, 0x83, 0x22, 0x31, 0x50, 0x04, 0xbe, 0xa8, 0x70, 0xd1, 0xc4,
0x4e, 0xe9, 0x24, 0x0b, 0xa3, 0x81, 0x40, 0x49, 0x23, 0x99, 0xb0, 0xc8, 0x21, 0xc8, 0xa1, 0x6a,
0x41, 0x55, 0x17, 0x5d, 0xf4, 0x05, 0xba, 0xe8, 0x2e, 0x9b, 0x02, 0x45, 0x37, 0x5d, 0x76, 0xdd,
0x87, 0xe8, 0x1b, 0x14, 0x7d, 0x90, 0x82, 0x33, 0x24, 0x4d, 0xc9, 0x16, 0xe4, 0x0b, 0x90, 0x9d,
0x66, 0xe6, 0x3b, 0xdf, 0x77, 0x2e, 0x73, 0xce, 0x50, 0xb0, 0x49, 0x2e, 0x0c, 0xcb, 0xe9, 0x12,
0x4f, 0x0b, 0x7f, 0x38, 0x0d, 0xcd, 0xa8, 0x37, 0x4c, 0x56, 0xa7, 0xed, 0x3a, 0xe9, 0x11, 0xb7,
0xcf, 0xce, 0x4c, 0xbb, 0xa3, 0x3a, 0x2e, 0x65, 0x14, 0x97, 0x3b, 0xae, 0xd3, 0x54, 0x3b, 0x06,
0x23, 0xdf, 0x19, 0x7d, 0x35, 0x32, 0x55, 0x63, 0xd3, 0xd2, 0xff, 0x3a, 0x94, 0x76, 0xba, 0x44,
0x33, 0x1c, 0x53, 0x33, 0x6c, 0x9b, 0x32, 0x83, 0x99, 0xd4, 0xf6, 0x84, 0x79, 0x69, 0x3d, 0x3c,
0xe5, 0xab, 0x86, 0xdf, 0xd6, 0x88, 0xe5, 0xb0, 0x7e, 0x78, 0x58, 0x8a, 0x3d, 0xf1, 0xfc, 0x86,
0x66, 0x11, 0xcf, 0x33, 0x3a, 0x24, 0x32, 0x4c, 0x9e, 0x55, 0xc7, 0x0e, 0xcb, 0xe3, 0xac, 0xcc,
0xb4, 0x88, 0xc7, 0x0c, 0xcb, 0x11, 0x80, 0xca, 0x3f, 0x79, 0x28, 0xec, 0xee, 0x99, 0xec, 0xb8,
0x5d, 0x8b, 0x03, 0xc2, 0xef, 0x20, 0xe7, 0x99, 0x76, 0xa7, 0x4b, 0xea, 0x36, 0xf1, 0x18, 0x69,
0x15, 0x1f, 0xc9, 0x48, 0x91, 0xaa, 0x4f, 0xd5, 0x29, 0x21, 0xaa, 0xe3, 0x4c, 0xea, 0x11, 0xb7,
0xd7, 0x17, 0x05, 0x9d, 0x58, 0x61, 0x0c, 0xb3, 0xbe, 0x6f, 0xb6, 0x8a, 0x48, 0x46, 0x4a, 0x56,
0xe7, 0xbf, 0xf1, 0x2b, 0xc8, 0x84, 0x5a, 0x29, 0x39, 0x7d, 0x2f, 0xad, 0x90, 0x07, 0x97, 0x41,
0x6a, 0x77, 0xa9, 0xc1, 0xea, 0x3d, 0xa3, 0xeb, 0x93, 0x62, 0x5a, 0x46, 0x4a, 0x4a, 0x07, 0xbe,
0xf5, 0x36, 0xd8, 0xc1, 0x1f, 0xc1, 0x62, 0x8b, 0xfa, 0x8d, 0x2e, 0x09, 0x11, 0xb3, 0x32, 0x52,
0x90, 0x2e, 0x89, 0x3d, 0x01, 0x29, 0x83, 0x64, 0xda, 0xec, 0xf3, 0x27, 0x21, 0x62, 0x4e, 0x46,
0x4a, 0x5a, 0x07, 0xbe, 0x15, 0x73, 0xf8, 0x49, 0x44, 0x46, 0x46, 0xca, 0xac, 0x2e, 0xf9, 0x09,
0x88, 0xe0, 0xd8, 0xae, 0x86, 0x88, 0x79, 0x19, 0x29, 0x73, 0x9c, 0x63, 0xbb, 0x2a, 0x00, 0x8f,
0x21, 0xd7, 0x36, 0x2f, 0x48, 0x2b, 0x26, 0x59, 0x90, 0x91, 0x92, 0xd1, 0x17, 0xc3, 0xcd, 0x51,
0x50, 0xcc, 0x93, 0x95, 0x91, 0x32, 0x1f, 0x82, 0x22, 0xa6, 0xff, 0x03, 0x34, 0x28, 0xed, 0x86,
0x08, 0x90, 0x91, 0xb2, 0xa0, 0x67, 0x83, 0x9d, 0xd8, 0x59, 0x8f, 0xb9, 0xa6, 0xdd, 0x09, 0x01,
0x12, 0xcf, 0xbf, 0x24, 0xf6, 0x46, 0xe2, 0x89, 0x55, 0x72, 0x32, 0x52, 0x72, 0x22, 0x9e, 0x48,
0xe4, 0x6b, 0x00, 0x62, 0xfb, 0x56, 0x08, 0xc8, 0xcb, 0x48, 0xc9, 0x57, 0x37, 0xa7, 0x56, 0xeb,
0xc8, 0xb7, 0x88, 0x6b, 0x36, 0x6b, 0xb6, 0x6f, 0xe9, 0xd9, 0xc0, 0x5e, 0x90, 0x7d, 0x02, 0x79,
0x6f, 0x34, 0xae, 0x25, 0x19, 0x29, 0x4b, 0x7a, 0xce, 0x1b, 0x09, 0x2c, 0x86, 0xc5, 0x39, 0x2a,
0xc8, 0x48, 0x29, 0x44, 0xb0, 0x44, 0x35, 0xbc, 0xa4, 0xf7, 0xcb, 0x32, 0x52, 0x96, 0x75, 0xc9,
0x4b, 0x78, 0x1f, 0x42, 0x62, 0x1e, 0x2c, 0x23, 0x05, 0x0b, 0x48, 0xc4, 0x52, 0x85, 0x55, 0x97,
0x38, 0xc4, 0x60, 0xa4, 0x55, 0x1f, 0xc9, 0xd7, 0x8a, 0x9c, 0x56, 0xb2, 0xfa, 0x4a, 0x74, 0x78,
0x92, 0xc8, 0xdb, 0x33, 0x90, 0xa8, 0x4d, 0x82, 0xb1, 0x10, 0x74, 0x6d, 0xf1, 0x01, 0xef, 0x97,
0x35, 0x55, 0x74, 0x9f, 0x1a, 0x75, 0x9f, 0x5a, 0x0b, 0x4e, 0x0f, 0x67, 0x74, 0xe0, 0x60, 0xbe,
0xc2, 0x8f, 0x61, 0x51, 0x98, 0x0a, 0xad, 0xe2, 0x6a, 0x50, 0x95, 0xc3, 0x19, 0x5d, 0x10, 0x0a,
0x11, 0xfc, 0x2d, 0x64, 0x2d, 0xc3, 0x09, 0xfd, 0x58, 0xe3, 0x1d, 0xf2, 0xfc, 0xf6, 0x1d, 0xf2,
0xd2, 0x70, 0xb8, 0xbb, 0x35, 0x9b, 0xb9, 0x7d, 0x7d, 0xc1, 0x0a, 0x97, 0xf8, 0x02, 0x56, 0x2c,
0xc3, 0x71, 0xc6, 0xe3, 0x7d, 0xc8, 0x75, 0x0e, 0xef, 0xa4, 0xe3, 0x8c, 0xe4, 0x47, 0x08, 0x2e,
0x5b, 0xe3, 0xfb, 0x09, 0x65, 0xd1, 0xb5, 0xa1, 0x72, 0xf1, 0x7e, 0xca, 0x62, 0x12, 0x5c, 0x55,
0x4e, 0xec, 0xe3, 0x1d, 0x28, 0xda, 0xd4, 0xde, 0xa7, 0x76, 0x8f, 0xd8, 0xc1, 0x1c, 0x36, 0xba,
0x47, 0x86, 0x25, 0xda, 0xbe, 0x58, 0xe2, 0x8d, 0x31, 0xf1, 0x1c, 0xef, 0xc3, 0x52, 0x3c, 0x47,
0x43, 0x8f, 0xd7, 0x79, 0xc5, 0x4b, 0x57, 0x2a, 0xfe, 0x3a, 0xc2, 0xe9, 0xf9, 0xd8, 0x84, 0x93,
0x94, 0x7e, 0x47, 0x90, 0xb9, 0x1c, 0x88, 0xb6, 0x61, 0x91, 0x68, 0x20, 0x06, 0xbf, 0xf1, 0x1a,
0x64, 0x0c, 0x8b, 0xfa, 0x36, 0x2b, 0xa6, 0x78, 0x0f, 0x86, 0x2b, 0xfc, 0x0d, 0xa4, 0xe8, 0x39,
0x9f, 0x66, 0xf9, 0xea, 0xee, 0x5d, 0x87, 0xa4, 0x7a, 0x40, 0x88, 0xc3, 0x7b, 0x31, 0x45, 0xcf,
0x2b, 0x65, 0x58, 0x88, 0xd6, 0x38, 0x0b, 0x73, 0x5f, 0xee, 0xbe, 0x38, 0xa9, 0x15, 0x66, 0xf0,
0x02, 0xcc, 0xbe, 0xd6, 0xdf, 0xd4, 0x0a, 0xa8, 0x64, 0x42, 0x6e, 0xe4, 0xea, 0xe0, 0x02, 0xa4,
0xcf, 0x49, 0x3f, 0xf4, 0x37, 0xf8, 0x89, 0xf7, 0x60, 0x4e, 0x24, 0x22, 0x75, 0x87, 0x81, 0x20,
0x4c, 0x77, 0x52, 0x4f, 0x51, 0xe9, 0x00, 0xd6, 0xae, 0xbf, 0x3d, 0xd7, 0x68, 0x3e, 0x48, 0x6a,
0x66, 0x93, 0x2c, 0x3f, 0x44, 0x2c, 0xe3, 0x37, 0xe1, 0x1a, 0x96, 0xa3, 0x24, 0xcb, 0x7d, 0x1e,
0x9e, 0x4b, 0xfd, 0xbd, 0x5c, 0x34, 0x0e, 0xf8, 0xd6, 0x86, 0x0c, 0x52, 0x22, 0xdc, 0x20, 0xb1,
0xa7, 0x35, 0xfd, 0xb8, 0x30, 0x83, 0xe7, 0x21, 0x7d, 0x7c, 0x54, 0x2b, 0xa0, 0xea, 0x2f, 0x12,
0x3c, 0x1c, 0xe7, 0x3d, 0x21, 0x6e, 0xcf, 0x6c, 0x12, 0xfc, 0x3e, 0x0d, 0x99, 0x7d, 0x37, 0x18,
0x39, 0x78, 0xeb, 0xd6, 0xce, 0x95, 0x6e, 0x6f, 0x52, 0xf9, 0x23, 0xf5, 0xe3, 0xdf, 0xff, 0xfe,
0x9c, 0xfa, 0x2d, 0x55, 0xf9, 0x35, 0xa5, 0xf5, 0xb6, 0xa2, 0xaf, 0x9f, 0xeb, 0xbe, 0x7d, 0xb4,
0x41, 0xe2, 0x8d, 0x1d, 0x6a, 0x83, 0xe4, 0x83, 0x3a, 0xd4, 0x06, 0x89, 0x49, 0x3b, 0xd4, 0x3c,
0xe2, 0x18, 0xae, 0xc1, 0xa8, 0xab, 0x0d, 0xfc, 0x91, 0x83, 0x41, 0x62, 0x66, 0x0f, 0xb5, 0xc1,
0xc8, 0xa0, 0x8f, 0xd6, 0x89, 0xf3, 0xcb, 0x27, 0x6e, 0xa8, 0x0d, 0x92, 0x03, 0xeb, 0x0b, 0x8f,
0xb9, 0x8e, 0x4b, 0xda, 0xe6, 0x85, 0xb6, 0x31, 0x14, 0x22, 0x09, 0x33, 0x6f, 0x9c, 0xc7, 0x1b,
0x17, 0xf2, 0xc6, 0x0c, 0x46, 0x9d, 0x9c, 0x34, 0x0d, 0x86, 0xf8, 0x3d, 0x02, 0x10, 0x05, 0xda,
0xa3, 0xad, 0xfe, 0x07, 0x2a, 0xd2, 0x06, 0xaf, 0xd1, 0xc7, 0x95, 0xf2, 0x94, 0x0a, 0xed, 0xa0,
0x0d, 0xfc, 0x3d, 0x64, 0x5e, 0x50, 0x7a, 0xee, 0x3b, 0x78, 0x49, 0x0d, 0x3e, 0x12, 0xd5, 0xaf,
0x5a, 0x2f, 0xc5, 0x67, 0xe2, 0x5d, 0x94, 0x55, 0xae, 0xac, 0xe0, 0x4f, 0xa7, 0xde, 0x8d, 0xe0,
0xcb, 0x6e, 0x88, 0x7f, 0x42, 0x90, 0x79, 0xe3, 0xb4, 0xee, 0x78, 0x7f, 0x27, 0x3c, 0xa2, 0x95,
0x2d, 0xee, 0xc5, 0x67, 0xa5, 0x1b, 0x7a, 0x11, 0xa4, 0xc1, 0x80, 0xcc, 0x01, 0xe9, 0x12, 0x46,
0xae, 0xa6, 0x61, 0x92, 0x4a, 0x18, 0xeb, 0xc6, 0x4d, 0x63, 0xfd, 0x0b, 0xc1, 0x6c, 0xad, 0x79,
0x46, 0xb1, 0x32, 0x21, 0x52, 0xcf, 0x6f, 0xa8, 0x62, 0xb4, 0x45, 0xd2, 0x37, 0x46, 0x56, 0x9a,
0xdc, 0x99, 0x77, 0x78, 0x73, 0x9a, 0x33, 0xa4, 0x79, 0x46, 0xb5, 0x81, 0xb8, 0xb8, 0xa7, 0x8f,
0x2a, 0x05, 0xad, 0x57, 0x8d, 0xf1, 0xc1, 0xd9, 0x8e, 0x18, 0x55, 0xa7, 0x18, 0x5f, 0x39, 0xc2,
0x7f, 0x22, 0x58, 0x0c, 0x5e, 0x83, 0x57, 0x06, 0x3b, 0xe3, 0x91, 0x7c, 0x98, 0xeb, 0xfc, 0x9c,
0xc7, 0xf6, 0xac, 0xf2, 0x64, 0x6a, 0xa2, 0x47, 0xfe, 0x99, 0xa8, 0xc1, 0x5b, 0xc9, 0x8b, 0xfb,
0x16, 0xe6, 0x83, 0xb7, 0x96, 0xfa, 0x0c, 0x4f, 0x28, 0xe6, 0xc4, 0x22, 0xaf, 0x73, 0xed, 0x55,
0xbc, 0x92, 0x4c, 0x06, 0x13, 0x64, 0x7b, 0xd2, 0x69, 0x36, 0x76, 0xbb, 0x91, 0xe1, 0x96, 0xdb,
0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xdb, 0xf3, 0x9f, 0xf3, 0x1a, 0x0e, 0x00, 0x00,
}

View file

@ -0,0 +1,772 @@
// Code generated by protoc-gen-grpc-gateway
// source: examples/examplepb/a_bit_of_everything.proto
// DO NOT EDIT!
/*
Package examplepb is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package examplepb
import (
"io"
"net/http"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/empty"
"github.com/grpc-ecosystem/grpc-gateway/examples/sub"
"github.com/grpc-ecosystem/grpc-gateway/examples/sub2"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
)
var _ codes.Code
var _ io.Reader
var _ = runtime.String
var _ = utilities.NewDoubleArray
var (
filter_ABitOfEverythingService_Create_0 = &utilities.DoubleArray{Encoding: map[string]int{"float_value": 0, "double_value": 1, "int64_value": 2, "uint64_value": 3, "int32_value": 4, "fixed64_value": 5, "fixed32_value": 6, "bool_value": 7, "string_value": 8, "uint32_value": 9, "sfixed32_value": 10, "sfixed64_value": 11, "sint32_value": 12, "sint64_value": 13, "nonConventionalNameValue": 14}, Base: []int{1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Check: []int{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}
)
func request_ABitOfEverythingService_Create_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ABitOfEverything
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["float_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "float_value")
}
protoReq.FloatValue, err = runtime.Float32(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["double_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "double_value")
}
protoReq.DoubleValue, err = runtime.Float64(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["int64_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "int64_value")
}
protoReq.Int64Value, err = runtime.Int64(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["uint64_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "uint64_value")
}
protoReq.Uint64Value, err = runtime.Uint64(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["int32_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "int32_value")
}
protoReq.Int32Value, err = runtime.Int32(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["fixed64_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "fixed64_value")
}
protoReq.Fixed64Value, err = runtime.Uint64(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["fixed32_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "fixed32_value")
}
protoReq.Fixed32Value, err = runtime.Uint32(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["bool_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "bool_value")
}
protoReq.BoolValue, err = runtime.Bool(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["string_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "string_value")
}
protoReq.StringValue, err = runtime.String(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["uint32_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "uint32_value")
}
protoReq.Uint32Value, err = runtime.Uint32(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["sfixed32_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "sfixed32_value")
}
protoReq.Sfixed32Value, err = runtime.Int32(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["sfixed64_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "sfixed64_value")
}
protoReq.Sfixed64Value, err = runtime.Int64(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["sint32_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "sint32_value")
}
protoReq.Sint32Value, err = runtime.Int32(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["sint64_value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "sint64_value")
}
protoReq.Sint64Value, err = runtime.Int64(val)
if err != nil {
return nil, metadata, err
}
val, ok = pathParams["nonConventionalNameValue"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "nonConventionalNameValue")
}
protoReq.NonConventionalNameValue, err = runtime.String(val)
if err != nil {
return nil, metadata, err
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ABitOfEverythingService_Create_0); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ABitOfEverythingService_CreateBody_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ABitOfEverything
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.CreateBody(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ABitOfEverythingService_Lookup_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq sub2.IdMessage
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["uuid"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "uuid")
}
protoReq.Uuid, err = runtime.String(val)
if err != nil {
return nil, metadata, err
}
msg, err := client.Lookup(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ABitOfEverythingService_Update_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ABitOfEverything
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["uuid"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "uuid")
}
protoReq.Uuid, err = runtime.String(val)
if err != nil {
return nil, metadata, err
}
msg, err := client.Update(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ABitOfEverythingService_Delete_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq sub2.IdMessage
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["uuid"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "uuid")
}
protoReq.Uuid, err = runtime.String(val)
if err != nil {
return nil, metadata, err
}
msg, err := client.Delete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ABitOfEverythingService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq sub.StringMessage
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["value"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "value")
}
protoReq.Value, err = runtime.StringP(val)
if err != nil {
return nil, metadata, err
}
msg, err := client.Echo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ABitOfEverythingService_Echo_1(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq sub.StringMessage
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq.Value); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Echo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
var (
filter_ABitOfEverythingService_Echo_2 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_ABitOfEverythingService_Echo_2(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq sub.StringMessage
var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ABitOfEverythingService_Echo_2); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Echo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ABitOfEverythingService_DeepPathEcho_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ABitOfEverything
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["single_nested.name"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "single_nested.name")
}
err = runtime.PopulateFieldFromPath(&protoReq, "single_nested.name", val)
if err != nil {
return nil, metadata, err
}
msg, err := client.DeepPathEcho(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_ABitOfEverythingService_Timeout_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq empty.Empty
var metadata runtime.ServerMetadata
msg, err := client.Timeout(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterABitOfEverythingServiceHandlerFromEndpoint is same as RegisterABitOfEverythingServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterABitOfEverythingServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterABitOfEverythingServiceHandler(ctx, mux, conn)
}
// RegisterABitOfEverythingServiceHandler registers the http handlers for service ABitOfEverythingService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterABitOfEverythingServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
client := NewABitOfEverythingServiceClient(conn)
mux.Handle("POST", pattern_ABitOfEverythingService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_Create_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_Create_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_ABitOfEverythingService_CreateBody_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_CreateBody_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_CreateBody_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_ABitOfEverythingService_Lookup_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_Lookup_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_Lookup_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_ABitOfEverythingService_Update_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_Update_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_Update_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_ABitOfEverythingService_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_Delete_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_Delete_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_ABitOfEverythingService_Echo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_Echo_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_Echo_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_ABitOfEverythingService_Echo_1, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_Echo_1(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_Echo_1(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_ABitOfEverythingService_Echo_2, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_Echo_2(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_Echo_2(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_ABitOfEverythingService_DeepPathEcho_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_DeepPathEcho_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_DeepPathEcho_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_ABitOfEverythingService_Timeout_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_ABitOfEverythingService_Timeout_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_ABitOfEverythingService_Timeout_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_ABitOfEverythingService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5, 2, 6, 1, 0, 4, 1, 5, 7, 1, 0, 4, 1, 5, 8, 1, 0, 4, 1, 5, 9, 1, 0, 4, 1, 5, 10, 1, 0, 4, 1, 5, 11, 2, 12, 1, 0, 4, 2, 5, 13, 1, 0, 4, 1, 5, 14, 1, 0, 4, 1, 5, 15, 1, 0, 4, 1, 5, 16, 1, 0, 4, 1, 5, 17, 1, 0, 4, 1, 5, 18, 1, 0, 4, 1, 5, 19}, []string{"v1", "example", "a_bit_of_everything", "float_value", "double_value", "int64_value", "separator", "uint64_value", "int32_value", "fixed64_value", "fixed32_value", "bool_value", "strprefix", "string_value", "uint32_value", "sfixed32_value", "sfixed64_value", "sint32_value", "sint64_value", "nonConventionalNameValue"}, ""))
pattern_ABitOfEverythingService_CreateBody_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "example", "a_bit_of_everything"}, ""))
pattern_ABitOfEverythingService_Lookup_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "example", "a_bit_of_everything", "uuid"}, ""))
pattern_ABitOfEverythingService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "example", "a_bit_of_everything", "uuid"}, ""))
pattern_ABitOfEverythingService_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "example", "a_bit_of_everything", "uuid"}, ""))
pattern_ABitOfEverythingService_Echo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "example", "a_bit_of_everything", "echo", "value"}, ""))
pattern_ABitOfEverythingService_Echo_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "example", "echo"}, ""))
pattern_ABitOfEverythingService_Echo_2 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "example", "echo"}, ""))
pattern_ABitOfEverythingService_DeepPathEcho_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "example", "a_bit_of_everything", "single_nested.name"}, ""))
pattern_ABitOfEverythingService_Timeout_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "example", "timeout"}, ""))
)
var (
forward_ABitOfEverythingService_Create_0 = runtime.ForwardResponseMessage
forward_ABitOfEverythingService_CreateBody_0 = runtime.ForwardResponseMessage
forward_ABitOfEverythingService_Lookup_0 = runtime.ForwardResponseMessage
forward_ABitOfEverythingService_Update_0 = runtime.ForwardResponseMessage
forward_ABitOfEverythingService_Delete_0 = runtime.ForwardResponseMessage
forward_ABitOfEverythingService_Echo_0 = runtime.ForwardResponseMessage
forward_ABitOfEverythingService_Echo_1 = runtime.ForwardResponseMessage
forward_ABitOfEverythingService_Echo_2 = runtime.ForwardResponseMessage
forward_ABitOfEverythingService_DeepPathEcho_0 = runtime.ForwardResponseMessage
forward_ABitOfEverythingService_Timeout_0 = runtime.ForwardResponseMessage
)

View file

@ -0,0 +1,123 @@
syntax = "proto3";
option go_package = "examplepb";
package grpc.gateway.examples.examplepb;
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "examples/sub/message.proto";
import "examples/sub2/message.proto";
import "google/protobuf/timestamp.proto";
// Intentionaly complicated message type to cover much features of Protobuf.
// NEXT ID: 27
message ABitOfEverything {
// Nested is nested type.
message Nested {
// name is nested field.
string name = 1;
uint32 amount = 2;
// DeepEnum is one or zero.
enum DeepEnum {
// FALSE is false.
FALSE = 0;
// TRUE is true.
TRUE = 1;
}
DeepEnum ok = 3;
}
Nested single_nested = 25;
string uuid = 1;
repeated Nested nested = 2;
float float_value = 3;
double double_value = 4;
int64 int64_value = 5;
uint64 uint64_value = 6;
int32 int32_value = 7;
fixed64 fixed64_value = 8;
fixed32 fixed32_value = 9;
bool bool_value = 10;
string string_value = 11;
// TODO(yugui) add bytes_value
uint32 uint32_value = 13;
NumericEnum enum_value = 14;
sfixed32 sfixed32_value = 15;
sfixed64 sfixed64_value = 16;
sint32 sint32_value = 17;
sint64 sint64_value = 18;
repeated string repeated_string_value = 19;
oneof oneof_value {
google.protobuf.Empty oneof_empty = 20;
string oneof_string = 21;
}
map<string, NumericEnum> map_value = 22;
map<string, string> mapped_string_value = 23;
map<string, Nested> mapped_nested_value = 24;
string nonConventionalNameValue = 26;
google.protobuf.Timestamp timestamp_value = 27;
}
// NumericEnum is one or zero.
enum NumericEnum {
// ZERO means 0
ZERO = 0;
// ONE means 1
ONE = 1;
}
service ABitOfEverythingService {
rpc Create(ABitOfEverything) returns (ABitOfEverything) {
// TODO add enum_value
option (google.api.http) = {
post: "/v1/example/a_bit_of_everything/{float_value}/{double_value}/{int64_value}/separator/{uint64_value}/{int32_value}/{fixed64_value}/{fixed32_value}/{bool_value}/{string_value=strprefix/*}/{uint32_value}/{sfixed32_value}/{sfixed64_value}/{sint32_value}/{sint64_value}/{nonConventionalNameValue}"
};
}
rpc CreateBody(ABitOfEverything) returns (ABitOfEverything) {
option (google.api.http) = {
post: "/v1/example/a_bit_of_everything"
body: "*"
};
}
rpc Lookup(sub2.IdMessage) returns (ABitOfEverything) {
option (google.api.http) = {
get: "/v1/example/a_bit_of_everything/{uuid}"
};
}
rpc Update(ABitOfEverything) returns (google.protobuf.Empty) {
option (google.api.http) = {
put: "/v1/example/a_bit_of_everything/{uuid}"
body: "*"
};
}
rpc Delete(sub2.IdMessage) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete: "/v1/example/a_bit_of_everything/{uuid}"
};
}
rpc Echo(grpc.gateway.examples.sub.StringMessage) returns (grpc.gateway.examples.sub.StringMessage) {
option (google.api.http) = {
get: "/v1/example/a_bit_of_everything/echo/{value}"
additional_bindings {
post: "/v2/example/echo"
body: "value"
}
additional_bindings {
get: "/v2/example/echo"
}
};
}
rpc DeepPathEcho(ABitOfEverything) returns (ABitOfEverything) {
option (google.api.http) = {
post: "/v1/example/a_bit_of_everything/{single_nested.name}"
body: "*"
};
}
rpc Timeout(google.protobuf.Empty) returns (google.protobuf.Empty) {
option (google.api.http) = {
get: "/v2/example/timeout",
};
}
}

View file

@ -0,0 +1,543 @@
{
"swagger": "2.0",
"info": {
"title": "examples/examplepb/a_bit_of_everything.proto",
"version": "version not set"
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v1/example/a_bit_of_everything": {
"post": {
"operationId": "CreateBody",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/examplepbABitOfEverything"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/examplepbABitOfEverything"
}
}
],
"tags": [
"ABitOfEverythingService"
]
}
},
"/v1/example/a_bit_of_everything/echo/{value}": {
"get": {
"operationId": "Echo",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/subStringMessage"
}
}
},
"parameters": [
{
"name": "value",
"in": "path",
"required": true,
"type": "string",
"format": "string"
}
],
"tags": [
"ABitOfEverythingService"
]
}
},
"/v1/example/a_bit_of_everything/{float_value}/{double_value}/{int64_value}/separator/{uint64_value}/{int32_value}/{fixed64_value}/{fixed32_value}/{bool_value}/{string_value}/{uint32_value}/{sfixed32_value}/{sfixed64_value}/{sint32_value}/{sint64_value}/{nonConventionalNameValue}": {
"post": {
"operationId": "Create",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/examplepbABitOfEverything"
}
}
},
"parameters": [
{
"name": "float_value",
"in": "path",
"required": true,
"type": "number",
"format": "float"
},
{
"name": "double_value",
"in": "path",
"required": true,
"type": "number",
"format": "double"
},
{
"name": "int64_value",
"in": "path",
"required": true,
"type": "string",
"format": "int64"
},
{
"name": "uint64_value",
"in": "path",
"required": true,
"type": "string",
"format": "uint64"
},
{
"name": "int32_value",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "fixed64_value",
"in": "path",
"required": true,
"type": "string",
"format": "uint64"
},
{
"name": "fixed32_value",
"in": "path",
"required": true,
"type": "integer",
"format": "int64"
},
{
"name": "bool_value",
"in": "path",
"required": true,
"type": "boolean",
"format": "boolean"
},
{
"name": "string_value",
"in": "path",
"required": true,
"type": "string",
"format": "string"
},
{
"name": "uint32_value",
"in": "path",
"required": true,
"type": "integer",
"format": "int64"
},
{
"name": "sfixed32_value",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "sfixed64_value",
"in": "path",
"required": true,
"type": "string",
"format": "int64"
},
{
"name": "sint32_value",
"in": "path",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "sint64_value",
"in": "path",
"required": true,
"type": "string",
"format": "int64"
},
{
"name": "nonConventionalNameValue",
"in": "path",
"required": true,
"type": "string",
"format": "string"
}
],
"tags": [
"ABitOfEverythingService"
]
}
},
"/v1/example/a_bit_of_everything/{single_nested.name}": {
"post": {
"operationId": "DeepPathEcho",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/examplepbABitOfEverything"
}
}
},
"parameters": [
{
"name": "single_nested.name",
"in": "path",
"required": true,
"type": "string",
"format": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/examplepbABitOfEverything"
}
}
],
"tags": [
"ABitOfEverythingService"
]
}
},
"/v1/example/a_bit_of_everything/{uuid}": {
"get": {
"operationId": "Lookup",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/examplepbABitOfEverything"
}
}
},
"parameters": [
{
"name": "uuid",
"in": "path",
"required": true,
"type": "string",
"format": "string"
}
],
"tags": [
"ABitOfEverythingService"
]
},
"delete": {
"operationId": "Delete",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/protobufEmpty"
}
}
},
"parameters": [
{
"name": "uuid",
"in": "path",
"required": true,
"type": "string",
"format": "string"
}
],
"tags": [
"ABitOfEverythingService"
]
},
"put": {
"operationId": "Update",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/protobufEmpty"
}
}
},
"parameters": [
{
"name": "uuid",
"in": "path",
"required": true,
"type": "string",
"format": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/examplepbABitOfEverything"
}
}
],
"tags": [
"ABitOfEverythingService"
]
}
},
"/v2/example/echo": {
"get": {
"operationId": "Echo",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/subStringMessage"
}
}
},
"tags": [
"ABitOfEverythingService"
]
},
"post": {
"operationId": "Echo",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/subStringMessage"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"type": "string",
"format": "string"
}
}
],
"tags": [
"ABitOfEverythingService"
]
}
},
"/v2/example/timeout": {
"get": {
"operationId": "Timeout",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/protobufEmpty"
}
}
},
"tags": [
"ABitOfEverythingService"
]
}
}
},
"definitions": {
"ABitOfEverythingNested": {
"type": "object",
"properties": {
"amount": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string",
"format": "string",
"description": "name is nested field."
},
"ok": {
"$ref": "#/definitions/NestedDeepEnum"
}
},
"description": "Nested is nested type."
},
"NestedDeepEnum": {
"type": "string",
"enum": [
"FALSE",
"TRUE"
],
"default": "FALSE",
"description": "DeepEnum is one or zero.\n\n - FALSE: FALSE is false.\n - TRUE: TRUE is true."
},
"examplepbABitOfEverything": {
"type": "object",
"properties": {
"bool_value": {
"type": "boolean",
"format": "boolean"
},
"double_value": {
"type": "number",
"format": "double"
},
"enum_value": {
"$ref": "#/definitions/examplepbNumericEnum"
},
"fixed32_value": {
"type": "integer",
"format": "int64"
},
"fixed64_value": {
"type": "string",
"format": "uint64"
},
"float_value": {
"type": "number",
"format": "float"
},
"int32_value": {
"type": "integer",
"format": "int32"
},
"int64_value": {
"type": "string",
"format": "int64"
},
"map_value": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/examplepbNumericEnum"
}
},
"mapped_nested_value": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/ABitOfEverythingNested"
}
},
"mapped_string_value": {
"type": "object",
"additionalProperties": {
"type": "string",
"format": "string"
}
},
"nested": {
"type": "array",
"items": {
"$ref": "#/definitions/ABitOfEverythingNested"
}
},
"nonConventionalNameValue": {
"type": "string",
"format": "string"
},
"oneof_empty": {
"$ref": "#/definitions/protobufEmpty"
},
"oneof_string": {
"type": "string",
"format": "string"
},
"repeated_string_value": {
"type": "array",
"items": {
"type": "string",
"format": "string"
}
},
"sfixed32_value": {
"type": "integer",
"format": "int32"
},
"sfixed64_value": {
"type": "string",
"format": "int64"
},
"single_nested": {
"$ref": "#/definitions/ABitOfEverythingNested"
},
"sint32_value": {
"type": "integer",
"format": "int32"
},
"sint64_value": {
"type": "string",
"format": "int64"
},
"string_value": {
"type": "string",
"format": "string"
},
"timestamp_value": {
"type": "string",
"format": "date-time"
},
"uint32_value": {
"type": "integer",
"format": "int64",
"title": "TODO(yugui) add bytes_value"
},
"uint64_value": {
"type": "string",
"format": "uint64"
},
"uuid": {
"type": "string",
"format": "string"
}
},
"title": "Intentionaly complicated message type to cover much features of Protobuf.\nNEXT ID: 27"
},
"examplepbNumericEnum": {
"type": "string",
"enum": [
"ZERO",
"ONE"
],
"default": "ZERO",
"description": "NumericEnum is one or zero.\n\n - ZERO: ZERO means 0\n - ONE: ONE means 1"
},
"protobufEmpty": {
"type": "object",
"description": "service Foo {\n rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);\n }\n\nThe JSON representation for `Empty` is empty JSON object `{}`.",
"title": "A generic empty message that you can re-use to avoid defining duplicated\nempty messages in your APIs. A typical example is to use it as the request\nor the response type of an API method. For instance:"
},
"sub2IdMessage": {
"type": "object",
"properties": {
"uuid": {
"type": "string",
"format": "string"
}
}
},
"subStringMessage": {
"type": "object",
"properties": {
"value": {
"type": "string",
"format": "string"
}
}
}
}
}

View file

@ -0,0 +1,200 @@
// Code generated by protoc-gen-go.
// source: examples/examplepb/echo_service.proto
// DO NOT EDIT!
/*
Package examplepb is a generated protocol buffer package.
Echo Service
Echo Service API consists of a single service which returns
a message.
It is generated from these files:
examples/examplepb/echo_service.proto
examples/examplepb/a_bit_of_everything.proto
examples/examplepb/stream.proto
examples/examplepb/flow_combination.proto
It has these top-level messages:
SimpleMessage
ABitOfEverything
EmptyProto
NonEmptyProto
UnaryProto
NestedProto
SingleNestedProto
*/
package examplepb
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// SimpleMessage represents a simple message sent to the Echo service.
type SimpleMessage struct {
// Id represents the message identifier.
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
}
func (m *SimpleMessage) Reset() { *m = SimpleMessage{} }
func (m *SimpleMessage) String() string { return proto.CompactTextString(m) }
func (*SimpleMessage) ProtoMessage() {}
func (*SimpleMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func init() {
proto.RegisterType((*SimpleMessage)(nil), "grpc.gateway.examples.examplepb.SimpleMessage")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for EchoService service
type EchoServiceClient interface {
// Echo method receives a simple message and returns it.
//
// The message posted as the id parameter will also be
// returned.
Echo(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error)
// EchoBody method receives a simple message and returns it.
EchoBody(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error)
}
type echoServiceClient struct {
cc *grpc.ClientConn
}
func NewEchoServiceClient(cc *grpc.ClientConn) EchoServiceClient {
return &echoServiceClient{cc}
}
func (c *echoServiceClient) Echo(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error) {
out := new(SimpleMessage)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.EchoService/Echo", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *echoServiceClient) EchoBody(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error) {
out := new(SimpleMessage)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.EchoService/EchoBody", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for EchoService service
type EchoServiceServer interface {
// Echo method receives a simple message and returns it.
//
// The message posted as the id parameter will also be
// returned.
Echo(context.Context, *SimpleMessage) (*SimpleMessage, error)
// EchoBody method receives a simple message and returns it.
EchoBody(context.Context, *SimpleMessage) (*SimpleMessage, error)
}
func RegisterEchoServiceServer(s *grpc.Server, srv EchoServiceServer) {
s.RegisterService(&_EchoService_serviceDesc, srv)
}
func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SimpleMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EchoServiceServer).Echo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.EchoService/Echo",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).Echo(ctx, req.(*SimpleMessage))
}
return interceptor(ctx, in, info, handler)
}
func _EchoService_EchoBody_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SimpleMessage)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(EchoServiceServer).EchoBody(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.EchoService/EchoBody",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(EchoServiceServer).EchoBody(ctx, req.(*SimpleMessage))
}
return interceptor(ctx, in, info, handler)
}
var _EchoService_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.gateway.examples.examplepb.EchoService",
HandlerType: (*EchoServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Echo",
Handler: _EchoService_Echo_Handler,
},
{
MethodName: "EchoBody",
Handler: _EchoService_EchoBody_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "examples/examplepb/echo_service.proto",
}
func init() { proto.RegisterFile("examples/examplepb/echo_service.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 229 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x52, 0x4d, 0xad, 0x48, 0xcc,
0x2d, 0xc8, 0x49, 0x2d, 0xd6, 0x87, 0x32, 0x0a, 0x92, 0xf4, 0x53, 0x93, 0x33, 0xf2, 0xe3, 0x8b,
0x53, 0x8b, 0xca, 0x32, 0x93, 0x53, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85, 0xe4, 0xd3, 0x8b,
0x0a, 0x92, 0xf5, 0xd2, 0x13, 0x4b, 0x52, 0xcb, 0x13, 0x2b, 0xf5, 0x60, 0x7a, 0xf4, 0xe0, 0x7a,
0xa4, 0x64, 0xd2, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0x13, 0x0b, 0x32, 0xf5, 0x13, 0xf3, 0xf2,
0xf2, 0x4b, 0x12, 0x4b, 0x32, 0xf3, 0xf3, 0x8a, 0x21, 0xda, 0x95, 0xe4, 0xb9, 0x78, 0x83, 0x33,
0x41, 0x2a, 0x7d, 0x53, 0x8b, 0x8b, 0x13, 0xd3, 0x53, 0x85, 0xf8, 0xb8, 0x98, 0x32, 0x53, 0x24,
0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0x98, 0x32, 0x53, 0x8c, 0x96, 0x30, 0x71, 0x71, 0xbb, 0x26,
0x67, 0xe4, 0x07, 0x43, 0x6c, 0x15, 0x6a, 0x65, 0xe4, 0x62, 0x01, 0xf1, 0x85, 0xf4, 0xf4, 0x08,
0xd8, 0xac, 0x87, 0x62, 0xb0, 0x14, 0x89, 0xea, 0x95, 0x64, 0x9b, 0x2e, 0x3f, 0x99, 0xcc, 0x24,
0xae, 0x24, 0xaa, 0x5f, 0x66, 0x08, 0x0b, 0x02, 0x70, 0x00, 0xe8, 0x57, 0x67, 0xa6, 0xd4, 0x0a,
0xf5, 0x30, 0x72, 0x71, 0x80, 0xdc, 0xe1, 0x94, 0x9f, 0x52, 0x49, 0x73, 0xb7, 0x28, 0x80, 0xdd,
0x22, 0x85, 0xe9, 0x96, 0xf8, 0xa4, 0xfc, 0x94, 0x4a, 0x2b, 0x46, 0x2d, 0x27, 0xee, 0x28, 0x4e,
0xb8, 0xe6, 0x24, 0x36, 0x70, 0xd8, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x26, 0x96, 0x37,
0xac, 0xc3, 0x01, 0x00, 0x00,
}

View file

@ -0,0 +1,169 @@
// Code generated by protoc-gen-grpc-gateway
// source: examples/examplepb/echo_service.proto
// DO NOT EDIT!
/*
Package examplepb is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package examplepb
import (
"io"
"net/http"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
)
var _ codes.Code
var _ io.Reader
var _ = runtime.String
var _ = utilities.NewDoubleArray
func request_EchoService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client EchoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SimpleMessage
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["id"]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
}
protoReq.Id, err = runtime.String(val)
if err != nil {
return nil, metadata, err
}
msg, err := client.Echo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_EchoService_EchoBody_0(ctx context.Context, marshaler runtime.Marshaler, client EchoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SimpleMessage
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.EchoBody(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterEchoServiceHandlerFromEndpoint is same as RegisterEchoServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterEchoServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterEchoServiceHandler(ctx, mux, conn)
}
// RegisterEchoServiceHandler registers the http handlers for service EchoService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterEchoServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
client := NewEchoServiceClient(conn)
mux.Handle("POST", pattern_EchoService_Echo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_EchoService_Echo_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_EchoService_Echo_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_EchoService_EchoBody_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_EchoService_EchoBody_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_EchoService_EchoBody_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_EchoService_Echo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "example", "echo", "id"}, ""))
pattern_EchoService_EchoBody_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "example", "echo_body"}, ""))
)
var (
forward_EchoService_Echo_0 = runtime.ForwardResponseMessage
forward_EchoService_EchoBody_0 = runtime.ForwardResponseMessage
)

View file

@ -0,0 +1,36 @@
syntax = "proto3";
option go_package = "examplepb";
// Echo Service
//
// Echo Service API consists of a single service which returns
// a message.
package grpc.gateway.examples.examplepb;
import "google/api/annotations.proto";
// SimpleMessage represents a simple message sent to the Echo service.
message SimpleMessage {
// Id represents the message identifier.
string id = 1;
}
// Echo service responds to incoming echo requests.
service EchoService {
// Echo method receives a simple message and returns it.
//
// The message posted as the id parameter will also be
// returned.
rpc Echo(SimpleMessage) returns (SimpleMessage) {
option (google.api.http) = {
post: "/v1/example/echo/{id}"
};
}
// EchoBody method receives a simple message and returns it.
rpc EchoBody(SimpleMessage) returns (SimpleMessage) {
option (google.api.http) = {
post: "/v1/example/echo_body"
body: "*"
};
}
}

View file

@ -0,0 +1,87 @@
{
"swagger": "2.0",
"info": {
"title": "Echo Service",
"description": "Echo Service API consists of a single service which returns\na message.",
"version": "version not set"
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/v1/example/echo/{id}": {
"post": {
"summary": "Echo method receives a simple message and returns it.",
"description": "The message posted as the id parameter will also be\nreturned.",
"operationId": "Echo",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/examplepbSimpleMessage"
}
}
},
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"type": "string",
"format": "string"
}
],
"tags": [
"EchoService"
]
}
},
"/v1/example/echo_body": {
"post": {
"summary": "EchoBody method receives a simple message and returns it.",
"operationId": "EchoBody",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/examplepbSimpleMessage"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/examplepbSimpleMessage"
}
}
],
"tags": [
"EchoService"
]
}
}
},
"definitions": {
"examplepbSimpleMessage": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "string",
"description": "Id represents the message identifier."
}
},
"description": "SimpleMessage represents a simple message sent to the Echo service."
}
}
}

View file

@ -0,0 +1,681 @@
// Code generated by protoc-gen-go.
// source: examples/examplepb/flow_combination.proto
// DO NOT EDIT!
package examplepb
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type EmptyProto struct {
}
func (m *EmptyProto) Reset() { *m = EmptyProto{} }
func (m *EmptyProto) String() string { return proto.CompactTextString(m) }
func (*EmptyProto) ProtoMessage() {}
func (*EmptyProto) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{0} }
type NonEmptyProto struct {
A string `protobuf:"bytes,1,opt,name=a" json:"a,omitempty"`
B string `protobuf:"bytes,2,opt,name=b" json:"b,omitempty"`
C string `protobuf:"bytes,3,opt,name=c" json:"c,omitempty"`
}
func (m *NonEmptyProto) Reset() { *m = NonEmptyProto{} }
func (m *NonEmptyProto) String() string { return proto.CompactTextString(m) }
func (*NonEmptyProto) ProtoMessage() {}
func (*NonEmptyProto) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{1} }
type UnaryProto struct {
Str string `protobuf:"bytes,1,opt,name=str" json:"str,omitempty"`
}
func (m *UnaryProto) Reset() { *m = UnaryProto{} }
func (m *UnaryProto) String() string { return proto.CompactTextString(m) }
func (*UnaryProto) ProtoMessage() {}
func (*UnaryProto) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{2} }
type NestedProto struct {
A *UnaryProto `protobuf:"bytes,1,opt,name=a" json:"a,omitempty"`
B string `protobuf:"bytes,2,opt,name=b" json:"b,omitempty"`
C string `protobuf:"bytes,3,opt,name=c" json:"c,omitempty"`
}
func (m *NestedProto) Reset() { *m = NestedProto{} }
func (m *NestedProto) String() string { return proto.CompactTextString(m) }
func (*NestedProto) ProtoMessage() {}
func (*NestedProto) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{3} }
func (m *NestedProto) GetA() *UnaryProto {
if m != nil {
return m.A
}
return nil
}
type SingleNestedProto struct {
A *UnaryProto `protobuf:"bytes,1,opt,name=a" json:"a,omitempty"`
}
func (m *SingleNestedProto) Reset() { *m = SingleNestedProto{} }
func (m *SingleNestedProto) String() string { return proto.CompactTextString(m) }
func (*SingleNestedProto) ProtoMessage() {}
func (*SingleNestedProto) Descriptor() ([]byte, []int) { return fileDescriptor3, []int{4} }
func (m *SingleNestedProto) GetA() *UnaryProto {
if m != nil {
return m.A
}
return nil
}
func init() {
proto.RegisterType((*EmptyProto)(nil), "grpc.gateway.examples.examplepb.EmptyProto")
proto.RegisterType((*NonEmptyProto)(nil), "grpc.gateway.examples.examplepb.NonEmptyProto")
proto.RegisterType((*UnaryProto)(nil), "grpc.gateway.examples.examplepb.UnaryProto")
proto.RegisterType((*NestedProto)(nil), "grpc.gateway.examples.examplepb.NestedProto")
proto.RegisterType((*SingleNestedProto)(nil), "grpc.gateway.examples.examplepb.SingleNestedProto")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for FlowCombination service
type FlowCombinationClient interface {
RpcEmptyRpc(ctx context.Context, in *EmptyProto, opts ...grpc.CallOption) (*EmptyProto, error)
RpcEmptyStream(ctx context.Context, in *EmptyProto, opts ...grpc.CallOption) (FlowCombination_RpcEmptyStreamClient, error)
StreamEmptyRpc(ctx context.Context, opts ...grpc.CallOption) (FlowCombination_StreamEmptyRpcClient, error)
StreamEmptyStream(ctx context.Context, opts ...grpc.CallOption) (FlowCombination_StreamEmptyStreamClient, error)
RpcBodyRpc(ctx context.Context, in *NonEmptyProto, opts ...grpc.CallOption) (*EmptyProto, error)
RpcPathSingleNestedRpc(ctx context.Context, in *SingleNestedProto, opts ...grpc.CallOption) (*EmptyProto, error)
RpcPathNestedRpc(ctx context.Context, in *NestedProto, opts ...grpc.CallOption) (*EmptyProto, error)
RpcBodyStream(ctx context.Context, in *NonEmptyProto, opts ...grpc.CallOption) (FlowCombination_RpcBodyStreamClient, error)
RpcPathSingleNestedStream(ctx context.Context, in *SingleNestedProto, opts ...grpc.CallOption) (FlowCombination_RpcPathSingleNestedStreamClient, error)
RpcPathNestedStream(ctx context.Context, in *NestedProto, opts ...grpc.CallOption) (FlowCombination_RpcPathNestedStreamClient, error)
}
type flowCombinationClient struct {
cc *grpc.ClientConn
}
func NewFlowCombinationClient(cc *grpc.ClientConn) FlowCombinationClient {
return &flowCombinationClient{cc}
}
func (c *flowCombinationClient) RpcEmptyRpc(ctx context.Context, in *EmptyProto, opts ...grpc.CallOption) (*EmptyProto, error) {
out := new(EmptyProto)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.FlowCombination/RpcEmptyRpc", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *flowCombinationClient) RpcEmptyStream(ctx context.Context, in *EmptyProto, opts ...grpc.CallOption) (FlowCombination_RpcEmptyStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_FlowCombination_serviceDesc.Streams[0], c.cc, "/grpc.gateway.examples.examplepb.FlowCombination/RpcEmptyStream", opts...)
if err != nil {
return nil, err
}
x := &flowCombinationRpcEmptyStreamClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type FlowCombination_RpcEmptyStreamClient interface {
Recv() (*EmptyProto, error)
grpc.ClientStream
}
type flowCombinationRpcEmptyStreamClient struct {
grpc.ClientStream
}
func (x *flowCombinationRpcEmptyStreamClient) Recv() (*EmptyProto, error) {
m := new(EmptyProto)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *flowCombinationClient) StreamEmptyRpc(ctx context.Context, opts ...grpc.CallOption) (FlowCombination_StreamEmptyRpcClient, error) {
stream, err := grpc.NewClientStream(ctx, &_FlowCombination_serviceDesc.Streams[1], c.cc, "/grpc.gateway.examples.examplepb.FlowCombination/StreamEmptyRpc", opts...)
if err != nil {
return nil, err
}
x := &flowCombinationStreamEmptyRpcClient{stream}
return x, nil
}
type FlowCombination_StreamEmptyRpcClient interface {
Send(*EmptyProto) error
CloseAndRecv() (*EmptyProto, error)
grpc.ClientStream
}
type flowCombinationStreamEmptyRpcClient struct {
grpc.ClientStream
}
func (x *flowCombinationStreamEmptyRpcClient) Send(m *EmptyProto) error {
return x.ClientStream.SendMsg(m)
}
func (x *flowCombinationStreamEmptyRpcClient) CloseAndRecv() (*EmptyProto, error) {
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
m := new(EmptyProto)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *flowCombinationClient) StreamEmptyStream(ctx context.Context, opts ...grpc.CallOption) (FlowCombination_StreamEmptyStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_FlowCombination_serviceDesc.Streams[2], c.cc, "/grpc.gateway.examples.examplepb.FlowCombination/StreamEmptyStream", opts...)
if err != nil {
return nil, err
}
x := &flowCombinationStreamEmptyStreamClient{stream}
return x, nil
}
type FlowCombination_StreamEmptyStreamClient interface {
Send(*EmptyProto) error
Recv() (*EmptyProto, error)
grpc.ClientStream
}
type flowCombinationStreamEmptyStreamClient struct {
grpc.ClientStream
}
func (x *flowCombinationStreamEmptyStreamClient) Send(m *EmptyProto) error {
return x.ClientStream.SendMsg(m)
}
func (x *flowCombinationStreamEmptyStreamClient) Recv() (*EmptyProto, error) {
m := new(EmptyProto)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *flowCombinationClient) RpcBodyRpc(ctx context.Context, in *NonEmptyProto, opts ...grpc.CallOption) (*EmptyProto, error) {
out := new(EmptyProto)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.FlowCombination/RpcBodyRpc", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *flowCombinationClient) RpcPathSingleNestedRpc(ctx context.Context, in *SingleNestedProto, opts ...grpc.CallOption) (*EmptyProto, error) {
out := new(EmptyProto)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.FlowCombination/RpcPathSingleNestedRpc", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *flowCombinationClient) RpcPathNestedRpc(ctx context.Context, in *NestedProto, opts ...grpc.CallOption) (*EmptyProto, error) {
out := new(EmptyProto)
err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.FlowCombination/RpcPathNestedRpc", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *flowCombinationClient) RpcBodyStream(ctx context.Context, in *NonEmptyProto, opts ...grpc.CallOption) (FlowCombination_RpcBodyStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_FlowCombination_serviceDesc.Streams[3], c.cc, "/grpc.gateway.examples.examplepb.FlowCombination/RpcBodyStream", opts...)
if err != nil {
return nil, err
}
x := &flowCombinationRpcBodyStreamClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type FlowCombination_RpcBodyStreamClient interface {
Recv() (*EmptyProto, error)
grpc.ClientStream
}
type flowCombinationRpcBodyStreamClient struct {
grpc.ClientStream
}
func (x *flowCombinationRpcBodyStreamClient) Recv() (*EmptyProto, error) {
m := new(EmptyProto)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *flowCombinationClient) RpcPathSingleNestedStream(ctx context.Context, in *SingleNestedProto, opts ...grpc.CallOption) (FlowCombination_RpcPathSingleNestedStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_FlowCombination_serviceDesc.Streams[4], c.cc, "/grpc.gateway.examples.examplepb.FlowCombination/RpcPathSingleNestedStream", opts...)
if err != nil {
return nil, err
}
x := &flowCombinationRpcPathSingleNestedStreamClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type FlowCombination_RpcPathSingleNestedStreamClient interface {
Recv() (*EmptyProto, error)
grpc.ClientStream
}
type flowCombinationRpcPathSingleNestedStreamClient struct {
grpc.ClientStream
}
func (x *flowCombinationRpcPathSingleNestedStreamClient) Recv() (*EmptyProto, error) {
m := new(EmptyProto)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *flowCombinationClient) RpcPathNestedStream(ctx context.Context, in *NestedProto, opts ...grpc.CallOption) (FlowCombination_RpcPathNestedStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_FlowCombination_serviceDesc.Streams[5], c.cc, "/grpc.gateway.examples.examplepb.FlowCombination/RpcPathNestedStream", opts...)
if err != nil {
return nil, err
}
x := &flowCombinationRpcPathNestedStreamClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type FlowCombination_RpcPathNestedStreamClient interface {
Recv() (*EmptyProto, error)
grpc.ClientStream
}
type flowCombinationRpcPathNestedStreamClient struct {
grpc.ClientStream
}
func (x *flowCombinationRpcPathNestedStreamClient) Recv() (*EmptyProto, error) {
m := new(EmptyProto)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for FlowCombination service
type FlowCombinationServer interface {
RpcEmptyRpc(context.Context, *EmptyProto) (*EmptyProto, error)
RpcEmptyStream(*EmptyProto, FlowCombination_RpcEmptyStreamServer) error
StreamEmptyRpc(FlowCombination_StreamEmptyRpcServer) error
StreamEmptyStream(FlowCombination_StreamEmptyStreamServer) error
RpcBodyRpc(context.Context, *NonEmptyProto) (*EmptyProto, error)
RpcPathSingleNestedRpc(context.Context, *SingleNestedProto) (*EmptyProto, error)
RpcPathNestedRpc(context.Context, *NestedProto) (*EmptyProto, error)
RpcBodyStream(*NonEmptyProto, FlowCombination_RpcBodyStreamServer) error
RpcPathSingleNestedStream(*SingleNestedProto, FlowCombination_RpcPathSingleNestedStreamServer) error
RpcPathNestedStream(*NestedProto, FlowCombination_RpcPathNestedStreamServer) error
}
func RegisterFlowCombinationServer(s *grpc.Server, srv FlowCombinationServer) {
s.RegisterService(&_FlowCombination_serviceDesc, srv)
}
func _FlowCombination_RpcEmptyRpc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(EmptyProto)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(FlowCombinationServer).RpcEmptyRpc(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.FlowCombination/RpcEmptyRpc",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FlowCombinationServer).RpcEmptyRpc(ctx, req.(*EmptyProto))
}
return interceptor(ctx, in, info, handler)
}
func _FlowCombination_RpcEmptyStream_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(EmptyProto)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(FlowCombinationServer).RpcEmptyStream(m, &flowCombinationRpcEmptyStreamServer{stream})
}
type FlowCombination_RpcEmptyStreamServer interface {
Send(*EmptyProto) error
grpc.ServerStream
}
type flowCombinationRpcEmptyStreamServer struct {
grpc.ServerStream
}
func (x *flowCombinationRpcEmptyStreamServer) Send(m *EmptyProto) error {
return x.ServerStream.SendMsg(m)
}
func _FlowCombination_StreamEmptyRpc_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(FlowCombinationServer).StreamEmptyRpc(&flowCombinationStreamEmptyRpcServer{stream})
}
type FlowCombination_StreamEmptyRpcServer interface {
SendAndClose(*EmptyProto) error
Recv() (*EmptyProto, error)
grpc.ServerStream
}
type flowCombinationStreamEmptyRpcServer struct {
grpc.ServerStream
}
func (x *flowCombinationStreamEmptyRpcServer) SendAndClose(m *EmptyProto) error {
return x.ServerStream.SendMsg(m)
}
func (x *flowCombinationStreamEmptyRpcServer) Recv() (*EmptyProto, error) {
m := new(EmptyProto)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _FlowCombination_StreamEmptyStream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(FlowCombinationServer).StreamEmptyStream(&flowCombinationStreamEmptyStreamServer{stream})
}
type FlowCombination_StreamEmptyStreamServer interface {
Send(*EmptyProto) error
Recv() (*EmptyProto, error)
grpc.ServerStream
}
type flowCombinationStreamEmptyStreamServer struct {
grpc.ServerStream
}
func (x *flowCombinationStreamEmptyStreamServer) Send(m *EmptyProto) error {
return x.ServerStream.SendMsg(m)
}
func (x *flowCombinationStreamEmptyStreamServer) Recv() (*EmptyProto, error) {
m := new(EmptyProto)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _FlowCombination_RpcBodyRpc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(NonEmptyProto)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(FlowCombinationServer).RpcBodyRpc(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.FlowCombination/RpcBodyRpc",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FlowCombinationServer).RpcBodyRpc(ctx, req.(*NonEmptyProto))
}
return interceptor(ctx, in, info, handler)
}
func _FlowCombination_RpcPathSingleNestedRpc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SingleNestedProto)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(FlowCombinationServer).RpcPathSingleNestedRpc(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.FlowCombination/RpcPathSingleNestedRpc",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FlowCombinationServer).RpcPathSingleNestedRpc(ctx, req.(*SingleNestedProto))
}
return interceptor(ctx, in, info, handler)
}
func _FlowCombination_RpcPathNestedRpc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(NestedProto)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(FlowCombinationServer).RpcPathNestedRpc(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpc.gateway.examples.examplepb.FlowCombination/RpcPathNestedRpc",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FlowCombinationServer).RpcPathNestedRpc(ctx, req.(*NestedProto))
}
return interceptor(ctx, in, info, handler)
}
func _FlowCombination_RpcBodyStream_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(NonEmptyProto)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(FlowCombinationServer).RpcBodyStream(m, &flowCombinationRpcBodyStreamServer{stream})
}
type FlowCombination_RpcBodyStreamServer interface {
Send(*EmptyProto) error
grpc.ServerStream
}
type flowCombinationRpcBodyStreamServer struct {
grpc.ServerStream
}
func (x *flowCombinationRpcBodyStreamServer) Send(m *EmptyProto) error {
return x.ServerStream.SendMsg(m)
}
func _FlowCombination_RpcPathSingleNestedStream_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(SingleNestedProto)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(FlowCombinationServer).RpcPathSingleNestedStream(m, &flowCombinationRpcPathSingleNestedStreamServer{stream})
}
type FlowCombination_RpcPathSingleNestedStreamServer interface {
Send(*EmptyProto) error
grpc.ServerStream
}
type flowCombinationRpcPathSingleNestedStreamServer struct {
grpc.ServerStream
}
func (x *flowCombinationRpcPathSingleNestedStreamServer) Send(m *EmptyProto) error {
return x.ServerStream.SendMsg(m)
}
func _FlowCombination_RpcPathNestedStream_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(NestedProto)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(FlowCombinationServer).RpcPathNestedStream(m, &flowCombinationRpcPathNestedStreamServer{stream})
}
type FlowCombination_RpcPathNestedStreamServer interface {
Send(*EmptyProto) error
grpc.ServerStream
}
type flowCombinationRpcPathNestedStreamServer struct {
grpc.ServerStream
}
func (x *flowCombinationRpcPathNestedStreamServer) Send(m *EmptyProto) error {
return x.ServerStream.SendMsg(m)
}
var _FlowCombination_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.gateway.examples.examplepb.FlowCombination",
HandlerType: (*FlowCombinationServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "RpcEmptyRpc",
Handler: _FlowCombination_RpcEmptyRpc_Handler,
},
{
MethodName: "RpcBodyRpc",
Handler: _FlowCombination_RpcBodyRpc_Handler,
},
{
MethodName: "RpcPathSingleNestedRpc",
Handler: _FlowCombination_RpcPathSingleNestedRpc_Handler,
},
{
MethodName: "RpcPathNestedRpc",
Handler: _FlowCombination_RpcPathNestedRpc_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "RpcEmptyStream",
Handler: _FlowCombination_RpcEmptyStream_Handler,
ServerStreams: true,
},
{
StreamName: "StreamEmptyRpc",
Handler: _FlowCombination_StreamEmptyRpc_Handler,
ClientStreams: true,
},
{
StreamName: "StreamEmptyStream",
Handler: _FlowCombination_StreamEmptyStream_Handler,
ServerStreams: true,
ClientStreams: true,
},
{
StreamName: "RpcBodyStream",
Handler: _FlowCombination_RpcBodyStream_Handler,
ServerStreams: true,
},
{
StreamName: "RpcPathSingleNestedStream",
Handler: _FlowCombination_RpcPathSingleNestedStream_Handler,
ServerStreams: true,
},
{
StreamName: "RpcPathNestedStream",
Handler: _FlowCombination_RpcPathNestedStream_Handler,
ServerStreams: true,
},
},
Metadata: "examples/examplepb/flow_combination.proto",
}
func init() { proto.RegisterFile("examples/examplepb/flow_combination.proto", fileDescriptor3) }
var fileDescriptor3 = []byte{
// 656 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x96, 0x3f, 0x8f, 0x12, 0x4f,
0x18, 0xc7, 0xf3, 0x70, 0xc9, 0x2f, 0xb9, 0xe1, 0xfe, 0x70, 0xcb, 0x2f, 0x08, 0x1c, 0x1e, 0x77,
0xe3, 0x25, 0xe2, 0xbf, 0x5d, 0x82, 0xd5, 0x51, 0x9e, 0xd1, 0x92, 0x5c, 0xb8, 0xd8, 0x6c, 0x63,
0x66, 0x87, 0x15, 0x48, 0x60, 0x67, 0x6e, 0x77, 0x0d, 0x5e, 0x08, 0x31, 0xb1, 0xb1, 0xb4, 0xf0,
0x05, 0x58, 0x5a, 0xf9, 0x06, 0xec, 0xac, 0x6c, 0x4c, 0x2c, 0x4c, 0xec, 0xec, 0xec, 0x7c, 0x13,
0x66, 0x67, 0x66, 0x77, 0x58, 0x05, 0x37, 0x18, 0xb1, 0xdb, 0x99, 0x79, 0x9e, 0x67, 0x3e, 0xf3,
0x7d, 0xbe, 0x0f, 0x01, 0xdd, 0x70, 0x9f, 0x92, 0x31, 0x1f, 0xb9, 0x81, 0xa5, 0x3e, 0xb8, 0x63,
0x3d, 0x1e, 0xb1, 0xc9, 0x23, 0xca, 0xc6, 0xce, 0xd0, 0x23, 0xe1, 0x90, 0x79, 0x26, 0xf7, 0x59,
0xc8, 0x8c, 0x7a, 0xdf, 0xe7, 0xd4, 0xec, 0x93, 0xd0, 0x9d, 0x90, 0x4b, 0x33, 0xce, 0x33, 0x93,
0xbc, 0x6a, 0xad, 0xcf, 0x58, 0x7f, 0xe4, 0x5a, 0x84, 0x0f, 0x2d, 0xe2, 0x79, 0x2c, 0x14, 0xd9,
0x81, 0x4c, 0xc7, 0x5b, 0x08, 0xdd, 0x1f, 0xf3, 0xf0, 0xf2, 0x4c, 0xac, 0x4e, 0xd0, 0x76, 0x87,
0x79, 0x7a, 0xc3, 0xd8, 0x42, 0x40, 0xca, 0x70, 0x08, 0x8d, 0xcd, 0x2e, 0x90, 0x68, 0xe5, 0x94,
0x73, 0x72, 0xe5, 0x44, 0x2b, 0x5a, 0xde, 0x90, 0x2b, 0x8a, 0x0f, 0x10, 0x7a, 0xe8, 0x11, 0x5f,
0xe5, 0x15, 0xd0, 0x46, 0x10, 0xfa, 0x2a, 0x33, 0xfa, 0xc4, 0x3d, 0x94, 0xef, 0xb8, 0x41, 0xe8,
0xf6, 0x64, 0xc0, 0x49, 0x5c, 0x38, 0xdf, 0xba, 0x65, 0x66, 0x3c, 0xc1, 0xd4, 0x85, 0xb3, 0x28,
0x3a, 0x68, 0xef, 0x7c, 0xe8, 0xf5, 0x47, 0xee, 0xdf, 0xb9, 0xab, 0xf5, 0x71, 0x17, 0xed, 0x3e,
0x18, 0xb1, 0xc9, 0x3d, 0xad, 0xbb, 0xf1, 0x0c, 0xe5, 0xbb, 0x9c, 0x0a, 0x91, 0xba, 0x9c, 0x1a,
0xd9, 0x25, 0xb5, 0x9e, 0xd5, 0x55, 0x82, 0x71, 0xe9, 0xf9, 0xe7, 0x6f, 0xaf, 0x72, 0x05, 0xbc,
0x63, 0xf9, 0x9c, 0x5a, 0x6e, 0x74, 0x10, 0x7d, 0x19, 0x2f, 0x00, 0xed, 0xc4, 0x04, 0xe7, 0xa1,
0xef, 0x92, 0xf1, 0x1a, 0x21, 0x2a, 0x02, 0xa2, 0x88, 0xf7, 0xe6, 0x20, 0x02, 0x71, 0x69, 0x13,
0x04, 0x89, 0x24, 0xf8, 0x07, 0x72, 0x68, 0x12, 0x79, 0xbf, 0x56, 0xa4, 0x01, 0xc6, 0x4b, 0x40,
0x7b, 0x73, 0x24, 0x6b, 0x97, 0xa5, 0x26, 0x60, 0x4a, 0xf8, 0xff, 0x34, 0x8c, 0x5c, 0x34, 0xa0,
0x09, 0xc6, 0xdb, 0x1c, 0x42, 0x5d, 0x4e, 0x4f, 0x59, 0x4f, 0xe8, 0x62, 0x66, 0x56, 0x4f, 0x4d,
0xde, 0x6a, 0x34, 0xef, 0x41, 0xe0, 0xbc, 0x03, 0xbc, 0x2d, 0xda, 0xe4, 0xb0, 0x9e, 0x10, 0xa6,
0x0d, 0x37, 0xed, 0x7d, 0x5c, 0x11, 0x7b, 0x9c, 0x84, 0x03, 0x6b, 0x4a, 0x66, 0xd6, 0xd4, 0x99,
0x59, 0x53, 0x3a, 0x8b, 0x36, 0xed, 0xd8, 0x5c, 0x17, 0x4f, 0x5c, 0x5f, 0x64, 0xd8, 0x75, 0x5c,
0xd5, 0x25, 0x52, 0x39, 0xa2, 0x1e, 0xb5, 0xcb, 0xb8, 0xa8, 0x03, 0x92, 0xbc, 0xe8, 0xe4, 0x08,
0xd7, 0x16, 0xa4, 0xa6, 0x42, 0x2a, 0xf8, 0x4a, 0x1a, 0x26, 0x39, 0x35, 0x5e, 0x03, 0x2a, 0x75,
0x39, 0x3d, 0x23, 0xe1, 0x60, 0x7e, 0x84, 0x23, 0xed, 0x5a, 0x99, 0x5a, 0xfc, 0x32, 0xf4, 0xab,
0xe9, 0x77, 0x2c, 0xe4, 0x3b, 0x50, 0xfc, 0x11, 0xdc, 0x1d, 0x4f, 0xd4, 0xb2, 0xa6, 0xc4, 0x0c,
0x42, 0x5f, 0x3c, 0xde, 0xf8, 0x0a, 0xa8, 0xa0, 0x08, 0x35, 0xdb, 0xed, 0xec, 0xbe, 0xfe, 0x29,
0x95, 0x27, 0xa8, 0x06, 0xf8, 0x70, 0x29, 0xd5, 0x5c, 0x5b, 0x32, 0xe0, 0x93, 0xe6, 0x2c, 0x39,
0x6f, 0x03, 0x35, 0x3e, 0xe4, 0xd0, 0xb6, 0x72, 0xac, 0x9a, 0x9f, 0xb5, 0x9a, 0xf6, 0x8b, 0x34,
0xed, 0x27, 0xc0, 0x05, 0x6d, 0x1b, 0x39, 0x40, 0x91, 0x6f, 0xe7, 0x1f, 0x94, 0xf2, 0xad, 0x0c,
0xb1, 0xe3, 0x9f, 0x24, 0xe9, 0x20, 0xb5, 0x89, 0xf1, 0xd5, 0x25, 0xee, 0x8d, 0x0b, 0x53, 0x7b,
0x1f, 0x97, 0x7e, 0x36, 0xb0, 0x3e, 0x3c, 0xc6, 0xf5, 0xa5, 0x1e, 0xd6, 0x51, 0x35, 0x35, 0x24,
0x0b, 0x03, 0x9a, 0x60, 0xbc, 0x01, 0x54, 0x59, 0xe0, 0x65, 0xa5, 0xea, 0xda, 0xed, 0x7c, 0x5d,
0x08, 0x7b, 0xa4, 0x9e, 0xb2, 0xa8, 0xe3, 0x09, 0xe9, 0x77, 0x40, 0xc5, 0x94, 0xa7, 0x15, 0xe3,
0x1a, 0x6d, 0x3d, 0x11, 0x74, 0x17, 0xf8, 0xda, 0x6f, 0x6d, 0xad, 0xc5, 0xce, 0x7e, 0x47, 0xd2,
0xb5, 0xe5, 0x21, 0x6d, 0xa0, 0x4d, 0x38, 0xcd, 0xdb, 0x9b, 0x09, 0x92, 0xf3, 0x9f, 0xf8, 0x07,
0x74, 0xf7, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0xaf, 0x85, 0xaf, 0x3c, 0x6d, 0x09, 0x00, 0x00,
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,168 @@
syntax = "proto3";
option go_package = "examplepb";
package grpc.gateway.examples.examplepb;
import "google/api/annotations.proto";
message EmptyProto {}
message NonEmptyProto {
string a = 1;
string b = 2;
string c = 3;
}
message UnaryProto {
string str = 1;
}
message NestedProto {
UnaryProto a = 1;
string b = 2;
string c = 3;
}
message SingleNestedProto {
UnaryProto a = 1;
}
service FlowCombination {
rpc RpcEmptyRpc(EmptyProto) returns (EmptyProto) {
option (google.api.http) = {
post: "/rpc/empty/rpc"
};
}
rpc RpcEmptyStream(EmptyProto) returns (stream EmptyProto) {
option (google.api.http) = {
post: "/rpc/empty/stream"
};
}
rpc StreamEmptyRpc(stream EmptyProto) returns (EmptyProto) {
option (google.api.http) = {
post: "/stream/empty/rpc"
};
}
rpc StreamEmptyStream(stream EmptyProto) returns (stream EmptyProto) {
option (google.api.http) = {
post: "/stream/empty/stream"
};
}
rpc RpcBodyRpc(NonEmptyProto) returns (EmptyProto) {
option (google.api.http) = {
// w/ body; w/o path; w/o query
post: "/rpc/body/rpc"
body: "*"
// w/o body; w/ path; w/o query
additional_bindings {
post: "/rpc/path/{a}/{b}/{c}/rpc"
}
// w/o body; w/o path; w/ query
additional_bindings {
post: "/rpc/query/rpc"
}
// w/ body; w/ path; w/o query
additional_bindings {
post: "/rpc/body/path/{a}/{b}/rpc"
body: "c"
}
// w/ body; w/o path; w/ query
additional_bindings {
post: "/rpc/body/query/rpc"
body: "c"
}
// w/ body; w/ path; w/ query
additional_bindings {
post: "/rpc/body/path/{a}/query/rpc"
body: "c"
}
// w/o body; w/ path; w/ query
additional_bindings {
post: "/rpc/path/{a}/query/rpc"
}
};
}
rpc RpcPathSingleNestedRpc(SingleNestedProto) returns (EmptyProto) {
option (google.api.http) = {
// w/o body; w/ path (IsNestedProto3); w/o query
post: "/rpc/path-nested/{a.str}/rpc"
};
}
rpc RpcPathNestedRpc(NestedProto) returns (EmptyProto) {
option (google.api.http) = {
// w/ body; w/ path (IsNestedProto3); w/o query
post: "/rpc/path-nested/{a.str}/{b}/rpc"
body: "c"
// w/o body; w/ path (IsNestedProto3); w/ query
additional_bindings {
post: "/rpc/path-nested/{a.str}/rpc"
}
// w/ body; w/ path (IsNestedProto3); w/ query
additional_bindings {
post: "/rpc/path-nested/{a.str}/rpc"
body: "c"
}
};
}
rpc RpcBodyStream(NonEmptyProto) returns (stream EmptyProto) {
option (google.api.http) = {
// w/ body; w/o path; w/o query
post: "/rpc/body/stream"
body: "*"
// w/o body; w/ path; w/o query
additional_bindings {
post: "/rpc/path/{a}/{b}/{c}/stream"
}
// w/o body; w/o path; w/ query
additional_bindings {
post: "/rpc/query/stream"
}
// w/ body; w/ path; w/o query
additional_bindings {
post: "/rpc/body/path/{a}/{b}/stream"
body: "c"
}
// w/ body; w/o path; w/ query
additional_bindings {
post: "/rpc/body/query/stream"
body: "c"
}
// w/ body; w/ path; w/ query
additional_bindings {
post: "/rpc/body/path/{a}/query/stream"
body: "c"
}
// w/o body; w/ path; w/ query
additional_bindings {
post: "/rpc/path/{a}/query/stream"
}
};
}
rpc RpcPathSingleNestedStream(SingleNestedProto) returns (stream EmptyProto) {
option (google.api.http) = {
// w/o body; w/ path (IsNestedProto3); w/o query
post: "/rpc/path-nested/{a.str}/stream"
};
}
rpc RpcPathNestedStream(NestedProto) returns (stream EmptyProto) {
option (google.api.http) = {
// w/ body; w/ path (IsNestedProto3); w/o query
post: "/rpc/path-nested/{a.str}/{b}/stream"
body: "c"
// w/o body; w/ path (IsNestedProto3); w/ query
additional_bindings {
post: "/rpc/path-nested/{a.str}/stream"
}
// w/ body; w/ path (IsNestedProto3); w/ query
additional_bindings {
post: "/rpc/path-nested/{a.str}/stream"
body: "c"
}
};
}
}

View file

@ -0,0 +1,279 @@
// Code generated by protoc-gen-go.
// source: examples/examplepb/stream.proto
// DO NOT EDIT!
package examplepb
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import _ "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"
import google_protobuf1 "github.com/golang/protobuf/ptypes/empty"
import grpc_gateway_examples_sub "github.com/grpc-ecosystem/grpc-gateway/examples/sub"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for StreamService service
type StreamServiceClient interface {
BulkCreate(ctx context.Context, opts ...grpc.CallOption) (StreamService_BulkCreateClient, error)
List(ctx context.Context, in *google_protobuf1.Empty, opts ...grpc.CallOption) (StreamService_ListClient, error)
BulkEcho(ctx context.Context, opts ...grpc.CallOption) (StreamService_BulkEchoClient, error)
}
type streamServiceClient struct {
cc *grpc.ClientConn
}
func NewStreamServiceClient(cc *grpc.ClientConn) StreamServiceClient {
return &streamServiceClient{cc}
}
func (c *streamServiceClient) BulkCreate(ctx context.Context, opts ...grpc.CallOption) (StreamService_BulkCreateClient, error) {
stream, err := grpc.NewClientStream(ctx, &_StreamService_serviceDesc.Streams[0], c.cc, "/grpc.gateway.examples.examplepb.StreamService/BulkCreate", opts...)
if err != nil {
return nil, err
}
x := &streamServiceBulkCreateClient{stream}
return x, nil
}
type StreamService_BulkCreateClient interface {
Send(*ABitOfEverything) error
CloseAndRecv() (*google_protobuf1.Empty, error)
grpc.ClientStream
}
type streamServiceBulkCreateClient struct {
grpc.ClientStream
}
func (x *streamServiceBulkCreateClient) Send(m *ABitOfEverything) error {
return x.ClientStream.SendMsg(m)
}
func (x *streamServiceBulkCreateClient) CloseAndRecv() (*google_protobuf1.Empty, error) {
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
m := new(google_protobuf1.Empty)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *streamServiceClient) List(ctx context.Context, in *google_protobuf1.Empty, opts ...grpc.CallOption) (StreamService_ListClient, error) {
stream, err := grpc.NewClientStream(ctx, &_StreamService_serviceDesc.Streams[1], c.cc, "/grpc.gateway.examples.examplepb.StreamService/List", opts...)
if err != nil {
return nil, err
}
x := &streamServiceListClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type StreamService_ListClient interface {
Recv() (*ABitOfEverything, error)
grpc.ClientStream
}
type streamServiceListClient struct {
grpc.ClientStream
}
func (x *streamServiceListClient) Recv() (*ABitOfEverything, error) {
m := new(ABitOfEverything)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *streamServiceClient) BulkEcho(ctx context.Context, opts ...grpc.CallOption) (StreamService_BulkEchoClient, error) {
stream, err := grpc.NewClientStream(ctx, &_StreamService_serviceDesc.Streams[2], c.cc, "/grpc.gateway.examples.examplepb.StreamService/BulkEcho", opts...)
if err != nil {
return nil, err
}
x := &streamServiceBulkEchoClient{stream}
return x, nil
}
type StreamService_BulkEchoClient interface {
Send(*grpc_gateway_examples_sub.StringMessage) error
Recv() (*grpc_gateway_examples_sub.StringMessage, error)
grpc.ClientStream
}
type streamServiceBulkEchoClient struct {
grpc.ClientStream
}
func (x *streamServiceBulkEchoClient) Send(m *grpc_gateway_examples_sub.StringMessage) error {
return x.ClientStream.SendMsg(m)
}
func (x *streamServiceBulkEchoClient) Recv() (*grpc_gateway_examples_sub.StringMessage, error) {
m := new(grpc_gateway_examples_sub.StringMessage)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for StreamService service
type StreamServiceServer interface {
BulkCreate(StreamService_BulkCreateServer) error
List(*google_protobuf1.Empty, StreamService_ListServer) error
BulkEcho(StreamService_BulkEchoServer) error
}
func RegisterStreamServiceServer(s *grpc.Server, srv StreamServiceServer) {
s.RegisterService(&_StreamService_serviceDesc, srv)
}
func _StreamService_BulkCreate_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(StreamServiceServer).BulkCreate(&streamServiceBulkCreateServer{stream})
}
type StreamService_BulkCreateServer interface {
SendAndClose(*google_protobuf1.Empty) error
Recv() (*ABitOfEverything, error)
grpc.ServerStream
}
type streamServiceBulkCreateServer struct {
grpc.ServerStream
}
func (x *streamServiceBulkCreateServer) SendAndClose(m *google_protobuf1.Empty) error {
return x.ServerStream.SendMsg(m)
}
func (x *streamServiceBulkCreateServer) Recv() (*ABitOfEverything, error) {
m := new(ABitOfEverything)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _StreamService_List_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(google_protobuf1.Empty)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(StreamServiceServer).List(m, &streamServiceListServer{stream})
}
type StreamService_ListServer interface {
Send(*ABitOfEverything) error
grpc.ServerStream
}
type streamServiceListServer struct {
grpc.ServerStream
}
func (x *streamServiceListServer) Send(m *ABitOfEverything) error {
return x.ServerStream.SendMsg(m)
}
func _StreamService_BulkEcho_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(StreamServiceServer).BulkEcho(&streamServiceBulkEchoServer{stream})
}
type StreamService_BulkEchoServer interface {
Send(*grpc_gateway_examples_sub.StringMessage) error
Recv() (*grpc_gateway_examples_sub.StringMessage, error)
grpc.ServerStream
}
type streamServiceBulkEchoServer struct {
grpc.ServerStream
}
func (x *streamServiceBulkEchoServer) Send(m *grpc_gateway_examples_sub.StringMessage) error {
return x.ServerStream.SendMsg(m)
}
func (x *streamServiceBulkEchoServer) Recv() (*grpc_gateway_examples_sub.StringMessage, error) {
m := new(grpc_gateway_examples_sub.StringMessage)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
var _StreamService_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.gateway.examples.examplepb.StreamService",
HandlerType: (*StreamServiceServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "BulkCreate",
Handler: _StreamService_BulkCreate_Handler,
ClientStreams: true,
},
{
StreamName: "List",
Handler: _StreamService_List_Handler,
ServerStreams: true,
},
{
StreamName: "BulkEcho",
Handler: _StreamService_BulkEcho_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "examples/examplepb/stream.proto",
}
func init() { proto.RegisterFile("examples/examplepb/stream.proto", fileDescriptor2) }
var fileDescriptor2 = []byte{
// 314 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x90, 0xbf, 0x4a, 0x43, 0x31,
0x14, 0xc6, 0xb9, 0x2a, 0xa2, 0x11, 0x97, 0x0c, 0x0e, 0x51, 0x28, 0x16, 0xc1, 0x2a, 0x92, 0xb4,
0xba, 0xb9, 0x59, 0xe9, 0xa6, 0x38, 0x74, 0x73, 0x29, 0xc9, 0xe5, 0x34, 0x0d, 0xbd, 0xf7, 0x26,
0x24, 0xe7, 0x56, 0x0b, 0x4e, 0x8e, 0xae, 0x7d, 0x11, 0xdf, 0xc5, 0x57, 0xf0, 0x41, 0xa4, 0xf7,
0xdf, 0xd4, 0xd2, 0xba, 0x25, 0x9c, 0x2f, 0xf9, 0x7e, 0xe7, 0x47, 0x5a, 0xf0, 0x2e, 0x53, 0x97,
0x40, 0x10, 0xd5, 0xc1, 0x29, 0x11, 0xd0, 0x83, 0x4c, 0xb9, 0xf3, 0x16, 0x2d, 0x6d, 0x69, 0xef,
0x62, 0xae, 0x25, 0xc2, 0x9b, 0x9c, 0xf3, 0x3a, 0xcd, 0x9b, 0x34, 0x3b, 0xd3, 0xd6, 0xea, 0x04,
0x84, 0x74, 0x46, 0xc8, 0x2c, 0xb3, 0x28, 0xd1, 0xd8, 0x2c, 0x94, 0xcf, 0xd9, 0x69, 0x35, 0x2d,
0x6e, 0x2a, 0x1f, 0x0b, 0x48, 0x1d, 0xce, 0xab, 0xe1, 0xcd, 0x8a, 0x72, 0x39, 0x52, 0x06, 0x47,
0x76, 0x3c, 0x82, 0x19, 0xf8, 0x39, 0x4e, 0x4c, 0xa6, 0xab, 0x34, 0x6b, 0xd2, 0x21, 0x57, 0x22,
0x85, 0x10, 0xa4, 0x86, 0x72, 0x76, 0xfb, 0xbd, 0x4b, 0x8e, 0x87, 0x05, 0xf6, 0x10, 0xfc, 0xcc,
0xc4, 0x40, 0xbf, 0x22, 0x42, 0xfa, 0x79, 0x32, 0x7d, 0xf4, 0x20, 0x11, 0x68, 0x8f, 0x6f, 0xd8,
0x83, 0x3f, 0xf4, 0x0d, 0xbe, 0x8c, 0x07, 0x4d, 0x2b, 0x3b, 0xe1, 0x25, 0x3b, 0xaf, 0xd9, 0xf9,
0x60, 0xc9, 0xde, 0x16, 0x9f, 0x3f, 0xbf, 0x8b, 0x9d, 0xab, 0xf6, 0x85, 0x98, 0xf5, 0x6a, 0xf0,
0x55, 0xd8, 0x42, 0xe5, 0xc9, 0xf4, 0x3e, 0xba, 0xee, 0x44, 0xf4, 0x83, 0xec, 0x3d, 0x99, 0x80,
0x74, 0xcd, 0x97, 0xec, 0xff, 0x74, 0xed, 0xcb, 0x82, 0xe2, 0x9c, 0xb6, 0x36, 0x50, 0x74, 0x23,
0xba, 0x88, 0xc8, 0xc1, 0x52, 0xc5, 0x20, 0x9e, 0x58, 0xda, 0x59, 0x53, 0x15, 0x72, 0xc5, 0x87,
0xe8, 0x4d, 0xa6, 0x9f, 0x4b, 0xb3, 0x6c, 0xeb, 0xe4, 0xf6, 0x46, 0x20, 0x9e, 0xd8, 0xc2, 0x48,
0x37, 0xea, 0x1f, 0xbd, 0x1e, 0x36, 0xeb, 0xa9, 0xfd, 0x42, 0xc8, 0xdd, 0x5f, 0x00, 0x00, 0x00,
0xff, 0xff, 0xbc, 0x52, 0x49, 0x85, 0x8f, 0x02, 0x00, 0x00,
}

View file

@ -0,0 +1,273 @@
// Code generated by protoc-gen-grpc-gateway
// source: examples/examplepb/stream.proto
// DO NOT EDIT!
/*
Package examplepb is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package examplepb
import (
"io"
"net/http"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/empty"
"github.com/grpc-ecosystem/grpc-gateway/examples/sub"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
)
var _ codes.Code
var _ io.Reader
var _ = runtime.String
var _ = utilities.NewDoubleArray
func request_StreamService_BulkCreate_0(ctx context.Context, marshaler runtime.Marshaler, client StreamServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var metadata runtime.ServerMetadata
stream, err := client.BulkCreate(ctx)
if err != nil {
grpclog.Printf("Failed to start streaming: %v", err)
return nil, metadata, err
}
dec := marshaler.NewDecoder(req.Body)
for {
var protoReq ABitOfEverything
err = dec.Decode(&protoReq)
if err == io.EOF {
break
}
if err != nil {
grpclog.Printf("Failed to decode request: %v", err)
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
if err = stream.Send(&protoReq); err != nil {
grpclog.Printf("Failed to send request: %v", err)
return nil, metadata, err
}
}
if err := stream.CloseSend(); err != nil {
grpclog.Printf("Failed to terminate client stream: %v", err)
return nil, metadata, err
}
header, err := stream.Header()
if err != nil {
grpclog.Printf("Failed to get header from client: %v", err)
return nil, metadata, err
}
metadata.HeaderMD = header
msg, err := stream.CloseAndRecv()
metadata.TrailerMD = stream.Trailer()
return msg, metadata, err
}
func request_StreamService_List_0(ctx context.Context, marshaler runtime.Marshaler, client StreamServiceClient, req *http.Request, pathParams map[string]string) (StreamService_ListClient, runtime.ServerMetadata, error) {
var protoReq empty.Empty
var metadata runtime.ServerMetadata
stream, err := client.List(ctx, &protoReq)
if err != nil {
return nil, metadata, err
}
header, err := stream.Header()
if err != nil {
return nil, metadata, err
}
metadata.HeaderMD = header
return stream, metadata, nil
}
func request_StreamService_BulkEcho_0(ctx context.Context, marshaler runtime.Marshaler, client StreamServiceClient, req *http.Request, pathParams map[string]string) (StreamService_BulkEchoClient, runtime.ServerMetadata, error) {
var metadata runtime.ServerMetadata
stream, err := client.BulkEcho(ctx)
if err != nil {
grpclog.Printf("Failed to start streaming: %v", err)
return nil, metadata, err
}
dec := marshaler.NewDecoder(req.Body)
handleSend := func() error {
var protoReq sub.StringMessage
err = dec.Decode(&protoReq)
if err == io.EOF {
return err
}
if err != nil {
grpclog.Printf("Failed to decode request: %v", err)
return err
}
if err = stream.Send(&protoReq); err != nil {
grpclog.Printf("Failed to send request: %v", err)
return err
}
return nil
}
if err := handleSend(); err != nil {
if cerr := stream.CloseSend(); cerr != nil {
grpclog.Printf("Failed to terminate client stream: %v", cerr)
}
if err == io.EOF {
return stream, metadata, nil
}
return nil, metadata, err
}
go func() {
for {
if err := handleSend(); err != nil {
break
}
}
if err := stream.CloseSend(); err != nil {
grpclog.Printf("Failed to terminate client stream: %v", err)
}
}()
header, err := stream.Header()
if err != nil {
grpclog.Printf("Failed to get header from client: %v", err)
return nil, metadata, err
}
metadata.HeaderMD = header
return stream, metadata, nil
}
// RegisterStreamServiceHandlerFromEndpoint is same as RegisterStreamServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterStreamServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterStreamServiceHandler(ctx, mux, conn)
}
// RegisterStreamServiceHandler registers the http handlers for service StreamService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterStreamServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
client := NewStreamServiceClient(conn)
mux.Handle("POST", pattern_StreamService_BulkCreate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_StreamService_BulkCreate_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_StreamService_BulkCreate_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_StreamService_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_StreamService_List_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_StreamService_List_0(ctx, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_StreamService_BulkEcho_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_StreamService_BulkEcho_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_StreamService_BulkEcho_0(ctx, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_StreamService_BulkCreate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "example", "a_bit_of_everything", "bulk"}, ""))
pattern_StreamService_List_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "example", "a_bit_of_everything"}, ""))
pattern_StreamService_BulkEcho_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "example", "a_bit_of_everything", "echo"}, ""))
)
var (
forward_StreamService_BulkCreate_0 = runtime.ForwardResponseMessage
forward_StreamService_List_0 = runtime.ForwardResponseStream
forward_StreamService_BulkEcho_0 = runtime.ForwardResponseStream
)

View file

@ -0,0 +1,29 @@
syntax = "proto3";
option go_package = "examplepb";
package grpc.gateway.examples.examplepb;
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "examples/examplepb/a_bit_of_everything.proto";
import "examples/sub/message.proto";
// Defines some more operations to be added to ABitOfEverythingService
service StreamService {
rpc BulkCreate(stream ABitOfEverything) returns (google.protobuf.Empty) {
option (google.api.http) = {
post: "/v1/example/a_bit_of_everything/bulk"
body: "*"
};
}
rpc List(google.protobuf.Empty) returns (stream ABitOfEverything) {
option (google.api.http) = {
get: "/v1/example/a_bit_of_everything"
};
}
rpc BulkEcho(stream grpc.gateway.examples.sub.StringMessage) returns (stream grpc.gateway.examples.sub.StringMessage) {
option (google.api.http) = {
post: "/v1/example/a_bit_of_everything/echo"
body: "*"
};
}
}

View file

@ -0,0 +1,719 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"reflect"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/empty"
gw "github.com/grpc-ecosystem/grpc-gateway/examples/examplepb"
sub "github.com/grpc-ecosystem/grpc-gateway/examples/sub"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
)
type errorBody struct {
Error string `json:"error"`
Code int `json:"code"`
}
func TestEcho(t *testing.T) {
if testing.Short() {
t.Skip()
return
}
testEcho(t, 8080, "application/json")
testEchoBody(t)
}
func TestForwardResponseOption(t *testing.T) {
go func() {
if err := Run(
":8081",
runtime.WithForwardResponseOption(
func(_ context.Context, w http.ResponseWriter, _ proto.Message) error {
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.1+json")
return nil
},
),
); err != nil {
t.Errorf("gw.Run() failed with %v; want success", err)
return
}
}()
time.Sleep(100 * time.Millisecond)
testEcho(t, 8081, "application/vnd.docker.plugins.v1.1+json")
}
func testEcho(t *testing.T, port int, contentType string) {
url := fmt.Sprintf("http://localhost:%d/v1/example/echo/myid", port)
resp, err := http.Post(url, "application/json", strings.NewReader("{}"))
if err != nil {
t.Errorf("http.Post(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err)
return
}
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
t.Logf("%s", buf)
}
var msg gw.SimpleMessage
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if got, want := msg.Id, "myid"; got != want {
t.Errorf("msg.Id = %q; want %q", got, want)
}
if value := resp.Header.Get("Content-Type"); value != contentType {
t.Errorf("Content-Type was %s, wanted %s", value, contentType)
}
}
func testEchoBody(t *testing.T) {
sent := gw.SimpleMessage{Id: "example"}
var m jsonpb.Marshaler
payload, err := m.MarshalToString(&sent)
if err != nil {
t.Fatalf("m.MarshalToString(%#v) failed with %v; want success", payload, err)
}
url := "http://localhost:8080/v1/example/echo_body"
resp, err := http.Post(url, "", strings.NewReader(payload))
if err != nil {
t.Errorf("http.Post(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err)
return
}
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
t.Logf("%s", buf)
}
var received gw.SimpleMessage
if err := jsonpb.UnmarshalString(string(buf), &received); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if got, want := received, sent; !reflect.DeepEqual(got, want) {
t.Errorf("msg.Id = %q; want %q", got, want)
}
if got, want := resp.Header.Get("Grpc-Metadata-Foo"), "foo1"; got != want {
t.Errorf("Grpc-Header-Foo was %q, wanted %q", got, want)
}
if got, want := resp.Header.Get("Grpc-Metadata-Bar"), "bar1"; got != want {
t.Errorf("Grpc-Header-Bar was %q, wanted %q", got, want)
}
if got, want := resp.Trailer.Get("Grpc-Trailer-Foo"), "foo2"; got != want {
t.Errorf("Grpc-Trailer-Foo was %q, wanted %q", got, want)
}
if got, want := resp.Trailer.Get("Grpc-Trailer-Bar"), "bar2"; got != want {
t.Errorf("Grpc-Trailer-Bar was %q, wanted %q", got, want)
}
}
func TestABE(t *testing.T) {
if testing.Short() {
t.Skip()
return
}
testABECreate(t)
testABECreateBody(t)
testABEBulkCreate(t)
testABELookup(t)
testABELookupNotFound(t)
testABEList(t)
testABEBulkEcho(t)
testABEBulkEchoZeroLength(t)
testAdditionalBindings(t)
}
func testABECreate(t *testing.T) {
want := gw.ABitOfEverything{
FloatValue: 1.5,
DoubleValue: 2.5,
Int64Value: 4294967296,
Uint64Value: 9223372036854775807,
Int32Value: -2147483648,
Fixed64Value: 9223372036854775807,
Fixed32Value: 4294967295,
BoolValue: true,
StringValue: "strprefix/foo",
Uint32Value: 4294967295,
Sfixed32Value: 2147483647,
Sfixed64Value: -4611686018427387904,
Sint32Value: 2147483647,
Sint64Value: 4611686018427387903,
NonConventionalNameValue: "camelCase",
}
url := fmt.Sprintf("http://localhost:8080/v1/example/a_bit_of_everything/%f/%f/%d/separator/%d/%d/%d/%d/%v/%s/%d/%d/%d/%d/%d/%s", want.FloatValue, want.DoubleValue, want.Int64Value, want.Uint64Value, want.Int32Value, want.Fixed64Value, want.Fixed32Value, want.BoolValue, want.StringValue, want.Uint32Value, want.Sfixed32Value, want.Sfixed64Value, want.Sint32Value, want.Sint64Value, want.NonConventionalNameValue)
resp, err := http.Post(url, "application/json", strings.NewReader("{}"))
if err != nil {
t.Errorf("http.Post(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err)
return
}
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
t.Logf("%s", buf)
}
var msg gw.ABitOfEverything
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if msg.Uuid == "" {
t.Error("msg.Uuid is empty; want not empty")
}
msg.Uuid = ""
if got := msg; !reflect.DeepEqual(got, want) {
t.Errorf("msg= %v; want %v", &got, &want)
}
}
func testABECreateBody(t *testing.T) {
want := gw.ABitOfEverything{
FloatValue: 1.5,
DoubleValue: 2.5,
Int64Value: 4294967296,
Uint64Value: 9223372036854775807,
Int32Value: -2147483648,
Fixed64Value: 9223372036854775807,
Fixed32Value: 4294967295,
BoolValue: true,
StringValue: "strprefix/foo",
Uint32Value: 4294967295,
Sfixed32Value: 2147483647,
Sfixed64Value: -4611686018427387904,
Sint32Value: 2147483647,
Sint64Value: 4611686018427387903,
NonConventionalNameValue: "camelCase",
Nested: []*gw.ABitOfEverything_Nested{
{
Name: "bar",
Amount: 10,
},
{
Name: "baz",
Amount: 20,
},
},
RepeatedStringValue: []string{"a", "b", "c"},
OneofValue: &gw.ABitOfEverything_OneofString{
OneofString: "x",
},
MapValue: map[string]gw.NumericEnum{
"a": gw.NumericEnum_ONE,
"b": gw.NumericEnum_ZERO,
},
MappedStringValue: map[string]string{
"a": "x",
"b": "y",
},
MappedNestedValue: map[string]*gw.ABitOfEverything_Nested{
"a": {Name: "x", Amount: 1},
"b": {Name: "y", Amount: 2},
},
}
url := "http://localhost:8080/v1/example/a_bit_of_everything"
var m jsonpb.Marshaler
payload, err := m.MarshalToString(&want)
if err != nil {
t.Fatalf("m.MarshalToString(%#v) failed with %v; want success", want, err)
}
resp, err := http.Post(url, "application/json", strings.NewReader(payload))
if err != nil {
t.Errorf("http.Post(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err)
return
}
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
t.Logf("%s", buf)
}
var msg gw.ABitOfEverything
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if msg.Uuid == "" {
t.Error("msg.Uuid is empty; want not empty")
}
msg.Uuid = ""
if got := msg; !reflect.DeepEqual(got, want) {
t.Errorf("msg= %v; want %v", &got, &want)
}
}
func testABEBulkCreate(t *testing.T) {
count := 0
r, w := io.Pipe()
go func(w io.WriteCloser) {
defer func() {
if cerr := w.Close(); cerr != nil {
t.Errorf("w.Close() failed with %v; want success", cerr)
}
}()
for _, val := range []string{
"foo", "bar", "baz", "qux", "quux",
} {
want := gw.ABitOfEverything{
FloatValue: 1.5,
DoubleValue: 2.5,
Int64Value: 4294967296,
Uint64Value: 9223372036854775807,
Int32Value: -2147483648,
Fixed64Value: 9223372036854775807,
Fixed32Value: 4294967295,
BoolValue: true,
StringValue: fmt.Sprintf("strprefix/%s", val),
Uint32Value: 4294967295,
Sfixed32Value: 2147483647,
Sfixed64Value: -4611686018427387904,
Sint32Value: 2147483647,
Sint64Value: 4611686018427387903,
NonConventionalNameValue: "camelCase",
Nested: []*gw.ABitOfEverything_Nested{
{
Name: "hoge",
Amount: 10,
},
{
Name: "fuga",
Amount: 20,
},
},
}
var m jsonpb.Marshaler
if err := m.Marshal(w, &want); err != nil {
t.Fatalf("m.Marshal(%#v, w) failed with %v; want success", want, err)
}
if _, err := io.WriteString(w, "\n"); err != nil {
t.Errorf("w.Write(%q) failed with %v; want success", "\n", err)
return
}
count++
}
}(w)
url := "http://localhost:8080/v1/example/a_bit_of_everything/bulk"
resp, err := http.Post(url, "application/json", r)
if err != nil {
t.Errorf("http.Post(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err)
return
}
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
t.Logf("%s", buf)
}
var msg empty.Empty
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if got, want := resp.Header.Get("Grpc-Metadata-Count"), fmt.Sprintf("%d", count); got != want {
t.Errorf("Grpc-Header-Count was %q, wanted %q", got, want)
}
if got, want := resp.Trailer.Get("Grpc-Trailer-Foo"), "foo2"; got != want {
t.Errorf("Grpc-Trailer-Foo was %q, wanted %q", got, want)
}
if got, want := resp.Trailer.Get("Grpc-Trailer-Bar"), "bar2"; got != want {
t.Errorf("Grpc-Trailer-Bar was %q, wanted %q", got, want)
}
}
func testABELookup(t *testing.T) {
url := "http://localhost:8080/v1/example/a_bit_of_everything"
cresp, err := http.Post(url, "application/json", strings.NewReader(`
{"bool_value": true, "string_value": "strprefix/example"}
`))
if err != nil {
t.Errorf("http.Post(%q) failed with %v; want success", url, err)
return
}
defer cresp.Body.Close()
buf, err := ioutil.ReadAll(cresp.Body)
if err != nil {
t.Errorf("iotuil.ReadAll(cresp.Body) failed with %v; want success", err)
return
}
if got, want := cresp.StatusCode, http.StatusOK; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
t.Logf("%s", buf)
return
}
var want gw.ABitOfEverything
if err := jsonpb.UnmarshalString(string(buf), &want); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &want) failed with %v; want success", buf, err)
return
}
url = fmt.Sprintf("%s/%s", url, want.Uuid)
resp, err := http.Get(url)
if err != nil {
t.Errorf("http.Get(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
buf, err = ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("ioutil.ReadAll(resp.Body) failed with %v; want success", err)
return
}
var msg gw.ABitOfEverything
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if got := msg; !reflect.DeepEqual(got, want) {
t.Errorf("msg= %v; want %v", &got, &want)
}
if got, want := resp.Header.Get("Grpc-Metadata-Uuid"), want.Uuid; got != want {
t.Errorf("Grpc-Metadata-Uuid was %s, wanted %s", got, want)
}
}
func testABELookupNotFound(t *testing.T) {
url := "http://localhost:8080/v1/example/a_bit_of_everything"
uuid := "not_exist"
url = fmt.Sprintf("%s/%s", url, uuid)
resp, err := http.Get(url)
if err != nil {
t.Errorf("http.Get(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("ioutil.ReadAll(resp.Body) failed with %v; want success", err)
return
}
if got, want := resp.StatusCode, http.StatusNotFound; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
t.Logf("%s", buf)
return
}
var msg errorBody
if err := json.Unmarshal(buf, &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
return
}
if got, want := msg.Code, int(codes.NotFound); got != want {
t.Errorf("msg.Code = %d; want %d", got, want)
return
}
if got, want := resp.Header.Get("Grpc-Metadata-Uuid"), uuid; got != want {
t.Errorf("Grpc-Metadata-Uuid was %s, wanted %s", got, want)
}
if got, want := resp.Trailer.Get("Grpc-Trailer-Foo"), "foo2"; got != want {
t.Errorf("Grpc-Trailer-Foo was %q, wanted %q", got, want)
}
if got, want := resp.Trailer.Get("Grpc-Trailer-Bar"), "bar2"; got != want {
t.Errorf("Grpc-Trailer-Bar was %q, wanted %q", got, want)
}
}
func testABEList(t *testing.T) {
url := "http://localhost:8080/v1/example/a_bit_of_everything"
resp, err := http.Get(url)
if err != nil {
t.Errorf("http.Get(%q) failed with %v; want success", url, err)
return
}
defer resp.Body.Close()
dec := json.NewDecoder(resp.Body)
var i int
for i = 0; ; i++ {
var item struct {
Result json.RawMessage `json:"result"`
Error map[string]interface{} `json:"error"`
}
err := dec.Decode(&item)
if err == io.EOF {
break
}
if err != nil {
t.Errorf("dec.Decode(&item) failed with %v; want success; i = %d", err, i)
}
if len(item.Error) != 0 {
t.Errorf("item.Error = %#v; want empty; i = %d", item.Error, i)
continue
}
var msg gw.ABitOfEverything
if err := jsonpb.UnmarshalString(string(item.Result), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", item.Result, err)
}
}
if i <= 0 {
t.Errorf("i == %d; want > 0", i)
}
value := resp.Header.Get("Grpc-Metadata-Count")
if value == "" {
t.Errorf("Grpc-Header-Count should not be empty")
}
count, err := strconv.Atoi(value)
if err != nil {
t.Errorf("failed to Atoi %q: %v", value, err)
}
if count <= 0 {
t.Errorf("count == %d; want > 0", count)
}
}
func testABEBulkEcho(t *testing.T) {
reqr, reqw := io.Pipe()
var wg sync.WaitGroup
var want []*sub.StringMessage
wg.Add(1)
go func() {
defer wg.Done()
defer reqw.Close()
var m jsonpb.Marshaler
for i := 0; i < 1000; i++ {
msg := sub.StringMessage{Value: proto.String(fmt.Sprintf("message %d", i))}
buf, err := m.MarshalToString(&msg)
if err != nil {
t.Errorf("m.Marshal(%v) failed with %v; want success", &msg, err)
return
}
if _, err := fmt.Fprintln(reqw, buf); err != nil {
t.Errorf("fmt.Fprintln(reqw, %q) failed with %v; want success", buf, err)
return
}
want = append(want, &msg)
}
}()
url := "http://localhost:8080/v1/example/a_bit_of_everything/echo"
req, err := http.NewRequest("POST", url, reqr)
if err != nil {
t.Errorf("http.NewRequest(%q, %q, reqr) failed with %v; want success", "POST", url, err)
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Transfer-Encoding", "chunked")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Errorf("http.Post(%q, %q, req) failed with %v; want success", url, "application/json", err)
return
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
}
var got []*sub.StringMessage
wg.Add(1)
go func() {
defer wg.Done()
dec := json.NewDecoder(resp.Body)
for i := 0; ; i++ {
var item struct {
Result json.RawMessage `json:"result"`
Error map[string]interface{} `json:"error"`
}
err := dec.Decode(&item)
if err == io.EOF {
break
}
if err != nil {
t.Errorf("dec.Decode(&item) failed with %v; want success; i = %d", err, i)
}
if len(item.Error) != 0 {
t.Errorf("item.Error = %#v; want empty; i = %d", item.Error, i)
continue
}
var msg sub.StringMessage
if err := jsonpb.UnmarshalString(string(item.Result), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%q, &msg) failed with %v; want success", item.Result, err)
}
got = append(got, &msg)
}
}()
wg.Wait()
if !reflect.DeepEqual(got, want) {
t.Errorf("got = %v; want %v", got, want)
}
}
func testABEBulkEchoZeroLength(t *testing.T) {
url := "http://localhost:8080/v1/example/a_bit_of_everything/echo"
req, err := http.NewRequest("POST", url, bytes.NewReader(nil))
if err != nil {
t.Errorf("http.NewRequest(%q, %q, bytes.NewReader(nil)) failed with %v; want success", "POST", url, err)
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Transfer-Encoding", "chunked")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Errorf("http.Post(%q, %q, req) failed with %v; want success", url, "application/json", err)
return
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
}
dec := json.NewDecoder(resp.Body)
var item struct {
Result json.RawMessage `json:"result"`
Error map[string]interface{} `json:"error"`
}
if err := dec.Decode(&item); err == nil {
t.Errorf("dec.Decode(&item) succeeded; want io.EOF; item = %#v", item)
} else if err != io.EOF {
t.Errorf("dec.Decode(&item) failed with %v; want success", err)
return
}
}
func testAdditionalBindings(t *testing.T) {
for i, f := range []func() *http.Response{
func() *http.Response {
url := "http://localhost:8080/v1/example/a_bit_of_everything/echo/hello"
resp, err := http.Get(url)
if err != nil {
t.Errorf("http.Get(%q) failed with %v; want success", url, err)
return nil
}
return resp
},
func() *http.Response {
url := "http://localhost:8080/v2/example/echo"
resp, err := http.Post(url, "application/json", strings.NewReader(`"hello"`))
if err != nil {
t.Errorf("http.Post(%q, %q, %q) failed with %v; want success", url, "application/json", `"hello"`, err)
return nil
}
return resp
},
func() *http.Response {
url := "http://localhost:8080/v2/example/echo?value=hello"
resp, err := http.Get(url)
if err != nil {
t.Errorf("http.Get(%q) failed with %v; want success", url, err)
return nil
}
return resp
},
} {
resp := f()
if resp == nil {
continue
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success; i=%d", err, i)
return
}
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("resp.StatusCode = %d; want %d; i=%d", got, want, i)
t.Logf("%s", buf)
}
var msg sub.StringMessage
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success; %d", buf, err, i)
return
}
if got, want := msg.GetValue(), "hello"; got != want {
t.Errorf("msg.GetValue() = %q; want %q", got, want)
}
}
}
func TestTimeout(t *testing.T) {
url := "http://localhost:8080/v2/example/timeout"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
t.Errorf(`http.NewRequest("GET", %q, nil) failed with %v; want success`, url, err)
return
}
req.Header.Set("Grpc-Timeout", "10m")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Errorf("http.DefaultClient.Do(%#v) failed with %v; want success", req, err)
return
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusRequestTimeout; got != want {
t.Errorf("resp.StatusCode = %d; want %d", got, want)
}
}

View file

@ -0,0 +1,110 @@
package main
import (
"flag"
"net/http"
"path"
"strings"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/examples/examplepb"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
var (
echoEndpoint = flag.String("echo_endpoint", "localhost:9090", "endpoint of EchoService")
abeEndpoint = flag.String("more_endpoint", "localhost:9090", "endpoint of ABitOfEverythingService")
flowEndpoint = flag.String("flow_endpoint", "localhost:9090", "endpoint of FlowCombination")
swaggerDir = flag.String("swagger_dir", "examples/examplepb", "path to the directory which contains swagger definitions")
)
// newGateway returns a new gateway server which translates HTTP into gRPC.
func newGateway(ctx context.Context, opts ...runtime.ServeMuxOption) (http.Handler, error) {
mux := runtime.NewServeMux(opts...)
dialOpts := []grpc.DialOption{grpc.WithInsecure()}
err := examplepb.RegisterEchoServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, dialOpts)
if err != nil {
return nil, err
}
err = examplepb.RegisterStreamServiceHandlerFromEndpoint(ctx, mux, *abeEndpoint, dialOpts)
if err != nil {
return nil, err
}
err = examplepb.RegisterABitOfEverythingServiceHandlerFromEndpoint(ctx, mux, *abeEndpoint, dialOpts)
if err != nil {
return nil, err
}
err = examplepb.RegisterFlowCombinationHandlerFromEndpoint(ctx, mux, *flowEndpoint, dialOpts)
if err != nil {
return nil, err
}
return mux, nil
}
func serveSwagger(w http.ResponseWriter, r *http.Request) {
if !strings.HasSuffix(r.URL.Path, ".swagger.json") {
glog.Errorf("Not Found: %s", r.URL.Path)
http.NotFound(w, r)
return
}
glog.Infof("Serving %s", r.URL.Path)
p := strings.TrimPrefix(r.URL.Path, "/swagger/")
p = path.Join(*swaggerDir, p)
http.ServeFile(w, r, p)
}
// allowCORS allows Cross Origin Resoruce Sharing from any origin.
// Don't do this without consideration in production systems.
func allowCORS(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" {
preflightHandler(w, r)
return
}
}
h.ServeHTTP(w, r)
})
}
func preflightHandler(w http.ResponseWriter, r *http.Request) {
headers := []string{"Content-Type", "Accept"}
w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ","))
methods := []string{"GET", "HEAD", "POST", "PUT", "DELETE"}
w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ","))
glog.Infof("preflight request for %s", r.URL.Path)
return
}
// Run starts a HTTP server and blocks forever if successful.
func Run(address string, opts ...runtime.ServeMuxOption) error {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := http.NewServeMux()
mux.HandleFunc("/swagger/", serveSwagger)
gw, err := newGateway(ctx, opts...)
if err != nil {
return err
}
mux.Handle("/", gw)
http.ListenAndServe(address, allowCORS(mux))
return nil
}
func main() {
flag.Parse()
defer glog.Flush()
if err := Run(":8080"); err != nil {
glog.Fatal(err)
}
}

View file

@ -0,0 +1,45 @@
package main
import (
"flag"
"fmt"
"os"
"testing"
"time"
server "github.com/grpc-ecosystem/grpc-gateway/examples/server"
)
func runServers() <-chan error {
ch := make(chan error, 2)
go func() {
if err := server.Run(); err != nil {
ch <- fmt.Errorf("cannot run grpc service: %v", err)
}
}()
go func() {
if err := Run(":8080"); err != nil {
ch <- fmt.Errorf("cannot run gateway service: %v", err)
}
}()
return ch
}
func TestMain(m *testing.M) {
flag.Parse()
errCh := runServers()
ch := make(chan int, 1)
go func() {
time.Sleep(100 * time.Millisecond)
ch <- m.Run()
}()
select {
case err := <-errCh:
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
case status := <-ch:
os.Exit(status)
}
}

View file

@ -0,0 +1,229 @@
package server
import (
"fmt"
"io"
"sync"
"github.com/golang/glog"
"github.com/golang/protobuf/ptypes/empty"
examples "github.com/grpc-ecosystem/grpc-gateway/examples/examplepb"
sub "github.com/grpc-ecosystem/grpc-gateway/examples/sub"
sub2 "github.com/grpc-ecosystem/grpc-gateway/examples/sub2"
"github.com/rogpeppe/fastuuid"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
)
// Implements of ABitOfEverythingServiceServer
var uuidgen = fastuuid.MustNewGenerator()
type _ABitOfEverythingServer struct {
v map[string]*examples.ABitOfEverything
m sync.Mutex
}
type ABitOfEverythingServer interface {
examples.ABitOfEverythingServiceServer
examples.StreamServiceServer
}
func newABitOfEverythingServer() ABitOfEverythingServer {
return &_ABitOfEverythingServer{
v: make(map[string]*examples.ABitOfEverything),
}
}
func (s *_ABitOfEverythingServer) Create(ctx context.Context, msg *examples.ABitOfEverything) (*examples.ABitOfEverything, error) {
s.m.Lock()
defer s.m.Unlock()
glog.Info(msg)
var uuid string
for {
uuid = fmt.Sprintf("%x", uuidgen.Next())
if _, ok := s.v[uuid]; !ok {
break
}
}
s.v[uuid] = msg
s.v[uuid].Uuid = uuid
glog.Infof("%v", s.v[uuid])
return s.v[uuid], nil
}
func (s *_ABitOfEverythingServer) CreateBody(ctx context.Context, msg *examples.ABitOfEverything) (*examples.ABitOfEverything, error) {
return s.Create(ctx, msg)
}
func (s *_ABitOfEverythingServer) BulkCreate(stream examples.StreamService_BulkCreateServer) error {
count := 0
ctx := stream.Context()
for {
msg, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
count++
glog.Error(msg)
if _, err = s.Create(ctx, msg); err != nil {
return err
}
}
err := stream.SendHeader(metadata.New(map[string]string{
"count": fmt.Sprintf("%d", count),
}))
if err != nil {
return nil
}
stream.SetTrailer(metadata.New(map[string]string{
"foo": "foo2",
"bar": "bar2",
}))
return stream.SendAndClose(new(empty.Empty))
}
func (s *_ABitOfEverythingServer) Lookup(ctx context.Context, msg *sub2.IdMessage) (*examples.ABitOfEverything, error) {
s.m.Lock()
defer s.m.Unlock()
glog.Info(msg)
err := grpc.SendHeader(ctx, metadata.New(map[string]string{
"uuid": msg.Uuid,
}))
if err != nil {
return nil, err
}
if a, ok := s.v[msg.Uuid]; ok {
return a, nil
}
grpc.SetTrailer(ctx, metadata.New(map[string]string{
"foo": "foo2",
"bar": "bar2",
}))
return nil, grpc.Errorf(codes.NotFound, "not found")
}
func (s *_ABitOfEverythingServer) List(_ *empty.Empty, stream examples.StreamService_ListServer) error {
s.m.Lock()
defer s.m.Unlock()
err := stream.SendHeader(metadata.New(map[string]string{
"count": fmt.Sprintf("%d", len(s.v)),
}))
if err != nil {
return nil
}
for _, msg := range s.v {
if err := stream.Send(msg); err != nil {
return err
}
}
// return error when metadata includes error header
if header, ok := metadata.FromContext(stream.Context()); ok {
if v, ok := header["error"]; ok {
stream.SetTrailer(metadata.New(map[string]string{
"foo": "foo2",
"bar": "bar2",
}))
return grpc.Errorf(codes.InvalidArgument, "error metadata: %v", v)
}
}
return nil
}
func (s *_ABitOfEverythingServer) Update(ctx context.Context, msg *examples.ABitOfEverything) (*empty.Empty, error) {
s.m.Lock()
defer s.m.Unlock()
glog.Info(msg)
if _, ok := s.v[msg.Uuid]; ok {
s.v[msg.Uuid] = msg
} else {
return nil, grpc.Errorf(codes.NotFound, "not found")
}
return new(empty.Empty), nil
}
func (s *_ABitOfEverythingServer) Delete(ctx context.Context, msg *sub2.IdMessage) (*empty.Empty, error) {
s.m.Lock()
defer s.m.Unlock()
glog.Info(msg)
if _, ok := s.v[msg.Uuid]; ok {
delete(s.v, msg.Uuid)
} else {
return nil, grpc.Errorf(codes.NotFound, "not found")
}
return new(empty.Empty), nil
}
func (s *_ABitOfEverythingServer) Echo(ctx context.Context, msg *sub.StringMessage) (*sub.StringMessage, error) {
s.m.Lock()
defer s.m.Unlock()
glog.Info(msg)
return msg, nil
}
func (s *_ABitOfEverythingServer) BulkEcho(stream examples.StreamService_BulkEchoServer) error {
var msgs []*sub.StringMessage
for {
msg, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
msgs = append(msgs, msg)
}
hmd := metadata.New(map[string]string{
"foo": "foo1",
"bar": "bar1",
})
if err := stream.SendHeader(hmd); err != nil {
return err
}
for _, msg := range msgs {
glog.Info(msg)
if err := stream.Send(msg); err != nil {
return err
}
}
stream.SetTrailer(metadata.New(map[string]string{
"foo": "foo2",
"bar": "bar2",
}))
return nil
}
func (s *_ABitOfEverythingServer) DeepPathEcho(ctx context.Context, msg *examples.ABitOfEverything) (*examples.ABitOfEverything, error) {
s.m.Lock()
defer s.m.Unlock()
glog.Info(msg)
return msg, nil
}
func (s *_ABitOfEverythingServer) Timeout(ctx context.Context, msg *empty.Empty) (*empty.Empty, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
}
}

View file

@ -0,0 +1,17 @@
package main
import (
"flag"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/examples/server"
)
func main() {
flag.Parse()
defer glog.Flush()
if err := server.Run(); err != nil {
glog.Fatal(err)
}
}

View file

@ -0,0 +1,35 @@
package server
import (
"github.com/golang/glog"
examples "github.com/grpc-ecosystem/grpc-gateway/examples/examplepb"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
// Implements of EchoServiceServer
type echoServer struct{}
func newEchoServer() examples.EchoServiceServer {
return new(echoServer)
}
func (s *echoServer) Echo(ctx context.Context, msg *examples.SimpleMessage) (*examples.SimpleMessage, error) {
glog.Info(msg)
return msg, nil
}
func (s *echoServer) EchoBody(ctx context.Context, msg *examples.SimpleMessage) (*examples.SimpleMessage, error) {
glog.Info(msg)
grpc.SendHeader(ctx, metadata.New(map[string]string{
"foo": "foo1",
"bar": "bar1",
}))
grpc.SetTrailer(ctx, metadata.New(map[string]string{
"foo": "foo2",
"bar": "bar2",
}))
return msg, nil
}

View file

@ -0,0 +1,72 @@
package server
import (
"io"
examples "github.com/grpc-ecosystem/grpc-gateway/examples/examplepb"
"golang.org/x/net/context"
)
type flowCombinationServer struct{}
func newFlowCombinationServer() examples.FlowCombinationServer {
return &flowCombinationServer{}
}
func (s flowCombinationServer) RpcEmptyRpc(ctx context.Context, req *examples.EmptyProto) (*examples.EmptyProto, error) {
return req, nil
}
func (s flowCombinationServer) RpcEmptyStream(req *examples.EmptyProto, stream examples.FlowCombination_RpcEmptyStreamServer) error {
return stream.Send(req)
}
func (s flowCombinationServer) StreamEmptyRpc(stream examples.FlowCombination_StreamEmptyRpcServer) error {
for {
_, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
}
return stream.SendAndClose(new(examples.EmptyProto))
}
func (s flowCombinationServer) StreamEmptyStream(stream examples.FlowCombination_StreamEmptyStreamServer) error {
for {
_, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
}
return stream.Send(new(examples.EmptyProto))
}
func (s flowCombinationServer) RpcBodyRpc(ctx context.Context, req *examples.NonEmptyProto) (*examples.EmptyProto, error) {
return new(examples.EmptyProto), nil
}
func (s flowCombinationServer) RpcPathSingleNestedRpc(ctx context.Context, req *examples.SingleNestedProto) (*examples.EmptyProto, error) {
return new(examples.EmptyProto), nil
}
func (s flowCombinationServer) RpcPathNestedRpc(ctx context.Context, req *examples.NestedProto) (*examples.EmptyProto, error) {
return new(examples.EmptyProto), nil
}
func (s flowCombinationServer) RpcBodyStream(req *examples.NonEmptyProto, stream examples.FlowCombination_RpcBodyStreamServer) error {
return stream.Send(new(examples.EmptyProto))
}
func (s flowCombinationServer) RpcPathSingleNestedStream(req *examples.SingleNestedProto, stream examples.FlowCombination_RpcPathSingleNestedStreamServer) error {
return stream.Send(new(examples.EmptyProto))
}
func (s flowCombinationServer) RpcPathNestedStream(req *examples.NestedProto, stream examples.FlowCombination_RpcPathNestedStreamServer) error {
return stream.Send(new(examples.EmptyProto))
}

View file

@ -0,0 +1,25 @@
package server
import (
"net"
examples "github.com/grpc-ecosystem/grpc-gateway/examples/examplepb"
"google.golang.org/grpc"
)
func Run() error {
l, err := net.Listen("tcp", ":9090")
if err != nil {
return err
}
s := grpc.NewServer()
examples.RegisterEchoServiceServer(s, newEchoServer())
examples.RegisterFlowCombinationServer(s, newFlowCombinationServer())
abe := newABitOfEverythingServer()
examples.RegisterABitOfEverythingServiceServer(s, abe)
examples.RegisterStreamServiceServer(s, abe)
s.Serve(l)
return nil
}

View file

@ -0,0 +1,63 @@
// Code generated by protoc-gen-go.
// source: examples/sub/message.proto
// DO NOT EDIT!
/*
Package sub is a generated protocol buffer package.
It is generated from these files:
examples/sub/message.proto
It has these top-level messages:
StringMessage
*/
package sub
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type StringMessage struct {
Value *string `protobuf:"bytes,1,req,name=value" json:"value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StringMessage) Reset() { *m = StringMessage{} }
func (m *StringMessage) String() string { return proto.CompactTextString(m) }
func (*StringMessage) ProtoMessage() {}
func (*StringMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *StringMessage) GetValue() string {
if m != nil && m.Value != nil {
return *m.Value
}
return ""
}
func init() {
proto.RegisterType((*StringMessage)(nil), "grpc.gateway.examples.sub.StringMessage")
}
func init() { proto.RegisterFile("examples/sub/message.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 111 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0xad, 0x48, 0xcc,
0x2d, 0xc8, 0x49, 0x2d, 0xd6, 0x2f, 0x2e, 0x4d, 0xd2, 0xcf, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c, 0x4f,
0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x4c, 0x2f, 0x2a, 0x48, 0xd6, 0x4b, 0x4f, 0x2c,
0x49, 0x2d, 0x4f, 0xac, 0xd4, 0x83, 0x29, 0xd4, 0x2b, 0x2e, 0x4d, 0x52, 0x52, 0xe5, 0xe2, 0x0d,
0x2e, 0x29, 0xca, 0xcc, 0x4b, 0xf7, 0x85, 0xe8, 0x10, 0x12, 0xe1, 0x62, 0x2d, 0x4b, 0xcc, 0x29,
0x4d, 0x95, 0x60, 0x54, 0x60, 0xd2, 0xe0, 0x0c, 0x82, 0x70, 0x9c, 0x58, 0xa3, 0x98, 0x8b, 0x4b,
0x93, 0x00, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbc, 0x10, 0x60, 0xa9, 0x65, 0x00, 0x00, 0x00,
}

View file

@ -0,0 +1,7 @@
syntax = "proto2";
option go_package = "sub";
package grpc.gateway.examples.sub;
message StringMessage {
required string value = 1;
}

View file

@ -0,0 +1,56 @@
// Code generated by protoc-gen-go.
// source: examples/sub2/message.proto
// DO NOT EDIT!
/*
Package sub2 is a generated protocol buffer package.
It is generated from these files:
examples/sub2/message.proto
It has these top-level messages:
IdMessage
*/
package sub2
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type IdMessage struct {
Uuid string `protobuf:"bytes,1,opt,name=uuid" json:"uuid,omitempty"`
}
func (m *IdMessage) Reset() { *m = IdMessage{} }
func (m *IdMessage) String() string { return proto.CompactTextString(m) }
func (*IdMessage) ProtoMessage() {}
func (*IdMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func init() {
proto.RegisterType((*IdMessage)(nil), "sub2.IdMessage")
}
func init() { proto.RegisterFile("examples/sub2/message.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 128 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0xad, 0x48, 0xcc,
0x2d, 0xc8, 0x49, 0x2d, 0xd6, 0x2f, 0x2e, 0x4d, 0x32, 0xd2, 0xcf, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c,
0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0x89, 0x29, 0xc9, 0x73, 0x71, 0x7a,
0xa6, 0xf8, 0x42, 0x24, 0x84, 0x84, 0xb8, 0x58, 0x4a, 0x4b, 0x33, 0x53, 0x24, 0x18, 0x15, 0x18,
0x35, 0x38, 0x83, 0xc0, 0x6c, 0x27, 0xb3, 0x28, 0x93, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd,
0xe4, 0xfc, 0x5c, 0xfd, 0xf4, 0xa2, 0x82, 0x64, 0xdd, 0xd4, 0xe4, 0xfc, 0xe2, 0xca, 0xe2, 0x92,
0x54, 0x28, 0x37, 0x3d, 0xb1, 0x24, 0xb5, 0x3c, 0xb1, 0x52, 0x1f, 0xc5, 0xb2, 0x24, 0x36, 0xb0,
0x2d, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x53, 0x75, 0xef, 0xe0, 0x84, 0x00, 0x00, 0x00,
}

View file

@ -0,0 +1,7 @@
syntax = "proto3";
option go_package = "github.com/grpc-ecosystem/grpc-gateway/examples/sub2";
package sub2;
message IdMessage {
string uuid = 1;
}

View file

@ -0,0 +1,27 @@
syntax = "proto2";
option go_package = "options";
package gengo.grpc.gateway;
import "google/protobuf/descriptor.proto";
message ApiMethodOptions {
// Use HttpRule instead.
option deprecated = true;
extend google.protobuf.MethodOptions {
// Describes how the gRPC method should be exported as a RESTful API.
//
// The id is a globally unique id for this option, assigned by
// protobuf-global-extension-registry@google.com.
optional ApiMethodOptions api_options = 1022;
}
// Path of the RESTful API method.
// Path components which start with colon is mapped to the corresponding fields in the request message.
required string path = 1;
// HTTP method of the RESTful API method
required string method = 2;
// Human-readable description of the method.
optional string description = 3;
}

View file

@ -0,0 +1,290 @@
package descriptor
import (
"fmt"
"path"
"path/filepath"
"strings"
"github.com/golang/glog"
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
)
// Registry is a registry of information extracted from plugin.CodeGeneratorRequest.
type Registry struct {
// msgs is a mapping from fully-qualified message name to descriptor
msgs map[string]*Message
// enums is a mapping from fully-qualified enum name to descriptor
enums map[string]*Enum
// files is a mapping from file path to descriptor
files map[string]*File
// prefix is a prefix to be inserted to golang package paths generated from proto package names.
prefix string
// pkgMap is a user-specified mapping from file path to proto package.
pkgMap map[string]string
// pkgAliases is a mapping from package aliases to package paths in go which are already taken.
pkgAliases map[string]string
}
// NewRegistry returns a new Registry.
func NewRegistry() *Registry {
return &Registry{
msgs: make(map[string]*Message),
enums: make(map[string]*Enum),
files: make(map[string]*File),
pkgMap: make(map[string]string),
pkgAliases: make(map[string]string),
}
}
// Load loads definitions of services, methods, messages, enumerations and fields from "req".
func (r *Registry) Load(req *plugin.CodeGeneratorRequest) error {
for _, file := range req.GetProtoFile() {
r.loadFile(file)
}
var targetPkg string
for _, name := range req.FileToGenerate {
target := r.files[name]
if target == nil {
return fmt.Errorf("no such file: %s", name)
}
name := packageIdentityName(target.FileDescriptorProto)
if targetPkg == "" {
targetPkg = name
} else {
if targetPkg != name {
return fmt.Errorf("inconsistent package names: %s %s", targetPkg, name)
}
}
if err := r.loadServices(target); err != nil {
return err
}
}
return nil
}
// loadFile loads messages, enumerations and fields from "file".
// It does not loads services and methods in "file". You need to call
// loadServices after loadFiles is called for all files to load services and methods.
func (r *Registry) loadFile(file *descriptor.FileDescriptorProto) {
pkg := GoPackage{
Path: r.goPackagePath(file),
Name: defaultGoPackageName(file),
}
if err := r.ReserveGoPackageAlias(pkg.Name, pkg.Path); err != nil {
for i := 0; ; i++ {
alias := fmt.Sprintf("%s_%d", pkg.Name, i)
if err := r.ReserveGoPackageAlias(alias, pkg.Path); err == nil {
pkg.Alias = alias
break
}
}
}
f := &File{
FileDescriptorProto: file,
GoPkg: pkg,
}
r.files[file.GetName()] = f
r.registerMsg(f, nil, file.GetMessageType())
r.registerEnum(f, nil, file.GetEnumType())
}
func (r *Registry) registerMsg(file *File, outerPath []string, msgs []*descriptor.DescriptorProto) {
for i, md := range msgs {
m := &Message{
File: file,
Outers: outerPath,
DescriptorProto: md,
Index: i,
}
for _, fd := range md.GetField() {
m.Fields = append(m.Fields, &Field{
Message: m,
FieldDescriptorProto: fd,
})
}
file.Messages = append(file.Messages, m)
r.msgs[m.FQMN()] = m
glog.V(1).Infof("register name: %s", m.FQMN())
var outers []string
outers = append(outers, outerPath...)
outers = append(outers, m.GetName())
r.registerMsg(file, outers, m.GetNestedType())
r.registerEnum(file, outers, m.GetEnumType())
}
}
func (r *Registry) registerEnum(file *File, outerPath []string, enums []*descriptor.EnumDescriptorProto) {
for i, ed := range enums {
e := &Enum{
File: file,
Outers: outerPath,
EnumDescriptorProto: ed,
Index: i,
}
file.Enums = append(file.Enums, e)
r.enums[e.FQEN()] = e
glog.V(1).Infof("register enum name: %s", e.FQEN())
}
}
// LookupMsg looks up a message type by "name".
// It tries to resolve "name" from "location" if "name" is a relative message name.
func (r *Registry) LookupMsg(location, name string) (*Message, error) {
glog.V(1).Infof("lookup %s from %s", name, location)
if strings.HasPrefix(name, ".") {
m, ok := r.msgs[name]
if !ok {
return nil, fmt.Errorf("no message found: %s", name)
}
return m, nil
}
if !strings.HasPrefix(location, ".") {
location = fmt.Sprintf(".%s", location)
}
components := strings.Split(location, ".")
for len(components) > 0 {
fqmn := strings.Join(append(components, name), ".")
if m, ok := r.msgs[fqmn]; ok {
return m, nil
}
components = components[:len(components)-1]
}
return nil, fmt.Errorf("no message found: %s", name)
}
// LookupEnum looks up a enum type by "name".
// It tries to resolve "name" from "location" if "name" is a relative enum name.
func (r *Registry) LookupEnum(location, name string) (*Enum, error) {
glog.V(1).Infof("lookup enum %s from %s", name, location)
if strings.HasPrefix(name, ".") {
e, ok := r.enums[name]
if !ok {
return nil, fmt.Errorf("no enum found: %s", name)
}
return e, nil
}
if !strings.HasPrefix(location, ".") {
location = fmt.Sprintf(".%s", location)
}
components := strings.Split(location, ".")
for len(components) > 0 {
fqen := strings.Join(append(components, name), ".")
if e, ok := r.enums[fqen]; ok {
return e, nil
}
components = components[:len(components)-1]
}
return nil, fmt.Errorf("no enum found: %s", name)
}
// LookupFile looks up a file by name.
func (r *Registry) LookupFile(name string) (*File, error) {
f, ok := r.files[name]
if !ok {
return nil, fmt.Errorf("no such file given: %s", name)
}
return f, nil
}
// AddPkgMap adds a mapping from a .proto file to proto package name.
func (r *Registry) AddPkgMap(file, protoPkg string) {
r.pkgMap[file] = protoPkg
}
// SetPrefix registeres the perfix to be added to go package paths generated from proto package names.
func (r *Registry) SetPrefix(prefix string) {
r.prefix = prefix
}
// ReserveGoPackageAlias reserves the unique alias of go package.
// If succeeded, the alias will be never used for other packages in generated go files.
// If failed, the alias is already taken by another package, so you need to use another
// alias for the package in your go files.
func (r *Registry) ReserveGoPackageAlias(alias, pkgpath string) error {
if taken, ok := r.pkgAliases[alias]; ok {
if taken == pkgpath {
return nil
}
return fmt.Errorf("package name %s is already taken. Use another alias", alias)
}
r.pkgAliases[alias] = pkgpath
return nil
}
// goPackagePath returns the go package path which go files generated from "f" should have.
// It respects the mapping registered by AddPkgMap if exists. Or use go_package as import path
// if it includes a slash, Otherwide, it generates a path from the file name of "f".
func (r *Registry) goPackagePath(f *descriptor.FileDescriptorProto) string {
name := f.GetName()
if pkg, ok := r.pkgMap[name]; ok {
return path.Join(r.prefix, pkg)
}
gopkg := f.Options.GetGoPackage()
idx := strings.LastIndex(gopkg, "/")
if idx >= 0 {
return gopkg
}
return path.Join(r.prefix, path.Dir(name))
}
// GetAllFQMNs returns a list of all FQMNs
func (r *Registry) GetAllFQMNs() []string {
var keys []string
for k := range r.msgs {
keys = append(keys, k)
}
return keys
}
// GetAllFQENs returns a list of all FQENs
func (r *Registry) GetAllFQENs() []string {
var keys []string
for k := range r.enums {
keys = append(keys, k)
}
return keys
}
// defaultGoPackageName returns the default go package name to be used for go files generated from "f".
// You might need to use an unique alias for the package when you import it. Use ReserveGoPackageAlias to get a unique alias.
func defaultGoPackageName(f *descriptor.FileDescriptorProto) string {
name := packageIdentityName(f)
return strings.Replace(name, ".", "_", -1)
}
// packageIdentityName returns the identity of packages.
// protoc-gen-grpc-gateway rejects CodeGenerationRequests which contains more than one packages
// as protoc-gen-go does.
func packageIdentityName(f *descriptor.FileDescriptorProto) string {
if f.Options != nil && f.Options.GoPackage != nil {
gopkg := f.Options.GetGoPackage()
idx := strings.LastIndex(gopkg, "/")
if idx < 0 {
return gopkg
}
return gopkg[idx+1:]
}
if f.Package == nil {
base := filepath.Base(f.GetName())
ext := filepath.Ext(base)
return strings.TrimSuffix(base, ext)
}
return f.GetPackage()
}

View file

@ -0,0 +1,533 @@
package descriptor
import (
"testing"
"github.com/golang/protobuf/proto"
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
)
func loadFile(t *testing.T, reg *Registry, src string) *descriptor.FileDescriptorProto {
var file descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &file); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", src, err)
}
reg.loadFile(&file)
return &file
}
func load(t *testing.T, reg *Registry, src string) error {
var req plugin.CodeGeneratorRequest
if err := proto.UnmarshalText(src, &req); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &file) failed with %v; want success", src, err)
}
return reg.Load(&req)
}
func TestLoadFile(t *testing.T) {
reg := NewRegistry()
fd := loadFile(t, reg, `
name: 'example.proto'
package: 'example'
message_type <
name: 'ExampleMessage'
field <
name: 'str'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 1
>
>
`)
file := reg.files["example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: ".", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
msg, err := reg.LookupMsg("", ".example.ExampleMessage")
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".example.ExampleMessage", err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".example.ExampleMessage", got, want)
}
if got, want := msg.File, file; got != want {
t.Errorf("msg.File = %v; want %v", got, want)
}
if got := msg.Outers; got != nil {
t.Errorf("msg.Outers = %v; want %v", got, nil)
}
if got, want := len(msg.Fields), 1; got != want {
t.Errorf("len(msg.Fields) = %d; want %d", got, want)
} else if got, want := msg.Fields[0].FieldDescriptorProto, fd.MessageType[0].Field[0]; got != want {
t.Errorf("msg.Fields[0].FieldDescriptorProto = %v; want %v", got, want)
} else if got, want := msg.Fields[0].Message, msg; got != want {
t.Errorf("msg.Fields[0].Message = %v; want %v", got, want)
}
if got, want := len(file.Messages), 1; got != want {
t.Errorf("file.Meeesages = %#v; want %#v", file.Messages, []*Message{msg})
}
if got, want := file.Messages[0], msg; got != want {
t.Errorf("file.Meeesages[0] = %v; want %v", got, want)
}
}
func TestLoadFileNestedPackage(t *testing.T) {
reg := NewRegistry()
loadFile(t, reg, `
name: 'example.proto'
package: 'example.nested.nested2'
`)
file := reg.files["example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: ".", Name: "example_nested_nested2"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithDir(t *testing.T) {
reg := NewRegistry()
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
file := reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "path/to", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithoutPackage(t *testing.T) {
reg := NewRegistry()
loadFile(t, reg, `
name: 'path/to/example_file.proto'
`)
file := reg.files["path/to/example_file.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "path/to", Name: "example_file"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithMapping(t *testing.T) {
reg := NewRegistry()
reg.AddPkgMap("path/to/example.proto", "example.com/proj/example/proto")
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
file := reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "example.com/proj/example/proto", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithPackageNameCollision(t *testing.T) {
reg := NewRegistry()
loadFile(t, reg, `
name: 'path/to/another.proto'
package: 'example'
`)
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
if err := reg.ReserveGoPackageAlias("ioutil", "io/ioutil"); err != nil {
t.Fatalf("reg.ReserveGoPackageAlias(%q) failed with %v; want success", "ioutil", err)
}
loadFile(t, reg, `
name: 'path/to/ioutil.proto'
package: 'ioutil'
`)
file := reg.files["path/to/another.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/another.proto")
return
}
wantPkg := GoPackage{Path: "path/to", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
file = reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/example.proto")
return
}
wantPkg = GoPackage{Path: "path/to", Name: "example", Alias: ""}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
file = reg.files["path/to/ioutil.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "path/to/ioutil.proto")
return
}
wantPkg = GoPackage{Path: "path/to", Name: "ioutil", Alias: "ioutil_0"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithIdenticalGoPkg(t *testing.T) {
reg := NewRegistry()
reg.AddPkgMap("path/to/another.proto", "example.com/example")
reg.AddPkgMap("path/to/example.proto", "example.com/example")
loadFile(t, reg, `
name: 'path/to/another.proto'
package: 'example'
`)
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
file := reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "example.com/example", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
file = reg.files["path/to/another.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg = GoPackage{Path: "example.com/example", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLoadFileWithPrefix(t *testing.T) {
reg := NewRegistry()
reg.SetPrefix("third_party")
loadFile(t, reg, `
name: 'path/to/example.proto'
package: 'example'
`)
file := reg.files["path/to/example.proto"]
if file == nil {
t.Errorf("reg.files[%q] = nil; want non-nil", "example.proto")
return
}
wantPkg := GoPackage{Path: "third_party/path/to", Name: "example"}
if got, want := file.GoPkg, wantPkg; got != want {
t.Errorf("file.GoPkg = %#v; want %#v", got, want)
}
}
func TestLookupMsgWithoutPackage(t *testing.T) {
reg := NewRegistry()
fd := loadFile(t, reg, `
name: 'example.proto'
message_type <
name: 'ExampleMessage'
field <
name: 'str'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 1
>
>
`)
msg, err := reg.LookupMsg("", ".ExampleMessage")
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", "", ".ExampleMessage", err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", "", ".ExampleMessage", got, want)
}
}
func TestLookupMsgWithNestedPackage(t *testing.T) {
reg := NewRegistry()
fd := loadFile(t, reg, `
name: 'example.proto'
package: 'nested.nested2.mypackage'
message_type <
name: 'ExampleMessage'
field <
name: 'str'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 1
>
>
`)
for _, name := range []string{
"nested.nested2.mypackage.ExampleMessage",
"nested2.mypackage.ExampleMessage",
"mypackage.ExampleMessage",
"ExampleMessage",
} {
msg, err := reg.LookupMsg("nested.nested2.mypackage", name)
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", ".nested.nested2.mypackage", name, err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", ".nested.nested2.mypackage", name, got, want)
}
}
for _, loc := range []string{
".nested.nested2.mypackage",
"nested.nested2.mypackage",
".nested.nested2",
"nested.nested2",
".nested",
"nested",
".",
"",
"somewhere.else",
} {
name := "nested.nested2.mypackage.ExampleMessage"
msg, err := reg.LookupMsg(loc, name)
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want)
}
}
for _, loc := range []string{
".nested.nested2.mypackage",
"nested.nested2.mypackage",
".nested.nested2",
"nested.nested2",
".nested",
"nested",
} {
name := "nested2.mypackage.ExampleMessage"
msg, err := reg.LookupMsg(loc, name)
if err != nil {
t.Errorf("reg.LookupMsg(%q, %q)) failed with %v; want success", loc, name, err)
return
}
if got, want := msg.DescriptorProto, fd.MessageType[0]; got != want {
t.Errorf("reg.lookupMsg(%q, %q).DescriptorProto = %#v; want %#v", loc, name, got, want)
}
}
}
func TestLoadWithInconsistentTargetPackage(t *testing.T) {
for _, spec := range []struct {
req string
consistent bool
}{
// root package, no explicit go package
{
req: `
file_to_generate: 'a.proto'
file_to_generate: 'b.proto'
proto_file <
name: 'a.proto'
message_type < name: 'A' >
service <
name: "AService"
method <
name: "Meth"
input_type: "A"
output_type: "A"
options <
[google.api.http] < post: "/v1/a" body: "*" >
>
>
>
>
proto_file <
name: 'b.proto'
message_type < name: 'B' >
service <
name: "BService"
method <
name: "Meth"
input_type: "B"
output_type: "B"
options <
[google.api.http] < post: "/v1/b" body: "*" >
>
>
>
>
`,
consistent: false,
},
// named package, no explicit go package
{
req: `
file_to_generate: 'a.proto'
file_to_generate: 'b.proto'
proto_file <
name: 'a.proto'
package: 'example.foo'
message_type < name: 'A' >
service <
name: "AService"
method <
name: "Meth"
input_type: "A"
output_type: "A"
options <
[google.api.http] < post: "/v1/a" body: "*" >
>
>
>
>
proto_file <
name: 'b.proto'
package: 'example.foo'
message_type < name: 'B' >
service <
name: "BService"
method <
name: "Meth"
input_type: "B"
output_type: "B"
options <
[google.api.http] < post: "/v1/b" body: "*" >
>
>
>
>
`,
consistent: true,
},
// root package, explicit go package
{
req: `
file_to_generate: 'a.proto'
file_to_generate: 'b.proto'
proto_file <
name: 'a.proto'
options < go_package: 'foo' >
message_type < name: 'A' >
service <
name: "AService"
method <
name: "Meth"
input_type: "A"
output_type: "A"
options <
[google.api.http] < post: "/v1/a" body: "*" >
>
>
>
>
proto_file <
name: 'b.proto'
options < go_package: 'foo' >
message_type < name: 'B' >
service <
name: "BService"
method <
name: "Meth"
input_type: "B"
output_type: "B"
options <
[google.api.http] < post: "/v1/b" body: "*" >
>
>
>
>
`,
consistent: true,
},
// named package, explicit go package
{
req: `
file_to_generate: 'a.proto'
file_to_generate: 'b.proto'
proto_file <
name: 'a.proto'
package: 'example.foo'
options < go_package: 'foo' >
message_type < name: 'A' >
service <
name: "AService"
method <
name: "Meth"
input_type: "A"
output_type: "A"
options <
[google.api.http] < post: "/v1/a" body: "*" >
>
>
>
>
proto_file <
name: 'b.proto'
package: 'example.foo'
options < go_package: 'foo' >
message_type < name: 'B' >
service <
name: "BService"
method <
name: "Meth"
input_type: "B"
output_type: "B"
options <
[google.api.http] < post: "/v1/b" body: "*" >
>
>
>
>
`,
consistent: true,
},
} {
reg := NewRegistry()
err := load(t, reg, spec.req)
if got, want := err == nil, spec.consistent; got != want {
if want {
t.Errorf("reg.Load(%s) failed with %v; want success", spec.req, err)
continue
}
t.Errorf("reg.Load(%s) succeeded; want an package inconsistency error", spec.req)
}
}
}

View file

@ -0,0 +1,265 @@
package descriptor
import (
"fmt"
"strings"
"github.com/golang/glog"
"github.com/golang/protobuf/proto"
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
options "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"
)
// loadServices registers services and their methods from "targetFile" to "r".
// It must be called after loadFile is called for all files so that loadServices
// can resolve names of message types and their fields.
func (r *Registry) loadServices(file *File) error {
glog.V(1).Infof("Loading services from %s", file.GetName())
var svcs []*Service
for _, sd := range file.GetService() {
glog.V(2).Infof("Registering %s", sd.GetName())
svc := &Service{
File: file,
ServiceDescriptorProto: sd,
}
for _, md := range sd.GetMethod() {
glog.V(2).Infof("Processing %s.%s", sd.GetName(), md.GetName())
opts, err := extractAPIOptions(md)
if err != nil {
glog.Errorf("Failed to extract ApiMethodOptions from %s.%s: %v", svc.GetName(), md.GetName(), err)
return err
}
if opts == nil {
glog.V(1).Infof("Skip non-target method: %s.%s", svc.GetName(), md.GetName())
continue
}
meth, err := r.newMethod(svc, md, opts)
if err != nil {
return err
}
svc.Methods = append(svc.Methods, meth)
}
if len(svc.Methods) == 0 {
continue
}
glog.V(2).Infof("Registered %s with %d method(s)", svc.GetName(), len(svc.Methods))
svcs = append(svcs, svc)
}
file.Services = svcs
return nil
}
func (r *Registry) newMethod(svc *Service, md *descriptor.MethodDescriptorProto, opts *options.HttpRule) (*Method, error) {
requestType, err := r.LookupMsg(svc.File.GetPackage(), md.GetInputType())
if err != nil {
return nil, err
}
responseType, err := r.LookupMsg(svc.File.GetPackage(), md.GetOutputType())
if err != nil {
return nil, err
}
meth := &Method{
Service: svc,
MethodDescriptorProto: md,
RequestType: requestType,
ResponseType: responseType,
}
newBinding := func(opts *options.HttpRule, idx int) (*Binding, error) {
var (
httpMethod string
pathTemplate string
)
switch {
case opts.GetGet() != "":
httpMethod = "GET"
pathTemplate = opts.GetGet()
if opts.Body != "" {
return nil, fmt.Errorf("needs request body even though http method is GET: %s", md.GetName())
}
case opts.GetPut() != "":
httpMethod = "PUT"
pathTemplate = opts.GetPut()
case opts.GetPost() != "":
httpMethod = "POST"
pathTemplate = opts.GetPost()
case opts.GetDelete() != "":
httpMethod = "DELETE"
pathTemplate = opts.GetDelete()
if opts.Body != "" {
return nil, fmt.Errorf("needs request body even though http method is DELETE: %s", md.GetName())
}
case opts.GetPatch() != "":
httpMethod = "PATCH"
pathTemplate = opts.GetPatch()
case opts.GetCustom() != nil:
custom := opts.GetCustom()
httpMethod = custom.Kind
pathTemplate = custom.Path
default:
glog.Errorf("No pattern specified in google.api.HttpRule: %s", md.GetName())
return nil, fmt.Errorf("none of pattern specified")
}
parsed, err := httprule.Parse(pathTemplate)
if err != nil {
return nil, err
}
tmpl := parsed.Compile()
if md.GetClientStreaming() && len(tmpl.Fields) > 0 {
return nil, fmt.Errorf("cannot use path parameter in client streaming")
}
b := &Binding{
Method: meth,
Index: idx,
PathTmpl: tmpl,
HTTPMethod: httpMethod,
}
for _, f := range tmpl.Fields {
param, err := r.newParam(meth, f)
if err != nil {
return nil, err
}
b.PathParams = append(b.PathParams, param)
}
// TODO(yugui) Handle query params
b.Body, err = r.newBody(meth, opts.Body)
if err != nil {
return nil, err
}
return b, nil
}
b, err := newBinding(opts, 0)
if err != nil {
return nil, err
}
meth.Bindings = append(meth.Bindings, b)
for i, additional := range opts.GetAdditionalBindings() {
if len(additional.AdditionalBindings) > 0 {
return nil, fmt.Errorf("additional_binding in additional_binding not allowed: %s.%s", svc.GetName(), meth.GetName())
}
b, err := newBinding(additional, i+1)
if err != nil {
return nil, err
}
meth.Bindings = append(meth.Bindings, b)
}
return meth, nil
}
func extractAPIOptions(meth *descriptor.MethodDescriptorProto) (*options.HttpRule, error) {
if meth.Options == nil {
return nil, nil
}
if !proto.HasExtension(meth.Options, options.E_Http) {
return nil, nil
}
ext, err := proto.GetExtension(meth.Options, options.E_Http)
if err != nil {
return nil, err
}
opts, ok := ext.(*options.HttpRule)
if !ok {
return nil, fmt.Errorf("extension is %T; want an HttpRule", ext)
}
return opts, nil
}
func (r *Registry) newParam(meth *Method, path string) (Parameter, error) {
msg := meth.RequestType
fields, err := r.resolveFiledPath(msg, path)
if err != nil {
return Parameter{}, err
}
l := len(fields)
if l == 0 {
return Parameter{}, fmt.Errorf("invalid field access list for %s", path)
}
target := fields[l-1].Target
switch target.GetType() {
case descriptor.FieldDescriptorProto_TYPE_MESSAGE, descriptor.FieldDescriptorProto_TYPE_GROUP:
return Parameter{}, fmt.Errorf("aggregate type %s in parameter of %s.%s: %s", target.Type, meth.Service.GetName(), meth.GetName(), path)
}
return Parameter{
FieldPath: FieldPath(fields),
Method: meth,
Target: fields[l-1].Target,
}, nil
}
func (r *Registry) newBody(meth *Method, path string) (*Body, error) {
msg := meth.RequestType
switch path {
case "":
return nil, nil
case "*":
return &Body{FieldPath: nil}, nil
}
fields, err := r.resolveFiledPath(msg, path)
if err != nil {
return nil, err
}
return &Body{FieldPath: FieldPath(fields)}, nil
}
// lookupField looks up a field named "name" within "msg".
// It returns nil if no such field found.
func lookupField(msg *Message, name string) *Field {
for _, f := range msg.Fields {
if f.GetName() == name {
return f
}
}
return nil
}
// resolveFieldPath resolves "path" into a list of fieldDescriptor, starting from "msg".
func (r *Registry) resolveFiledPath(msg *Message, path string) ([]FieldPathComponent, error) {
if path == "" {
return nil, nil
}
root := msg
var result []FieldPathComponent
for i, c := range strings.Split(path, ".") {
if i > 0 {
f := result[i-1].Target
switch f.GetType() {
case descriptor.FieldDescriptorProto_TYPE_MESSAGE, descriptor.FieldDescriptorProto_TYPE_GROUP:
var err error
msg, err = r.LookupMsg(msg.FQMN(), f.GetTypeName())
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("not an aggregate type: %s in %s", f.GetName(), path)
}
}
glog.V(2).Infof("Lookup %s in %s", c, msg.FQMN())
f := lookupField(msg, c)
if f == nil {
return nil, fmt.Errorf("no field %q found in %s", path, root.GetName())
}
if f.GetLabel() == descriptor.FieldDescriptorProto_LABEL_REPEATED {
return nil, fmt.Errorf("repeated field not allowed in field path: %s in %s", f.GetName(), path)
}
result = append(result, FieldPathComponent{Name: c, Target: f})
}
return result, nil
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,322 @@
package descriptor
import (
"fmt"
"strings"
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
gogen "github.com/golang/protobuf/protoc-gen-go/generator"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
)
// GoPackage represents a golang package
type GoPackage struct {
// Path is the package path to the package.
Path string
// Name is the package name of the package
Name string
// Alias is an alias of the package unique within the current invokation of grpc-gateway generator.
Alias string
}
// Standard returns whether the import is a golang standard package.
func (p GoPackage) Standard() bool {
return !strings.Contains(p.Path, ".")
}
// String returns a string representation of this package in the form of import line in golang.
func (p GoPackage) String() string {
if p.Alias == "" {
return fmt.Sprintf("%q", p.Path)
}
return fmt.Sprintf("%s %q", p.Alias, p.Path)
}
// File wraps descriptor.FileDescriptorProto for richer features.
type File struct {
*descriptor.FileDescriptorProto
// GoPkg is the go package of the go file generated from this file..
GoPkg GoPackage
// Messages is the list of messages defined in this file.
Messages []*Message
// Enums is the list of enums defined in this file.
Enums []*Enum
// Services is the list of services defined in this file.
Services []*Service
}
// proto2 determines if the syntax of the file is proto2.
func (f *File) proto2() bool {
return f.Syntax == nil || f.GetSyntax() == "proto2"
}
// Message describes a protocol buffer message types
type Message struct {
// File is the file where the message is defined
File *File
// Outers is a list of outer messages if this message is a nested type.
Outers []string
*descriptor.DescriptorProto
Fields []*Field
// Index is proto path index of this message in File.
Index int
}
// FQMN returns a fully qualified message name of this message.
func (m *Message) FQMN() string {
components := []string{""}
if m.File.Package != nil {
components = append(components, m.File.GetPackage())
}
components = append(components, m.Outers...)
components = append(components, m.GetName())
return strings.Join(components, ".")
}
// GoType returns a go type name for the message type.
// It prefixes the type name with the package alias if
// its belonging package is not "currentPackage".
func (m *Message) GoType(currentPackage string) string {
var components []string
components = append(components, m.Outers...)
components = append(components, m.GetName())
name := strings.Join(components, "_")
if m.File.GoPkg.Path == currentPackage {
return name
}
pkg := m.File.GoPkg.Name
if alias := m.File.GoPkg.Alias; alias != "" {
pkg = alias
}
return fmt.Sprintf("%s.%s", pkg, name)
}
// Enum describes a protocol buffer enum types
type Enum struct {
// File is the file where the enum is defined
File *File
// Outers is a list of outer messages if this enum is a nested type.
Outers []string
*descriptor.EnumDescriptorProto
Index int
}
// FQEN returns a fully qualified enum name of this enum.
func (e *Enum) FQEN() string {
components := []string{""}
if e.File.Package != nil {
components = append(components, e.File.GetPackage())
}
components = append(components, e.Outers...)
components = append(components, e.GetName())
return strings.Join(components, ".")
}
// Service wraps descriptor.ServiceDescriptorProto for richer features.
type Service struct {
// File is the file where this service is defined.
File *File
*descriptor.ServiceDescriptorProto
// Methods is the list of methods defined in this service.
Methods []*Method
}
// Method wraps descriptor.MethodDescriptorProto for richer features.
type Method struct {
// Service is the service which this method belongs to.
Service *Service
*descriptor.MethodDescriptorProto
// RequestType is the message type of requests to this method.
RequestType *Message
// ResponseType is the message type of responses from this method.
ResponseType *Message
Bindings []*Binding
}
// Binding describes how an HTTP endpoint is bound to a gRPC method.
type Binding struct {
// Method is the method which the endpoint is bound to.
Method *Method
// Index is a zero-origin index of the binding in the target method
Index int
// PathTmpl is path template where this method is mapped to.
PathTmpl httprule.Template
// HTTPMethod is the HTTP method which this method is mapped to.
HTTPMethod string
// PathParams is the list of parameters provided in HTTP request paths.
PathParams []Parameter
// Body describes parameters provided in HTTP request body.
Body *Body
}
// ExplicitParams returns a list of explicitly bound parameters of "b",
// i.e. a union of field path for body and field paths for path parameters.
func (b *Binding) ExplicitParams() []string {
var result []string
if b.Body != nil {
result = append(result, b.Body.FieldPath.String())
}
for _, p := range b.PathParams {
result = append(result, p.FieldPath.String())
}
return result
}
// Field wraps descriptor.FieldDescriptorProto for richer features.
type Field struct {
// Message is the message type which this field belongs to.
Message *Message
// FieldMessage is the message type of the field.
FieldMessage *Message
*descriptor.FieldDescriptorProto
}
// Parameter is a parameter provided in http requests
type Parameter struct {
// FieldPath is a path to a proto field which this parameter is mapped to.
FieldPath
// Target is the proto field which this parameter is mapped to.
Target *Field
// Method is the method which this parameter is used for.
Method *Method
}
// ConvertFuncExpr returns a go expression of a converter function.
// The converter function converts a string into a value for the parameter.
func (p Parameter) ConvertFuncExpr() (string, error) {
tbl := proto3ConvertFuncs
if p.Target.Message.File.proto2() {
tbl = proto2ConvertFuncs
}
typ := p.Target.GetType()
conv, ok := tbl[typ]
if !ok {
return "", fmt.Errorf("unsupported field type %s of parameter %s in %s.%s", typ, p.FieldPath, p.Method.Service.GetName(), p.Method.GetName())
}
return conv, nil
}
// Body describes a http requtest body to be sent to the method.
type Body struct {
// FieldPath is a path to a proto field which the request body is mapped to.
// The request body is mapped to the request type itself if FieldPath is empty.
FieldPath FieldPath
}
// RHS returns a right-hand-side expression in go to be used to initialize method request object.
// It starts with "msgExpr", which is the go expression of the method request object.
func (b Body) RHS(msgExpr string) string {
return b.FieldPath.RHS(msgExpr)
}
// FieldPath is a path to a field from a request message.
type FieldPath []FieldPathComponent
// String returns a string representation of the field path.
func (p FieldPath) String() string {
var components []string
for _, c := range p {
components = append(components, c.Name)
}
return strings.Join(components, ".")
}
// IsNestedProto3 indicates whether the FieldPath is a nested Proto3 path.
func (p FieldPath) IsNestedProto3() bool {
if len(p) > 1 && !p[0].Target.Message.File.proto2() {
return true
}
return false
}
// RHS is a right-hand-side expression in go to be used to assign a value to the target field.
// It starts with "msgExpr", which is the go expression of the method request object.
func (p FieldPath) RHS(msgExpr string) string {
l := len(p)
if l == 0 {
return msgExpr
}
components := []string{msgExpr}
for i, c := range p {
if i == l-1 {
components = append(components, c.RHS())
continue
}
components = append(components, c.LHS())
}
return strings.Join(components, ".")
}
// FieldPathComponent is a path component in FieldPath
type FieldPathComponent struct {
// Name is a name of the proto field which this component corresponds to.
// TODO(yugui) is this necessary?
Name string
// Target is the proto field which this component corresponds to.
Target *Field
}
// RHS returns a right-hand-side expression in go for this field.
func (c FieldPathComponent) RHS() string {
return gogen.CamelCase(c.Name)
}
// LHS returns a left-hand-side expression in go for this field.
func (c FieldPathComponent) LHS() string {
if c.Target.Message.File.proto2() {
return fmt.Sprintf("Get%s()", gogen.CamelCase(c.Name))
}
return gogen.CamelCase(c.Name)
}
var (
proto3ConvertFuncs = map[descriptor.FieldDescriptorProto_Type]string{
descriptor.FieldDescriptorProto_TYPE_DOUBLE: "runtime.Float64",
descriptor.FieldDescriptorProto_TYPE_FLOAT: "runtime.Float32",
descriptor.FieldDescriptorProto_TYPE_INT64: "runtime.Int64",
descriptor.FieldDescriptorProto_TYPE_UINT64: "runtime.Uint64",
descriptor.FieldDescriptorProto_TYPE_INT32: "runtime.Int32",
descriptor.FieldDescriptorProto_TYPE_FIXED64: "runtime.Uint64",
descriptor.FieldDescriptorProto_TYPE_FIXED32: "runtime.Uint32",
descriptor.FieldDescriptorProto_TYPE_BOOL: "runtime.Bool",
descriptor.FieldDescriptorProto_TYPE_STRING: "runtime.String",
// FieldDescriptorProto_TYPE_GROUP
// FieldDescriptorProto_TYPE_MESSAGE
// FieldDescriptorProto_TYPE_BYTES
// TODO(yugui) Handle bytes
descriptor.FieldDescriptorProto_TYPE_UINT32: "runtime.Uint32",
// FieldDescriptorProto_TYPE_ENUM
// TODO(yugui) Handle Enum
descriptor.FieldDescriptorProto_TYPE_SFIXED32: "runtime.Int32",
descriptor.FieldDescriptorProto_TYPE_SFIXED64: "runtime.Int64",
descriptor.FieldDescriptorProto_TYPE_SINT32: "runtime.Int32",
descriptor.FieldDescriptorProto_TYPE_SINT64: "runtime.Int64",
}
proto2ConvertFuncs = map[descriptor.FieldDescriptorProto_Type]string{
descriptor.FieldDescriptorProto_TYPE_DOUBLE: "runtime.Float64P",
descriptor.FieldDescriptorProto_TYPE_FLOAT: "runtime.Float32P",
descriptor.FieldDescriptorProto_TYPE_INT64: "runtime.Int64P",
descriptor.FieldDescriptorProto_TYPE_UINT64: "runtime.Uint64P",
descriptor.FieldDescriptorProto_TYPE_INT32: "runtime.Int32P",
descriptor.FieldDescriptorProto_TYPE_FIXED64: "runtime.Uint64P",
descriptor.FieldDescriptorProto_TYPE_FIXED32: "runtime.Uint32P",
descriptor.FieldDescriptorProto_TYPE_BOOL: "runtime.BoolP",
descriptor.FieldDescriptorProto_TYPE_STRING: "runtime.StringP",
// FieldDescriptorProto_TYPE_GROUP
// FieldDescriptorProto_TYPE_MESSAGE
// FieldDescriptorProto_TYPE_BYTES
// TODO(yugui) Handle bytes
descriptor.FieldDescriptorProto_TYPE_UINT32: "runtime.Uint32P",
// FieldDescriptorProto_TYPE_ENUM
// TODO(yugui) Handle Enum
descriptor.FieldDescriptorProto_TYPE_SFIXED32: "runtime.Int32P",
descriptor.FieldDescriptorProto_TYPE_SFIXED64: "runtime.Int64P",
descriptor.FieldDescriptorProto_TYPE_SINT32: "runtime.Int32P",
descriptor.FieldDescriptorProto_TYPE_SINT64: "runtime.Int64P",
}
)

View file

@ -0,0 +1,206 @@
package descriptor
import (
"testing"
"github.com/golang/protobuf/proto"
descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
)
func TestGoPackageStandard(t *testing.T) {
for _, spec := range []struct {
pkg GoPackage
want bool
}{
{
pkg: GoPackage{Path: "fmt", Name: "fmt"},
want: true,
},
{
pkg: GoPackage{Path: "encoding/json", Name: "json"},
want: true,
},
{
pkg: GoPackage{Path: "github.com/golang/protobuf/jsonpb", Name: "jsonpb"},
want: false,
},
{
pkg: GoPackage{Path: "golang.org/x/net/context", Name: "context"},
want: false,
},
{
pkg: GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway", Name: "main"},
want: false,
},
{
pkg: GoPackage{Path: "github.com/google/googleapis/google/api/http.pb", Name: "http_pb", Alias: "htpb"},
want: false,
},
} {
if got, want := spec.pkg.Standard(), spec.want; got != want {
t.Errorf("%#v.Standard() = %v; want %v", spec.pkg, got, want)
}
}
}
func TestGoPackageString(t *testing.T) {
for _, spec := range []struct {
pkg GoPackage
want string
}{
{
pkg: GoPackage{Path: "fmt", Name: "fmt"},
want: `"fmt"`,
},
{
pkg: GoPackage{Path: "encoding/json", Name: "json"},
want: `"encoding/json"`,
},
{
pkg: GoPackage{Path: "github.com/golang/protobuf/jsonpb", Name: "jsonpb"},
want: `"github.com/golang/protobuf/jsonpb"`,
},
{
pkg: GoPackage{Path: "golang.org/x/net/context", Name: "context"},
want: `"golang.org/x/net/context"`,
},
{
pkg: GoPackage{Path: "github.com/grpc-ecosystem/grpc-gateway", Name: "main"},
want: `"github.com/grpc-ecosystem/grpc-gateway"`,
},
{
pkg: GoPackage{Path: "github.com/google/googleapis/google/api/http.pb", Name: "http_pb", Alias: "htpb"},
want: `htpb "github.com/google/googleapis/google/api/http.pb"`,
},
} {
if got, want := spec.pkg.String(), spec.want; got != want {
t.Errorf("%#v.String() = %q; want %q", spec.pkg, got, want)
}
}
}
func TestFieldPath(t *testing.T) {
var fds []*descriptor.FileDescriptorProto
for _, src := range []string{
`
name: 'example.proto'
package: 'example'
message_type <
name: 'Nest'
field <
name: 'nest2_field'
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: 'Nest2'
number: 1
>
field <
name: 'terminal_field'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 2
>
>
syntax: "proto3"
`, `
name: 'another.proto'
package: 'example'
message_type <
name: 'Nest2'
field <
name: 'nest_field'
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: 'Nest'
number: 1
>
field <
name: 'terminal_field'
label: LABEL_OPTIONAL
type: TYPE_STRING
number: 2
>
>
syntax: "proto2"
`,
} {
var fd descriptor.FileDescriptorProto
if err := proto.UnmarshalText(src, &fd); err != nil {
t.Fatalf("proto.UnmarshalText(%s, &fd) failed with %v; want success", src, err)
}
fds = append(fds, &fd)
}
nest := &Message{
DescriptorProto: fds[0].MessageType[0],
Fields: []*Field{
{FieldDescriptorProto: fds[0].MessageType[0].Field[0]},
{FieldDescriptorProto: fds[0].MessageType[0].Field[1]},
},
}
nest2 := &Message{
DescriptorProto: fds[1].MessageType[0],
Fields: []*Field{
{FieldDescriptorProto: fds[1].MessageType[0].Field[0]},
{FieldDescriptorProto: fds[1].MessageType[0].Field[1]},
},
}
file1 := &File{
FileDescriptorProto: fds[0],
GoPkg: GoPackage{Path: "example", Name: "example"},
Messages: []*Message{nest},
}
file2 := &File{
FileDescriptorProto: fds[1],
GoPkg: GoPackage{Path: "example", Name: "example"},
Messages: []*Message{nest2},
}
crossLinkFixture(file1)
crossLinkFixture(file2)
c1 := FieldPathComponent{
Name: "nest_field",
Target: nest2.Fields[0],
}
if got, want := c1.LHS(), "GetNestField()"; got != want {
t.Errorf("c1.LHS() = %q; want %q", got, want)
}
if got, want := c1.RHS(), "NestField"; got != want {
t.Errorf("c1.RHS() = %q; want %q", got, want)
}
c2 := FieldPathComponent{
Name: "nest2_field",
Target: nest.Fields[0],
}
if got, want := c2.LHS(), "Nest2Field"; got != want {
t.Errorf("c2.LHS() = %q; want %q", got, want)
}
if got, want := c2.LHS(), "Nest2Field"; got != want {
t.Errorf("c2.LHS() = %q; want %q", got, want)
}
fp := FieldPath{
c1, c2, c1, FieldPathComponent{
Name: "terminal_field",
Target: nest.Fields[1],
},
}
if got, want := fp.RHS("resp"), "resp.GetNestField().Nest2Field.GetNestField().TerminalField"; got != want {
t.Errorf("fp.RHS(%q) = %q; want %q", "resp", got, want)
}
fp2 := FieldPath{
c2, c1, c2, FieldPathComponent{
Name: "terminal_field",
Target: nest2.Fields[1],
},
}
if got, want := fp2.RHS("resp"), "resp.Nest2Field.GetNestField().Nest2Field.TerminalField"; got != want {
t.Errorf("fp2.RHS(%q) = %q; want %q", "resp", got, want)
}
var fpEmpty FieldPath
if got, want := fpEmpty.RHS("resp"), "resp"; got != want {
t.Errorf("fpEmpty.RHS(%q) = %q; want %q", "resp", got, want)
}
}

View file

@ -0,0 +1,13 @@
// Package generator provides an abstract interface to code generators.
package generator
import (
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
)
// Generator is an abstraction of code generators.
type Generator interface {
// Generate generates output files from input .proto files.
Generate(targets []*descriptor.File) ([]*plugin.CodeGeneratorResponse_File, error)
}

View file

@ -0,0 +1,2 @@
// Package gengateway provides a code generator for grpc gateway files.
package gengateway

View file

@ -0,0 +1,111 @@
package gengateway
import (
"errors"
"fmt"
"go/format"
"path"
"path/filepath"
"strings"
"github.com/golang/glog"
"github.com/golang/protobuf/proto"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
gen "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/generator"
)
var (
errNoTargetService = errors.New("no target service defined in the file")
)
type generator struct {
reg *descriptor.Registry
baseImports []descriptor.GoPackage
}
// New returns a new generator which generates grpc gateway files.
func New(reg *descriptor.Registry) gen.Generator {
var imports []descriptor.GoPackage
for _, pkgpath := range []string{
"io",
"net/http",
"github.com/grpc-ecosystem/grpc-gateway/runtime",
"github.com/grpc-ecosystem/grpc-gateway/utilities",
"github.com/golang/protobuf/proto",
"golang.org/x/net/context",
"google.golang.org/grpc",
"google.golang.org/grpc/codes",
"google.golang.org/grpc/grpclog",
} {
pkg := descriptor.GoPackage{
Path: pkgpath,
Name: path.Base(pkgpath),
}
if err := reg.ReserveGoPackageAlias(pkg.Name, pkg.Path); err != nil {
for i := 0; ; i++ {
alias := fmt.Sprintf("%s_%d", pkg.Name, i)
if err := reg.ReserveGoPackageAlias(alias, pkg.Path); err != nil {
continue
}
pkg.Alias = alias
break
}
}
imports = append(imports, pkg)
}
return &generator{reg: reg, baseImports: imports}
}
func (g *generator) Generate(targets []*descriptor.File) ([]*plugin.CodeGeneratorResponse_File, error) {
var files []*plugin.CodeGeneratorResponse_File
for _, file := range targets {
glog.V(1).Infof("Processing %s", file.GetName())
code, err := g.generate(file)
if err == errNoTargetService {
glog.V(1).Infof("%s: %v", file.GetName(), err)
continue
}
if err != nil {
return nil, err
}
formatted, err := format.Source([]byte(code))
if err != nil {
glog.Errorf("%v: %s", err, code)
return nil, err
}
name := file.GetName()
ext := filepath.Ext(name)
base := strings.TrimSuffix(name, ext)
output := fmt.Sprintf("%s.pb.gw.go", base)
files = append(files, &plugin.CodeGeneratorResponse_File{
Name: proto.String(output),
Content: proto.String(string(formatted)),
})
glog.V(1).Infof("Will emit %s", output)
}
return files, nil
}
func (g *generator) generate(file *descriptor.File) (string, error) {
pkgSeen := make(map[string]bool)
var imports []descriptor.GoPackage
for _, pkg := range g.baseImports {
pkgSeen[pkg.Path] = true
imports = append(imports, pkg)
}
for _, svc := range file.Services {
for _, m := range svc.Methods {
pkg := m.RequestType.File.GoPkg
if pkg == file.GoPkg {
continue
}
if pkgSeen[pkg.Path] {
continue
}
pkgSeen[pkg.Path] = true
imports = append(imports, pkg)
}
}
return applyTemplate(param{File: file, Imports: imports})
}

View file

@ -0,0 +1,376 @@
package gengateway
import (
"bytes"
"fmt"
"strings"
"text/template"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
)
type param struct {
*descriptor.File
Imports []descriptor.GoPackage
}
type binding struct {
*descriptor.Binding
}
// HasQueryParam determines if the binding needs parameters in query string.
//
// It sometimes returns true even though actually the binding does not need.
// But it is not serious because it just results in a small amount of extra codes generated.
func (b binding) HasQueryParam() bool {
if b.Body != nil && len(b.Body.FieldPath) == 0 {
return false
}
fields := make(map[string]bool)
for _, f := range b.Method.RequestType.Fields {
fields[f.GetName()] = true
}
if b.Body != nil {
delete(fields, b.Body.FieldPath.String())
}
for _, p := range b.PathParams {
delete(fields, p.FieldPath.String())
}
return len(fields) > 0
}
func (b binding) QueryParamFilter() queryParamFilter {
var seqs [][]string
if b.Body != nil {
seqs = append(seqs, strings.Split(b.Body.FieldPath.String(), "."))
}
for _, p := range b.PathParams {
seqs = append(seqs, strings.Split(p.FieldPath.String(), "."))
}
return queryParamFilter{utilities.NewDoubleArray(seqs)}
}
// queryParamFilter is a wrapper of utilities.DoubleArray which provides String() to output DoubleArray.Encoding in a stable and predictable format.
type queryParamFilter struct {
*utilities.DoubleArray
}
func (f queryParamFilter) String() string {
encodings := make([]string, len(f.Encoding))
for str, enc := range f.Encoding {
encodings[enc] = fmt.Sprintf("%q: %d", str, enc)
}
e := strings.Join(encodings, ", ")
return fmt.Sprintf("&utilities.DoubleArray{Encoding: map[string]int{%s}, Base: %#v, Check: %#v}", e, f.Base, f.Check)
}
func applyTemplate(p param) (string, error) {
w := bytes.NewBuffer(nil)
if err := headerTemplate.Execute(w, p); err != nil {
return "", err
}
var methodSeen bool
for _, svc := range p.Services {
for _, meth := range svc.Methods {
glog.V(2).Infof("Processing %s.%s", svc.GetName(), meth.GetName())
methodSeen = true
for _, b := range meth.Bindings {
if err := handlerTemplate.Execute(w, binding{Binding: b}); err != nil {
return "", err
}
}
}
}
if !methodSeen {
return "", errNoTargetService
}
if err := trailerTemplate.Execute(w, p.Services); err != nil {
return "", err
}
return w.String(), nil
}
var (
headerTemplate = template.Must(template.New("header").Parse(`
// Code generated by protoc-gen-grpc-gateway
// source: {{.GetName}}
// DO NOT EDIT!
/*
Package {{.GoPkg.Name}} is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package {{.GoPkg.Name}}
import (
{{range $i := .Imports}}{{if $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}
{{range $i := .Imports}}{{if not $i.Standard}}{{$i | printf "%s\n"}}{{end}}{{end}}
)
var _ codes.Code
var _ io.Reader
var _ = runtime.String
var _ = utilities.NewDoubleArray
`))
handlerTemplate = template.Must(template.New("handler").Parse(`
{{if and .Method.GetClientStreaming .Method.GetServerStreaming}}
{{template "bidi-streaming-request-func" .}}
{{else if .Method.GetClientStreaming}}
{{template "client-streaming-request-func" .}}
{{else}}
{{template "client-rpc-request-func" .}}
{{end}}
`))
_ = template.Must(handlerTemplate.New("request-func-signature").Parse(strings.Replace(`
{{if .Method.GetServerStreaming}}
func request_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}(ctx context.Context, marshaler runtime.Marshaler, client {{.Method.Service.GetName}}Client, req *http.Request, pathParams map[string]string) ({{.Method.Service.GetName}}_{{.Method.GetName}}Client, runtime.ServerMetadata, error)
{{else}}
func request_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}(ctx context.Context, marshaler runtime.Marshaler, client {{.Method.Service.GetName}}Client, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error)
{{end}}`, "\n", "", -1)))
_ = template.Must(handlerTemplate.New("client-streaming-request-func").Parse(`
{{template "request-func-signature" .}} {
var metadata runtime.ServerMetadata
stream, err := client.{{.Method.GetName}}(ctx)
if err != nil {
grpclog.Printf("Failed to start streaming: %v", err)
return nil, metadata, err
}
dec := marshaler.NewDecoder(req.Body)
for {
var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
err = dec.Decode(&protoReq)
if err == io.EOF {
break
}
if err != nil {
grpclog.Printf("Failed to decode request: %v", err)
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
if err = stream.Send(&protoReq); err != nil {
grpclog.Printf("Failed to send request: %v", err)
return nil, metadata, err
}
}
if err := stream.CloseSend(); err != nil {
grpclog.Printf("Failed to terminate client stream: %v", err)
return nil, metadata, err
}
header, err := stream.Header()
if err != nil {
grpclog.Printf("Failed to get header from client: %v", err)
return nil, metadata, err
}
metadata.HeaderMD = header
{{if .Method.GetServerStreaming}}
return stream, metadata, nil
{{else}}
msg, err := stream.CloseAndRecv()
metadata.TrailerMD = stream.Trailer()
return msg, metadata, err
{{end}}
}
`))
_ = template.Must(handlerTemplate.New("client-rpc-request-func").Parse(`
{{if .HasQueryParam}}
var (
filter_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}} = {{.QueryParamFilter}}
)
{{end}}
{{template "request-func-signature" .}} {
var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
var metadata runtime.ServerMetadata
{{if .Body}}
if err := marshaler.NewDecoder(req.Body).Decode(&{{.Body.RHS "protoReq"}}); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
{{end}}
{{if .PathParams}}
var (
val string
ok bool
err error
_ = err
)
{{range $param := .PathParams}}
val, ok = pathParams[{{$param | printf "%q"}}]
if !ok {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", {{$param | printf "%q"}})
}
{{if $param.IsNestedProto3 }}
err = runtime.PopulateFieldFromPath(&protoReq, {{$param | printf "%q"}}, val)
{{else}}
{{$param.RHS "protoReq"}}, err = {{$param.ConvertFuncExpr}}(val)
{{end}}
if err != nil {
return nil, metadata, err
}
{{end}}
{{end}}
{{if .HasQueryParam}}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_{{.Method.Service.GetName}}_{{.Method.GetName}}_{{.Index}}); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
{{end}}
{{if .Method.GetServerStreaming}}
stream, err := client.{{.Method.GetName}}(ctx, &protoReq)
if err != nil {
return nil, metadata, err
}
header, err := stream.Header()
if err != nil {
return nil, metadata, err
}
metadata.HeaderMD = header
return stream, metadata, nil
{{else}}
msg, err := client.{{.Method.GetName}}(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
{{end}}
}`))
_ = template.Must(handlerTemplate.New("bidi-streaming-request-func").Parse(`
{{template "request-func-signature" .}} {
var metadata runtime.ServerMetadata
stream, err := client.{{.Method.GetName}}(ctx)
if err != nil {
grpclog.Printf("Failed to start streaming: %v", err)
return nil, metadata, err
}
dec := marshaler.NewDecoder(req.Body)
handleSend := func() error {
var protoReq {{.Method.RequestType.GoType .Method.Service.File.GoPkg.Path}}
err = dec.Decode(&protoReq)
if err == io.EOF {
return err
}
if err != nil {
grpclog.Printf("Failed to decode request: %v", err)
return err
}
if err = stream.Send(&protoReq); err != nil {
grpclog.Printf("Failed to send request: %v", err)
return err
}
return nil
}
if err := handleSend(); err != nil {
if cerr := stream.CloseSend(); cerr != nil {
grpclog.Printf("Failed to terminate client stream: %v", cerr)
}
if err == io.EOF {
return stream, metadata, nil
}
return nil, metadata, err
}
go func() {
for {
if err := handleSend(); err != nil {
break
}
}
if err := stream.CloseSend(); err != nil {
grpclog.Printf("Failed to terminate client stream: %v", err)
}
}()
header, err := stream.Header()
if err != nil {
grpclog.Printf("Failed to get header from client: %v", err)
return nil, metadata, err
}
metadata.HeaderMD = header
return stream, metadata, nil
}
`))
trailerTemplate = template.Must(template.New("trailer").Parse(`
{{range $svc := .}}
// Register{{$svc.GetName}}HandlerFromEndpoint is same as Register{{$svc.GetName}}Handler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func Register{{$svc.GetName}}HandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return Register{{$svc.GetName}}Handler(ctx, mux, conn)
}
// Register{{$svc.GetName}}Handler registers the http handlers for service {{$svc.GetName}} to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func Register{{$svc.GetName}}Handler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
client := New{{$svc.GetName}}Client(conn)
{{range $m := $svc.Methods}}
{{range $b := $m.Bindings}}
mux.Handle({{$b.HTTPMethod | printf "%q"}}, pattern_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
{{if $m.GetServerStreaming}}
forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(ctx, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
{{else}}
forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}}(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
{{end}}
})
{{end}}
{{end}}
return nil
}
var (
{{range $m := $svc.Methods}}
{{range $b := $m.Bindings}}
pattern_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = runtime.MustPattern(runtime.NewPattern({{$b.PathTmpl.Version}}, {{$b.PathTmpl.OpCodes | printf "%#v"}}, {{$b.PathTmpl.Pool | printf "%#v"}}, {{$b.PathTmpl.Verb | printf "%q"}}))
{{end}}
{{end}}
)
var (
{{range $m := $svc.Methods}}
{{range $b := $m.Bindings}}
forward_{{$svc.GetName}}_{{$m.GetName}}_{{$b.Index}} = {{if $m.GetServerStreaming}}runtime.ForwardResponseStream{{else}}runtime.ForwardResponseMessage{{end}}
{{end}}
{{end}}
)
{{end}}`))
)

View file

@ -0,0 +1,404 @@
package gengateway
import (
"strings"
"testing"
"github.com/golang/protobuf/proto"
protodescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
)
func crossLinkFixture(f *descriptor.File) *descriptor.File {
for _, m := range f.Messages {
m.File = f
}
for _, svc := range f.Services {
svc.File = f
for _, m := range svc.Methods {
m.Service = svc
for _, b := range m.Bindings {
b.Method = m
for _, param := range b.PathParams {
param.Method = m
}
}
}
}
return f
}
func TestApplyTemplateHeader(t *testing.T) {
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Example"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth},
}
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
Name: proto.String("example.proto"),
Package: proto.String("example"),
Dependency: []string{"a.example/b/c.proto", "a.example/d/e.proto"},
MessageType: []*protodescriptor.DescriptorProto{msgdesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "GET",
Body: &descriptor.Body{FieldPath: nil},
},
},
},
},
},
},
}
got, err := applyTemplate(param{File: crossLinkFixture(&file)})
if err != nil {
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
return
}
if want := "package example_pb\n"; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
}
func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) {
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("nested"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
TypeName: proto.String("NestedMessage"),
Number: proto.Int32(1),
},
},
}
nesteddesc := &protodescriptor.DescriptorProto{
Name: proto.String("NestedMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("int32"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
Number: proto.Int32(1),
},
{
Name: proto.String("bool"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
Number: proto.Int32(2),
},
},
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Echo"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
ClientStreaming: proto.Bool(false),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth},
}
for _, spec := range []struct {
serverStreaming bool
sigWant string
}{
{
serverStreaming: false,
sigWant: `func request_ExampleService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {`,
},
{
serverStreaming: true,
sigWant: `func request_ExampleService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (ExampleService_EchoClient, runtime.ServerMetadata, error) {`,
},
} {
meth.ServerStreaming = proto.Bool(spec.serverStreaming)
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
nested := &descriptor.Message{
DescriptorProto: nesteddesc,
}
nestedField := &descriptor.Field{
Message: msg,
FieldDescriptorProto: msg.GetField()[0],
}
intField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[0],
}
boolField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[1],
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
Name: proto.String("example.proto"),
Package: proto.String("example"),
MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg, nested},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "POST",
PathTmpl: httprule.Template{
Version: 1,
OpCodes: []int{0, 0},
},
PathParams: []descriptor.Parameter{
{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "int32",
Target: intField,
},
}),
Target: intField,
},
},
Body: &descriptor.Body{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "bool",
Target: boolField,
},
}),
},
},
},
},
},
},
},
}
got, err := applyTemplate(param{File: crossLinkFixture(&file)})
if err != nil {
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
return
}
if want := spec.sigWant; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `marshaler.NewDecoder(req.Body).Decode(&protoReq.GetNested().Bool)`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `val, ok = pathParams["nested.int32"]`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `protoReq.GetNested().Int32, err = runtime.Int32P(val)`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `func RegisterExampleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `pattern_ExampleService_Echo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{0, 0}, []string(nil), ""))`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
}
}
func TestApplyTemplateRequestWithClientStreaming(t *testing.T) {
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("nested"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
TypeName: proto.String("NestedMessage"),
Number: proto.Int32(1),
},
},
}
nesteddesc := &protodescriptor.DescriptorProto{
Name: proto.String("NestedMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("int32"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
Number: proto.Int32(1),
},
{
Name: proto.String("bool"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
Number: proto.Int32(2),
},
},
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Echo"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
ClientStreaming: proto.Bool(true),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth},
}
for _, spec := range []struct {
serverStreaming bool
sigWant string
}{
{
serverStreaming: false,
sigWant: `func request_ExampleService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {`,
},
{
serverStreaming: true,
sigWant: `func request_ExampleService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (ExampleService_EchoClient, runtime.ServerMetadata, error) {`,
},
} {
meth.ServerStreaming = proto.Bool(spec.serverStreaming)
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
nested := &descriptor.Message{
DescriptorProto: nesteddesc,
}
nestedField := &descriptor.Field{
Message: msg,
FieldDescriptorProto: msg.GetField()[0],
}
intField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[0],
}
boolField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[1],
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
Name: proto.String("example.proto"),
Package: proto.String("example"),
MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg, nested},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "POST",
PathTmpl: httprule.Template{
Version: 1,
OpCodes: []int{0, 0},
},
PathParams: []descriptor.Parameter{
{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "int32",
Target: intField,
},
}),
Target: intField,
},
},
Body: &descriptor.Body{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "bool",
Target: boolField,
},
}),
},
},
},
},
},
},
},
}
got, err := applyTemplate(param{File: crossLinkFixture(&file)})
if err != nil {
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
return
}
if want := spec.sigWant; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `marshaler.NewDecoder(req.Body)`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `func RegisterExampleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
if want := `pattern_ExampleService_Echo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{0, 0}, []string(nil), ""))`; !strings.Contains(got, want) {
t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want)
}
}
}

View file

@ -0,0 +1,117 @@
package httprule
import (
"github.com/grpc-ecosystem/grpc-gateway/utilities"
)
const (
opcodeVersion = 1
)
// Template is a compiled representation of path templates.
type Template struct {
// Version is the version number of the format.
Version int
// OpCodes is a sequence of operations.
OpCodes []int
// Pool is a constant pool
Pool []string
// Verb is a VERB part in the template.
Verb string
// Fields is a list of field paths bound in this template.
Fields []string
// Original template (example: /v1/a_bit_of_everything)
Template string
}
// Compiler compiles utilities representation of path templates into marshallable operations.
// They can be unmarshalled by runtime.NewPattern.
type Compiler interface {
Compile() Template
}
type op struct {
// code is the opcode of the operation
code utilities.OpCode
// str is a string operand of the code.
// num is ignored if str is not empty.
str string
// num is a numeric operand of the code.
num int
}
func (w wildcard) compile() []op {
return []op{
{code: utilities.OpPush},
}
}
func (w deepWildcard) compile() []op {
return []op{
{code: utilities.OpPushM},
}
}
func (l literal) compile() []op {
return []op{
{
code: utilities.OpLitPush,
str: string(l),
},
}
}
func (v variable) compile() []op {
var ops []op
for _, s := range v.segments {
ops = append(ops, s.compile()...)
}
ops = append(ops, op{
code: utilities.OpConcatN,
num: len(v.segments),
}, op{
code: utilities.OpCapture,
str: v.path,
})
return ops
}
func (t template) Compile() Template {
var rawOps []op
for _, s := range t.segments {
rawOps = append(rawOps, s.compile()...)
}
var (
ops []int
pool []string
fields []string
)
consts := make(map[string]int)
for _, op := range rawOps {
ops = append(ops, int(op.code))
if op.str == "" {
ops = append(ops, op.num)
} else {
if _, ok := consts[op.str]; !ok {
consts[op.str] = len(pool)
pool = append(pool, op.str)
}
ops = append(ops, consts[op.str])
}
if op.code == utilities.OpCapture {
fields = append(fields, op.str)
}
}
return Template{
Version: opcodeVersion,
OpCodes: ops,
Pool: pool,
Verb: t.verb,
Fields: fields,
Template: t.template,
}
}

View file

@ -0,0 +1,122 @@
package httprule
import (
"reflect"
"testing"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
)
const (
operandFiller = 0
)
func TestCompile(t *testing.T) {
for _, spec := range []struct {
segs []segment
verb string
ops []int
pool []string
fields []string
}{
{},
{
segs: []segment{
wildcard{},
},
ops: []int{int(utilities.OpPush), operandFiller},
},
{
segs: []segment{
deepWildcard{},
},
ops: []int{int(utilities.OpPushM), operandFiller},
},
{
segs: []segment{
literal("v1"),
},
ops: []int{int(utilities.OpLitPush), 0},
pool: []string{"v1"},
},
{
segs: []segment{
literal("v1"),
},
verb: "LOCK",
ops: []int{int(utilities.OpLitPush), 0},
pool: []string{"v1"},
},
{
segs: []segment{
variable{
path: "name.nested",
segments: []segment{
wildcard{},
},
},
},
ops: []int{
int(utilities.OpPush), operandFiller,
int(utilities.OpConcatN), 1,
int(utilities.OpCapture), 0,
},
pool: []string{"name.nested"},
fields: []string{"name.nested"},
},
{
segs: []segment{
literal("obj"),
variable{
path: "name.nested",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
variable{
path: "obj",
segments: []segment{
deepWildcard{},
},
},
},
ops: []int{
int(utilities.OpLitPush), 0,
int(utilities.OpLitPush), 1,
int(utilities.OpPush), operandFiller,
int(utilities.OpLitPush), 2,
int(utilities.OpConcatN), 3,
int(utilities.OpCapture), 3,
int(utilities.OpPushM), operandFiller,
int(utilities.OpConcatN), 1,
int(utilities.OpCapture), 0,
},
pool: []string{"obj", "a", "b", "name.nested"},
fields: []string{"name.nested", "obj"},
},
} {
tmpl := template{
segments: spec.segs,
verb: spec.verb,
}
compiled := tmpl.Compile()
if got, want := compiled.Version, opcodeVersion; got != want {
t.Errorf("tmpl.Compile().Version = %d; want %d; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.OpCodes, spec.ops; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().OpCodes = %v; want %v; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Pool, spec.pool; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().Pool = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Verb, spec.verb; got != want {
t.Errorf("tmpl.Compile().Verb = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Fields, spec.fields; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().Fields = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
}
}

View file

@ -0,0 +1,351 @@
package httprule
import (
"fmt"
"strings"
"github.com/golang/glog"
)
// InvalidTemplateError indicates that the path template is not valid.
type InvalidTemplateError struct {
tmpl string
msg string
}
func (e InvalidTemplateError) Error() string {
return fmt.Sprintf("%s: %s", e.msg, e.tmpl)
}
// Parse parses the string representation of path template
func Parse(tmpl string) (Compiler, error) {
if !strings.HasPrefix(tmpl, "/") {
return template{}, InvalidTemplateError{tmpl: tmpl, msg: "no leading /"}
}
tokens, verb := tokenize(tmpl[1:])
p := parser{tokens: tokens}
segs, err := p.topLevelSegments()
if err != nil {
return template{}, InvalidTemplateError{tmpl: tmpl, msg: err.Error()}
}
return template{
segments: segs,
verb: verb,
template: tmpl,
}, nil
}
func tokenize(path string) (tokens []string, verb string) {
if path == "" {
return []string{eof}, ""
}
const (
init = iota
field
nested
)
var (
st = init
)
for path != "" {
var idx int
switch st {
case init:
idx = strings.IndexAny(path, "/{")
case field:
idx = strings.IndexAny(path, ".=}")
case nested:
idx = strings.IndexAny(path, "/}")
}
if idx < 0 {
tokens = append(tokens, path)
break
}
switch r := path[idx]; r {
case '/', '.':
case '{':
st = field
case '=':
st = nested
case '}':
st = init
}
if idx == 0 {
tokens = append(tokens, path[idx:idx+1])
} else {
tokens = append(tokens, path[:idx], path[idx:idx+1])
}
path = path[idx+1:]
}
l := len(tokens)
t := tokens[l-1]
if idx := strings.LastIndex(t, ":"); idx == 0 {
tokens, verb = tokens[:l-1], t[1:]
} else if idx > 0 {
tokens[l-1], verb = t[:idx], t[idx+1:]
}
tokens = append(tokens, eof)
return tokens, verb
}
// parser is a parser of the template syntax defined in third_party/googleapis/google/api/httprule.proto.
type parser struct {
tokens []string
accepted []string
}
// topLevelSegments is the target of this parser.
func (p *parser) topLevelSegments() ([]segment, error) {
glog.V(1).Infof("Parsing %q", p.tokens)
segs, err := p.segments()
if err != nil {
return nil, err
}
glog.V(2).Infof("accept segments: %q; %q", p.accepted, p.tokens)
if _, err := p.accept(typeEOF); err != nil {
return nil, fmt.Errorf("unexpected token %q after segments %q", p.tokens[0], strings.Join(p.accepted, ""))
}
glog.V(2).Infof("accept eof: %q; %q", p.accepted, p.tokens)
return segs, nil
}
func (p *parser) segments() ([]segment, error) {
s, err := p.segment()
if err != nil {
return nil, err
}
glog.V(2).Infof("accept segment: %q; %q", p.accepted, p.tokens)
segs := []segment{s}
for {
if _, err := p.accept("/"); err != nil {
return segs, nil
}
s, err := p.segment()
if err != nil {
return segs, err
}
segs = append(segs, s)
glog.V(2).Infof("accept segment: %q; %q", p.accepted, p.tokens)
}
}
func (p *parser) segment() (segment, error) {
if _, err := p.accept("*"); err == nil {
return wildcard{}, nil
}
if _, err := p.accept("**"); err == nil {
return deepWildcard{}, nil
}
if l, err := p.literal(); err == nil {
return l, nil
}
v, err := p.variable()
if err != nil {
return nil, fmt.Errorf("segment neither wildcards, literal or variable: %v", err)
}
return v, err
}
func (p *parser) literal() (segment, error) {
lit, err := p.accept(typeLiteral)
if err != nil {
return nil, err
}
return literal(lit), nil
}
func (p *parser) variable() (segment, error) {
if _, err := p.accept("{"); err != nil {
return nil, err
}
path, err := p.fieldPath()
if err != nil {
return nil, err
}
var segs []segment
if _, err := p.accept("="); err == nil {
segs, err = p.segments()
if err != nil {
return nil, fmt.Errorf("invalid segment in variable %q: %v", path, err)
}
} else {
segs = []segment{wildcard{}}
}
if _, err := p.accept("}"); err != nil {
return nil, fmt.Errorf("unterminated variable segment: %s", path)
}
return variable{
path: path,
segments: segs,
}, nil
}
func (p *parser) fieldPath() (string, error) {
c, err := p.accept(typeIdent)
if err != nil {
return "", err
}
components := []string{c}
for {
if _, err = p.accept("."); err != nil {
return strings.Join(components, "."), nil
}
c, err := p.accept(typeIdent)
if err != nil {
return "", fmt.Errorf("invalid field path component: %v", err)
}
components = append(components, c)
}
}
// A termType is a type of terminal symbols.
type termType string
// These constants define some of valid values of termType.
// They improve readability of parse functions.
//
// You can also use "/", "*", "**", "." or "=" as valid values.
const (
typeIdent = termType("ident")
typeLiteral = termType("literal")
typeEOF = termType("$")
)
const (
// eof is the terminal symbol which always appears at the end of token sequence.
eof = "\u0000"
)
// accept tries to accept a token in "p".
// This function consumes a token and returns it if it matches to the specified "term".
// If it doesn't match, the function does not consume any tokens and return an error.
func (p *parser) accept(term termType) (string, error) {
t := p.tokens[0]
switch term {
case "/", "*", "**", ".", "=", "{", "}":
if t != string(term) {
return "", fmt.Errorf("expected %q but got %q", term, t)
}
case typeEOF:
if t != eof {
return "", fmt.Errorf("expected EOF but got %q", t)
}
case typeIdent:
if err := expectIdent(t); err != nil {
return "", err
}
case typeLiteral:
if err := expectPChars(t); err != nil {
return "", err
}
default:
return "", fmt.Errorf("unknown termType %q", term)
}
p.tokens = p.tokens[1:]
p.accepted = append(p.accepted, t)
return t, nil
}
// expectPChars determines if "t" consists of only pchars defined in RFC3986.
//
// https://www.ietf.org/rfc/rfc3986.txt, P.49
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
// / "*" / "+" / "," / ";" / "="
// pct-encoded = "%" HEXDIG HEXDIG
func expectPChars(t string) error {
const (
init = iota
pct1
pct2
)
st := init
for _, r := range t {
if st != init {
if !isHexDigit(r) {
return fmt.Errorf("invalid hexdigit: %c(%U)", r, r)
}
switch st {
case pct1:
st = pct2
case pct2:
st = init
}
continue
}
// unreserved
switch {
case 'A' <= r && r <= 'Z':
continue
case 'a' <= r && r <= 'z':
continue
case '0' <= r && r <= '9':
continue
}
switch r {
case '-', '.', '_', '~':
// unreserved
case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=':
// sub-delims
case ':', '@':
// rest of pchar
case '%':
// pct-encoded
st = pct1
default:
return fmt.Errorf("invalid character in path segment: %q(%U)", r, r)
}
}
if st != init {
return fmt.Errorf("invalid percent-encoding in %q", t)
}
return nil
}
// expectIdent determines if "ident" is a valid identifier in .proto schema ([[:alpha:]_][[:alphanum:]_]*).
func expectIdent(ident string) error {
if ident == "" {
return fmt.Errorf("empty identifier")
}
for pos, r := range ident {
switch {
case '0' <= r && r <= '9':
if pos == 0 {
return fmt.Errorf("identifier starting with digit: %s", ident)
}
continue
case 'A' <= r && r <= 'Z':
continue
case 'a' <= r && r <= 'z':
continue
case r == '_':
continue
default:
return fmt.Errorf("invalid character %q(%U) in identifier: %s", r, r, ident)
}
}
return nil
}
func isHexDigit(r rune) bool {
switch {
case '0' <= r && r <= '9':
return true
case 'A' <= r && r <= 'F':
return true
case 'a' <= r && r <= 'f':
return true
}
return false
}

View file

@ -0,0 +1,313 @@
package httprule
import (
"flag"
"fmt"
"reflect"
"testing"
"github.com/golang/glog"
)
func TestTokenize(t *testing.T) {
for _, spec := range []struct {
src string
tokens []string
}{
{
src: "",
tokens: []string{eof},
},
{
src: "v1",
tokens: []string{"v1", eof},
},
{
src: "v1/b",
tokens: []string{"v1", "/", "b", eof},
},
{
src: "v1/endpoint/*",
tokens: []string{"v1", "/", "endpoint", "/", "*", eof},
},
{
src: "v1/endpoint/**",
tokens: []string{"v1", "/", "endpoint", "/", "**", eof},
},
{
src: "v1/b/{bucket_name=*}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "*", "}",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}/o",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
"o",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}/o/{name}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
"o", "/", "{", "name", "}",
eof,
},
},
{
src: "v1/a=b&c=d;e=f:g/endpoint.rdf",
tokens: []string{
"v1", "/",
"a=b&c=d;e=f:g", "/",
"endpoint.rdf",
eof,
},
},
} {
tokens, verb := tokenize(spec.src)
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
}
if got, want := verb, ""; got != want {
t.Errorf("tokenize(%q) = _, %q; want _, %q", spec.src, got, want)
}
src := fmt.Sprintf("%s:%s", spec.src, "LOCK")
tokens, verb = tokenize(src)
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
t.Errorf("tokenize(%q) = %q, _; want %q, _", src, got, want)
}
if got, want := verb, "LOCK"; got != want {
t.Errorf("tokenize(%q) = _, %q; want _, %q", src, got, want)
}
}
}
func TestParseSegments(t *testing.T) {
flag.Set("v", "3")
for _, spec := range []struct {
tokens []string
want []segment
}{
{
tokens: []string{"v1", eof},
want: []segment{
literal("v1"),
},
},
{
tokens: []string{"-._~!$&'()*+,;=:@", eof},
want: []segment{
literal("-._~!$&'()*+,;=:@"),
},
},
{
tokens: []string{"%e7%ac%ac%e4%b8%80%e7%89%88", eof},
want: []segment{
literal("%e7%ac%ac%e4%b8%80%e7%89%88"),
},
},
{
tokens: []string{"v1", "/", "*", eof},
want: []segment{
literal("v1"),
wildcard{},
},
},
{
tokens: []string{"v1", "/", "**", eof},
want: []segment{
literal("v1"),
deepWildcard{},
},
},
{
tokens: []string{"{", "name", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "name", "=", "*", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "field", ".", "nested", ".", "nested2", "=", "*", "}", eof},
want: []segment{
variable{
path: "field.nested.nested2",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "name", "=", "a", "/", "b", "/", "*", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
},
},
},
},
{
tokens: []string{
"v1", "/",
"{",
"name", ".", "nested", ".", "nested2",
"=",
"a", "/", "b", "/", "*",
"}", "/",
"o", "/",
"{",
"another_name",
"=",
"a", "/", "b", "/", "*", "/", "c",
"}", "/",
"**",
eof},
want: []segment{
literal("v1"),
variable{
path: "name.nested.nested2",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
},
},
literal("o"),
variable{
path: "another_name",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
literal("c"),
},
},
deepWildcard{},
},
},
} {
p := parser{tokens: spec.tokens}
segs, err := p.topLevelSegments()
if err != nil {
t.Errorf("parser{%q}.segments() failed with %v; want success", spec.tokens, err)
continue
}
if got, want := segs, spec.want; !reflect.DeepEqual(got, want) {
t.Errorf("parser{%q}.segments() = %#v; want %#v", spec.tokens, got, want)
}
if got := p.tokens; len(got) > 0 {
t.Errorf("p.tokens = %q; want []; spec.tokens=%q", got, spec.tokens)
}
}
}
func TestParseSegmentsWithErrors(t *testing.T) {
flag.Set("v", "3")
for _, spec := range []struct {
tokens []string
}{
{
// double slash
tokens: []string{"/", eof},
},
{
// invalid literal
tokens: []string{"a?b", eof},
},
{
// invalid percent-encoding
tokens: []string{"%", eof},
},
{
// invalid percent-encoding
tokens: []string{"%2", eof},
},
{
// invalid percent-encoding
tokens: []string{"a%2z", eof},
},
{
// empty segments
tokens: []string{eof},
},
{
// unterminated variable
tokens: []string{"{", "name", eof},
},
{
// unterminated variable
tokens: []string{"{", "name", "=", eof},
},
{
// unterminated variable
tokens: []string{"{", "name", "=", "*", eof},
},
{
// empty component in field path
tokens: []string{"{", "name", ".", "}", eof},
},
{
// empty component in field path
tokens: []string{"{", "name", ".", ".", "nested", "}", eof},
},
{
// invalid character in identifier
tokens: []string{"{", "field-name", "}", eof},
},
{
// no slash between segments
tokens: []string{"v1", "endpoint", eof},
},
{
// no slash between segments
tokens: []string{"v1", "{", "name", "}", eof},
},
} {
p := parser{tokens: spec.tokens}
segs, err := p.topLevelSegments()
if err == nil {
t.Errorf("parser{%q}.segments() succeeded; want InvalidTemplateError; accepted %#v", spec.tokens, segs)
continue
}
glog.V(1).Info(err)
}
}

View file

@ -0,0 +1,60 @@
package httprule
import (
"fmt"
"strings"
)
type template struct {
segments []segment
verb string
template string
}
type segment interface {
fmt.Stringer
compile() (ops []op)
}
type wildcard struct{}
type deepWildcard struct{}
type literal string
type variable struct {
path string
segments []segment
}
func (wildcard) String() string {
return "*"
}
func (deepWildcard) String() string {
return "**"
}
func (l literal) String() string {
return string(l)
}
func (v variable) String() string {
var segs []string
for _, s := range v.segments {
segs = append(segs, s.String())
}
return fmt.Sprintf("{%s=%s}", v.path, strings.Join(segs, "/"))
}
func (t template) String() string {
var segs []string
for _, s := range t.segments {
segs = append(segs, s.String())
}
str := strings.Join(segs, "/")
if t.verb != "" {
str = fmt.Sprintf("%s:%s", str, t.verb)
}
return "/" + str
}

View file

@ -0,0 +1,91 @@
package httprule
import (
"fmt"
"testing"
)
func TestTemplateStringer(t *testing.T) {
for _, spec := range []struct {
segs []segment
want string
}{
{
segs: []segment{
literal("v1"),
},
want: "/v1",
},
{
segs: []segment{
wildcard{},
},
want: "/*",
},
{
segs: []segment{
deepWildcard{},
},
want: "/**",
},
{
segs: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
},
},
},
want: "/{name=a}",
},
{
segs: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
},
want: "/{name=a/*/b}",
},
{
segs: []segment{
literal("v1"),
variable{
path: "name",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
literal("c"),
variable{
path: "field.nested",
segments: []segment{
wildcard{},
literal("d"),
},
},
wildcard{},
literal("e"),
deepWildcard{},
},
want: "/v1/{name=a/*/b}/c/{field.nested=*/d}/*/e/**",
},
} {
tmpl := template{segments: spec.segs}
if got, want := tmpl.String(), spec.want; got != want {
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
}
tmpl.verb = "LOCK"
if got, want := tmpl.String(), fmt.Sprintf("%s:LOCK", spec.want); got != want {
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
}
}
}

View file

@ -0,0 +1,118 @@
// Command protoc-gen-grpc-gateway is a plugin for Google protocol buffer
// compiler to generate a reverse-proxy, which converts incoming RESTful
// HTTP/1 requests gRPC invocation.
// You rarely need to run this program directly. Instead, put this program
// into your $PATH with a name "protoc-gen-grpc-gateway" and run
// protoc --grpc-gateway_out=output_directory path/to/input.proto
//
// See README.md for more details.
package main
import (
"flag"
"io"
"io/ioutil"
"os"
"strings"
"github.com/golang/glog"
"github.com/golang/protobuf/proto"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/gengateway"
)
var (
importPrefix = flag.String("import_prefix", "", "prefix to be added to go package paths for imported proto files")
)
func parseReq(r io.Reader) (*plugin.CodeGeneratorRequest, error) {
glog.V(1).Info("Parsing code generator request")
input, err := ioutil.ReadAll(r)
if err != nil {
glog.Errorf("Failed to read code generator request: %v", err)
return nil, err
}
req := new(plugin.CodeGeneratorRequest)
if err = proto.Unmarshal(input, req); err != nil {
glog.Errorf("Failed to unmarshal code generator request: %v", err)
return nil, err
}
glog.V(1).Info("Parsed code generator request")
return req, nil
}
func main() {
flag.Parse()
defer glog.Flush()
reg := descriptor.NewRegistry()
glog.V(1).Info("Processing code generator request")
req, err := parseReq(os.Stdin)
if err != nil {
glog.Fatal(err)
}
if req.Parameter != nil {
for _, p := range strings.Split(req.GetParameter(), ",") {
spec := strings.SplitN(p, "=", 2)
if len(spec) == 1 {
if err := flag.CommandLine.Set(spec[0], ""); err != nil {
glog.Fatalf("Cannot set flag %s", p)
}
continue
}
name, value := spec[0], spec[1]
if strings.HasPrefix(name, "M") {
reg.AddPkgMap(name[1:], value)
continue
}
if err := flag.CommandLine.Set(name, value); err != nil {
glog.Fatalf("Cannot set flag %s", p)
}
}
}
g := gengateway.New(reg)
reg.SetPrefix(*importPrefix)
if err := reg.Load(req); err != nil {
emitError(err)
return
}
var targets []*descriptor.File
for _, target := range req.FileToGenerate {
f, err := reg.LookupFile(target)
if err != nil {
glog.Fatal(err)
}
targets = append(targets, f)
}
out, err := g.Generate(targets)
glog.V(1).Info("Processed code generator request")
if err != nil {
emitError(err)
return
}
emitFiles(out)
}
func emitFiles(out []*plugin.CodeGeneratorResponse_File) {
emitResp(&plugin.CodeGeneratorResponse{File: out})
}
func emitError(err error) {
emitResp(&plugin.CodeGeneratorResponse{Error: proto.String(err.Error())})
}
func emitResp(resp *plugin.CodeGeneratorResponse) {
buf, err := proto.Marshal(resp)
if err != nil {
glog.Fatal(err)
}
if _, err := os.Stdout.Write(buf); err != nil {
glog.Fatal(err)
}
}

View file

@ -0,0 +1,2 @@
// Package genswagger provides a code generator for swagger.
package genswagger

View file

@ -0,0 +1,58 @@
package genswagger
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"path/filepath"
"strings"
"github.com/golang/glog"
"github.com/golang/protobuf/proto"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
gen "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/generator"
)
var (
errNoTargetService = errors.New("no target service defined in the file")
)
type generator struct {
reg *descriptor.Registry
}
// New returns a new generator which generates grpc gateway files.
func New(reg *descriptor.Registry) gen.Generator {
return &generator{reg: reg}
}
func (g *generator) Generate(targets []*descriptor.File) ([]*plugin.CodeGeneratorResponse_File, error) {
var files []*plugin.CodeGeneratorResponse_File
for _, file := range targets {
glog.V(1).Infof("Processing %s", file.GetName())
code, err := applyTemplate(param{File: file, reg: g.reg})
if err == errNoTargetService {
glog.V(1).Infof("%s: %v", file.GetName(), err)
continue
}
if err != nil {
return nil, err
}
var formatted bytes.Buffer
json.Indent(&formatted, []byte(code), "", " ")
name := file.GetName()
ext := filepath.Ext(name)
base := strings.TrimSuffix(name, ext)
output := fmt.Sprintf("%s.swagger.json", base)
files = append(files, &plugin.CodeGeneratorResponse_File{
Name: proto.String(output),
Content: proto.String(formatted.String()),
})
glog.V(1).Infof("Will emit %s", output)
}
return files, nil
}

View file

@ -0,0 +1,708 @@
package genswagger
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
pbdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
)
// findServicesMessagesAndEnumerations discovers all messages and enums defined in the RPC methods of the service.
func findServicesMessagesAndEnumerations(s []*descriptor.Service, reg *descriptor.Registry, m messageMap, e enumMap) {
for _, svc := range s {
for _, meth := range svc.Methods {
m[fullyQualifiedNameToSwaggerName(meth.RequestType.FQMN(), reg)] = meth.RequestType
findNestedMessagesAndEnumerations(meth.RequestType, reg, m, e)
m[fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)] = meth.ResponseType
findNestedMessagesAndEnumerations(meth.ResponseType, reg, m, e)
}
}
}
// findNestedMessagesAndEnumerations those can be generated by the services.
func findNestedMessagesAndEnumerations(message *descriptor.Message, reg *descriptor.Registry, m messageMap, e enumMap) {
// Iterate over all the fields that
for _, t := range message.Fields {
fieldType := t.GetTypeName()
// If the type is an empty string then it is a proto primitive
if fieldType != "" {
if _, ok := m[fieldType]; !ok {
msg, err := reg.LookupMsg("", fieldType)
if err != nil {
enum, err := reg.LookupEnum("", fieldType)
if err != nil {
panic(err)
}
e[fieldType] = enum
continue
}
m[fieldType] = msg
findNestedMessagesAndEnumerations(msg, reg, m, e)
}
}
}
}
func renderMessagesAsDefinition(messages messageMap, d swaggerDefinitionsObject, reg *descriptor.Registry) {
for name, msg := range messages {
switch name {
case ".google.protobuf.Timestamp":
continue
}
if opt := msg.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
continue
}
schema := swaggerSchemaObject{
schemaCore: schemaCore{
Type: "object",
},
Properties: make(map[string]swaggerSchemaObject),
}
msgComments := protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index))
if err := updateSwaggerDataFromComments(&schema, msgComments); err != nil {
panic(err)
}
for i, f := range msg.Fields {
fieldValue := schemaOfField(f, reg)
fieldProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil)), "Field")
fieldProtoComments := protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index), fieldProtoPath, int32(i))
if err := updateSwaggerDataFromComments(&fieldValue, fieldProtoComments); err != nil {
panic(err)
}
schema.Properties[f.GetName()] = fieldValue
}
d[fullyQualifiedNameToSwaggerName(msg.FQMN(), reg)] = schema
}
}
// schemaOfField returns a swagger Schema Object for a protobuf field.
func schemaOfField(f *descriptor.Field, reg *descriptor.Registry) swaggerSchemaObject {
const (
singular = 0
array = 1
object = 2
)
var (
core schemaCore
aggregate int
)
fd := f.FieldDescriptorProto
if m, err := reg.LookupMsg("", f.GetTypeName()); err == nil {
if opt := m.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
fd = m.GetField()[1]
aggregate = object
}
}
if fd.GetLabel() == pbdescriptor.FieldDescriptorProto_LABEL_REPEATED {
aggregate = array
}
switch ft := fd.GetType(); ft {
case pbdescriptor.FieldDescriptorProto_TYPE_ENUM, pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE, pbdescriptor.FieldDescriptorProto_TYPE_GROUP:
if fd.GetTypeName() == ".google.protobuf.Timestamp" && pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE == ft {
core = schemaCore{
Type: "string",
Format: "date-time",
}
} else {
core = schemaCore{
Ref: "#/definitions/" + fullyQualifiedNameToSwaggerName(fd.GetTypeName(), reg),
}
}
default:
ftype, format, ok := primitiveSchema(ft)
if ok {
core = schemaCore{Type: ftype, Format: format}
} else {
core = schemaCore{Type: ft.String(), Format: "UNKNOWN"}
}
}
switch aggregate {
case array:
return swaggerSchemaObject{
schemaCore: schemaCore{
Type: "array",
},
Items: (*swaggerItemsObject)(&core),
}
case object:
return swaggerSchemaObject{
schemaCore: schemaCore{
Type: "object",
},
AdditionalProperties: &swaggerSchemaObject{schemaCore: core},
}
default:
return swaggerSchemaObject{schemaCore: core}
}
}
// primitiveSchema returns a pair of "Type" and "Format" in JSON Schema for
// the given primitive field type.
// The last return parameter is true iff the field type is actually primitive.
func primitiveSchema(t pbdescriptor.FieldDescriptorProto_Type) (ftype, format string, ok bool) {
switch t {
case pbdescriptor.FieldDescriptorProto_TYPE_DOUBLE:
return "number", "double", true
case pbdescriptor.FieldDescriptorProto_TYPE_FLOAT:
return "number", "float", true
case pbdescriptor.FieldDescriptorProto_TYPE_INT64:
return "string", "int64", true
case pbdescriptor.FieldDescriptorProto_TYPE_UINT64:
// 64bit integer types are marshaled as string in the default JSONPb marshaler.
// TODO(yugui) Add an option to declare 64bit integers as int64.
//
// NOTE: uint64 is not a predefined format of integer type in Swagger spec.
// So we cannot expect that uint64 is commonly supported by swagger processor.
return "string", "uint64", true
case pbdescriptor.FieldDescriptorProto_TYPE_INT32:
return "integer", "int32", true
case pbdescriptor.FieldDescriptorProto_TYPE_FIXED64:
// Ditto.
return "string", "uint64", true
case pbdescriptor.FieldDescriptorProto_TYPE_FIXED32:
// Ditto.
return "integer", "int64", true
case pbdescriptor.FieldDescriptorProto_TYPE_BOOL:
return "boolean", "boolean", true
case pbdescriptor.FieldDescriptorProto_TYPE_STRING:
return "string", "string", true
case pbdescriptor.FieldDescriptorProto_TYPE_BYTES:
return "string", "byte", true
case pbdescriptor.FieldDescriptorProto_TYPE_UINT32:
// Ditto.
return "integer", "int64", true
case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED32:
return "integer", "int32", true
case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED64:
return "string", "int64", true
case pbdescriptor.FieldDescriptorProto_TYPE_SINT32:
return "integer", "int32", true
case pbdescriptor.FieldDescriptorProto_TYPE_SINT64:
return "string", "int64", true
default:
return "", "", false
}
}
// renderEnumerationsAsDefinition inserts enums into the definitions object.
func renderEnumerationsAsDefinition(enums enumMap, d swaggerDefinitionsObject, reg *descriptor.Registry) {
valueProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.EnumDescriptorProto)(nil)), "Value")
for _, enum := range enums {
enumComments := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index))
var enumNames []string
// it may be necessary to sort the result of the GetValue function.
var defaultValue string
var valueDescriptions []string
for valueIdx, value := range enum.GetValue() {
enumNames = append(enumNames, value.GetName())
if defaultValue == "" && value.GetNumber() == 0 {
defaultValue = value.GetName()
}
valueDescription := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index), valueProtoPath, int32(valueIdx))
if valueDescription != "" {
valueDescriptions = append(valueDescriptions, value.GetName()+": "+valueDescription)
}
}
if len(valueDescriptions) > 0 {
enumComments += "\n\n - " + strings.Join(valueDescriptions, "\n - ")
}
enumSchemaObject := swaggerSchemaObject{
schemaCore: schemaCore{
Type: "string",
},
Enum: enumNames,
Default: defaultValue,
}
if err := updateSwaggerDataFromComments(&enumSchemaObject, enumComments); err != nil {
panic(err)
}
d[fullyQualifiedNameToSwaggerName(enum.FQEN(), reg)] = enumSchemaObject
}
}
// Take in a FQMN or FQEN and return a swagger safe version of the FQMN
func fullyQualifiedNameToSwaggerName(fqn string, reg *descriptor.Registry) string {
return resolveFullyQualifiedNameToSwaggerName(fqn, append(reg.GetAllFQMNs(), reg.GetAllFQENs()...))
}
// Take the names of every proto and "uniq-ify" them. The idea is to produce a
// set of names that meet a couple of conditions. They must be stable, they
// must be unique, and they must be shorter than the FQN.
//
// This likely could be made better. This will always generate the same names
// but may not always produce optimal names. This is a reasonably close
// approximation of what they should look like in most cases.
func resolveFullyQualifiedNameToSwaggerName(fqn string, messages []string) string {
packagesByDepth := make(map[int][][]string)
uniqueNames := make(map[string]string)
hierarchy := func(pkg string) []string {
return strings.Split(pkg, ".")
}
for _, p := range messages {
h := hierarchy(p)
for depth := range h {
if _, ok := packagesByDepth[depth]; !ok {
packagesByDepth[depth] = make([][]string, 0)
}
packagesByDepth[depth] = append(packagesByDepth[depth], h[len(h)-depth:])
}
}
count := func(list [][]string, item []string) int {
i := 0
for _, element := range list {
if reflect.DeepEqual(element, item) {
i++
}
}
return i
}
for _, p := range messages {
h := hierarchy(p)
for depth := 0; depth < len(h); depth++ {
if count(packagesByDepth[depth], h[len(h)-depth:]) == 1 {
uniqueNames[p] = strings.Join(h[len(h)-depth-1:], "")
break
}
if depth == len(h)-1 {
uniqueNames[p] = strings.Join(h, "")
}
}
}
return uniqueNames[fqn]
}
// Swagger expects paths of the form /path/{string_value} but grpc-gateway paths are expected to be of the form /path/{string_value=strprefix/*}. This should reformat it correctly.
func templateToSwaggerPath(path string) string {
// It seems like the right thing to do here is to just use
// strings.Split(path, "/") but that breaks badly when you hit a url like
// /{my_field=prefix/*}/ and end up with 2 sections representing my_field.
// Instead do the right thing and write a small pushdown (counter) automata
// for it.
var parts []string
depth := 0
buffer := ""
for _, char := range path {
switch char {
case '{':
// Push on the stack
depth++
buffer += string(char)
break
case '}':
if depth == 0 {
panic("Encountered } without matching { before it.")
}
// Pop from the stack
depth--
buffer += "}"
case '/':
if depth == 0 {
parts = append(parts, buffer)
buffer = ""
// Since the stack was empty when we hit the '/' we are done with this
// section.
continue
}
default:
buffer += string(char)
break
}
}
// Now append the last element to parts
parts = append(parts, buffer)
// Parts is now an array of segments of the path. Interestingly, since the
// syntax for this subsection CAN be handled by a regexp since it has no
// memory.
re := regexp.MustCompile("{([a-zA-Z][a-zA-Z0-9_.]*).*}")
for index, part := range parts {
parts[index] = re.ReplaceAllString(part, "{$1}")
}
return strings.Join(parts, "/")
}
func renderServices(services []*descriptor.Service, paths swaggerPathsObject, reg *descriptor.Registry) error {
// Correctness of svcIdx and methIdx depends on 'services' containing the services in the same order as the 'file.Service' array.
for svcIdx, svc := range services {
for methIdx, meth := range svc.Methods {
for _, b := range meth.Bindings {
// Iterate over all the swagger parameters
parameters := swaggerParametersObject{}
for _, parameter := range b.PathParams {
var paramType, paramFormat string
switch pt := parameter.Target.GetType(); pt {
case pbdescriptor.FieldDescriptorProto_TYPE_GROUP, pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE:
return fmt.Errorf("only primitive types are allowed in path parameters")
case pbdescriptor.FieldDescriptorProto_TYPE_ENUM:
paramType = fullyQualifiedNameToSwaggerName(parameter.Target.GetTypeName(), reg)
paramFormat = ""
default:
var ok bool
paramType, paramFormat, ok = primitiveSchema(pt)
if !ok {
return fmt.Errorf("unknown field type %v", pt)
}
}
parameters = append(parameters, swaggerParameterObject{
Name: parameter.String(),
In: "path",
Required: true,
// Parameters in gRPC-Gateway can only be strings?
Type: paramType,
Format: paramFormat,
})
}
// Now check if there is a body parameter
if b.Body != nil {
var schema swaggerSchemaObject
if len(b.Body.FieldPath) == 0 {
schema = swaggerSchemaObject{
schemaCore: schemaCore{
Ref: fmt.Sprintf("#/definitions/%s", fullyQualifiedNameToSwaggerName(meth.RequestType.FQMN(), reg)),
},
}
} else {
lastField := b.Body.FieldPath[len(b.Body.FieldPath) - 1]
schema = schemaOfField(lastField.Target, reg)
}
desc := ""
if meth.GetClientStreaming() {
desc = "(streaming inputs)"
}
parameters = append(parameters, swaggerParameterObject{
Name: "body",
Description: desc,
In: "body",
Required: true,
Schema: &schema,
})
}
pathItemObject, ok := paths[templateToSwaggerPath(b.PathTmpl.Template)]
if !ok {
pathItemObject = swaggerPathItemObject{}
}
methProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.ServiceDescriptorProto)(nil)), "Method")
desc := ""
if meth.GetServerStreaming() {
desc += "(streaming responses)"
}
operationObject := &swaggerOperationObject{
Tags: []string{svc.GetName()},
OperationID: fmt.Sprintf("%s", meth.GetName()),
Parameters: parameters,
Responses: swaggerResponsesObject{
"200": swaggerResponseObject{
Description: desc,
Schema: swaggerSchemaObject{
schemaCore: schemaCore{
Ref: fmt.Sprintf("#/definitions/%s", fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)),
},
},
},
},
}
methComments := protoComments(reg, svc.File, nil, "Service", int32(svcIdx), methProtoPath, int32(methIdx))
if err := updateSwaggerDataFromComments(operationObject, methComments); err != nil {
panic(err)
}
switch b.HTTPMethod {
case "DELETE":
pathItemObject.Delete = operationObject
break
case "GET":
pathItemObject.Get = operationObject
break
case "POST":
pathItemObject.Post = operationObject
break
case "PUT":
pathItemObject.Put = operationObject
break
}
paths[templateToSwaggerPath(b.PathTmpl.Template)] = pathItemObject
}
}
}
// Success! return nil on the error object
return nil
}
// This function is called with a param which contains the entire definition of a method.
func applyTemplate(p param) (string, error) {
// Create the basic template object. This is the object that everything is
// defined off of.
s := swaggerObject{
// Swagger 2.0 is the version of this document
Swagger: "2.0",
Schemes: []string{"http", "https"},
Consumes: []string{"application/json"},
Produces: []string{"application/json"},
Paths: make(swaggerPathsObject),
Definitions: make(swaggerDefinitionsObject),
Info: swaggerInfoObject{
Title: *p.File.Name,
Version: "version not set",
},
}
// Loops through all the services and their exposed GET/POST/PUT/DELETE definitions
// and create entries for all of them.
renderServices(p.Services, s.Paths, p.reg)
// Find all the service's messages and enumerations that are defined (recursively) and then
// write their request and response types out as definition objects.
m := messageMap{}
e := enumMap{}
findServicesMessagesAndEnumerations(p.Services, p.reg, m, e)
renderMessagesAsDefinition(m, s.Definitions, p.reg)
renderEnumerationsAsDefinition(e, s.Definitions, p.reg)
// File itself might have some comments and metadata.
packageProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "Package")
packageComments := protoComments(p.reg, p.File, nil, "Package", packageProtoPath)
if err := updateSwaggerDataFromComments(&s, packageComments); err != nil {
panic(err)
}
// We now have rendered the entire swagger object. Write the bytes out to a
// string so it can be written to disk.
var w bytes.Buffer
enc := json.NewEncoder(&w)
enc.Encode(&s)
return w.String(), nil
}
// updateSwaggerDataFromComments updates a Swagger object based on a comment
// from the proto file.
//
// First paragraph of a comment is used for summary. Remaining paragraphs of a
// comment are used for description. If 'Summary' field is not present on the
// passed swaggerObject, the summary and description are joined by \n\n.
//
// If there is a field named 'Info', its 'Summary' and 'Description' fields
// will be updated instead.
//
// If there is no 'Summary', the same behavior will be attempted on 'Title',
// but only if the last character is not a period.
func updateSwaggerDataFromComments(swaggerObject interface{}, comment string) error {
if len(comment) == 0 {
return nil
}
// Figure out what to apply changes to.
swaggerObjectValue := reflect.ValueOf(swaggerObject)
infoObjectValue := swaggerObjectValue.Elem().FieldByName("Info")
if !infoObjectValue.CanSet() {
// No such field? Apply summary and description directly to
// passed object.
infoObjectValue = swaggerObjectValue.Elem()
}
// Figure out which properties to update.
summaryValue := infoObjectValue.FieldByName("Summary")
descriptionValue := infoObjectValue.FieldByName("Description")
usingTitle := false
if !summaryValue.CanSet() {
summaryValue = infoObjectValue.FieldByName("Title")
usingTitle = true
}
// If there is a summary (or summary-equivalent), use the first
// paragraph as summary, and the rest as description.
if summaryValue.CanSet() {
paragraphs := strings.Split(comment, "\n\n")
summary := strings.TrimSpace(paragraphs[0])
description := strings.TrimSpace(strings.Join(paragraphs[1:], "\n\n"))
if !usingTitle || summary == "" || summary[len(summary)-1] != '.' {
if len(summary) > 0 {
summaryValue.Set(reflect.ValueOf(summary))
}
if len(description) > 0 {
if !descriptionValue.CanSet() {
return fmt.Errorf("Encountered object type with a summary, but no description")
}
descriptionValue.Set(reflect.ValueOf(description))
}
return nil
}
}
// There was no summary field on the swaggerObject. Try to apply the
// whole comment into description.
if descriptionValue.CanSet() {
descriptionValue.Set(reflect.ValueOf(comment))
return nil
}
return fmt.Errorf("No description nor summary property.")
}
func protoComments(reg *descriptor.Registry, file *descriptor.File, outers []string, typeName string, typeIndex int32, fieldPaths ...int32) string {
if file.SourceCodeInfo == nil {
// Curious! A file without any source code info.
// This could be a test that's providing incomplete
// descriptor.File information.
//
// We could simply return no comments, but panic
// could make debugging easier.
panic("descriptor.File should not contain nil SourceCodeInfo")
}
outerPaths := make([]int32, len(outers))
for i := range outers {
location := ""
if file.Package != nil {
location = file.GetPackage()
}
msg, err := reg.LookupMsg(location, strings.Join(outers[:i+1], "."))
if err != nil {
panic(err)
}
outerPaths[i] = int32(msg.Index)
}
for _, loc := range file.SourceCodeInfo.Location {
if !isProtoPathMatches(loc.Path, outerPaths, typeName, typeIndex, fieldPaths) {
continue
}
comments := ""
if loc.LeadingComments != nil {
comments = strings.TrimRight(*loc.LeadingComments, "\n")
comments = strings.TrimSpace(comments)
// TODO(ivucica): this is a hack to fix "// " being interpreted as "//".
// perhaps we should:
// - split by \n
// - determine if every (but first and last) line begins with " "
// - trim every line only if that is the case
// - join by \n
comments = strings.Replace(comments, "\n ", "\n", -1)
}
return comments
}
return ""
}
var messageProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "MessageType")
var nestedProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil)), "NestedType")
var packageProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "Package")
func isProtoPathMatches(paths []int32, outerPaths []int32, typeName string, typeIndex int32, fieldPaths []int32) bool {
if typeName == "Package" && typeIndex == packageProtoPath {
// path for package comments is just [2], and all the other processing
// is too complex for it.
if len(paths) == 0 || typeIndex != paths[0] {
return false
}
return true
}
if len(paths) != len(outerPaths)*2+2+len(fieldPaths) {
return false
}
typeNameDescriptor := reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil))
if len(outerPaths) > 0 {
if paths[0] != messageProtoPath || paths[1] != outerPaths[0] {
return false
}
paths = paths[2:]
outerPaths = outerPaths[1:]
for i, v := range outerPaths {
if paths[i*2] != nestedProtoPath || paths[i*2+1] != v {
return false
}
}
paths = paths[len(outerPaths)*2:]
if typeName == "MessageType" {
typeName = "NestedType"
}
typeNameDescriptor = reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil))
}
if paths[0] != protoPathIndex(typeNameDescriptor, typeName) || paths[1] != typeIndex {
return false
}
paths = paths[2:]
for i, v := range fieldPaths {
if paths[i] != v {
return false
}
}
return true
}
// protoPathIndex returns a path component for google.protobuf.descriptor.SourceCode_Location.
//
// Specifically, it returns an id as generated from descriptor proto which
// can be used to determine what type the id following it in the path is.
// For example, if we are trying to locate comments related to a field named
// `Address` in a message named `Person`, the path will be:
//
// [4, a, 2, b]
//
// While `a` gets determined by the order in which the messages appear in
// the proto file, and `b` is the field index specified in the proto
// file itself, the path actually needs to specify that `a` refers to a
// message and not, say, a service; and that `b` refers to a field and not
// an option.
//
// protoPathIndex figures out the values 4 and 2 in the above example. Because
// messages are top level objects, the value of 4 comes from field id for
// `MessageType` inside `google.protobuf.descriptor.FileDescriptor` message.
// This field has a message type `google.protobuf.descriptor.DescriptorProto`.
// And inside message `DescriptorProto`, there is a field named `Field` with id
// 2.
//
// Some code generators seem to be hardcoding these values; this method instead
// interprets them from `descriptor.proto`-derived Go source as necessary.
func protoPathIndex(descriptorType reflect.Type, what string) int32 {
field, ok := descriptorType.Elem().FieldByName(what)
if !ok {
panic(fmt.Errorf("Could not find protobuf descriptor type id for %s.", what))
}
pbtag := field.Tag.Get("protobuf")
if pbtag == "" {
panic(fmt.Errorf("No Go tag 'protobuf' on protobuf descriptor for %s.", what))
}
path, err := strconv.Atoi(strings.Split(pbtag, ",")[1])
if err != nil {
panic(fmt.Errorf("Protobuf descriptor id for %s cannot be converted to a number: %s", what, err.Error()))
}
return int32(path)
}

View file

@ -0,0 +1,497 @@
package genswagger
import (
"encoding/json"
"reflect"
"testing"
"github.com/golang/protobuf/proto"
protodescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
)
func crossLinkFixture(f *descriptor.File) *descriptor.File {
for _, m := range f.Messages {
m.File = f
}
for _, svc := range f.Services {
svc.File = f
for _, m := range svc.Methods {
m.Service = svc
for _, b := range m.Bindings {
b.Method = m
for _, param := range b.PathParams {
param.Method = m
}
}
}
}
return f
}
func TestApplyTemplateSimple(t *testing.T) {
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Example"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth},
}
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
Name: proto.String("example.proto"),
Package: proto.String("example"),
Dependency: []string{"a.example/b/c.proto", "a.example/d/e.proto"},
MessageType: []*protodescriptor.DescriptorProto{msgdesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "GET",
Body: &descriptor.Body{FieldPath: nil},
PathTmpl: httprule.Template{
Version: 1,
OpCodes: []int{0, 0},
Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
},
},
},
},
},
},
},
}
result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: descriptor.NewRegistry()})
if err != nil {
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
return
}
got := new(swaggerObject)
err = json.Unmarshal([]byte(result), got)
if err != nil {
t.Errorf("json.Unmarshal(%s) failed with %v; want success", result, err)
return
}
if want, is, name := "2.0", got.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
}
if want, is, name := "", got.BasePath, "BasePath"; !reflect.DeepEqual(is, want) {
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
}
if want, is, name := []string{"http", "https"}, got.Schemes, "Schemes"; !reflect.DeepEqual(is, want) {
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
}
if want, is, name := []string{"application/json"}, got.Consumes, "Consumes"; !reflect.DeepEqual(is, want) {
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
}
if want, is, name := []string{"application/json"}, got.Produces, "Produces"; !reflect.DeepEqual(is, want) {
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
}
// If there was a failure, print out the input and the json result for debugging.
if t.Failed() {
t.Errorf("had: %s", file)
t.Errorf("got: %s", result)
}
}
func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) {
t.Skip()
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("nested"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
TypeName: proto.String("NestedMessage"),
Number: proto.Int32(1),
},
},
}
nesteddesc := &protodescriptor.DescriptorProto{
Name: proto.String("NestedMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("int32"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
Number: proto.Int32(1),
},
{
Name: proto.String("bool"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
Number: proto.Int32(2),
},
},
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Echo"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
ClientStreaming: proto.Bool(false),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth},
}
meth.ServerStreaming = proto.Bool(false)
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
nested := &descriptor.Message{
DescriptorProto: nesteddesc,
}
nestedField := &descriptor.Field{
Message: msg,
FieldDescriptorProto: msg.GetField()[0],
}
intField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[0],
}
boolField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[1],
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
Name: proto.String("example.proto"),
Package: proto.String("example"),
MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg, nested},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "POST",
PathTmpl: httprule.Template{
Version: 1,
OpCodes: []int{0, 0},
Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be
},
PathParams: []descriptor.Parameter{
{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "int32",
Target: intField,
},
}),
Target: intField,
},
},
Body: &descriptor.Body{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "bool",
Target: boolField,
},
}),
},
},
},
},
},
},
},
}
result, err := applyTemplate(param{File: crossLinkFixture(&file)})
if err != nil {
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
return
}
var obj swaggerObject
err = json.Unmarshal([]byte(result), &obj)
if err != nil {
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
return
}
if want, got := "2.0", obj.Swagger; !reflect.DeepEqual(got, want) {
t.Errorf("applyTemplate(%#v).Swagger = %s want to be %s", file, got, want)
}
if want, got := "", obj.BasePath; !reflect.DeepEqual(got, want) {
t.Errorf("applyTemplate(%#v).BasePath = %s want to be %s", file, got, want)
}
if want, got := []string{"http", "https"}, obj.Schemes; !reflect.DeepEqual(got, want) {
t.Errorf("applyTemplate(%#v).Schemes = %s want to be %s", file, got, want)
}
if want, got := []string{"application/json"}, obj.Consumes; !reflect.DeepEqual(got, want) {
t.Errorf("applyTemplate(%#v).Consumes = %s want to be %s", file, got, want)
}
if want, got := []string{"application/json"}, obj.Produces; !reflect.DeepEqual(got, want) {
t.Errorf("applyTemplate(%#v).Produces = %s want to be %s", file, got, want)
}
if want, got, name := "Generated for ExampleService.Echo - ", obj.Paths["/v1/echo"].Post.Summary, "Paths[/v1/echo].Post.Summary"; !reflect.DeepEqual(got, want) {
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
}
// If there was a failure, print out the input and the json result for debugging.
if t.Failed() {
t.Errorf("had: %s", file)
t.Errorf("got: %s", result)
}
}
func TestApplyTemplateRequestWithClientStreaming(t *testing.T) {
t.Skip()
msgdesc := &protodescriptor.DescriptorProto{
Name: proto.String("ExampleMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("nested"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
TypeName: proto.String("NestedMessage"),
Number: proto.Int32(1),
},
},
}
nesteddesc := &protodescriptor.DescriptorProto{
Name: proto.String("NestedMessage"),
Field: []*protodescriptor.FieldDescriptorProto{
{
Name: proto.String("int32"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
Number: proto.Int32(1),
},
{
Name: proto.String("bool"),
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
Number: proto.Int32(2),
},
},
}
meth := &protodescriptor.MethodDescriptorProto{
Name: proto.String("Echo"),
InputType: proto.String("ExampleMessage"),
OutputType: proto.String("ExampleMessage"),
ClientStreaming: proto.Bool(true),
ServerStreaming: proto.Bool(true),
}
svc := &protodescriptor.ServiceDescriptorProto{
Name: proto.String("ExampleService"),
Method: []*protodescriptor.MethodDescriptorProto{meth},
}
msg := &descriptor.Message{
DescriptorProto: msgdesc,
}
nested := &descriptor.Message{
DescriptorProto: nesteddesc,
}
nestedField := &descriptor.Field{
Message: msg,
FieldDescriptorProto: msg.GetField()[0],
}
intField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[0],
}
boolField := &descriptor.Field{
Message: nested,
FieldDescriptorProto: nested.GetField()[1],
}
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
Name: proto.String("example.proto"),
Package: proto.String("example"),
MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
Service: []*protodescriptor.ServiceDescriptorProto{svc},
},
GoPkg: descriptor.GoPackage{
Path: "example.com/path/to/example/example.pb",
Name: "example_pb",
},
Messages: []*descriptor.Message{msg, nested},
Services: []*descriptor.Service{
{
ServiceDescriptorProto: svc,
Methods: []*descriptor.Method{
{
MethodDescriptorProto: meth,
RequestType: msg,
ResponseType: msg,
Bindings: []*descriptor.Binding{
{
HTTPMethod: "POST",
PathTmpl: httprule.Template{
Version: 1,
OpCodes: []int{0, 0},
Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be
},
PathParams: []descriptor.Parameter{
{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "int32",
Target: intField,
},
}),
Target: intField,
},
},
Body: &descriptor.Body{
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
{
Name: "nested",
Target: nestedField,
},
{
Name: "bool",
Target: boolField,
},
}),
},
},
},
},
},
},
},
}
_, err := applyTemplate(param{File: crossLinkFixture(&file)})
if err == nil {
t.Errorf("applyTemplate(%#v) should have failed cause swagger doesn't support streaming", file)
return
}
}
func TestTemplateToSwaggerPath(t *testing.T) {
var tests = []struct {
input string
expected string
}{
{"/test", "/test"},
{"/{test}", "/{test}"},
{"/{test=prefix/*}", "/{test}"},
{"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
{"/{test1}/{test2}", "/{test1}/{test2}"},
{"/{test1}/{test2}/", "/{test1}/{test2}/"},
}
for _, data := range tests {
actual := templateToSwaggerPath(data.input)
if data.expected != actual {
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
}
}
}
func TestResolveFullyQualifiedNameToSwaggerName(t *testing.T) {
var tests = []struct {
input string
output string
listOfFQMNs []string
}{
{
".a.b.C",
"C",
[]string{
".a.b.C",
},
},
{
".a.b.C",
"abC",
[]string{
".a.C",
".a.b.C",
},
},
{
".a.b.C",
"abC",
[]string{
".C",
".a.C",
".a.b.C",
},
},
}
for _, data := range tests {
output := resolveFullyQualifiedNameToSwaggerName(data.input, data.listOfFQMNs)
if output != data.output {
t.Errorf("Expected fullyQualifiedNameToSwaggerName(%v) to be %s but got %s",
data.input, data.output, output)
}
}
}
func TestFQMNtoSwaggerName(t *testing.T) {
var tests = []struct {
input string
expected string
}{
{"/test", "/test"},
{"/{test}", "/{test}"},
{"/{test=prefix/*}", "/{test}"},
{"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
{"/{test1}/{test2}", "/{test1}/{test2}"},
{"/{test1}/{test2}/", "/{test1}/{test2}/"},
}
for _, data := range tests {
actual := templateToSwaggerPath(data.input)
if data.expected != actual {
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
}
}
}

View file

@ -0,0 +1,151 @@
package genswagger
import (
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
)
type param struct {
*descriptor.File
reg *descriptor.Registry
}
type binding struct {
*descriptor.Binding
}
// http://swagger.io/specification/#infoObject
type swaggerInfoObject struct {
Title string `json:"title"`
Description string `json:"description,omitempty"`
TermsOfService string `json:"termsOfService,omitempty"`
Version string `json:"version"`
Contact *swaggerContactObject `json:"contact,omitempty"`
License *swaggerLicenseObject `json:"license,omitempty"`
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
}
// http://swagger.io/specification/#contactObject
type swaggerContactObject struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
Email string `json:"email,omitempty"`
}
// http://swagger.io/specification/#licenseObject
type swaggerLicenseObject struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
}
// http://swagger.io/specification/#externalDocumentationObject
type swaggerExternalDocumentationObject struct {
Description string `json:"description,omitempty"`
URL string `json:"url,omitempty"`
}
// http://swagger.io/specification/#swaggerObject
type swaggerObject struct {
Swagger string `json:"swagger"`
Info swaggerInfoObject `json:"info"`
Host string `json:"host,omitempty"`
BasePath string `json:"basePath,omitempty"`
Schemes []string `json:"schemes"`
Consumes []string `json:"consumes"`
Produces []string `json:"produces"`
Paths swaggerPathsObject `json:"paths"`
Definitions swaggerDefinitionsObject `json:"definitions"`
}
// http://swagger.io/specification/#pathsObject
type swaggerPathsObject map[string]swaggerPathItemObject
// http://swagger.io/specification/#pathItemObject
type swaggerPathItemObject struct {
Get *swaggerOperationObject `json:"get,omitempty"`
Delete *swaggerOperationObject `json:"delete,omitempty"`
Post *swaggerOperationObject `json:"post,omitempty"`
Put *swaggerOperationObject `json:"put,omitempty"`
}
// http://swagger.io/specification/#operationObject
type swaggerOperationObject struct {
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
OperationID string `json:"operationId"`
Responses swaggerResponsesObject `json:"responses"`
Parameters swaggerParametersObject `json:"parameters,omitempty"`
Tags []string `json:"tags,omitempty"`
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
}
type swaggerParametersObject []swaggerParameterObject
// http://swagger.io/specification/#parameterObject
type swaggerParameterObject struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
In string `json:"in,omitempty"`
Required bool `json:"required"`
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
Items *swaggerItemsObject `json:"items,omitempty"`
// Or you can explicitly refer to another type. If this is defined all
// other fields should be empty
Schema *swaggerSchemaObject `json:"schema,omitempty"`
}
// core part of schema, which is common to itemsObject and schemaObject.
// http://swagger.io/specification/#itemsObject
type schemaCore struct {
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
Ref string `json:"$ref,omitempty"`
}
type swaggerItemsObject schemaCore
// http://swagger.io/specification/#responsesObject
type swaggerResponsesObject map[string]swaggerResponseObject
// http://swagger.io/specification/#responseObject
type swaggerResponseObject struct {
Description string `json:"description"`
Schema swaggerSchemaObject `json:"schema"`
}
// http://swagger.io/specification/#schemaObject
type swaggerSchemaObject struct {
schemaCore
// Properties can be recursively defined
Properties map[string]swaggerSchemaObject `json:"properties,omitempty"`
AdditionalProperties *swaggerSchemaObject `json:"additionalProperties,omitempty"`
Items *swaggerItemsObject `json:"items,omitempty"`
// If the item is an enumeration include a list of all the *NAMES* of the
// enum values. I'm not sure how well this will work but assuming all enums
// start from 0 index it will be great. I don't think that is a good assumption.
Enum []string `json:"enum,omitempty"`
Default string `json:"default,omitempty"`
Description string `json:"description,omitempty"`
Title string `json:"title,omitempty"`
}
// http://swagger.io/specification/#referenceObject
type swaggerReferenceObject struct {
Ref string `json:"$ref"`
}
// http://swagger.io/specification/#definitionsObject
type swaggerDefinitionsObject map[string]swaggerSchemaObject
// Internal type mapping from FQMN to descriptor.Message. Used as a set by the
// findServiceMessages function.
type messageMap map[string]*descriptor.Message
// Internal type mapping from FQEN to descriptor.Enum. Used as a set by the
// findServiceMessages function.
type enumMap map[string]*descriptor.Enum

View file

@ -0,0 +1,115 @@
package main
import (
"flag"
"io"
"io/ioutil"
"os"
"strings"
"github.com/golang/glog"
"github.com/golang/protobuf/proto"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger"
)
var (
importPrefix = flag.String("import_prefix", "", "prefix to be added to go package paths for imported proto files")
file = flag.String("file", "stdin", "where to load data from")
)
func parseReq(r io.Reader) (*plugin.CodeGeneratorRequest, error) {
glog.V(1).Info("Parsing code generator request")
input, err := ioutil.ReadAll(r)
if err != nil {
glog.Errorf("Failed to read code generator request: %v", err)
return nil, err
}
req := new(plugin.CodeGeneratorRequest)
if err = proto.Unmarshal(input, req); err != nil {
glog.Errorf("Failed to unmarshal code generator request: %v", err)
return nil, err
}
glog.V(1).Info("Parsed code generator request")
return req, nil
}
func main() {
flag.Parse()
defer glog.Flush()
reg := descriptor.NewRegistry()
glog.V(1).Info("Processing code generator request")
f := os.Stdin
if *file != "stdin" {
f, _ = os.Open("input.txt")
}
req, err := parseReq(f)
if err != nil {
glog.Fatal(err)
}
if req.Parameter != nil {
for _, p := range strings.Split(req.GetParameter(), ",") {
spec := strings.SplitN(p, "=", 2)
if len(spec) == 1 {
if err := flag.CommandLine.Set(spec[0], ""); err != nil {
glog.Fatalf("Cannot set flag %s", p)
}
continue
}
name, value := spec[0], spec[1]
if strings.HasPrefix(name, "M") {
reg.AddPkgMap(name[1:], value)
continue
}
if err := flag.CommandLine.Set(name, value); err != nil {
glog.Fatalf("Cannot set flag %s", p)
}
}
}
g := genswagger.New(reg)
reg.SetPrefix(*importPrefix)
if err := reg.Load(req); err != nil {
emitError(err)
return
}
var targets []*descriptor.File
for _, target := range req.FileToGenerate {
f, err := reg.LookupFile(target)
if err != nil {
glog.Fatal(err)
}
targets = append(targets, f)
}
out, err := g.Generate(targets)
glog.V(1).Info("Processed code generator request")
if err != nil {
emitError(err)
return
}
emitFiles(out)
}
func emitFiles(out []*plugin.CodeGeneratorResponse_File) {
emitResp(&plugin.CodeGeneratorResponse{File: out})
}
func emitError(err error) {
emitResp(&plugin.CodeGeneratorResponse{Error: proto.String(err.Error())})
}
func emitResp(resp *plugin.CodeGeneratorResponse) {
buf, err := proto.Marshal(resp)
if err != nil {
glog.Fatal(err)
}
if _, err := os.Stdout.Write(buf); err != nil {
glog.Fatal(err)
}
}

View file

@ -0,0 +1,143 @@
package runtime
import (
"fmt"
"net"
"net/http"
"strconv"
"strings"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
)
// MetadataHeaderPrefix is prepended to HTTP headers in order to convert them to
// gRPC metadata for incoming requests processed by grpc-gateway
const MetadataHeaderPrefix = "Grpc-Metadata-"
// MetadataTrailerPrefix is prepended to gRPC metadata as it is converted to
// HTTP headers in a response handled by grpc-gateway
const MetadataTrailerPrefix = "Grpc-Trailer-"
const metadataGrpcTimeout = "Grpc-Timeout"
const xForwardedFor = "X-Forwarded-For"
const xForwardedHost = "X-Forwarded-Host"
var (
// DefaultContextTimeout is used for gRPC call context.WithTimeout whenever a Grpc-Timeout inbound
// header isn't present. If the value is 0 the sent `context` will not have a timeout.
DefaultContextTimeout = 0 * time.Second
)
/*
AnnotateContext adds context information such as metadata from the request.
At a minimum, the RemoteAddr is included in the fashion of "X-Forwarded-For",
except that the forwarded destination is not another HTTP service but rather
a gRPC service.
*/
func AnnotateContext(ctx context.Context, req *http.Request) (context.Context, error) {
var pairs []string
timeout := DefaultContextTimeout
if tm := req.Header.Get(metadataGrpcTimeout); tm != "" {
var err error
timeout, err = timeoutDecode(tm)
if err != nil {
return nil, grpc.Errorf(codes.InvalidArgument, "invalid grpc-timeout: %s", tm)
}
}
for key, vals := range req.Header {
for _, val := range vals {
if key == "Authorization" {
pairs = append(pairs, "authorization", val)
continue
}
if strings.HasPrefix(key, MetadataHeaderPrefix) {
pairs = append(pairs, key[len(MetadataHeaderPrefix):], val)
}
}
}
if host := req.Header.Get(xForwardedHost); host != "" {
pairs = append(pairs, strings.ToLower(xForwardedHost), host)
} else if req.Host != "" {
pairs = append(pairs, strings.ToLower(xForwardedHost), req.Host)
}
if addr := req.RemoteAddr; addr != "" {
if remoteIP, _, err := net.SplitHostPort(addr); err == nil {
if fwd := req.Header.Get(xForwardedFor); fwd == "" {
pairs = append(pairs, strings.ToLower(xForwardedFor), remoteIP)
} else {
pairs = append(pairs, strings.ToLower(xForwardedFor), fmt.Sprintf("%s, %s", fwd, remoteIP))
}
} else {
grpclog.Printf("invalid remote addr: %s", addr)
}
}
if timeout != 0 {
ctx, _ = context.WithTimeout(ctx, timeout)
}
if len(pairs) == 0 {
return ctx, nil
}
return metadata.NewContext(ctx, metadata.Pairs(pairs...)), nil
}
// ServerMetadata consists of metadata sent from gRPC server.
type ServerMetadata struct {
HeaderMD metadata.MD
TrailerMD metadata.MD
}
type serverMetadataKey struct{}
// NewServerMetadataContext creates a new context with ServerMetadata
func NewServerMetadataContext(ctx context.Context, md ServerMetadata) context.Context {
return context.WithValue(ctx, serverMetadataKey{}, md)
}
// ServerMetadataFromContext returns the ServerMetadata in ctx
func ServerMetadataFromContext(ctx context.Context) (md ServerMetadata, ok bool) {
md, ok = ctx.Value(serverMetadataKey{}).(ServerMetadata)
return
}
func timeoutDecode(s string) (time.Duration, error) {
size := len(s)
if size < 2 {
return 0, fmt.Errorf("timeout string is too short: %q", s)
}
d, ok := timeoutUnitToDuration(s[size-1])
if !ok {
return 0, fmt.Errorf("timeout unit is not recognized: %q", s)
}
t, err := strconv.ParseInt(s[:size-1], 10, 64)
if err != nil {
return 0, err
}
return d * time.Duration(t), nil
}
func timeoutUnitToDuration(u uint8) (d time.Duration, ok bool) {
switch u {
case 'H':
return time.Hour, true
case 'M':
return time.Minute, true
case 'S':
return time.Second, true
case 'm':
return time.Millisecond, true
case 'u':
return time.Microsecond, true
case 'n':
return time.Nanosecond, true
default:
}
return
}

View file

@ -0,0 +1,169 @@
package runtime_test
import (
"net/http"
"reflect"
"testing"
"time"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc/metadata"
)
const (
emptyForwardMetaCount = 1
)
func TestAnnotateContext_WorksWithEmpty(t *testing.T) {
ctx := context.Background()
request, err := http.NewRequest("GET", "http://www.example.com", nil)
if err != nil {
t.Fatalf("http.NewRequest(%q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
}
request.Header.Add("Some-Irrelevant-Header", "some value")
annotated, err := runtime.AnnotateContext(ctx, request)
if err != nil {
t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
return
}
md, ok := metadata.FromContext(annotated)
if !ok || len(md) != emptyForwardMetaCount {
t.Errorf("Expected %d metadata items in context; got %v", emptyForwardMetaCount, md)
}
}
func TestAnnotateContext_ForwardsGrpcMetadata(t *testing.T) {
ctx := context.Background()
request, err := http.NewRequest("GET", "http://www.example.com", nil)
if err != nil {
t.Fatalf("http.NewRequest(%q, %q, nil) failed with %v; want success", "GET", "http://www.example.com", err)
}
request.Header.Add("Some-Irrelevant-Header", "some value")
request.Header.Add("Grpc-Metadata-FooBar", "Value1")
request.Header.Add("Grpc-Metadata-Foo-BAZ", "Value2")
request.Header.Add("Grpc-Metadata-foo-bAz", "Value3")
request.Header.Add("Authorization", "Token 1234567890")
annotated, err := runtime.AnnotateContext(ctx, request)
if err != nil {
t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
return
}
md, ok := metadata.FromContext(annotated)
if got, want := len(md), emptyForwardMetaCount+3; !ok || got != want {
t.Errorf("Expected %d metadata items in context; got %d", got, want)
}
if got, want := md["foobar"], []string{"Value1"}; !reflect.DeepEqual(got, want) {
t.Errorf(`md["foobar"] = %q; want %q`, got, want)
}
if got, want := md["foo-baz"], []string{"Value2", "Value3"}; !reflect.DeepEqual(got, want) {
t.Errorf(`md["foo-baz"] = %q want %q`, got, want)
}
if got, want := md["authorization"], []string{"Token 1234567890"}; !reflect.DeepEqual(got, want) {
t.Errorf(`md["authorization"] = %q want %q`, got, want)
}
}
func TestAnnotateContext_XForwardedFor(t *testing.T) {
ctx := context.Background()
request, err := http.NewRequest("GET", "http://bar.foo.example.com", nil)
if err != nil {
t.Fatalf("http.NewRequest(%q, %q, nil) failed with %v; want success", "GET", "http://bar.foo.example.com", err)
}
request.Header.Add("X-Forwarded-For", "192.0.2.100") // client
request.RemoteAddr = "192.0.2.200:12345" // proxy
annotated, err := runtime.AnnotateContext(ctx, request)
if err != nil {
t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
return
}
md, ok := metadata.FromContext(annotated)
if !ok || len(md) != emptyForwardMetaCount+1 {
t.Errorf("Expected %d metadata items in context; got %v", emptyForwardMetaCount+1, md)
}
if got, want := md["x-forwarded-host"], []string{"bar.foo.example.com"}; !reflect.DeepEqual(got, want) {
t.Errorf(`md["host"] = %v; want %v`, got, want)
}
// Note: it must be in order client, proxy1, proxy2
if got, want := md["x-forwarded-for"], []string{"192.0.2.100, 192.0.2.200"}; !reflect.DeepEqual(got, want) {
t.Errorf(`md["x-forwarded-for"] = %v want %v`, got, want)
}
}
func TestAnnotateContext_SupportsTimeouts(t *testing.T) {
ctx := context.Background()
request, err := http.NewRequest("GET", "http://example.com", nil)
if err != nil {
t.Fatalf(`http.NewRequest("GET", "http://example.com", nil failed with %v; want success`, err)
}
annotated, err := runtime.AnnotateContext(ctx, request)
if err != nil {
t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
return
}
if _, ok := annotated.Deadline(); ok {
// no deadline by default
t.Errorf("annotated.Deadline() = _, true; want _, false")
}
const acceptableError = 50 * time.Millisecond
runtime.DefaultContextTimeout = 10 * time.Second
annotated, err = runtime.AnnotateContext(ctx, request)
if err != nil {
t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
return
}
deadline, ok := annotated.Deadline()
if !ok {
t.Errorf("annotated.Deadline() = _, false; want _, true")
}
if got, want := deadline.Sub(time.Now()), runtime.DefaultContextTimeout; got-want > acceptableError || got-want < -acceptableError {
t.Errorf("deadline.Sub(time.Now()) = %v; want %v; with error %v", got, want, acceptableError)
}
for _, spec := range []struct {
timeout string
want time.Duration
}{
{
timeout: "17H",
want: 17 * time.Hour,
},
{
timeout: "19M",
want: 19 * time.Minute,
},
{
timeout: "23S",
want: 23 * time.Second,
},
{
timeout: "1009m",
want: 1009 * time.Millisecond,
},
{
timeout: "1000003u",
want: 1000003 * time.Microsecond,
},
{
timeout: "100000007n",
want: 100000007 * time.Nanosecond,
},
} {
request.Header.Set("Grpc-Timeout", spec.timeout)
annotated, err = runtime.AnnotateContext(ctx, request)
if err != nil {
t.Errorf("runtime.AnnotateContext(ctx, %#v) failed with %v; want success", request, err)
return
}
deadline, ok := annotated.Deadline()
if !ok {
t.Errorf("annotated.Deadline() = _, false; want _, true; timeout = %q", spec.timeout)
}
if got, want := deadline.Sub(time.Now()), spec.want; got-want > acceptableError || got-want < -acceptableError {
t.Errorf("deadline.Sub(time.Now()) = %v; want %v; with error %v; timeout= %q", got, want, acceptableError, spec.timeout)
}
}
}

View file

@ -0,0 +1,58 @@
package runtime
import (
"strconv"
)
// String just returns the given string.
// It is just for compatibility to other types.
func String(val string) (string, error) {
return val, nil
}
// Bool converts the given string representation of a boolean value into bool.
func Bool(val string) (bool, error) {
return strconv.ParseBool(val)
}
// Float64 converts the given string representation into representation of a floating point number into float64.
func Float64(val string) (float64, error) {
return strconv.ParseFloat(val, 64)
}
// Float32 converts the given string representation of a floating point number into float32.
func Float32(val string) (float32, error) {
f, err := strconv.ParseFloat(val, 32)
if err != nil {
return 0, err
}
return float32(f), nil
}
// Int64 converts the given string representation of an integer into int64.
func Int64(val string) (int64, error) {
return strconv.ParseInt(val, 0, 64)
}
// Int32 converts the given string representation of an integer into int32.
func Int32(val string) (int32, error) {
i, err := strconv.ParseInt(val, 0, 32)
if err != nil {
return 0, err
}
return int32(i), nil
}
// Uint64 converts the given string representation of an integer into uint64.
func Uint64(val string) (uint64, error) {
return strconv.ParseUint(val, 0, 64)
}
// Uint32 converts the given string representation of an integer into uint32.
func Uint32(val string) (uint32, error) {
i, err := strconv.ParseUint(val, 0, 32)
if err != nil {
return 0, err
}
return uint32(i), nil
}

View file

@ -0,0 +1,5 @@
/*
Package runtime contains runtime helper functions used by
servers which protoc-gen-grpc-gateway generates.
*/
package runtime

View file

@ -0,0 +1,121 @@
package runtime
import (
"io"
"net/http"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
)
// HTTPStatusFromCode converts a gRPC error code into the corresponding HTTP response status.
func HTTPStatusFromCode(code codes.Code) int {
switch code {
case codes.OK:
return http.StatusOK
case codes.Canceled:
return http.StatusRequestTimeout
case codes.Unknown:
return http.StatusInternalServerError
case codes.InvalidArgument:
return http.StatusBadRequest
case codes.DeadlineExceeded:
return http.StatusRequestTimeout
case codes.NotFound:
return http.StatusNotFound
case codes.AlreadyExists:
return http.StatusConflict
case codes.PermissionDenied:
return http.StatusForbidden
case codes.Unauthenticated:
return http.StatusUnauthorized
case codes.ResourceExhausted:
return http.StatusForbidden
case codes.FailedPrecondition:
return http.StatusPreconditionFailed
case codes.Aborted:
return http.StatusConflict
case codes.OutOfRange:
return http.StatusBadRequest
case codes.Unimplemented:
return http.StatusNotImplemented
case codes.Internal:
return http.StatusInternalServerError
case codes.Unavailable:
return http.StatusServiceUnavailable
case codes.DataLoss:
return http.StatusInternalServerError
}
grpclog.Printf("Unknown gRPC error code: %v", code)
return http.StatusInternalServerError
}
var (
// HTTPError replies to the request with the error.
// You can set a custom function to this variable to customize error format.
HTTPError = DefaultHTTPError
// OtherErrorHandler handles the following error used by the gateway: StatusMethodNotAllowed StatusNotFound and StatusBadRequest
OtherErrorHandler = DefaultOtherErrorHandler
)
type errorBody struct {
Error string `protobuf:"bytes,1,name=error" json:"error"`
Code int `protobuf:"bytes,2,name=code" json:"code"`
}
//Make this also conform to proto.Message for builtin JSONPb Marshaler
func (e *errorBody) Reset() { *e = errorBody{} }
func (e *errorBody) String() string { return proto.CompactTextString(e) }
func (*errorBody) ProtoMessage() {}
// DefaultHTTPError is the default implementation of HTTPError.
// If "err" is an error from gRPC system, the function replies with the status code mapped by HTTPStatusFromCode.
// If otherwise, it replies with http.StatusInternalServerError.
//
// The response body returned by this function is a JSON object,
// which contains a member whose key is "error" and whose value is err.Error().
func DefaultHTTPError(ctx context.Context, marshaler Marshaler, w http.ResponseWriter, _ *http.Request, err error) {
const fallback = `{"error": "failed to marshal error message"}`
w.Header().Del("Trailer")
w.Header().Set("Content-Type", marshaler.ContentType())
body := &errorBody{
Error: grpc.ErrorDesc(err),
Code: int(grpc.Code(err)),
}
buf, merr := marshaler.Marshal(body)
if merr != nil {
grpclog.Printf("Failed to marshal error message %q: %v", body, merr)
w.WriteHeader(http.StatusInternalServerError)
if _, err := io.WriteString(w, fallback); err != nil {
grpclog.Printf("Failed to write response: %v", err)
}
return
}
md, ok := ServerMetadataFromContext(ctx)
if !ok {
grpclog.Printf("Failed to extract ServerMetadata from context")
}
handleForwardResponseServerMetadata(w, md)
handleForwardResponseTrailerHeader(w, md)
st := HTTPStatusFromCode(grpc.Code(err))
w.WriteHeader(st)
if _, err := w.Write(buf); err != nil {
grpclog.Printf("Failed to write response: %v", err)
}
handleForwardResponseTrailer(w, md)
}
// DefaultOtherErrorHandler is the default implementation of OtherErrorHandler.
// It simply writes a string representation of the given error into "w".
func DefaultOtherErrorHandler(w http.ResponseWriter, _ *http.Request, msg string, code int) {
http.Error(w, msg, code)
}

Some files were not shown because too many files have changed in this diff Show more