Compare commits

...

493 Commits

Author SHA1 Message Date
Stanley Cheung 7ef7bd7efe
Fix protoc plugin docker image build (#1486) 2025-07-08 13:54:48 -07:00
Vuhag123 dd96066a52
fixed protoc-plugin build failure (#1483)
* fixed protoc-plugin build failure

* fixed protoc-plugin build failure and docker compose build.

---------

Co-authored-by: Stanley Cheung <stanleycheung@google.com>
2025-07-08 11:04:29 -07:00
Eryu Xia 70f2b595ce
Roadmap update (#1484) 2025-06-10 13:07:35 -07:00
Eryu Xia 7ea072a94d
Add abort API support for promise calls (#1478)
For #1478

- Also removed CallOptions in favor of PromiseCallOptions (to align with internal codebase)
- Also updated chrome to 112.0.5615.165 for supporting abort with reason.
2025-04-20 14:55:04 -07:00
Andreas Sommer b5ff5d303d
Fix webpack command in tutorials (#1471) 2025-01-14 15:37:09 -08:00
Red Daly ecb890429f
When a release is published, upload a stable source archive. (#1464)
* When a release is published, upload a stable source archive.

Add a new github actions script to release a stable source archive.

Github doesn't guarantee stability of source archives for more than 6 months[1].
More stability is required by projects like Bazel Central Registry[2][3].

[1]: https://github.blog/open-source/git/update-on-the-future-stability-of-source-code-archives-and-hashes/
[2]: https://github.com/bazelbuild/bazel-central-registry/blob/main/docs/README.md#validations
[3]: https://blog.bazel.build/2023/02/15/github-archive-checksum.html

* Use actions/checkout without a commit hash.
2024-10-01 14:22:40 -07:00
Noel Kim (김민혁) 7bd92c665a
fix: format of typescript definition (#1463) 2024-09-20 17:25:42 -07:00
Red Daly 9856bfea5d
Bazel bzlmod support for grpc-web. (#1456)
* Bazel bzlmod support for grpc-web.

Updates the bazel-based build of grpc-web to use bzlmod.

The WORKSPACE file is retained for now, but it should probably be deleted since
it should be unused.

* Delete WORKSPACE files.

* bazel mod tidy

* MODULE.bazel fixups

* Update version to 1.6.0.rc1 in MODULE.bazel

* Update module and dockerfile in response to pr comments about build failure and protobuf version

* Fix lint issue with MODULE.bazel
2024-08-26 17:27:30 -07:00
Eryu Xia 8ba8fbeb5f
Update protobuf-JS to 3.21.4 (#1452) 2024-07-22 19:38:19 -07:00
Eryu Xia 20790fc259
Revert "Revert "Temporarily disable `protoc-plugin` in CI"" (#1451)
* Revert "Revert "Temporarily disable protoc-plugin in CI (#1447)" (#1448)"

This reverts commit e6909cd284.

* Update run_basic_tests.sh
2024-06-24 22:41:24 -07:00
Eryu Xia 132ed2cd94
Revert "Do not force third_party/protobuf to some old version. (#1446)" (#1450)
This reverts commit 0137f1fc61.
2024-06-24 22:38:57 -07:00
Eryu Xia e6909cd284
Revert "Temporarily disable protoc-plugin in CI (#1447)" (#1448)
This reverts commit 31e9b61ed8.
2024-06-24 22:15:48 -07:00
Benjamin Peterson 0137f1fc61
Do not force third_party/protobuf to some old version. (#1446) 2024-06-24 22:14:25 -07:00
Eryu Xia 31e9b61ed8
Temporarily disable protoc-plugin in CI (#1447) 2024-06-24 22:05:53 -07:00
Benjamin Peterson 2c39859be8
Upgrade protobuf to 27.1 and modernize codegen using new APIs (e.g. has_presence()) (#1445)
Fixes https://github.com/grpc/grpc-web/issues/1437.
2024-06-24 20:58:54 -07:00
Eryu Xia 8ab32b945c
Upgrade to Bazel 6.5.0 (#1441)
Updated Bazel to `6.5.0`
- `4.1.0` no longer works on my mac.
- `7.2.0` seems not yet supported by internal CI tooling :)

Also upgraded
- Protobuf -> `25.0` (`27.0` is generating errors :))
- gRPC Bazel -> `1.64.2`
2024-06-23 21:18:58 -07:00
subhraOffGit 76ef002ca6
[vote]Added Active maintainers into MAINTAINERS.md. (#1430)
[vote] Added sampajano into grpc/grpc-web active maintainers list. Removed  stanley-cheung from active maintainers list and moved them to emeritus. Removed org from emeritus.
Please comment as "Agree" if you agree to the change, if not please comment as "Disagree". Your comment will be counted as a vote.
The PR needs to stay open for 14 days, I.e. till 15th June 2024 as per governance guidelines
2024-06-23 18:05:59 -07:00
Eryu Xia 3da394753a
Update roadmap (#1438) 2024-06-23 17:25:06 -07:00
Eryu Xia ce8aa02c7a
Fix response return type for grpcwebclientbase_test (#1433) 2024-06-12 11:01:19 -07:00
Eryu Xia e91e540fc2
Update missing text (#1420) 2024-04-15 17:04:36 -07:00
Eryu Xia cfcc5e39d9
Improve plugin install instructions (#1419) 2024-04-15 16:57:50 -07:00
Michael Diamond a639b4cf26
Revert "Update document to mention Nginx server-streaming support (#1403)" (#1408)
This reverts commit 55b9218d2b.
2024-03-06 16:06:15 -08:00
Thomas Segismont 0350052fab
Update interop-test-descriptions.md (#1404)
The proto definition link leads to a 404

The actual location of the file seems to be https://github.com/grpc/grpc/blob/master/src/proto/grpc/testing/test.proto
2024-02-13 13:47:36 -08:00
Eryu Xia 55b9218d2b
Update document to mention Nginx server-streaming support (#1403) 2024-02-08 18:09:19 -08:00
Eryu Xia 4aab99f73e
Reduce confusion around streaming support in binary format (#1400) 2024-01-31 16:06:54 -08:00
Michael Diamond ea2ba42337
Update http2_protocol_options pattern (#1397) 2024-01-24 16:31:19 -08:00
Eryu Xia 83eec72cc3
Document Vite compatibility (#1389) 2023-11-28 17:05:25 -08:00
Eryu Xia 9d2e24462a
Post-release updates (1.5.0) (#1384) 2023-11-09 15:18:49 -08:00
Eryu Xia de3557acc3
Bump version to 1.5.0 (#1382) 2023-11-08 15:39:29 -08:00
Eryu Xia db386b99e6
Internal code sync (#1381) 2023-11-07 16:24:28 -08:00
Andrew Benton 1ab0bdc25b
feat(typescript): mark some `metadata` parameters as optional (#1369)
Resolves https://github.com/grpc/grpc-web/issues/1368
2023-10-16 14:35:11 -07:00
Eryu Xia 49d3b70868
Adding some docs on interceptors. (#1370) 2023-10-05 16:29:14 -07:00
Red Daly 3cd7e0d434
Update ES6 .d.ts imports with comment about corresponding proto import path. (#1330)
https://github.com/grpc/grpc-web/pull/1313 updated the code generator to print a
`proto import: "foo/bar.proto"` suffix for each import statement for the
generated gRPC-web client code. This commit updates ensures the .d.ts file
output by grpc-web contains the same type of comments.
2023-10-05 13:57:37 -07:00
Eryu Xia 9cb8524caa
Document on how to use Promises (#1357) 2023-08-30 17:57:40 -07:00
icoco 4d7dc44c2d
Update echo.proto, remark unsupported method (#1354)
Client side streaming and Bidi streaming are not supported at the moment.
2023-08-18 14:52:32 -07:00
Eryu Xia 7c52878457
Install python in `prereqs` image (#1350)
Maybe `buildpack-deps` has recently removed python installation by
default and hence is observing some errors.
2023-07-28 17:48:44 -07:00
Eryu Xia 9c48d290b2
Clarify on streaming wire format requirement. (#1346) 2023-06-26 17:21:35 -07:00
Eryu Xia 3bfa7641a8
Fix build for `interop-test` docker image (#1338)
The in-browser test is still not working, but it can at least build and start :)
2023-05-16 14:48:47 -07:00
Cyril Joy d28d3f6814
Punctuation fix in roadmap.md (#1337) 2023-05-16 13:18:16 -07:00
Eryu Xia c4f0905288
Allow mixed-case headers (#1334) 2023-05-14 03:12:19 -07:00
Eryu Xia f8fbbe32f9
Update Debian (and other deps) and remove Java In-process Proxy (#1335) 2023-05-14 02:57:11 -07:00
Layo Folaranmi c5c1e29756
Update README.md (#1336)
Simple typo error fix on line 162.
2023-05-03 13:00:51 -07:00
dependabot[bot] 063bb42d85
Bump jetty-server from 9.4.41.v20210516 to 10.0.14 in /src/connector (#1332)
Bumps [jetty-server](https://github.com/eclipse/jetty.project) from 9.4.41.v20210516 to 10.0.14.
- [Release notes](https://github.com/eclipse/jetty.project/releases)
- [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.41.v20210516...jetty-10.0.14)

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-server
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-18 16:30:10 -07:00
Red Daly e4084fd259
Update ES6 imports with comment about corresponding proto import path. (#1313)
I was experiencing a long import path in the generated code that doesn't
work. Outputting the proto file that corresponds to a JS import is helpful for
both diagnosing the issue and writing a wrapper for altering the import path to
my satisfaction.

Before:

```js
import * as github_com_gonzojive_rules_ts_proto_example_prefix_greeting_pb from '../../../../../github.com/gonzojive/rules_ts_proto/example/prefix/greeting_pb';
```

After:

```js
import * as github_com_gonzojive_rules_ts_proto_example_prefix_greeting_pb from '../../../../../github.com/gonzojive/rules_ts_proto/example/prefix/greeting_pb'; // proto import: "github.com/gonzojive/rules_ts_proto/example/prefix/greeting.proto"
```
2023-03-26 10:09:35 -07:00
Eryu Xia 7b6e8ed491
Roadmap update (Bi-di Streaming) (#1326) 2023-03-22 14:04:45 -07:00
Eryu Xia b1297e616a
Mention `Access-Control-Expose-Headers` in CORS guide. (#1320) 2023-02-15 17:18:43 -08:00
Eryu Xia 2d42fd608d
Improve Typescript example docs. (#1318) 2023-02-07 16:37:23 -08:00
Eryu Xia 60caece154
Fix generated Typescript file documentation. (#1314) 2023-01-19 15:15:17 -08:00
caption e49389873d
update io.grpc:grpc-core 1.30.0 to 1.33.0 (#1305) 2022-12-02 14:07:22 -08:00
Eryu Xia 9cacb7606a
Fix maven versions broken in previous 1.4.2 update :) (#1302) 2022-11-18 18:28:14 -08:00
Eryu Xia 6d1ee0d3df
1.4.2 Post-release updates (#1299) 2022-10-28 18:13:30 -07:00
Eryu Xia 0ec55aaad7
Bump version to 1.4.2 (#1298) 2022-10-28 14:17:52 -07:00
pro-wh 6577c66b53
GrpcWebClientBase: clean up !useUnaryResponse callback args (#1297) 2022-10-26 16:08:34 -07:00
pro-wh e11903b337
GrpcWebClientReadableStream: keep falsy data (#1230) 2022-10-25 00:49:54 -07:00
Eryu Xia b0ea9c7c45
1.4.1 Post-release updates (incl. release notes) (#1290) 2022-09-30 16:39:48 -07:00
Eryu Xia 4974a7b5a9
Expose getName() in MethodDescriptor and fix TS definitions. (#1289) 2022-09-30 16:28:51 -07:00
Eryu Xia ac17547d78
Bump version to 1.4.1 (#1287) 2022-09-27 22:04:46 -07:00
Eryu Xia 8a123090f8
Fix duplicate dot in enum name (#1286) 2022-09-26 17:00:57 -07:00
Eryu Xia d1d99eb80b
Internal code sync (#1280) 2022-09-26 14:12:01 -07:00
Eryu Xia 17e6a81709
Bump protoc plugin version to `1.4.0` (#1282) 2022-09-23 13:07:22 -07:00
Eryu Xia 1e8e8c926b
Bump version to 1.4.0 (#1281) 2022-09-22 16:45:44 -07:00
Chandraaditya 903601a426
fixes the status codes ordering (#1279)
in the grpc-web package the status codes are in the wrong order causing them to be interpreted differently, so when comparing status codes that we get from a service it is not the same, this commit fixes that
2022-09-19 15:58:02 -07:00
Eryu Xia b3d7dbdd5e
Fix Enum with module in generated TS interface. (#1278)
Verified with the example in https://github.com/grpc/grpc-web/issues/1271

// enum.proto
```proto
syntax = "proto3";

enum DocEnum {
    DOC_ENUM_PDF = 0;
    DOC_ENUM_HTML = 1;
}
```

// test.proto
```proto
syntax = "proto3";

import "enum.proto";

package Test;

message HelloRequest {
    DocEnum doc = 1;
}
```

generate:
```
protoc --proto_path=. --js_out=import_style=commonjs,binary:. --grpc-web_out=import_style=typescript,mode=grpcwebtext:. *.proto
```
2022-09-16 16:26:10 -07:00
Kiryl Valkovich 94a98d7ced
Add Tonic framework to README.md (#1273) 2022-09-06 13:52:44 -07:00
Eryu Xia a5bd765d1d
Fix code and documentation to pass `deadline` metadata as a String. (#1269)
- As Metadata is a <string, string> map per Closure and TS definition.
2022-08-30 16:08:35 -07:00
Eryu Xia ccef7bd239
Mention supported streaming modes. (#1265) 2022-08-19 11:58:38 -07:00
Eryu Xia 39200f6cb0
Point to the new `/protocolbuffers/protobuf-javascript` protoc and add note (#1263) 2022-08-10 12:17:12 -07:00
Eryu Xia 056a1c652f
Add new "ecosystem" section. (#1261) 2022-08-02 14:22:40 -07:00
Eryu Xia e1660d71b4
Mention Apache APISIX as a proxy with grpc-web support. (#1260) 2022-07-20 17:07:07 -07:00
River 7fe18d1e08
Remove Trailing Slashes from Hostname (#1254)
Hostnames can be passed in with trailing slashes. Since we blindly concat a path with a leading slash, paths like http://hostname//$rpc/path (note the double slash before $rpc) are possible. The duplicated slash can cause issues down the line, especially if the path is separated from the host in requests.

Browsers/servers generally allow double slashes, and while they are valid syntactically under the RFC spec, there is no specified semantic meaning. So a server could interpret a path like http://hostname//$rpc/path as equivalent to http://hostname/$rpc/path (most common), as a separate path with an empty path component before /$rpc, or as something else. The problem is several interpretations are equally valid under the spec and it's implementation-dependent which is used.

Additionally, the path can be separate from the host in a request depending on whether it's in origin form versus absolute form (see https://datatracker.ietf.org/doc/html/rfc7230#section-5.3). In absolute form (GET http://hostname//$rpc/path), the path and host are clear. In origin form (GET //$rpc/path, Host: hostname), it is still clear to us looking at it, but may not be clear to a server. For example the server may think the //$rpc/path is in absolute form due to the double slash, and thus parse it as host: $rpc, path: /path. This is especially problematic as https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 says:

"When a proxy receives a request with an absolute-form of request-target, the proxy MUST ignore the received Host header field (if any) and instead replace it with the host information of the request-target. A proxy that forwards such a request MUST generate a new Host field-value based on the received request-target rather than forward the received Host field-value."

Which means a proxy would corrupt the request when in this format by overwriting the old host value with $rpc

This parsing concern is not idle speculation either. Java's main URI class (https://docs.oracle.com/javase/8/docs/api/java/net/URI.html), parses a URI of the form //$rpc/path as having host/authority $rpc and path /path. Real bugs are observed from usage of this in commonly used libraries such as Netty, and various proxies.

Basically, this doesn't cause issues in the most common cases, but is a bug waiting to happen in many other edge cases, and affects widely used infrastructure.
2022-06-28 15:30:43 -07:00
Zhao e5ebedd3b4
fix Zig setup step in CI (#1252) 2022-06-13 11:43:40 -07:00
Zhao 3ca2e70edf
Use Zig to build aarch64 binaries (#1249) 2022-06-06 12:20:21 -07:00
Eryu Xia 012b226922
Update async to `3.2.3` (fix vulnerability) (#1247) 2022-05-24 14:57:09 -07:00
ericb-summit ef287fa478
make sure envoy is pid 1 (#1246)
This ensures signals are handled properly so docker stop respond quickly and properly and don't need to wait for time out (10s by default)
2022-05-24 14:22:12 -07:00
Hein Meling 1779661dde
Add version flag and version info in generated code (#1231)
Example output (edited):

```javascript
"use strict";
/**
 * @fileoverview gRPC-Web generated client stub for ag
 * @enhanceable
 * @public
 */
exports.__esModule = true;
exports.AutograderServiceClient = void 0;
// Code generated by protoc-gen-grpc-web. DO NOT EDIT.
// versions:
//  protoc-gen-grpc-web v1.3.1
//  protoc              v3.19.4
// source: ag/ag.proto
/* eslint-disable */
// @ts-nocheck
```
2022-04-29 16:36:38 -07:00
Eryu Xia 35284bfe15
Add npm badge to main README :) (#1226) 2022-04-21 16:28:27 -07:00
Eryu Xia a1b706ade0
Internal code sync (#1225) 2022-04-21 16:18:19 -07:00
Eryu Xia 176e831b07
Move node-client.js to avoid confusion. (#1224) 2022-04-21 16:02:12 -07:00
Eryu Xia c6ef0a55b6
Fixing Envoy config yaml formatting (#1223) 2022-04-21 15:52:46 -07:00
tomk9 58e8c1574f
Update envoy version to 1.22 (with config updates) (#1222)
Update envoy version to 1.22 (with config updates)
2022-04-21 12:12:54 -07:00
Aapeli Vuorinen 8c55021864
Upgrade protobuf and grpc deps (#1211)
* Upgrade protobuf and grpc deps

* Update protobuf version in prereqs dockerfile

Co-authored-by: Eryu Xia <eryu@google.com>
2022-03-08 13:40:04 -08:00
j-k 78313a239a
Make the static flag overridable (#1210) 2022-03-01 20:44:51 -08:00
Eryu Xia 0fe8d4d586
Mention Java in-process proxy is now "on hold". (#1208) 2022-02-18 13:43:44 -08:00
Eryu Xia 3102383234
Add Github Actions (workflows) for building grpc-web protoc plugins. (#1203) 2022-02-18 11:40:04 -08:00
Eryu Xia ce7d734e8a
Update references to version 1.3.1 (#1198) 2022-02-08 11:32:09 -08:00
Eryu Xia 173cb546b7
Fix release notes script to handle empty PR descriptions. (#1196) 2022-02-08 11:21:45 -08:00
Eryu Xia 81ce4c52ff
Revert "Expose MethodDescriptor's public methods (#1160)" (#1199)
This reverts commit 97baed4dbe.
2022-01-31 17:50:44 -08:00
Eryu Xia 454fe5f458
Bump version to 1.3.1 (#1195) 2022-01-29 00:02:14 -08:00
Eryu Xia 11370185b6
Internal code sync (#1194) 2022-01-28 23:35:44 -08:00
Eryu Xia 4dbd519bc8
Disable caching of local generated main.js file (#1193) 2022-01-28 21:47:21 -08:00
Matt Nathan eb313c1f3d
Correctly support proto3 optional fields in commonjs+dts .d.ts output (#1184)
* Correctly support proto3 optional fields in commonjs+dts .d.ts output

Fixes #1072

* Use has_optional_keyword instead of is_optional

* Improve the readability of the condition guarding hasXxx .d.ts field generation.
2022-01-28 12:41:59 -08:00
Stanley Cheung 3fcc2a2a8a
Update envoy version to 1.20 (#1173) 2021-11-19 15:25:35 -08:00
Lukas Möller 6b1d1e97a9
Fix missing TypeScript return type for `serverStreaming` calls. (#1167)
* Add strongly types to MethodDescriptor constructor
* Adds return type declaration to server streaming call

Co-authored-by: Eryu Xia <eryu@google.com>
2021-11-19 15:15:34 -08:00
Cirillo Ferreira 97baed4dbe
Expose MethodDescriptor's public methods (#1160)
Co-authored-by: Eryu Xia <eryu@google.com>
2021-11-19 01:44:39 -08:00
Eryu Xia cc1a135855
Fix issue where no RPC is issued when `deadline` is specified. (#1172) 2021-11-19 01:01:18 -08:00
Eryu Xia b849db4dfa
Use latest typescript and manually move `*_pb.js` files for the echo client (#1171) 2021-11-18 11:32:55 -08:00
Eryu Xia f1d863f2dc
Pin typescript to @4.4.4 to fix CI (#1170) 2021-11-18 10:34:01 -08:00
Eryu Xia 9f76a56ac9
Update Echo App instructions to pull `prereqs` (#1149)
.. as it's currently specified as a runtime dependency (in docker-compose.yml), even tho it's technically only needed at build time.

(Ideas needed: Anyone has idea on how to specify an image to only be needed at build time? 😃)
2021-11-17 13:59:47 -08:00
Eryu Xia 1efe741695
Add missing exports from RpcError and add test. (#1166) 2021-11-12 11:55:38 -08:00
Tin Rabzelj 53964f3668
Add missing class exports (#1164) 2021-11-10 10:32:48 -08:00
Eryu Xia 5831a96b56
Update generator to stop mentioning methodInfo (in TS files) (#1152) 2021-10-20 16:21:17 -07:00
Eryu Xia 0368e31aaa
Update references to version 1.3.0 (#1148) 2021-10-14 14:30:22 -07:00
Eryu Xia 4835cb6df8
Update g++ flags based on MacOS v.s. Linux (#1147) 2021-10-13 20:27:55 -07:00
Eryu Xia 4676d14333
Adding `-static` to generator g++ build flag (#1146) 2021-10-13 18:25:59 -07:00
Eryu Xia 7f70efec6e
Bump version to 1.3.0 (#1145) 2021-10-13 16:52:01 -07:00
Rohit Joshi 541e3aed20
document stream cancel function (#1081)
Co-authored-by: Eryu Xia <eryu@google.com>
2021-10-07 17:21:01 -07:00
Michal Augustýn 41f7cc71cf
docs: example showing binary format generation fixed (#1135)
Co-authored-by: Eryu Xia <eryu@google.com>
2021-10-07 13:31:56 -07:00
Eryu Xia 3956560ad0
Redo #1063 - Also set timeout on HTTP request if deadline for grpc call is set. (#1142) 2021-09-28 18:44:18 -07:00
Eryu Xia a992d943f8
Pin chrome version to `93.0.4577.63` (as in the Docker image) (#1143) 2021-09-28 14:53:27 -07:00
Eryu Xia f1fe57473f
Internal code sync (#1140) 2021-09-28 10:48:20 -07:00
Eryu Xia 627e33718d
Internal code sync (#1139)
- Internal code sync (up to Aug 4, 2021 for now..) & relevant improvements. :)
- Updated Closure dependency to 20210808.0.0 (necessary dependency)
2021-09-22 16:53:00 -07:00
Eryu Xia 78ddf996d8
Clean up Javascript-related Bazel rules. (#1138) 2021-09-21 23:16:28 -07:00
Eryu Xia 32fe12459b
Revamp Closure JsUnit tests runtime and optimize test/build flows. (#1137) 2021-09-21 15:27:21 -07:00
Ola Flisbäck d9a6c7a738
Require assert in hello world README example (#1116) 2021-09-08 01:29:54 -07:00
Eryu Xia a489de6f9e Replace use of `third_party/closure-library` submodule
... in favor of `google-closure-library` npm package.

Removed the third_party/closure-library submodule following the instructions here:
https://git.wiki.kernel.org/index.php/GitSubmoduleTutorial#Removal

Tested:
- Verified that `npm run build` inside `packages/grpc-web` generates
  exactly the same output as before.
- Test ran closure client: `docker-compose up --build node-server envoy closure-client`
2021-09-01 18:17:14 -07:00
06kellyjac a56e7212a3 Allow for custom install prefix
Also create the dir if it doesn't exist
2021-08-11 14:01:24 -07:00
Eryu Xia da552fc7ee Optimize `prereqs` Dockerfile to checkout Git submodules first
Due to Docker build cache, this can usually save me 20s+ build time on my MacbookPro each time i
rebuild after a local code change.
2021-08-06 10:51:14 -07:00
Eryu Xia fb3f864a0d Add troubleshooting info on Bazel crashes (OOM).
Explains what to do when Bazel crashes (OOM).
2021-08-05 10:50:08 -07:00
Eryu Xia ef0f44e1ce Bump Bazel version -> 4.1.0 and Protobuf version -> 3.17.3 2021-08-05 00:26:31 -07:00
Stanley Cheung 3c74b0da5c Internal code sync 2021-08-03 15:05:08 -07:00
Eryu Xia b24f5ca6ec Update Envoy timeout configs 2021-08-02 21:28:16 -07:00
dependabot[bot] 511cc32525 Bump jetty-server in /src/connector
Bumps [jetty-server](https://github.com/eclipse/jetty.project) from 9.4.40.v20210413 to 9.4.41.v20210516.
- [Release notes](https://github.com/eclipse/jetty.project/releases)
- [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.40.v20210413...jetty-9.4.41.v20210516)

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-server
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-27 11:07:07 -07:00
Stanley Cheung 78d5ded10f Fix grpc-js dep version 2021-05-10 13:41:45 -07:00
Yannic Bonenberger 6c1a2ceda1 Also set timeout on HTTP request if deadline for grpc call is set 2021-05-10 13:09:14 -07:00
Piotr Morgwai Kotarbinski 9c0169748a Cleanup and refresh maven pom.xml files
It was impossible to build a project using grpcweb-java due to multiple
enforcer errors. Fixing grpcweb-java broke examples hence these had to
be cleaned as well.
2021-05-10 12:37:25 -07:00
Warren He 35c16a9e4e ts: fill in other MethodDescriptor methods 2021-03-15 11:37:13 -07:00
Warren He b63099d977 ts: declare MethodType 2021-03-15 11:37:13 -07:00
Stanley Cheung c5bd9f7061 Clean up docker builds 2021-03-15 00:45:08 -07:00
Stanley Cheung 7b944d9dd4 Removed old nginx gateway code 2021-03-14 23:59:11 -07:00
Stanley Cheung 5a2ab7a75f Linter changes 2021-03-13 23:51:20 -08:00
Stanley Cheung 7afe497d8f Internal code sync 2021-03-13 23:30:51 -08:00
Yannic 7984205077 Set upstream timeout for envoy in echo example
Without this, streaming calls fail after a few seconds because envoy terminates the connection to the gRPC upstream.
2021-03-13 01:31:06 -08:00
Stanley Cheung 247e211031 Use bazel to build echo_server example 2021-03-13 01:04:51 -08:00
Stanley Cheung 3d7789ab28 Fix go env var for 1.16 2021-03-13 00:07:38 -08:00
nghialv ecf7512b88 Fix envoy config in the guide of helloworld example 2021-03-12 23:10:40 -08:00
Stanley Cheung b9d865ef4c Attempt to fix master build 2021-03-12 21:00:01 -08:00
Stanley Cheung 6b046c2fab Revert "Revert "bump envoy to v1.17.0""
This reverts commit de43d4bbf9.
2021-01-22 18:34:28 -08:00
Stanley Cheung de43d4bbf9 Revert "bump envoy to v1.17.0"
This reverts commit f960b8f89b.
2021-01-22 18:09:31 -08:00
Warren He 201c689222 Allow null response 2021-01-22 17:03:41 -08:00
Tom Reznik f960b8f89b bump envoy to v1.17.0 2021-01-22 16:40:30 -08:00
Srini Polavarapu 1580f3a1cd Add security policy doc 2021-01-22 16:39:43 -08:00
Stanley Cheung 042ccd160e fix build 2021-01-13 23:49:23 -08:00
Stanley Cheung 3fc20fbaf5 Code sync from internal code base 2021-01-13 23:49:23 -08:00
Alberto Calvo e88fdc809d Fix methodDescriptor param typing in index.d.ts
Closes #981, updates #944
2020-12-11 21:49:00 -08:00
Yannic Bonenberger 302ef34811 Upgrade rules_closure 2020-12-11 21:25:54 -08:00
Stanley Cheung 45ad6bec6d Revert "Bump grpc version to 1.34.0"
This reverts commit ab4d32e4cf.
2020-12-04 23:30:50 -08:00
Stanley Cheung ab4d32e4cf Bump grpc version to 1.34.0 2020-12-04 19:36:23 -08:00
Stanley Cheung e2bae61baf Bump closure library version to v20201102 2020-12-04 18:38:55 -08:00
Stanley Cheung 367f25a25a Bump protobuf to version 3.14.0 2020-12-04 18:10:05 -08:00
Alberto Calvo 85a76adcf5 Update and correct types
* Add `unaryInterceptors` and `streamInterceptors` to `GrpcWebClientBaseOptions`.
* Make `GrpcWebClientBaseOptions` optional in the constructor of `GrpcWebClientBase`.
2020-12-04 17:15:57 -08:00
Stanley Cheung 5c815503f1 Bump Envoy usage to 1.16.1 2020-12-04 17:07:56 -08:00
Stanley Cheung 2e3e8d2c50 Internal code sync 2020-11-20 21:10:26 -08:00
Stanley Cheung eecc629b00 Internal code sync 2020-11-20 21:10:26 -08:00
Yannic Bonenberger 1d37ed57bf Pass Bazel-CI 2020-11-20 15:25:58 -08:00
jimmy-ly 35aaf77cba Add missing command in echo example 2020-11-20 13:27:38 -08:00
Yannic 8569c5b554 Update load labeö 2020-11-20 12:51:47 -08:00
Yannic Bonenberger 9f20f76411 Fix buildifier warning 2020-11-20 12:51:47 -08:00
Yannic Bonenberger 6b6b7ac2ea Add plumbing for ClosureJS echo-example 2020-11-20 12:51:47 -08:00
Yannic 2637f14d91 Upgrade Bazel to 3.7.0 2020-11-19 18:24:42 -08:00
Tim Schindler 5783b0f657 Update README.md 2020-11-09 13:49:36 -08:00
Vasu Nori 969cc8fce4 upgrade jetty plugins to latest 2020-10-21 18:00:24 -07:00
dependabot[bot] 5de092aa6b Bump junit from 4.13 to 4.13.1 in /src/connector
Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-12 15:39:45 -07:00
Vasu Nori e444626fd2
Merge pull request #972 from grpc/dependabot/maven/net/grpc/gateway/examples/grpc-web-java/greeter-service/junit-junit-4.13.1
Bump junit from 4.13 to 4.13.1 in /net/grpc/gateway/examples/grpc-web-java/greeter-service
2020-10-12 15:18:10 -07:00
dependabot[bot] 2b9850e9e0
Bump junit in /net/grpc/gateway/examples/grpc-web-java/greeter-service
Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-12 22:07:06 +00:00
Stanley Cheung cce2f19faa Add ClientOptions class 2020-10-12 15:06:28 -07:00
Wenbo Zhu 83cb0406c1
Update streaming-roadmap.md 2020-10-09 10:19:57 -07:00
Stanley Cheung 0c62fe8a3a Fix non-determinism in code generator 2020-10-05 20:49:15 -07:00
Vasu Nori 4a4923f928 change grpc-web java package name to 'io.grpcweb' 2020-10-02 19:38:36 -07:00
Vasu Nori 30747b3796 Add README files to explain how to run the grpc-web java in-process proxy.
This is not final yet. Just want to get some README's in place so we can review the whole code + READMEs as a whole before saying this is officially ready for some beta customer.
2020-09-25 12:42:05 -07:00
Vasu Nori f71bbb8c41 Make grpc-web Java example code to be more like a unittest - so that we can easily run it with maven command. 2020-09-24 17:06:55 -07:00
Yannic Bonenberger fecfe1cef4 Fix Protobuf .d.ts typings for .proto files without package
Fixes #939
2020-09-01 14:14:05 -07:00
JoeSchr 69a16b6033 Remove SayHelloAfterDelay from node-client.ts
was removed from `server.js` and `client.js` in last commit as well.
2020-09-01 14:13:19 -07:00
Richard Pringle 6d786ac023 Add withCredentials option to index.d.ts 2020-09-01 14:09:45 -07:00
Stanley Cheung 0c56ed1de3 Update references to version 1.2.1 2020-08-18 16:40:56 -07:00
Stanley Cheung 3d921fffb7 Review fixes 2020-08-18 12:39:10 -07:00
Stanley Cheung 0238da3b6e Add immutable methods withGrpcCallOption, withMetadata to grpc.web.request, response 2020-08-18 12:39:10 -07:00
Stanley Cheung 6f5ecb0ad1 Some more internal refactor 2020-08-13 16:26:21 -07:00
Stanley Cheung 91702e7461 Migrate to ES6 classes 2020-08-10 10:42:31 -07:00
Stanley Cheung 68c1810a37 Refactor ClientUnaryCallImpl 2020-08-08 23:20:10 -07:00
Stanley Cheung 0599c38922 Roll forward of #853 2020-08-07 15:50:24 -07:00
Stanley Cheung 0a4b48e2d0 Fix some type annotations 2020-08-05 17:10:44 -07:00
Wenbo Zhu 71be2dc948
Update streaming-roadmap.md 2020-07-31 15:20:46 -07:00
Wenbo Zhu 9ecc15db8b
Update roadmap.md 2020-07-31 15:13:57 -07:00
Wenbo Zhu 9c1a174e19
Update in-process-proxy.md 2020-07-31 15:11:48 -07:00
Wenbo Zhu 90e83cd618
Update browser-features.md 2020-07-31 15:07:47 -07:00
Wenbo Zhu 7ea35cfacc
Update roadmap.md 2020-07-30 17:12:19 -07:00
Wenbo Zhu 222cc8d484
Create streaming-roadmap.md 2020-07-30 17:11:49 -07:00
Wenbo Zhu d8e039f255
Update roadmap.md 2020-07-30 14:40:14 -07:00
Wenbo Zhu fdcff71b47
Update roadmap.md 2020-07-30 14:31:29 -07:00
Stanley Cheung bcdf3520b0 Update MethodDescriptorInterface 2020-07-24 18:24:47 -07:00
Stanley Cheung 85f7751e77 Remove duplicate decodeURIComponent call 2020-07-24 13:27:03 -07:00
wapa5pow 1e450e683c refactor hanndle error 2020-07-24 13:12:41 -07:00
wapa5pow 630ab16f4a add decode error message 2020-07-24 13:12:41 -07:00
Jack Pearkes 6ad62b1670 guard against null getResponseText()
Given the READY_STATE_CHANGE event can be trigged from for various
reasons and be in a situation where the response is not yet
written, this guards against a null value which is possible per
the specification:

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseText
2020-07-21 21:31:14 -07:00
Stanley Cheung 675deb6e34 Add test to show how to access metadata in interceptor 2020-07-20 17:57:04 -07:00
Stanley Cheung 5276b1e36d Add error handling to a few error conditions 2020-07-17 11:45:12 -07:00
Yannic Bonenberger e994bb108b [bazel] Introduce grpc_web_toolchain
This change adds a new rule `grpc_web_toolchain` which we will use to
provide the generator/runtime to `closure_grpc_web_library`.

Updates #507

RELNOTES: [bazel] `closure_grpc_web_library` now uses toolchains to
resolve the generator and runtime. To migrate, add the following snipped
to your `WORKSPACE`:
```starlark
load("@com_github_grpc_grpc_web//bazel:repositories.bzl", "grpc_web_toolchains")
grpc_web_toolchains()
```
2020-07-17 11:26:31 -07:00
Yannic 9d90faacca Update Bazel to 3.3.1 2020-07-14 13:36:44 -07:00
Stanley Cheung 0aac5494fe Revert "Use methoddescriptorinterface"
This reverts commit 1ac8aa5a63.
2020-07-13 21:44:18 -07:00
Stanley Cheung 1ac8aa5a63 Use methoddescriptorinterface 2020-07-13 17:23:52 -07:00
Stanley Cheung 80e410bbc4 Bump version to 1.2.1 2020-07-13 12:38:12 -07:00
Stanley Cheung fb5641e629 Pin @types/mocha back to fix master tests 2020-07-13 12:00:47 -07:00
Stanley Cheung 714501a544 break long lines 2020-07-13 10:56:43 -07:00
Stanley Cheung e9092acf0f Remove typing fallback. Add more tests 2020-07-13 10:56:43 -07:00
Stanley Cheung 916d6cbef4 Capitalize generic types to avoid ambiguity, and replace deprecated MethodInfo with MethodDescriptor 2020-07-13 10:56:43 -07:00
Stanley Cheung 03691eeae4 Add test for promise-based unaryInterceptor too 2020-07-13 10:56:43 -07:00
Stanley Cheung 023cad0644 Add test for StreamInterceptor 2020-07-13 10:56:43 -07:00
Stanley Cheung 9b73daa521 Add missing types definitions 2020-07-13 10:56:43 -07:00
Stanley Cheung 25a888c974 Make mvn commands quieter 2020-07-08 16:30:12 -07:00
Stanley Cheung a913c18158 Minor tweak to methoddescriptor argument type 2020-07-08 15:11:00 -07:00
Stanley Cheung 50e456a7cc Fix protoc-plugin build 2020-07-08 14:51:41 -07:00
Stanley Cheung dfa6e478f2 Add @interface MethodDescroptorInterface 2020-07-08 14:42:47 -07:00
Stanley Cheung f1e3cbff5f Build the protoc-plugin docker image in master runs 2020-07-08 13:18:46 -07:00
Stanley Cheung 664fd9c3e9 Bump Envoy to 1.15.0 2020-07-08 12:03:58 -07:00
Stanley Cheung d5550cd52b Update init_submodules.sh 2020-07-08 11:45:32 -07:00
Stanley Cheung e25afb40de Review fixes. Run buildifier on WORKSPACE file too 2020-07-08 11:45:32 -07:00
Stanley Cheung 6c7cde153d Bump grpc 2020-07-08 11:45:32 -07:00
Stanley Cheung e27be18859 Update code generator to support Proto3 optional 2020-07-08 11:45:32 -07:00
Vasu Nori 76c04d6fe9 initialize the connection to grpc-service before 1st request arrives 2020-07-06 17:02:44 -07:00
Yannic Bonenberger cd059a28b2 [generator] Refactor dependency management
No functional changes intended.
2020-07-06 14:18:47 -07:00
Yannic 3cf33f96ff Update Bazel to 3.3.1 2020-07-02 22:23:17 -07:00
Nathan H. Leung 4b3959852f Update package README to say that gRPC-Web is GA.
Follow-up to #339
2020-07-02 22:06:39 -07:00
Stanley Cheung e0835e2d1c Bug fix 2020-07-02 21:24:12 -07:00
Stanley Cheung 95db26456b Add an option goog_promise to generator 2020-07-02 21:24:12 -07:00
Yenru Chin a816d9c56e Add removeListener type and missing metadata event 2020-06-29 12:15:21 -07:00
Vasu Nori 74ea9b3b2a automate running of interop tests on java connector code 2020-06-24 17:08:46 -07:00
Stanley Cheung 550c8d2375 Make UnaryResponse an interface 2020-06-24 11:19:02 -07:00
Vasu Nori 62a03ec722 Update pom.xml so executable jar can be built easily 2020-06-23 18:16:59 -07:00
Stanley Cheung 01c3421c74 Revert #738 2020-06-23 13:57:44 -07:00
Vasu Nori 88d70ecbdb
Merge pull request #861 from vnorigoog/props-in-file
Separate Core code from Examples and interop-test code.
2020-06-23 13:12:59 -07:00
Vasu Nori 0a283f6c6e Merge branch 'props-in-file' of https://github.com/vnorigoog/grpc-web into props-in-file 2020-06-23 10:13:35 -07:00
Vasu Nori 4f5a803f34 Merge branch 'master' into props-in-file 2020-06-23 10:13:14 -07:00
Vasu Nori 4e470884bd
Merge branch 'master' into props-in-file 2020-06-22 14:35:59 -07:00
Stanley Cheung 0d747ec02c Update references to 1.2.0 2020-06-22 14:20:38 -07:00
Vasu Nori 65b43d9971 Merge branch 'props-in-file' of https://github.com/vnorigoog/grpc-web into props-in-file 2020-06-22 14:09:21 -07:00
Vasu Nori c470eaa0f5 separate base files from example code 2020-06-22 13:49:08 -07:00
Stanley Cheung a8416b5e7a Refactor error handling in grpcwebclientbase 2020-06-21 22:18:52 -07:00
David Maixner daf7cc8f17 [fix] envoy deprecated fields
This gets rid of warnings in envoy log files for deprecated fields.
2020-06-21 22:02:33 -07:00
Vasu Nori fdbfdb1aeb separate base files from example code 2020-06-21 16:43:22 -07:00
Stanley Cheung c9b2e34e57 Fix @fileoverview comments 2020-06-19 10:52:40 -07:00
Stanley Cheung 670326124a Migrate to ES6 classes 2020-06-19 10:52:40 -07:00
小橋 大助 8c96b8cee3 Add docker port mapping(for Mac/Win) to helloworld example 2020-06-16 00:07:16 -07:00
Stanley Cheung 7a93e7f93e Rollback a change to streambodyclientreadablestream 2020-06-11 17:57:47 -07:00
Stanley Cheung d32a30a5a6 Update to use @grpc/grpc-js node package, and update helloworld example 2020-06-11 15:37:46 -07:00
Stanley Cheung 6e632b4183 Add a ThenableCall base class for the promise-based unaryCall function 2020-06-11 15:37:32 -07:00
Vasu Nori b89d8f122c Add Dependency Injection(Guice) and copyright notice 2020-06-09 18:31:45 -07:00
Stanley Cheung b80a65f6e6 Bump version to 1.2.0 2020-06-09 18:07:51 -07:00
Stanley Cheung 6900db20c0 Fix issues with callbacks 2020-06-09 17:38:01 -07:00
Stanley Cheung aadbdb7e3b Some refactor on streambodyclientreadablestream 2020-06-08 22:58:45 -07:00
Patrice Chalin 6b99a37519 README: updates to better match other grpc repos 2020-06-05 22:09:37 -07:00
Stanley Cheung 05029f3d00 Fix code generator bug and add tests 2020-06-05 22:03:38 -07:00
Yannic Bonenberger a055960f80 Add experimental ES6 import style
This change adds a new `import_style` that emits ES6 modules.
For now, this only re-exports the symbols from `import_style=closure`.

In the future, this will no longer require emitting google modules
(`goog.provide`). If protobuf ever adds support for emitting ES6
modules, we will use them instead of the `goog.provided`'d versions
as well.

Note that the Bazel integration is currently broken because of a
limitation in `rules_closure`.
2020-06-04 16:46:05 -07:00
Stanley Cheung f516ccbf3c Removed some unused code 2020-06-04 15:31:04 -07:00
Stanley Cheung 721afdf958 Linter changes 2020-06-02 11:40:06 -07:00
Stanley Cheung de0f650a72 Generate the callback client and promise client in two different files 2020-06-01 19:08:30 -07:00
hanabi1224 825dd2a9ee add missing comma 2020-05-31 22:01:01 -07:00
hanabi1224 9d96b7a4b8 Avoid double slash in url when client hostname has tailing slash 2020-05-31 22:01:01 -07:00
Stanley Cheung 504c43ecb0 Move docs into doc/ folder 2020-05-31 02:35:25 -07:00
Stanley Cheung 7f16bffd79 Add a script to generate changelog 2020-05-31 02:05:30 -07:00
Stanley Cheung 8ff7b57ac7 Remove some older unused scripts 2020-05-29 14:30:06 -07:00
xsbchen c963bcfec5 use explicit envoy release tag 2020-05-28 21:07:58 -07:00
Stanley Cheung 2391e1cc6b Remove some unnecessary dependencies 2020-05-28 20:16:21 -07:00
Stanley Cheung 9ad8ba64c7 Update references to 1.1.0 2020-05-28 19:08:04 -07:00
Vasu Nori 1fda10af5f All interop tests work now. 2020-05-20 20:20:33 -07:00
Stanley Cheung 569d92f528 Bump version to 1.1.0 2020-05-19 17:54:32 -07:00
Vasu Nori c73f5f40b9 Major modifications and most of the interop-tests now work.
Remaining work
1. make this interop test work: custom-metadata
2. cleanup and add more unittests
3. review w. Stanley - find missing functionality
2020-05-08 13:23:19 -07:00
Stanley Cheung c7dedab92c Bump some third_party links 2020-05-04 20:18:59 -07:00
Stanley Cheung 7293d9d421 make the build slightly more quiet 2020-05-01 11:00:16 -07:00
Stanley Cheung 39753a38ee Fix nginx build after internal absl change 2020-04-30 22:37:43 -07:00
Stanley Cheung 5c1b6c7060 Quote string keys 2020-04-30 15:01:47 -07:00
Stanley Cheung 5864e5dd80 Fix a bug in test: callback not properly intercepted 2020-04-30 15:01:47 -07:00
Stanley Cheung 3487a96c9c Build a couple more images for presubmit tests 2020-04-29 17:06:38 -07:00
Stanley Cheung d6f959b17b No need to specify the env var explicitly 2020-04-29 15:51:24 -07:00
Stanley Cheung fffbf2df1d Trying to speed up tests 2020-04-29 15:51:24 -07:00
Stanley Cheung 36c3064c68 Split basic tests with interop tests 2020-04-28 18:29:38 -07:00
Stanley Cheung 781f53da94 Remove advanced.yml 2020-04-28 16:15:54 -07:00
Stanley Cheung 0b214401e7 Update version to 1.1.0-rc.1 2020-04-28 14:50:43 -07:00
Stanley Cheung df81680d5e Only build subset of docker images for presubmit runs 2020-04-28 14:19:59 -07:00
Stanley Cheung fa21417814 Update interop test description doc 2020-04-28 12:44:49 -07:00
Stanley Cheung cc6cdfb428 Linter changes 2020-04-27 18:51:58 -07:00
Stanley Cheung b62d3629c2 grpc-web interceptors implementation 2020-04-27 17:32:56 -07:00
Vasu Nori 319c2c6836 make grpc-web interop-tests work 2020-04-27 16:54:43 -07:00
Stanley Cheung 1dffd21b6a Some clean-up on comments and instructions 2020-04-27 16:18:56 -07:00
Stanley Cheung 499f644a8b Re-organize kokoro.sh a bit 2020-04-27 16:18:56 -07:00
Stanley Cheung 621dd26180 Test both text and binary mode 2020-04-27 16:18:56 -07:00
Stanley Cheung 17fbe35265 Refactor interop tests to be automated 2020-04-27 16:18:56 -07:00
Stanley Cheung ea88b10600 Add interop spec and interop tests 2020-04-27 16:18:56 -07:00
Jan Tattermusch 0bf05fd26a fix typos in in proces proxy docs 2020-04-27 10:50:50 -07:00
Wenbo Zhu acdd180c7a
Update IN-PROCESS-PROXY.md 2020-04-23 14:24:46 -07:00
Richard Pringle e908cd0f60 Add entrypoint for node-server 2020-04-20 23:59:42 -07:00
Stanley Cheung 3eccf72fbc use explicit envoy release tag 2020-04-20 23:27:14 -07:00
Tomi Äijö 289ec8020f Add missing separator to imports from external files 2020-04-08 16:37:59 -07:00
Julien Roncaglia 39f7736410 Make kokoro run under macOS 2020-04-08 14:53:13 -07:00
Stanley Cheung b0ea6a1e55 Some refactoring prework before interceptors 2020-04-07 23:31:33 -07:00
Stanley Cheung 6e2a1877da Update tests 2020-04-06 15:28:45 -07:00
Stanley Cheung d2858c7440 Add .on(metadata,...) callback to distinguish initial metadata 2020-04-06 15:28:45 -07:00
Carlo Nomes aca18bc2bb Update README for Windows / MacOS users 2020-04-03 15:34:43 -07:00
Stanley Cheung eeb65e66f9 fix whitespace 2020-04-03 14:33:23 -07:00
Yannic Bonenberger 1bcdae540a Remove extra semicolon 2020-04-03 13:27:47 -07:00
Yannic Bonenberger 2badafa034 Fix compilation error 2020-04-03 13:27:47 -07:00
Yannic Bonenberger fe3617c511 [generator] Move options parsing into dedicated class
*** Note: There is no behavior change from this patch. ***
2020-04-03 13:27:47 -07:00
Stanley Cheung d2f9e0a1eb Add Status to UnaryResponse 2020-04-02 16:21:08 -07:00
Stanley Cheung cc5cbf6e40 Fix BUILD.bazel 2020-03-23 23:20:35 -07:00
Stanley Cheung 508f50a32e Update generic client 2020-03-23 23:20:35 -07:00
Stanley Cheung 40aeadef9a Some cleanup 2020-03-19 13:01:27 -07:00
IagoLast 87209cb94b Disable static checkers on generated js files 2020-03-19 01:06:03 -07:00
Stanley Cheung e100cfa712 Update google closure compiler 2020-03-19 00:33:26 -07:00
Florent Castelli b7fb330e3e Enable builder pattern in Typescript protobuf messages. 2020-03-18 23:41:06 -07:00
Yannic Bonenberger 40cbb49772 [bazel] Update rules_closure + fix linter warnings
Working towards resolving #744, #575, #550, #700
2020-03-18 22:56:59 -07:00
Stanley Cheung 16a54fa05a Add eval test for TypeScript generated code 2020-03-18 22:15:18 -07:00
Florent Castelli 7f9f7e8e5a Generate Promise based overloads for unary calls in Typescript
Fixes #580
2020-03-18 21:26:13 -07:00
travikk 410ca10711 Added @override to address compiler issues 2020-03-18 18:38:41 -07:00
travikk ebb0ec6549 Added missing semicolons 2020-03-18 18:38:41 -07:00
travikk 4206f4a546 Allow GrpcWebStreamParser to accept Uint8Array 2020-03-18 18:38:41 -07:00
Stanley Cheung 63d78243e9 Fix unit test 2020-03-18 17:40:38 -07:00
Vasu Nori dc25fa759a
Merge pull request #748 from vnorigoog/master
add Message Deframer to parse frames in the input messages
2020-03-12 10:52:47 -07:00
Vasu Nori d8b362128c add Message Deframer to parse frames in the input messages 2020-03-06 16:56:42 -08:00
Vasu Nori 9ce4ea51b4
Merge pull request #740 from vnorigoog/latest
fixed a lot of nits and reorg'ed the code
2020-02-26 14:24:17 -08:00
Vasu Nori edaccf6a9b fixed a lot of nits and reorg'ed the code 2020-02-26 14:16:34 -08:00
Vasu Nori 086e1cf706
Add files via upload 2020-02-26 13:59:22 -08:00
Vasu Nori 26e6df7005
Another Readme - to say that the proto is just for testing 2020-02-25 15:34:37 -08:00
Vasu Nori cdc8ef19ac
Top level Readme file 2020-02-25 15:33:43 -08:00
Vasu Nori 61cb3e4218
Initial commit of skeleton files.
This is an initial commit. No where near ready to release. But plan to get there very quick in the next 2-3 weeks.
2020-02-25 15:28:33 -08:00
Stanley Cheung 48f547f4bb Update bazel version 2020-02-06 14:25:44 -08:00
Stanley Cheung 7b3f343ff0 Fix grpcwebproxy interop 2020-01-31 14:05:01 -08:00
Stanley Cheung 9a672c93f3 moving envoy back to :latest 2020-01-30 15:27:31 -08:00
noconnor aff8c3d6de allow_origin is deprecated in latest envoy server
`allow_origin` configuration has been deprecated in favour of `allow_origin_string_match`

The latest version of envoy proxy will not start if `allow_origin` option is used
2020-01-30 15:03:38 -08:00
Stanley Cheung 69f23cb5cf Remove two unused goog.require 2020-01-22 11:55:42 -08:00
Holger Finger c90ae6b329 use grpc (native) library instead of grpc-js library for example server, because grpc-js is not fully compatible to the native grpc server. 2020-01-21 14:34:48 -08:00
Holger Finger 764d5bcd18 fix bug that grpc-status response headers resulted in doubled execution of the callback provided to rpcCall. 2020-01-21 14:34:48 -08:00
Stanley Cheung 4a5b5b3da6 Fix kokoro tests 2020-01-21 14:09:59 -08:00
Hunor Kovacs b92360af38 Written out in the HTML body that the output lies in the logs. The empty page may confuse learners that something didn't work. 2020-01-21 12:03:52 -08:00
Stanley Cheung d03435fc6e Do not hardcode CXX to g++ 2019-12-06 14:04:14 -08:00
Stanley Cheung 55ebde4719 Revert #683 2019-12-05 11:30:14 -08:00
travikk 5c65dc4a2c Pass raw buffer to stream parser 2019-12-02 13:22:36 -08:00
travikk 03b73fbe45 Optimise parsing incoming payload 2019-12-02 13:22:36 -08:00
Stanley Cheung 3fccf33401 Update references to 1.0.7 2019-11-07 14:40:05 -08:00
Stanley Cheung 4bedc90a84 Update dockerfile to build protoc plugin 2019-11-07 13:55:54 -08:00
Stanley Cheung 0e45b53c82 Update protobuf include 2019-11-07 00:17:16 -08:00
Stanley Cheung 80358bbeaa md linter 2019-10-29 21:48:21 -07:00
Stanley Cheung 6272637894 Update closure-library 2019-10-29 15:53:23 -07:00
Stanley Cheung 3ff3d6f153 Update third_party hashes and package.json 2019-10-29 15:53:23 -07:00
Tim 779167024d Clarify Typescript support in Readme
This was not very clear at all and cost me quite a bit of time.
2019-10-29 13:17:55 -07:00
Stanley Cheung 37636129a0 Fix bazel.build 2019-10-29 10:51:42 -07:00
Stanley Cheung a96b958120 Add metadata to error callback 2019-10-29 10:51:42 -07:00
Stanley Cheung 0f79fb4366 Revert "Add metadata in gRPC error"
This reverts commit fe2ea6b351.
2019-10-29 02:15:14 -07:00
A. Ishikawa fe2ea6b351 Add metadata in gRPC error 2019-10-29 01:03:38 -07:00
Stanley Cheung ef8adb3df4 Remove temp stuff 2019-10-28 15:50:58 -07:00
Stanley Cheung b0437e7014 WIP remove stream_body 2019-10-28 15:50:58 -07:00
Yannic 6bd0db08e4 Fix inconsitency 2019-10-25 11:00:10 -07:00
Yannic Bonenberger 7d9492b5ac Add config for Bazel CI
This will ensure gRPC-Web is compatible with the latest Bazel.
If we enable presubmit testing, we can remove Bazel from Kokoro
and speed-up CI.
2019-10-25 11:00:10 -07:00
Will Qiu 1c2a0b75f1 nginx example Expose-Headers add Grpc-Message,Grpc-Status 2019-10-21 09:47:42 -07:00
Stanley Cheung c22f57497b Remove unused require 2019-10-18 11:23:34 -07:00
Julien Roncaglia 401e54063c Ensure that the end callback is called
Previously the end callback was called by checking the state at the end of
the READY_STATE_CHANGE event. The problem with this approach is that there
are multiple early exit cases in this function and when they happen the
consumer is never informed of the end of the stream.

The new approach is to only dispatch the event when the COMPLETE event is
received (Except if an error is dispatched) so the last event is always
either 'error' or 'end').
2019-10-18 11:23:34 -07:00
Julien Roncaglia e38d7bb6c0 Use closure compiler from npm in build.js 2019-10-18 10:24:07 -07:00
Julien Roncaglia bf29f0272b Ignore MacOS .DS_Store files 2019-10-17 13:14:10 -07:00
Stanley Cheung bc3394170d Update dockerfiles 2019-10-16 12:59:34 -07:00
Stanley Cheung ab6ff71786 Fix error callback 2019-10-16 11:13:18 -07:00
Stanley Cheung c284b1533a Update grpcwebproxy to latest version 2019-10-04 22:02:06 -07:00
Stanley Cheung f0e6074dae Latest closure compiler broke test 2019-10-03 18:15:36 -07:00
Stanley Cheung 06bda9256e Add CallOptions class 2019-09-30 15:40:48 -07:00
Stanley Cheung 5def18f94c Update node version 2019-09-27 20:20:20 -07:00
Stanley Cheung 73b388c0c5 Update protobuf version 2019-09-27 15:43:37 -07:00
Srini Polavarapu 7f0bcc916b Add/update GOVERNANCE.md and CONTRIBUTING.md 2019-09-26 15:47:00 -07:00
Stanley Cheung c6af7c6130 Bump to version 1.0.7 2019-09-24 16:37:54 -07:00
Stanley Cheung 2fa4270334 Fix generated code return type, and remove unused var 2019-09-18 13:10:53 -07:00
vnorigoog 192fef8909
Update README.md 2019-09-10 15:48:03 -07:00
Stanley Cheung f16acc0a0d Add src/ connector 2019-09-10 14:47:36 -07:00
Stanley Cheung 8113edeeff Use nullptr instead 2019-09-09 15:34:04 -07:00
Alexey Smirnov 5ee2126d85 Fix output directory name when using import_style=typescript 2019-09-09 13:37:31 -07:00
Stanley Cheung ea4f1aa637 Added API for simple unary call 2019-09-09 13:19:11 -07:00
Harman Gakhal 1711674d66 Return specific grpc status code on http error 2019-08-23 16:10:03 -07:00
Stanley Cheung 9365fd62fc WIP: Generate method descriptors into multiple files 2019-08-22 10:49:30 -07:00
Geoffrey Salisi fd8ee6b8bf Remove `enabled` deprecated field 2019-08-21 12:57:06 -07:00
Stanley Cheung 5df306a8d6 Add maintainers list 2019-08-20 15:14:41 -07:00
Stanley Cheung ab906036ea Add support in code generator for printing only method descriptors 2019-08-20 12:58:50 -07:00
Stanley Cheung 6a3b282fb0 Remove internal frameworks mode 2019-08-17 00:19:50 -07:00
Stanley Cheung e2f999a0df Some linter formatting changes 2019-08-05 14:39:48 -07:00
Stanley Cheung d69f188c9e Fix constructor 2019-08-05 13:47:06 -07:00
Stanley Cheung 36598a2ab1 Fixed semicolons 2019-08-03 00:09:53 -07:00
Stanley Cheung 3b76ca5810 Fix status and error callbacks 2019-08-03 00:09:53 -07:00
Stanley Cheung 513a578f7f Update docs to 1.0.6 2019-08-01 23:45:13 -07:00
Stanley Cheung 8b501a96f4 Remove trailing whitespace; 2019-07-31 17:42:43 -07:00
Stanley Cheung 365783307e Add option to set withCredentials to true 2019-07-31 16:53:50 -07:00
Stanley Cheung 2ae8118d78 Adding some groundwork for generic client 2019-07-31 16:22:41 -07:00
Stanley Cheung 86d67cf8e1 Update closure flag 2019-07-31 00:37:56 -07:00
Stanley Cheung 8a01f3022a Bump to version 1.0.6 2019-07-30 22:25:33 -07:00
Stanley Cheung a651f78e67 Update eval test 2019-07-30 21:44:32 -07:00
Stanley Cheung 8a13b6f7db Add generated code eval test 2019-07-30 21:44:32 -07:00
lqs ab242d2bb9 fix wrong package name of input type 2019-07-30 17:51:33 -07:00
Gary Lo 66cab4cd35 HelloWorld: Bind ports to envoy admin
It was useful to be able to see the envoy admin to debug and understand how GRPC-Web works with Envoy
2019-07-21 22:00:13 -07:00
Gary Lo 952d0f5869 Fix: Helloworld Example - Enabled Deprecation
Fixes Envoy not starting up due to https://www.envoyproxy.io/docs/envoy/latest/intro/deprecated
```
[2019-07-18 01:48:05.301][7][critical][main] [source/server/server.cc:93] error initializing configuration '/etc/envoy/envoy.yaml': Proto constraint validation failed (Using deprecated option 'envoy.api.v2.route.CorsPolicy.enabled' from file route.proto. This configuration will be removed from Envoy soon. Please see https://www.envoyproxy.io/docs/envoy/latest/intro/deprecated for details. If continued use of this field is absolutely necessary, see https://www.envoyproxy.io/docs/envoy/latest/configuration/runtime#using-runtime-overrides-for-deprecated-features for how to apply a temporary and highly discouraged override.): allow_origin: "*"
allow_methods: "GET, PUT, DELETE, POST, OPTIONS"
allow_headers: "keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout"
expose_headers: "custom-header-1,grpc-status,grpc-message"
max_age: "1728000"
enabled {
  value: true
}

[2019-07-18 01:48:05.301][7][info][main] [source/server/server.cc:560] exiting
Proto constraint validation failed (Using deprecated option 'envoy.api.v2.route.CorsPolicy.enabled' from file route.proto. This configuration will be removed from Envoy soon. Please see https://www.envoyproxy.io/docs/envoy/latest/intro/deprecated for details. If continued use of this field is absolutely necessary, see https://www.envoyproxy.io/docs/envoy/latest/configuration/runtime#using-runtime-overrides-for-deprecated-features for how to apply a temporary and highly discouraged override.): allow_origin: "*"
allow_methods: "GET, PUT, DELETE, POST, OPTIONS"
allow_headers: "keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout"
expose_headers: "custom-header-1,grpc-status,grpc-message"
max_age: "1728000"
enabled {
  value: true
}
```
2019-07-19 23:22:07 -07:00
Mitar 3568cde5e9 Updating for new Envoy versions. 2019-07-19 23:04:42 -07:00
vnorigoog dad250d37e
Update IN-PROCESS-PROXY.md 2019-07-12 15:29:32 -07:00
Stanley Cheung e24206f1fb Update docs to point to 1.0.5 2019-07-09 17:36:18 -07:00
Stanley Cheung 6c6f7748a1 remove whitespace 2019-07-09 14:20:42 -07:00
Daniel Bader a3174cb1d0 Update Bazel to 0.27.1 2019-07-02 13:32:32 -07:00
Daniel Bader 885dfbc546 Let _proto_include_paths return list instead of depset. 2019-07-02 13:32:32 -07:00
Daniel Bader 04f78beb36 Update Bazel version to 0.27.0 2019-07-02 13:32:32 -07:00
Daniel Bader d74c3f0abf As of Bazel 0.27.0, depset is no longer iterable. 2019-07-02 13:32:32 -07:00
Daniel Bader ed2c747a34 Update rules_closure version. 2019-07-02 13:32:32 -07:00
Oleg Vodolazsky 3e92de6ef0 Update Envoy configuration in helloworld example README.md 2019-07-01 11:17:34 -07:00
Markus Padourek db6cd68df9 Add missing newline 2019-07-01 11:16:08 -07:00
Markus Padourek 10828d7ccc Ensure credentials are not undefined in typescript
Fixes #581
2019-07-01 11:16:08 -07:00
Ben Foxall 0c24e95a38 Uppercase enum keys
To match the generated JS
2019-07-01 10:38:09 -07:00
Stanley Cheung d16364954a Make MethodType a separate class 2019-06-06 15:39:50 -07:00
Stanley Cheung cee964f746 Fix missing bazel def 2019-06-06 15:39:50 -07:00
Stanley Cheung a8e54b3885 Introducing MethodDescriptor 2019-06-06 15:39:50 -07:00
Stanley Cheung b8bbb459d7 Bump to version 1.0.5 2019-05-20 11:20:35 -07:00
Henke Adolfsson 968b3d2bb2 Default to no grpc-timeout header when timeout is Infinity 2019-05-19 23:09:38 -07:00
Henke Adolfsson 9e666831d1 Use precalculated number for Infinity 2019-05-19 23:09:38 -07:00
Henke Adolfsson 81426015b3 Remove a isFinite that was already checked for 2019-05-19 23:09:38 -07:00
Henke Adolfsson d7fac321b6 Add fix for timeout of strings, NaN, Infinity and -Infinity 2019-05-19 23:09:38 -07:00
Stanley Cheung 671255e706 Adding new fields to MethodInfo 2019-05-13 11:22:06 -07:00
Jerry Chaves 8d90ec5dcb Add change notes for running Envoy in Win/Mac hosts
- Notes from issue 436 (https://github.com/grpc/grpc-web/issues/436)
2019-05-07 10:42:45 -07:00
Naoki Kishi c227d3d8d8 Update protoc version in Dockerfile 2019-05-05 00:04:24 -07:00
Parham Negahdar 7a5672712b Method Derserializer should take Uint8Array
Currently running into issue:

```
Type '{}' is not assignable to type 'Uint8Array'.
```

This fixes it for me.
2019-05-03 15:38:07 -07:00
Duncan Dean a4d898dcfe fix: Add command to update package lists
Add `apt-get -qq update` to quietly update the debian stretch package list
so the `apt-get -qq install -y default-jdk` does not fail due to 404s.
2019-04-29 13:45:00 -07:00
Stanley Cheung 15351a0401 Changes to deserializeBinary API; 2019-04-28 22:59:53 -07:00
Jian Yang ffe8e9c903 remove duplicated has$field$ method for oneof 2019-03-28 15:25:03 -07:00
Jonah Dahlquist aadfb92bdb Made constructor arguments `credentials` and `options` optional
Currently the TypeScript generated clients require that `null` be passed to `credentials` and `options` if they're not being used.  It's easier to simply make those arguments optional.  I've left the `null` portion of the type though to maintain backwards compatibility.
2019-03-26 10:43:36 -07:00
Stanley Cheung 91177052a5 remove blurb about Istio integration for now; 2019-03-22 15:50:39 -07:00
Stanley Cheung 591d16c47f Update doc to point to 1.0.4 2019-03-22 10:55:13 -07:00
Venil Noronha 9593703052 docs: add details about the Istio integration
This adds links related to the Istio integration for gRPC-Web.

Signed-off-by: Venil Noronha <veniln@vmware.com>
2019-03-21 21:20:21 -07:00
Stanley Cheung 3f1de33059 Bump to version 1.0.4 2019-03-21 13:48:26 -07:00
Stanley Cheung 556c9b30c6 Proto clean up 2019-03-21 13:04:23 -07:00
Stanley Cheung 97851b40f8 Update to be using envoyproxy/envoy-dev docker image base 2019-03-21 12:13:44 -07:00
Yannic Bonenberger 0de9635db4 Address review comments 2019-03-21 11:44:25 -07:00
Yannic Bonenberger ee21d8871e Remove buildifier after using it 2019-03-21 11:44:25 -07:00
Yannic Bonenberger 8d538a1ac4 Attempt to fix flaky bazel tests 2019-03-21 11:44:25 -07:00
Yannic d8ee2d9d24 Revert "Temporarily run bazel test multiple times"
This reverts commit ef94d2e7e0.
2019-03-21 11:44:25 -07:00
Stanley Cheung ef94d2e7e0 Temporarily run bazel test multiple times 2019-03-20 00:36:17 -07:00
Quentin f9bfe720b5 Remove a return that skip emission of end callback
When the connection is closed and there isn't any message sent, the end callback was not called.

Should fix #289, #384 and #467
2019-03-19 18:17:12 -07:00
Yannic Bonenberger cb3499c967 [bazel] Migrate protobuf info provider to new-style one 2019-03-18 10:40:06 -07:00
Stanley Cheung 5852e276e0 fix compiler warning 2019-03-01 13:22:03 -08:00
Zbigniew Mandziejewicz 626ce9702b feature: Typings codegen for bytes field type 2019-02-27 11:31:58 -08:00
Stanley Cheung ab82d7d133 remove trailing whitespace; 2019-02-27 10:28:36 -08:00
Stanley Cheung 459487e050 Add module alias to enums. Fixes #454 2019-02-27 09:45:54 -08:00
Tomáš Procházka aecdc2c96c Update README.md
add python3 alterative for running http server in example/helloworld/README.md file
2019-02-26 16:05:26 -08:00
Stanley Cheung c0d3a1d0e5 Fix long line 2019-02-26 15:58:24 -08:00
Jian Yang cfe789f4f0 add typescript definition for Oneof fields 2019-02-26 15:25:39 -08:00
Stanley Cheung 84e0ea356e Linter clean up 2019-02-25 22:52:48 -08:00
Zbigniew Mandziejewicz 95aba7c4d2 fix: Prefix reserved fields in AsObject 2019-02-25 22:13:08 -08:00
Zbigniew Mandziejewicz 3c869187d0 refactor: add methods for element type/name 2019-02-25 22:13:08 -08:00
Zbigniew Mandziejewicz 514edc7e27 fix: support jstype option, fix skipping map entry 2019-02-25 22:13:08 -08:00
Zbigniew Mandziejewicz 194c97e650 fix: exclude map entry message from typings, fix optional values 2019-02-25 22:13:08 -08:00
Stanley Cheung 5a296841ce Code sync 2019-02-22 00:15:36 -08:00
Stanley Cheung cc7dca23a2 Code sync 2019-02-21 16:02:32 -08:00
Stanley Cheung d139a6ad99 Code sync 2019-02-20 22:03:48 -08:00
Stanley Cheung b75825f660 Code sync 2019-02-20 19:01:41 -08:00
Zbigniew Mandziejewicz 73358722d8 Export Map types correctly, optional getter/setters for message types 2019-02-01 12:31:35 -08:00
Stanley Cheung 30f8b0e183
Fix typo 2019-02-01 10:35:12 -08:00
Zbigniew Mandziejewicz edb21bf544 Add @types/google-protobuf to ts-example 2019-01-30 10:52:16 -08:00
Zbigniew Mandziejewicz 347cea046c feature: Messages in typings extending jspb.Message, add missing generated methods 2019-01-30 10:52:16 -08:00
Yannic Bonenberger ec4d1480fa [bazel] Upgrade to 0.22.0 2019-01-28 15:44:37 -08:00
Yannic 35f2abed45 [bazel] Upgrade rules_closure 2019-01-28 15:44:37 -08:00
Yannic 4e23d78d4b [bazel] Upgrade to 0.21.0 2019-01-28 15:44:37 -08:00
Yannic 4aaa044916 [bazel] Use starlark version of http_archive 2019-01-28 15:44:37 -08:00
Wenbo Zhu aa1bfccf4b
Update BROWSER-FEATURES.md 2019-01-23 16:34:07 -08:00
Wenbo Zhu 1173a85e7f
Create IN-PROCESS-PROXY.md 2019-01-23 16:32:44 -08:00
Wenbo Zhu 9fd4a8d9b9
Identify mandatory features 2019-01-23 16:10:20 -08:00
Gary Lo 81350d639c fix broken link of PROTOCOL-WEB.md in roadmap 2019-01-22 10:43:50 -08:00
Zbigniew Mandziejewicz 01ed7060f6 Generate typings for protobuf files when no services are present 2019-01-15 10:53:52 -08:00
Zbigniew Mandziejewicz 4feb57a913 Import only messages used by service in grpc-web typings file 2019-01-15 10:53:52 -08:00
Zbigniew Mandziejewicz 8d7e89d34e Match name nesting and imports in .d.ts with .js files 2019-01-15 10:53:52 -08:00
Wenbo Zhu 66139ecf13
Update BROWSER-FEATURES.md
Clarify query-param support.
2019-01-10 10:43:08 -08:00
Jimmy Au bb3624ea8d Enable ADVANCED_OPTIMIZATIONS in Closure Compiler 2019-01-09 11:40:36 -08:00
rogchap c2536577a6 Promise function should use ES5 functions rather than fat arrows 2019-01-08 14:58:59 -08:00
Zbigniew Mandziejewicz 68eeabaf89 Fix kokoro warning for status details 2019-01-08 10:12:08 -08:00
Zbigniew Mandziejewicz bca630832e Emit status event on empty stream response 2019-01-08 10:12:08 -08:00
Johan Brandhorst f181d1efae Simplify ToCamelCase 2019-01-07 16:03:43 -08:00
Johan Brandhorst 3d280c34be Use camelCase in AsObject definition
Fixes #428
2019-01-07 16:03:43 -08:00
Johan Brandhorst 7852f2a2df Fix type error in serverStreaming method
Fixes #416
2019-01-07 12:51:07 -08:00
Cyrus Katrak 42e654c912 fix whitespace in build script 2019-01-07 10:35:09 -08:00
Ben Picolo 09604c4837 Fix metadata typings for TS client 2018-12-14 13:56:49 -08:00
Roger Chapman 1bbfb214c5 Generate Typescript definition for top level Enums (#404)
* Generate Typescript definition for top level Enums
* Export the top level enums that are use in the messages
* Add test to check compile of typescript
2018-12-06 21:25:05 -08:00
Stanley Cheung a24bcf46da Fix some readme 2018-11-29 23:20:06 -08:00
Stanley Cheung 6fe20c7092 Update README and simplify example to use prebuilt binaries 2018-11-29 22:28:08 -08:00
Daniel Bader d8a828a92a fix ?Object 2018-11-29 14:12:27 -08:00
Zbigniew Mandziejewicz 30ed257c49 Optional metadata argument 2018-11-28 14:38:14 -08:00
Daniel Bader fab336348d Suppress messages 2018-11-28 11:47:36 -08:00
Stanley Cheung b2d8b34c5e A script to compile protoc plugin 2018-11-27 17:08:37 -08:00
Stanley Cheung 7bf901ae7a Bump to version 1.0.3 2018-11-27 13:25:46 -08:00
Zbigniew Mandziejewicz 8bcf950813 Fix naming of nested enums/messages, include field message types in typings 2018-11-27 11:01:05 -08:00
Zbigniew Mandziejewicz 3ecf2939e9 Support nested types and enums 2018-11-26 23:25:41 -08:00
Christophe Hesters 3401083c03 Added other tools required to build protoc
I needed to install autoconf and libtool and also added g++. It might not be obvious for some users just wanting to experiment with grpc web and suddenly have to compile a c/c++ program.
2018-11-19 14:09:52 -08:00
factuno-db e03f5ddb63 Make the bazel rules work with current rules_closure. (#368)
* closure_js_library_impl --> create_closure_js_library
* [WORKSPACE] update closure rules to current
* _CloserWorker + _closure_library_base --> CLOSURE_JS_TOOLCHAIN_ATTRS
2018-11-13 13:41:15 -08:00
Stanley Cheung 7f64402965 Use latest bazel version 2018-11-13 11:04:49 -08:00
Mitch Kelley 63dac016bd update examples to use addService 2018-11-10 15:01:51 -08:00
Stanley Cheung def314aec4 Fix response header value with colon 2018-11-09 23:56:20 -08:00
weilip f28d6cb9ed Fix rpc method name clashes. When multiple services in the same proto file define same rpc 2018-11-09 13:22:30 -08:00
A. Ishikawa 5d104af9a8 Support sub directories for generated typescript files (#360) 2018-11-08 20:18:00 -08:00
Stanley Cheung 81c07aad88
Bump to version 1.0.2 (#359) 2018-11-07 19:59:30 -08:00
251 changed files with 18498 additions and 10353 deletions

14
.bazelci/presubmit.yml Normal file
View File

@ -0,0 +1,14 @@
---
# TODO(yannic): Enable buildifier and test on Windows and RBE (both unsupported by rules_closure).
platforms:
macos:
build_targets:
- //...
test_targets:
- //...
ubuntu1804:
build_targets:
- //...
test_targets:
- //...

2
.bazelignore Normal file
View File

@ -0,0 +1,2 @@
# //third_party conatins git submodules.
third_party/

14
.bazelrc Normal file
View File

@ -0,0 +1,14 @@
build --copt=-Wno-error=deprecated-declarations --host_copt=-Wno-error=deprecated-declarations
# Required until this is the default; expected in Bazel 7
common --enable_bzlmod
# Load any settings specific to the current user.
# .bazelrc.user should appear in .gitignore so that settings are not shared with team members
# This needs to be last statement in this
# config, as the user configuration should be able to overwrite flags from this file.
# See https://docs.bazel.build/versions/master/best-practices.html#bazelrc
# (Note that we use .bazelrc.user so the file appears next to .bazelrc in directory listing,
# rather than user.bazelrc as suggested in the Bazel docs)
try-import %workspace%/.bazelrc.user

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
**/dist
**/node_modules
packages/grpc-web/generated

42
.github/workflows/make-plugin-arm.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Make ARM Plugins (Windows/macOS/Linux)
on:
workflow_dispatch:
inputs:
version_number:
description: 'Version number'
required: true
default: '1.x.x'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: 'recursive'
- name: Setup Zig
run: |
mkdir -p $HOME/.local/bin $HOME/.local/zig
curl 'https://ziglang.org/download/0.9.1/zig-linux-x86_64-0.9.1.tar.xz' | tar xJ --strip-components=1 --directory=$HOME/.local/zig
ln -s $HOME/.local/zig/zig $HOME/.local/bin/zig
echo "PATH=$PATH:$HOME/.local/bin" >> $GITHUB_ENV
- name: Build plugin
env:
VERSION: ${{ github.event.inputs.version_number }}
run: |
cd javascript/net/grpc/web/generator
zig build -Drelease-fast
- name: gen and verify sha256
run: |
cd javascript/net/grpc/web/generator/zig-out/bin
for exe in $(ls)
do
openssl dgst -sha256 -r -out $exe'.sha256' $exe
sha256sum -c $exe'.sha256'
done
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: plugin
path: javascript/net/grpc/web/generator/zig-out/bin/

32
.github/workflows/make-plugin-linux.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Make Linux Plugin
on:
workflow_dispatch:
inputs:
version_number:
description: 'Version number'
required: true
default: '1.x.x'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build plugin docker image
run: docker-compose build prereqs protoc-plugin
- name: Copy binary from Docker image
run: |
docker cp $(docker create grpcweb/protoc-plugin):/github/grpc-web/javascript/net/grpc/web/generator/protoc-gen-grpc-web \
./protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-linux-x86_64
- name: gen sha256
run: |
openssl dgst -sha256 -r -out protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-linux-x86_64.sha256 \
protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-linux-x86_64
- name: verify sha256
run: sha256sum -c protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-linux-x86_64.sha256
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: plugin
path: protoc-gen-grpc-web*

View File

@ -0,0 +1,48 @@
name: Make MacOS Plugin
on:
workflow_dispatch:
inputs:
version_number:
description: 'Version number'
required: true
default: '1.x.x'
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Install build utils
run: brew install coreutils automake
- name: Checkout protobuf code
run: |
./scripts/init_submodules.sh
# Protobuf build instructions from:
# https://github.com/protocolbuffers/protobuf/blob/master/src/README.md
- name: Build protobuf (configure & make)
run: |
cd ./third_party/protobuf
./autogen.sh
./configure
make -j$(nproc)
make install
- name: Remove dynamite dependencies (similar to `-static` on linux)
run: rm /usr/local/lib/libproto*.dylib
- name: make
run: make clean && make plugin
- name: move
run: |
mv javascript/net/grpc/web/generator/protoc-gen-grpc-web \
./protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-darwin-x86_64
- name: gen sha256
run: |
openssl dgst -sha256 -r -out protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-darwin-x86_64.sha256 \
protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-darwin-x86_64
- name: verify sha256
run: sha256sum -c protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-darwin-x86_64.sha256
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: plugin
path: protoc-gen-grpc-web*

View File

@ -0,0 +1,37 @@
name: Make Windows Plugin
on:
workflow_dispatch:
inputs:
version_number:
description: 'Version number'
required: true
default: '1.x.x'
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Print Bazel version
run: |
bazel version
- name: build
run: bazel build javascript/net/grpc/web/generator:protoc-gen-grpc-web
- name: move
run: |
mv bazel-bin/javascript/net/grpc/web/generator/protoc-gen-grpc-web.exe \
./protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-windows-x86_64.exe
shell: bash
- name: gen sha256
run: |
openssl dgst -sha256 -r -out protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-windows-x86_64.exe.sha256 \
protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-windows-x86_64.exe
shell: bash
# TODO: Check sha256 (sha256sum not available for now. )
#- name: verify sha256
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: plugin
path: protoc-gen-grpc-web*

View File

@ -0,0 +1,36 @@
name: Publish Stable Source Archive
on:
release:
types: [published]
jobs:
# Whenever a release is published, this uploads an accompanying stable source archive.
#
# Github doesn't guarantee stability of source archives for more than 6 months[1].
# More stability is required by projects like Bazel Central Registry[2][3].
#
# [1]: https://github.blog/open-source/git/update-on-the-future-stability-of-source-code-archives-and-hashes/
# [2]: https://github.com/bazelbuild/bazel-central-registry/blob/main/docs/README.md#validations
# [3]: https://blog.bazel.build/2023/02/15/github-archive-checksum.html
bazel-release-archive:
defaults:
run:
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
shell: /usr/bin/bash -euxo pipefail {0}
env:
# github.ref_name is defined here:
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context
TAG: ${{github.ref_name}}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout
# GITHUB_REF is defined here:
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
- run: git archive --format zip --prefix "grpc-web-$TAG/" --output "grpc-web-source-${TAG}.zip" "$GITHUB_REF"
- run: git archive --format tar.gz --prefix "grpc-web-$TAG/" --output "grpc-web-source-${TAG}.tar.gz" "$GITHUB_REF"
- run: gh release upload "${TAG}" "grpc-web-source-${TAG}.zip" "grpc-web-source-${TAG}.tar.gz"
env:
GH_TOKEN: ${{ github.token }}

8
.gitignore vendored
View File

@ -1,3 +1,4 @@
.vscode
bazel-bin
bazel-genfiles
bazel-grpc-web
@ -5,3 +6,10 @@ bazel-out
bazel-testlogs
*.o
protoc-gen-*
.DS_Store
target
.project
.classpath
.settings
zig-out
zig-cache

15
.gitmodules vendored
View File

@ -1,12 +1,3 @@
[submodule "third_party/grpc"]
path = third_party/grpc
url = https://github.com/grpc/grpc.git
[submodule "third_party/openssl"]
path = third_party/openssl
url = https://github.com/openssl/openssl.git
[submodule "third_party/nginx/src"]
path = third_party/nginx/src
url = https://nginx.googlesource.com/nginx
[submodule "third_party/closure-library"]
path = third_party/closure-library
url = https://github.com/google/closure-library.git
[submodule "third_party/protobuf"]
path = third_party/protobuf
url = https://github.com/protocolbuffers/protobuf.git

266
CHANGELOG.md Normal file
View File

@ -0,0 +1,266 @@
[//]: # (GENERATED FILE -- DO NOT EDIT!)
[//]: # (See scripts/release_notes.py for more details.)
## 1.5.0
- [#1369](https://github.com/grpc/grpc-web/pull/1369) (Typescript) Mark some `metadata` parameters as optional @andrewmbenton
- [#1335](https://github.com/grpc/grpc-web/pull/1335) Update Debian (and other deps) and remove Java In-process Proxy
- [#1334](https://github.com/grpc/grpc-web/pull/1334) Allow mixed-case headers
- [#1330](https://github.com/grpc/grpc-web/pull/1330) Update ES6 .d.ts imports with comment about corresponding proto import... @gonzojive
- [#1313](https://github.com/grpc/grpc-web/pull/1313) Update ES6 imports with comment about corresponding proto import path. @reddaly
## 1.4.2
- [#1289](https://github.com/grpc/grpc-web/pull/1289) Expose getName() in MethodDescriptor and fix TS definitions.
- [#1230](https://github.com/grpc/grpc-web/pull/1230) GrpcWebClientReadableStream: keep falsy data @pro-wh
## 1.4.1
- [#1286](https://github.com/grpc/grpc-web/pull/1286) Fix duplicate dot in enum name (when "package" is specified)
## 1.4.0
### Major Features
- [#1249](https://github.com/grpc/grpc-web/pull/1249) Use Zig to build aarch64 binaries @hronro
- [#1203](https://github.com/grpc/grpc-web/pull/1203) Github Actions (workflows) for building `protoc-gen-grpc-web` plugins
### Other Changes
- [#1279](https://github.com/grpc/grpc-web/pull/1279) Fixes the status codes ordering in typescript definitions @chandraaditya
- [#1278](https://github.com/grpc/grpc-web/pull/1278) Fix Enum with module in generated TS interface.
- [#1254](https://github.com/grpc/grpc-web/pull/1254) Remove Trailing Slashes from Hostname @jkjk822
- [#1252](https://github.com/grpc/grpc-web/pull/1252) Fix Zig setup step in CI @hronro
- [#1231](https://github.com/grpc/grpc-web/pull/1231) Add version flag and version info in generated code @meling
- [#1225](https://github.com/grpc/grpc-web/pull/1225) Improve error message & Internal code sync
- [#1222](https://github.com/grpc/grpc-web/pull/1222) Update envoy version to 1.22 (with config updates) @tomk9
- [#1211](https://github.com/grpc/grpc-web/pull/1211) Upgrade protobuf and grpc deps @aapeliv
- [#1199](https://github.com/grpc/grpc-web/pull/1199) Revert "Expose MethodDescriptor's public methods"
## 1.3.1
- [#1184](https://github.com/grpc/grpc-web/pull/1184) Correctly support proto3 optional fields in commonjs+dts .d.ts output @mattnathan
- [#1173](https://github.com/grpc/grpc-web/pull/1173) Update envoy version to 1.20
- [#1172](https://github.com/grpc/grpc-web/pull/1172) Fix issue where **no RPC is issued when `deadline` is specified.**
- [#1167](https://github.com/grpc/grpc-web/pull/1167) Fix missing TypeScript return type for `serverStreaming` calls. @lukasmoellerch
- [#1166](https://github.com/grpc/grpc-web/pull/1166) Add missing exports from `RpcError` and add test.
- [#1164](https://github.com/grpc/grpc-web/pull/1164) Add missing class exports @tinrab
- [#1160](https://github.com/grpc/grpc-web/pull/1160) Expose MethodDescriptor's public methods @tomferreira
## 1.3.0
### Major Features
- [#1139](https://github.com/grpc/grpc-web/pull/1139) Improve error type with `RpcError` & internal code sync (contributor: @TomiBelan)
+ (experimental) Typescript users need to update type references from `Error` -> `RpcError`
### Other Changes
- [#1140](https://github.com/grpc/grpc-web/pull/1140) Improve `RpcError.code` typing & internal code sync (contributor: @richieforeman)
- [#1138](https://github.com/grpc/grpc-web/pull/1138) Remove Bazel in Javascript toolchain
- [#1137](https://github.com/grpc/grpc-web/pull/1137) Revamp Closure JsUnit tests runtime and optimize test/build flows.
- [#1115](https://github.com/grpc/grpc-web/pull/1115) Bump Bazel version -> 4.1.0 and Protobuf version -> 3.17.3
- [#1107](https://github.com/grpc/grpc-web/pull/1107) Allow for custom install prefix @06kellyjac
- [#1063](https://github.com/grpc/grpc-web/pull/1063) Also set timeout on HTTP request if deadline for grpc call is set @Yannic
- [#1004](https://github.com/grpc/grpc-web/pull/1004) Bump closure library version to v20201102
- [#1002](https://github.com/grpc/grpc-web/pull/1002) Bump Envoy version to 1.16.1
- [#998](https://github.com/grpc/grpc-web/pull/998) Fix GrpcWebClientBaseOptions types in index.d.ts @acalvo
- [#971](https://github.com/grpc/grpc-web/pull/971) Add grpc.web.ClientOptions to better document options and add type res... @jennnnny
- [#969](https://github.com/grpc/grpc-web/pull/969) Fix non-determinism in code generator
- [#941](https://github.com/grpc/grpc-web/pull/941) Fix Protobuf .d.ts typings for .proto files without package @Yannic
## 1.2.1
- [#910](https://github.com/grpc/grpc-web/pull/910) Add test to show how to access metadata in interceptor
- [#903](https://github.com/grpc/grpc-web/pull/903) Add error handling to a few error conditions
- [#886](https://github.com/grpc/grpc-web/pull/886) Add missing types definitions
- [#885](https://github.com/grpc/grpc-web/pull/885) Bump Envoy to 1.15.0
- [#884](https://github.com/grpc/grpc-web/pull/884) Update protoc plugin to support Proto3 optional
- [#882](https://github.com/grpc/grpc-web/pull/882) Add @interface MethodDescroptorInterface [@Jennnnny](https://github.com/Jennnnny)
- [#880](https://github.com/grpc/grpc-web/pull/880) Update Bazel to 3.3.1 [@Yannic](https://github.com/Yannic)
- [#874](https://github.com/grpc/grpc-web/pull/874) Add removeListener and missing metadata event types [@danielthank](https://github.com/danielthank)
- [#872](https://github.com/grpc/grpc-web/pull/872) [bazel] Introduce grpc_web_toolchain [@Yannic](https://github.com/Yannic)
- [#871](https://github.com/grpc/grpc-web/pull/871) [generator] Refactor dependency management [@Yannic](https://github.com/Yannic)
- [#869](https://github.com/grpc/grpc-web/pull/869) Add scripts to run interop-tests on grpc-web Java connector
## 1.2.0
### Major Features
- [#847](https://github.com/grpc/grpc-web/pull/847) Allow multiple .on() callbacks and fix issue with non-OK status
### Other Changes
- [#859](https://github.com/grpc/grpc-web/pull/859) Fix envoy.yaml deprecated fields [@dmaixner](https://github.com/dmaixner)
- [#858](https://github.com/grpc/grpc-web/pull/858) Refactor error handling in grpcwebclientbase
- [#857](https://github.com/grpc/grpc-web/pull/857) Migrate to ES6 classes
- [#852](https://github.com/grpc/grpc-web/pull/852) Update to use @grpc/grpc-js node package, and update helloworld exampl...
- [#851](https://github.com/grpc/grpc-web/pull/851) Add a ThenableCall base class for the promise-based unaryCall function [@Jennnnny](https://github.com/Jennnnny)
- [#844](https://github.com/grpc/grpc-web/pull/844) Fix code generator bug and add tests
- [#833](https://github.com/grpc/grpc-web/pull/833) Add proper author attribution to release notes / changelog
- [#827](https://github.com/grpc/grpc-web/pull/827) Splitting callback based client and Promise based client into multiple... [@Jennnnny](https://github.com/Jennnnny)
- [#822](https://github.com/grpc/grpc-web/pull/822) use explicit envoy release tag [@xsbchen](https://github.com/xsbchen)
- [#821](https://github.com/grpc/grpc-web/pull/821) Experimental Feature: Add ES6 import style [@Yannic](https://github.com/Yannic)
- [#738](https://github.com/grpc/grpc-web/pull/738) Avoid double slash in url when client hostname has tailing slash [@hanabi1224](https://github.com/hanabi1224)
## 1.1.0
### Major Features
- [#785](https://github.com/grpc/grpc-web/pull/785) grpc-web interceptors implementation [@Jennnnny](https://github.com/Jennnnny)
- [#772](https://github.com/grpc/grpc-web/pull/772) Add interop test spec and interop tests
### Other Changes
- [#818](https://github.com/grpc/grpc-web/pull/818) All java connector interop tests are passing now
- [#804](https://github.com/grpc/grpc-web/pull/804) Fix a bug in test: callback not properly intercepted
- [#801](https://github.com/grpc/grpc-web/pull/801) Trying to speed up tests
- [#797](https://github.com/grpc/grpc-web/pull/797) Split basic tests with interop tests
- [#780](https://github.com/grpc/grpc-web/pull/780) Add missing separator to imports from external files [@tomiaijo](https://github.com/tomiaijo)
- [#777](https://github.com/grpc/grpc-web/pull/777) Add .on(metadata,...) callback to distinguish initial metadata
- [#764](https://github.com/grpc/grpc-web/pull/764) [generator] Move options parsing into dedicated class [@Yannic](https://github.com/Yannic)
- [#761](https://github.com/grpc/grpc-web/pull/761) Update generic client [@Jennnnny](https://github.com/Jennnnny)
- [#756](https://github.com/grpc/grpc-web/pull/756) Add eval test for TypeScript generated code
- [#752](https://github.com/grpc/grpc-web/pull/752) Disable static checkers on generated js files [@IagoLast](https://github.com/IagoLast)
- [#747](https://github.com/grpc/grpc-web/pull/747) Enable builder pattern in Typescript protobuf messages. [@Orphis](https://github.com/Orphis)
- [#746](https://github.com/grpc/grpc-web/pull/746) Generate Promise based overloads for unary calls in Typescript [@Orphis](https://github.com/Orphis)
- [#745](https://github.com/grpc/grpc-web/pull/745) [bazel] Update rules_closure + fix linter warnings [@Yannic](https://github.com/Yannic)
- [#734](https://github.com/grpc/grpc-web/pull/734) Allow GrpcWebStreamParser to accept Uint8Array [@travikk](https://github.com/travikk)
- [#723](https://github.com/grpc/grpc-web/pull/723) Update bazel version
- [#720](https://github.com/grpc/grpc-web/pull/720) Fix grpcwebproxy interop
- [#716](https://github.com/grpc/grpc-web/pull/716) allow_origin is deprecated in latest envoy server [@noconnor](https://github.com/noconnor)
- [#695](https://github.com/grpc/grpc-web/pull/695) Fix issue 632 (double execution of callback) [@hfinger](https://github.com/hfinger)
- [#692](https://github.com/grpc/grpc-web/pull/692) Do not hardcode CXX to g++
## 1.0.7
- [#671](https://github.com/grpc/grpc-web/pull/671) Add metadata to error callback
- [#668](https://github.com/grpc/grpc-web/pull/668) Remove stream_body.proto
- [#665](https://github.com/grpc/grpc-web/pull/665) Add config for Bazel CI [@Yannic](https://github.com/Yannic)
- [#663](https://github.com/grpc/grpc-web/pull/663) nginx example Expose-Headers add Grpc-Message,Grpc-Status [@zsluedem](https://github.com/zsluedem)
- [#657](https://github.com/grpc/grpc-web/pull/657) Ensure that the end callback is called [@vbfox](https://github.com/vbfox)
- [#655](https://github.com/grpc/grpc-web/pull/655) Use closure compiler from npm in build.js [@vbfox](https://github.com/vbfox)
- [#654](https://github.com/grpc/grpc-web/pull/654) Ignore MacOS .DS_Store files [@vbfox](https://github.com/vbfox)
- [#652](https://github.com/grpc/grpc-web/pull/652) Fix error callback
- [#644](https://github.com/grpc/grpc-web/pull/644) Add CallOptions class [@Jennnnny](https://github.com/Jennnnny)
- [#641](https://github.com/grpc/grpc-web/pull/641) Add/update GOVERNANCE.md and CONTRIBUTING.md
- [#635](https://github.com/grpc/grpc-web/pull/635) Fix generated code return type, and remove unused var
- [#628](https://github.com/grpc/grpc-web/pull/628) Added API for simple unary call [@Jennnnny](https://github.com/Jennnnny)
- [#621](https://github.com/grpc/grpc-web/pull/621) Fix output directory name when using import_style=typescript [@asv](https://github.com/asv)
- [#619](https://github.com/grpc/grpc-web/pull/619) Return specific grpc status code on http error [@harmangakhal](https://github.com/harmangakhal)
- [#618](https://github.com/grpc/grpc-web/pull/618) Generate method descriptors into multiple files [@Jennnnny](https://github.com/Jennnnny)
- [#617](https://github.com/grpc/grpc-web/pull/617) Remove `enabled` deprecated field [@gsalisi](https://github.com/gsalisi)
- [#615](https://github.com/grpc/grpc-web/pull/615) Add support in code generator for printing only method descriptors [@Jennnnny](https://github.com/Jennnnny)
- [#608](https://github.com/grpc/grpc-web/pull/608) Fix status and error callbacks
## 1.0.6
- [#604](https://github.com/grpc/grpc-web/pull/604) Add option to set withCredentials to true
- [#603](https://github.com/grpc/grpc-web/pull/603) Adding some groundwork for generic client [@Jennnnny](https://github.com/Jennnnny)
- [#600](https://github.com/grpc/grpc-web/pull/600) Add generated code eval test
- [#599](https://github.com/grpc/grpc-web/pull/599) fix wrong package name of input type [@lqs](https://github.com/lqs)
- [#593](https://github.com/grpc/grpc-web/pull/593) Fix: Helloworld Example - Enabled Deprecation [@gary-lo](https://github.com/gary-lo)
## 1.0.5
- [#582](https://github.com/grpc/grpc-web/pull/582) Ensure credentials are not undefined in typescript [@Globegitter](https://github.com/Globegitter)
- [#579](https://github.com/grpc/grpc-web/pull/579) Uppercase enum keys in TypeScript definitions [@benfoxbotica](https://github.com/benfoxbotica)
- [#578](https://github.com/grpc/grpc-web/pull/578) Fix depset issues with/upgrade to Bazel 0.27.1 [@factuno-db](https://github.com/factuno-db)
- [#567](https://github.com/grpc/grpc-web/pull/567) Introducing MethodDescriptor [@Jennnnny](https://github.com/Jennnnny)
- [#559](https://github.com/grpc/grpc-web/pull/559) Adding new fields to MethodInfo [@Jennnnny](https://github.com/Jennnnny)
- [#556](https://github.com/grpc/grpc-web/pull/556) Add fix for deadline of strings, NaN, Infinity and -Infinity [@CatEars](https://github.com/CatEars)
- [#546](https://github.com/grpc/grpc-web/pull/546) Changes to deserializeBinary API
- [#540](https://github.com/grpc/grpc-web/pull/540) Method Derserializer should take Uint8Array [@pnegahdar](https://github.com/pnegahdar)
- [#519](https://github.com/grpc/grpc-web/pull/519) remove duplicated has$field$ method for oneof [@yangjian](https://github.com/yangjian)
- [#512](https://github.com/grpc/grpc-web/pull/512) Make client args `credentials` and `options` optional [@jonahbron](https://github.com/jonahbron)
## 1.0.4
- [#502](https://github.com/grpc/grpc-web/pull/502) Attempt to fix flakiness of 'bazel test' [@Yannic](https://github.com/Yannic)
- [#497](https://github.com/grpc/grpc-web/pull/497) Remove a return that skip emission of end callback [@tinou98](https://github.com/tinou98)
- [#494](https://github.com/grpc/grpc-web/pull/494) [bazel] Migrate protobuf info provider to new-style one [@Yannic](https://github.com/Yannic)
- [#482](https://github.com/grpc/grpc-web/pull/482) feature: Typings codegen for bytes field type [@shaxbee](https://github.com/shaxbee)
- [#481](https://github.com/grpc/grpc-web/pull/481) Add module alias to enums for Typescript [@rogchap](https://github.com/rogchap)
- [#460](https://github.com/grpc/grpc-web/pull/460) add typescript definition for Oneof fields [@yangjian](https://github.com/yangjian)
- [#452](https://github.com/grpc/grpc-web/pull/452) fix: exclude map entry message from typings, fix optional values [@shaxbee](https://github.com/shaxbee)
- [#448](https://github.com/grpc/grpc-web/pull/448) Export Map types correctly, optional getter/setters for message types [@shaxbee](https://github.com/shaxbee)
- [#444](https://github.com/grpc/grpc-web/pull/444) feature: Messages in typings extending jspb.Message [@shaxbee](https://github.com/shaxbee)
- [#433](https://github.com/grpc/grpc-web/pull/433) Match name nesting and imports in .d.ts with .js files [@shaxbee](https://github.com/shaxbee)
- [#430](https://github.com/grpc/grpc-web/pull/430) Use camelCase in AsObject definition [@johanbrandhorst](https://github.com/johanbrandhorst)
- [#429](https://github.com/grpc/grpc-web/pull/429) Fix type error in serverStreaming method [@johanbrandhorst](https://github.com/johanbrandhorst)
- [#427](https://github.com/grpc/grpc-web/pull/427) Promise function should use ES5 functions rather than fat arrows (IE b... [@rogchap](https://github.com/rogchap)
- [#422](https://github.com/grpc/grpc-web/pull/422) Enable ADVANCED_OPTIMIZATIONS in Closure Compiler [@jjbubudi](https://github.com/jjbubudi)
- [#421](https://github.com/grpc/grpc-web/pull/421) [bazel] Upgrade to 0.22.0 [@Yannic](https://github.com/Yannic)
- [#413](https://github.com/grpc/grpc-web/pull/413) Emit status event on empty stream response [@shaxbee](https://github.com/shaxbee)
- [#409](https://github.com/grpc/grpc-web/pull/409) Fix metadata typings for TS client [@bpicolo](https://github.com/bpicolo)
- [#404](https://github.com/grpc/grpc-web/pull/404) Generate Typescript definition for top level Enums [@rogchap](https://github.com/rogchap)
## 1.0.3
- [#391](https://github.com/grpc/grpc-web/pull/391) A script to compile protoc plugin
- [#385](https://github.com/grpc/grpc-web/pull/385) Codegen: Support nested types and enums [@shaxbee](https://github.com/shaxbee)
- [#368](https://github.com/grpc/grpc-web/pull/368) Make the bazel rules work with current rules_closure. [@factuno-db](https://github.com/factuno-db)
- [#367](https://github.com/grpc/grpc-web/pull/367) update examples to use addService [@mitchdraft](https://github.com/mitchdraft)
- [#365](https://github.com/grpc/grpc-web/pull/365) Fix response header value with colon
- [#362](https://github.com/grpc/grpc-web/pull/362) Fix the method name clashes for generated commonjs files [@weilip1803](https://github.com/weilip1803)
- [#360](https://github.com/grpc/grpc-web/pull/360) Fix the import path for generated typescript files [@at-ishikawa](https://github.com/at-ishikawa)
## 1.0.2
## 1.0.1
- [#354](https://github.com/grpc/grpc-web/pull/354) [dts] Generate PromiseClient type definitions in d.ts file [@at-ishikawa](https://github.com/at-ishikawa)
- [#352](https://github.com/grpc/grpc-web/pull/352) Add a max grpc timeout to the echo example. [@mjduijn](https://github.com/mjduijn)
- [#348](https://github.com/grpc/grpc-web/pull/348) Fix output dts about 'repeated' for --grpc-web_out=import_style=common... [@rybbchao](https://github.com/rybbchao)
- [#345](https://github.com/grpc/grpc-web/pull/345) update typescript generation to work in strict mode [@henriiik](https://github.com/henriiik)
- [#330](https://github.com/grpc/grpc-web/pull/330) Use official rules_closure repository [@Yannic](https://github.com/Yannic)
## 1.0.0
- [#314](https://github.com/grpc/grpc-web/pull/314) Add a unit test for proto with no package
- [#313](https://github.com/grpc/grpc-web/pull/313) Show how deadline can be set
- [#311](https://github.com/grpc/grpc-web/pull/311) Document how to prevent Envoy to timeout streaming [@mitar](https://github.com/mitar)
- [#310](https://github.com/grpc/grpc-web/pull/310) Correctly generate code if package name is empty [@mitar](https://github.com/mitar)
- [#304](https://github.com/grpc/grpc-web/pull/304) Add a simple Hello World Guide
- [#303](https://github.com/grpc/grpc-web/pull/303) Error code should be number
- [#276](https://github.com/grpc/grpc-web/pull/276) Fix plugin compile error
- [#272](https://github.com/grpc/grpc-web/pull/272) Fix cpp warnings
## 0.4.0
- [#263](https://github.com/grpc/grpc-web/pull/263) Make "Quick" start quicker
- [#258](https://github.com/grpc/grpc-web/pull/258) Experimental Typescript support
- [#257](https://github.com/grpc/grpc-web/pull/257) Fix bug with button in example
## 0.3.0
- [#249](https://github.com/grpc/grpc-web/pull/249) Various fixes to codegen plugin
- [#247](https://github.com/grpc/grpc-web/pull/247) Add generated code unit test
- [#240](https://github.com/grpc/grpc-web/pull/240) webpack demo
- [#239](https://github.com/grpc/grpc-web/pull/239) Expose response metadata for unary calls
- [#219](https://github.com/grpc/grpc-web/pull/219) Add bazel rule closure_grpc_web_library [@Yannic](https://github.com/Yannic)
- [#217](https://github.com/grpc/grpc-web/pull/217) Added multiple proxies interoperability
## 0.2.0
- [#212](https://github.com/grpc/grpc-web/pull/212) Added commonjs-example Dockerfile
- [#211](https://github.com/grpc/grpc-web/pull/211) commonjs support with import_style option [@zaucy](https://github.com/zaucy)
- [#210](https://github.com/grpc/grpc-web/pull/210) grpcweb npm runtime module [@zaucy](https://github.com/zaucy)
- [#209](https://github.com/grpc/grpc-web/pull/209) Add bazel integration and tests
- [#206](https://github.com/grpc/grpc-web/pull/206) Surface underlying XHR errors better
- [#185](https://github.com/grpc/grpc-web/pull/185) Support for proto files without packages [@zaucy](https://github.com/zaucy)

View File

@ -3,6 +3,10 @@
We definitely welcome patches and contribution to gRPC-Web! Here is some guideline
and information about how to do so.
Please read the gRPC
organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md)
and [contribution guidelines](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md) before proceeding.
## Getting started
### Legal requirements

1
GOVERNANCE.md Normal file
View File

@ -0,0 +1 @@
This repository is governed by the gRPC organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md).

16
MAINTAINERS.md Normal file
View File

@ -0,0 +1,16 @@
This page lists all active maintainers of this repository. If you were a
maintainer and would like to add your name to the Emeritus list, please send us a
PR.
See [GOVERNANCE.md](https://github.com/grpc/grpc-community/blob/master/governance.md)
for governance guidelines and how to become a maintainer.
See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md)
for general contribution guidelines.
## Maintainers (in alphabetical order)
- [sampajano](https://github.com/sampajano), Google Inc.
- [wenbozhu](https://github.com/wenbozhu), Google Inc.
## Emeritus Maintainers (in alphabetical order)
- [fengli79](https://github.com/fengli79)
- [stanley-cheung](https://github.com/stanley-cheung)

24
MODULE.bazel Normal file
View File

@ -0,0 +1,24 @@
"""
A bazel module for the grpc-web project.
Visit https://grpc.io/ and https://github.com/grpc/grpc-web for
more information about the project.
"""
module(
name = "grpc-web",
version = "1.6.0",
compatibility_level = 1,
repo_name = "com_github_grpc_grpc_web",
)
bazel_dep(name = "protobuf", version = "27.1", repo_name = "com_google_protobuf")
bazel_dep(name = "grpc", version = "1.65.0", repo_name = "com_github_grpc_grpc")
bazel_dep(name = "rules_cc", version = "0.0.2")
bazel_dep(name = "rules_proto", version = "6.0.2")
# Needed to resolve https://github.com/bazelbuild/bazel-central-registry/issues/2538.
single_version_override(
module_name = "grpc-java",
version = "1.64.0",
)

8886
MODULE.bazel.lock Normal file

File diff suppressed because it is too large Load Diff

114
Makefile
View File

@ -1,119 +1,13 @@
OS := $(shell uname)
CC := g++
ROOT_DIR := $(shell pwd)
GRPC_GATEWAY_PROTOS := $(ROOT_DIR)/net/grpc/gateway/protos
PROTO_INC := $(ROOT_DIR)/third_party/grpc/third_party/protobuf/include
PROTO_SRC := $(ROOT_DIR)/third_party/grpc/third_party/protobuf/src
PROTO_LIB := $(PROTO_SRC)/.libs
PROTOC := $(PROTO_SRC)/protoc
GRPC_INC := $(ROOT_DIR)/third_party/grpc/include
GRPC_SRC := $(ROOT_DIR)/third_party/grpc
GRPC_LIB := $(ROOT_DIR)/third_party/grpc/libs/opt
all: clean package_static
protos:
cd "$(ROOT_DIR)" && LD_LIBRARY_PATH="$(PROTO_LIB):$(GRPC_LIB)" "$(PROTOC)" \
--proto_path="$(GRPC_GATEWAY_PROTOS)" \
--proto_path="$(PROTO_SRC)" "$(GRPC_GATEWAY_PROTOS)/pair.proto" \
--cpp_out="$(GRPC_GATEWAY_PROTOS)"
cd "$(ROOT_DIR)" && LD_LIBRARY_PATH="$(PROTO_LIB):$(GRPC_LIB)" "$(PROTOC)" \
--proto_path="$(GRPC_GATEWAY_PROTOS)" \
--proto_path="$(PROTO_SRC)" "$(GRPC_GATEWAY_PROTOS)/stream_body.proto" \
--cpp_out="$(GRPC_GATEWAY_PROTOS)"
NGINX_DIR := third_party/nginx
NGINX_LD_OPT := -L"$(PROTO_LIB)" -L"$(GRPC_LIB)" -lgrpc++ \
-lgrpc -lprotobuf -lpthread -ldl -lrt -lstdc++ -lm
ifeq ($(OS), Darwin)
NGINX_LD_OPT := -L"$(PROTO_LIB)" -L"$(GRPC_LIB)" -lgrpc++ \
-lgrpc -lprotobuf -lpthread -lstdc++ -lm
endif
NGINX_STATIC_LD_OPT := -L"$(PROTO_LIB)" -L"$(GRPC_LIB)" \
-l:libgrpc++.a -l:libgrpc.a -l:libprotobuf.a -lpthread -ldl \
-lrt -lstdc++ -lm
ifeq ($(OS), Darwin)
NGINX_STATIC_LD_OPT := $(NGINX_LD_OPT)
endif
nginx_config:
cd "$(NGINX_DIR)/src" && LD_LIBRARY_PATH="$(PROTO_LIB):$(GRPC_LIB)" \
auto/configure \
--with-http_ssl_module \
--with-http_v2_module \
--with-cc-opt="-I /usr/local/include -I $(ROOT_DIR) -I $(PROTO_INC) -I $(PROTO_SRC) \
-I $(GRPC_INC) -I $(GRPC_SRC)" \
--with-ld-opt="$(NGINX_LD_OPT)" \
--with-openssl="$(ROOT_DIR)/third_party/openssl" \
--add-module="$(ROOT_DIR)/net/grpc/gateway/nginx"
nginx_config_static:
cd "$(NGINX_DIR)/src" && LD_LIBRARY_PATH="$(PROTO_LIB):$(GRPC_LIB)" \
auto/configure \
--with-http_ssl_module \
--with-http_v2_module \
--with-cc-opt="-I /usr/local/include -I $(ROOT_DIR) -I $(PROTO_INC) -I $(PROTO_SRC) \
-I $(GRPC_INC) -I $(GRPC_SRC)" \
--with-ld-opt="$(NGINX_STATIC_LD_OPT)" \
--add-module="$(ROOT_DIR)/net/grpc/gateway/nginx"
nginx: protos nginx_config
cd "$(NGINX_DIR)/src" && make
nginx_static: protos nginx_config_static
cd "$(NGINX_DIR)/src" && make
package: nginx
mkdir -p "$(ROOT_DIR)"/gConnector/conf
cp "$(ROOT_DIR)"/third_party/nginx/src/conf/* "$(ROOT_DIR)"/gConnector/conf
cp "$(ROOT_DIR)"/net/grpc/gateway/nginx/package/nginx.conf \
"$(ROOT_DIR)"/gConnector/conf
cp "$(ROOT_DIR)"/net/grpc/gateway/nginx/package/nginx.sh \
"$(ROOT_DIR)"/gConnector
cp "$(ROOT_DIR)"/third_party/nginx/src/objs/nginx \
"$(ROOT_DIR)"/gConnector
cd "$(ROOT_DIR)" && zip -r gConnector.zip gConnector/*
package_static: nginx_static
mkdir -p "$(ROOT_DIR)"/gConnector_static/conf
cp "$(ROOT_DIR)"/third_party/nginx/src/conf/* \
"$(ROOT_DIR)"/gConnector_static/conf
cp "$(ROOT_DIR)"/net/grpc/gateway/nginx/package/nginx.conf \
"$(ROOT_DIR)"/gConnector_static/conf
cp "$(ROOT_DIR)"/net/grpc/gateway/nginx/package/nginx.sh \
"$(ROOT_DIR)"/gConnector_static
cp "$(ROOT_DIR)"/third_party/nginx/src/objs/nginx \
"$(ROOT_DIR)"/gConnector_static
cd "$(ROOT_DIR)" && zip -r gConnector_static.zip gConnector_static/*
all: clean
plugin:
cd "$(ROOT_DIR)"/javascript/net/grpc/web && make
cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make
install-plugin:
cd "$(ROOT_DIR)"/javascript/net/grpc/web && make install
example: plugin
cd "$(ROOT_DIR)"/net/grpc/gateway/examples/echo && make
standalone-proxy: package_static
cd "$(ROOT_DIR)"/net/grpc/gateway/examples/echo && make standalone-proxy
echo_server:
cd "$(ROOT_DIR)"/net/grpc/gateway/examples/echo && make echo_server
client: plugin
cd "$(ROOT_DIR)"/net/grpc/gateway/examples/echo && make client
install-example:
cd "$(ROOT_DIR)"/net/grpc/gateway/examples/echo && make install
cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make install
clean:
cd "$(ROOT_DIR)" && rm -rf objs gConnector gConnector_static \
third_party/nginx/src/objs third_party/openssl/.openssl
cd "$(ROOT_DIR)" && rm -f gConnector.zip gConnector_static.zip \
"$(GRPC_GATEWAY_PROTOS)"/*.pb.cc "$(GRPC_GATEWAY_PROTOS)"/*.pb.h \
third_party/nginx/src/Makefile
cd "$(ROOT_DIR)"/net/grpc/gateway/examples/echo && make clean
cd "$(ROOT_DIR)"/javascript/net/grpc/web && make clean
cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make clean
cd "$(ROOT_DIR)"

270
README.md
View File

@ -1,26 +1,27 @@
## Overview
# gRPC Web &middot; [![npm version](https://img.shields.io/npm/v/grpc-web.svg?style=flat)](https://www.npmjs.com/package/grpc-web)
gRPC-Web provides a Javascript library that lets browser clients access a gRPC
service. You can find out much more about gRPC in its own
[website](https://grpc.io).
A JavaScript implementation of [gRPC][] for browser clients. For more information,
including a **quick start**, see the [gRPC-web documentation][grpc-web-docs].
gRPC-Web is now Generally Available, and considered stable enough for production
use.
gRPC-web clients connect to gRPC services via a special proxy; by default,
gRPC-web uses [Envoy][].
gRPC-Web clients connect to gRPC services via a special gateway proxy: the
current version of the library uses [Envoy](https://www.envoyproxy.io/) by
default, in which gRPC-Web support is built-in.
In the future, we expect gRPC-web to be supported in language-specific web
frameworks for languages such as Python, Java, and Node. For details, see the
[roadmap](doc/roadmap.md).
In the future, we expect gRPC-Web to be supported in language-specific Web
frameworks, such as Python, Java, and Node. See the
[roadmap](https://github.com/grpc/grpc-web/blob/master/ROADMAP.md) doc.
## Streaming Support
gRPC-web currently supports 2 RPC modes:
- Unary RPCs ([example](#make-a-unary-rpc-call))
- Server-side Streaming RPCs ([example](#server-side-streaming)) (NOTE: Only when [`grpcwebtext`](#wire-format-mode) mode is used.)
Client-side and Bi-directional streaming is not currently supported (see [streaming roadmap](doc/streaming-roadmap.md)).
## Quick Start Guide: Hello World
## Quick Start
You can follow the [Hello World Guide][] to get started with gRPC-Web quickly.
Eager to get started? Try the [Hello World example][]. From this example, you'll
learn how to do the following:
From the guide, you will learn how to
- Define your service using protocol buffers
- Implement a simple gRPC Service using NodeJS
- Configure the Envoy proxy
@ -36,43 +37,84 @@ streaming example.
From the repo root directory:
```sh
$ docker-compose pull prereqs common node-server envoy commonjs-client
$ docker-compose up -d node-server envoy commonjs-client
$ docker-compose pull prereqs node-server envoy commonjs-client
$ docker-compose up node-server envoy commonjs-client
```
Open a browser tab, and go to:
```
http://localhost:8081/echotest.html
```
Open a browser tab, and visit http://localhost:8081/echotest.html.
To shutdown: `docker-compose down`.
## Runtime Library
The gRPC-Web runtime library is available at `npm`:
The gRPC-web runtime library is available at `npm`:
```sh
$ npm i grpc-web
```
## Code Generator Plugins
## Code Generator Plugin
### (Prerequisite) 1. Protobuf (`protoc`)
You can compile the `protoc-gen-grpc-web` protoc plugin from this repo:
If you don't already have [`protoc`](https://github.com/protocolbuffers/protobuf)
installed, download it first from [here](https://github.com/protocolbuffers/protobuf/releases) and install it on your PATH.
If you use Homebrew (on macOS), you could run:
```sh
$ sudo make install-plugin
brew install protobuf
```
If you don't already have `protoc` installed, you may have to do this first:
### (Prerequisite) 2. Protobuf-javascript (`protoc-gen-js`)
If you don't have [`protoc-gen-js`](https://github.com/protocolbuffers/protobuf-javascript) installed, download it from [protocolbuffers/protobuf-javascript](https://github.com/protocolbuffers/protobuf-javascript/releases) and install it on your PATH.
Or, use the [third-party](https://www.npmjs.com/package/protoc-gen-js) NPM installer:
```
npm install -g protoc-gen-js
```
### 3. Install gRPC-Web Code Generator
You can download the `protoc-gen-grpc-web` protoc plugin from our
[release](https://github.com/grpc/grpc-web/releases) page:
Make sure all executables are discoverable from your PATH.
For example, on MacOS, you can do:
```sh
$ ./scripts/init_submodules.sh
$ cd third_party/grpc/third_party/protobuf
$ ./autogen.sh && ./configure && make -j8 && sudo make install
sudo mv protoc-gen-grpc-web-1.5.0-darwin-aarch64 \
/usr/local/bin/protoc-gen-grpc-web
chmod +x /usr/local/bin/protoc-gen-grpc-web
```
### (Optional) 4. Verify Installations
You can optionally verify the plugins works follwoing our [Hello world example](https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld#generating-stubs):
```sh
cd net/grpc/gateway/examples/helloworld
protoc -I=. helloworld.proto \
--js_out=import_style=commonjs:. \
--grpc-web_out=import_style=commonjs,mode=grpcwebtext:.
```
After the command runs successfully, you should now see two new files generated
in the current directory. By running:
```
ls -1 *_pb.js
```
Installation is successful if you see the following 2 files:
- `helloworld_pb.js` # Generated by `protoc-gen-js` plugin
- `helloworld_grpc_web_pb.js` - Generated by gRPC-Web plugin
## Client Configuration Options
@ -80,15 +122,14 @@ Typically, you will run the following command to generate the proto messages
and the service client stub from your `.proto` definitions:
```sh
$ protoc -I=$DIR echo.proto \
--js_out=import_style=commonjs:$OUT_DIR \
--grpc-web_out=import_style=commonjs,mode=grpcwebtext:$OUT_DIR
protoc -I=$DIR echo.proto \
--js_out=import_style=commonjs:$OUT_DIR \
--grpc-web_out=import_style=commonjs,mode=grpcwebtext:$OUT_DIR
```
You can then use Browserify, Webpack, Closure Compiler, etc. to resolve imports
at compile time.
### Import Style
`import_style=closure`: The default generated code has
@ -103,15 +144,16 @@ also supported.
typings file will also be generated for the protobuf messages and service stub.
`import_style=typescript`: (Experimental) The service stub will be generated
in TypeScript.
in TypeScript. See **TypeScript Support** below for information on how to
generate TypeScript files.
**Note: `commonjs+dts` and `typescript` only works with `--grpc-web_out=` import style.**
> **Note:** The `commonjs+dts` and `typescript` styles are only supported by
`--grpc-web_out=import_style=...`, not by `--js_out=import_style=...`.
### Wire Format Mode
For more information about the gRPC-Web wire format, please see the
[specification](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2)
here.
For more information about the gRPC-web wire format, see the
[specification](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2).
`mode=grpcwebtext`: The default generated code sends the payload in the
`grpc-web-text` format.
@ -124,21 +166,19 @@ here.
- `Content-type: application/grpc-web+proto`
- Payload are in the binary protobuf format.
- Only unary calls are supported for now.
- Only unary calls are supported.
## How It Works
Let's take a look at how gRPC-Web works with a simple example. You can find out
Let's take a look at how gRPC-web works with a simple example. You can find out
how to build, run and explore the example yourself in
[Build and Run the Echo Example](net/grpc/gateway/examples/echo).
### 1. Define your service
The first step when creating any gRPC service is to define it. Like all gRPC
services, gRPC-Web uses
[protocol buffers](https://developers.google.com/protocol-buffers/) to define
services, gRPC-web uses
[protocol buffers](https://developers.google.com/protocol-buffers) to define
its RPC service methods and their message request and response types.
```protobuf
@ -163,31 +203,30 @@ gateway proxy that allows the client to connect to the server. Our example
builds a simple Node gRPC backend server and the Envoy proxy.
For the Echo service: see the
[service implementations](https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/echo/node-server/server.js).
[service implementations](net/grpc/gateway/examples/echo/node-server/server.js).
For the Envoy proxy: see the
[config yaml file](https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/echo/envoy.yaml).
[config yaml file](net/grpc/gateway/examples/echo/envoy.yaml).
### 3. Write your JS client
Once the server and gateway are up and running, you can start making gRPC calls
from the browser!
Create your client
Create your client:
```js
var echoService = new proto.mypackage.EchoServiceClient(
'http://localhost:8080');
```
Make a unary RPC call
#### Make a unary RPC call:
```js
var request = new proto.mypackage.EchoRequest();
request.setMessage(msg);
var metadata = {'custom-header-1': 'value1'};
var call = echoService.echo(request, metadata, function(err, response) {
echoService.echo(request, metadata, function(err, response) {
if (err) {
console.log(err.code);
console.log(err.message);
@ -195,14 +234,9 @@ var call = echoService.echo(request, metadata, function(err, response) {
console.log(response.getMessage());
}
});
call.on('status', function(status) {
console.log(status.code);
console.log(status.details);
console.log(status.metadata);
});
```
Server-side streaming is supported!
#### Server-side streaming:
```js
var stream = echoService.serverStreamingEcho(streamRequest, metadata);
@ -217,10 +251,13 @@ stream.on('status', function(status) {
stream.on('end', function(end) {
// stream end signal
});
// to close the stream
stream.cancel()
```
You can find a more in-depth tutorial from
[this page](https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/echo/tutorial.md).
For an in-depth tutorial, see [this
page](net/grpc/gateway/examples/echo/tutorial.md).
## Setting Deadline
@ -231,7 +268,7 @@ should be a Unix timestamp, in milliseconds.
var deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);
client.sayHelloAfterDelay(request, {deadline: deadline.getTime()},
client.sayHelloAfterDelay(request, {deadline: deadline.getTime().toString()},
(err, response) => {
// err will be populated if the RPC exceeds the deadline
...
@ -249,9 +286,37 @@ either:
- `import_style=commonjs+dts`: existing CommonJS style stub + `.d.ts` typings
- `import_style=typescript`: full TypeScript output
Do *not* use `import_style=typescript` for `--js_out`, it will silently be
ignored. Instead you should use `--js_out=import_style=commonjs`, or
`--js_out=import_style=commonjs,binary` if you are using `mode=grpcweb`. The
`--js_out` plugin will generate JavaScript code (`echo_pb.js`), and the
`-grpc-web_out` plugin will generate a TypeScript definition file for it
(`echo_pb.d.ts`). This is a temporary hack until the `--js_out` supports
TypeScript itself.
For example, this is the command you should use to generate TypeScript code
using the binary wire format
```sh
protoc -I=$DIR echo.proto \
--js_out=import_style=commonjs,binary:$OUT_DIR \
--grpc-web_out=import_style=typescript,mode=grpcweb:$OUT_DIR
```
It will generate the following files:
* `EchoServiceClientPb.ts` - Generated by `--grpc-web_out`, contains the
TypeScript gRPC-web code.
* `echo_pb.js` - Generated by `--js_out`, contains the JavaScript Protobuf
code.
* `echo_pb.d.ts` - Generated by `--grpc-web_out`, contains TypeScript
definitions for `echo_pb.js`.
### Using Callbacks
```ts
import * as grpcWeb from 'grpc-web';
import {EchoServiceClient} from './echo_grpc_web_pb';
import {EchoServiceClient} from './EchoServiceClientPb';
import {EchoRequest, EchoResponse} from './echo_pb';
const echoService = new EchoServiceClient('http://localhost:8080', null, null);
@ -260,7 +325,7 @@ const request = new EchoRequest();
request.setMessage('Hello World!');
const call = echoService.echo(request, {'custom-header-1': 'value1'},
(err: grpcWeb.Error, response: EchoResponse) => {
(err: grpcWeb.RpcError, response: EchoResponse) => {
console.log(response.getMessage());
});
call.on('status', (status: grpcWeb.Status) => {
@ -268,39 +333,72 @@ call.on('status', (status: grpcWeb.Status) => {
});
```
See a full TypeScript example
[here](https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/echo/ts-example/client.ts).
(See [here](https://github.com/grpc/grpc-web/blob/4d7dc44c2df522376394d3e3315b7ab0e010b0c5/packages/grpc-web/index.d.ts#L29-L39) full list of possible `.on(...)` callbacks)
## Proxy Interoperability
### (Option) Using Promises (Limited features)
Multiple proxies supports the gRPC-Web protocol. Currently, the default proxy
is [Envoy](https://www.envoyproxy.io), which supports gRPC-Web out of the box.
> **NOTE:** It is not possible to access the `.on(...)` callbacks (e.g. for `metadata` and `status`) when Promise is used.
```sh
$ docker-compose up -d node-server envoy commonjs-client
```ts
// Create a Promise client instead
const echoService = new EchoServicePromiseClient('http://localhost:8080', null, null);
... (same as above)
this.echoService.echo(request, {'custom-header-1': 'value1'})
.then((response: EchoResponse) => {
console.log(`Received response: ${response.getMessage()}`);
}).catch((err: grpcWeb.RpcError) => {
console.log(`Received error: ${err.code}, ${err.message}`);
});
```
An alternative is to build Nginx that comes with this repository.
For the full TypeScript example, see
[ts-example/client.ts](net/grpc/gateway/examples/echo/ts-example/client.ts) with the [instructions](net/grpc/gateway/examples/echo/ts-example) to run.
```sh
$ docker-compose up -d node-server nginx commonjs-client
```
## Custom Interceptors
You can also try this
[gRPC-Web Go Proxy](https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy).
Custom interceptors can be implemented and chained, which could be useful for features like auth, retries, etc.
```sh
$ docker-compose up -d node-server grpcwebproxy binary-client
```
There are 2 types of interceptors ([interfaces](https://github.com/grpc/grpc-web/blob/3cd7e0d43493d4694fed78400e4ad78031d70c09/packages/grpc-web/index.d.ts#L55-L65)):
## Acknowledgement
- `UnaryInterceptor` ([doc](https://grpc.io/blog/grpc-web-interceptor/#stream-interceptor-example), [example](https://github.com/grpc/grpc-web/blob/master/packages/grpc-web/test/tsc-tests/client04.ts)) - Intercept Unary RPCs; can only be used with Promise clients.
- `StreamInterceptor` ([doc](https://grpc.io/blog/grpc-web-interceptor/#stream-interceptor-example), [example](https://github.com/grpc/grpc-web/blob/master/packages/grpc-web/test/tsc-tests/client03.ts)) - More versatile; can be used with regular clients.
Big thanks to the following contributors for making significant contributions to
this project!
* [zaucy](https://github.com/zaucy): NPM package, CommonJS
* [yannic](https://github.com/yannic): Bazel
* [mitar](https://github.com/mitar): Codegen Plugin
For more details, see [this blog post](https://grpc.io/blog/grpc-web-interceptor/).
[Hello World Guide]:https://github.com/grpc/grpc-web/blob/master/net/grpc/gateway/examples/helloworld/
## Ecosystem
### Proxy Interoperability
Multiple proxies support the gRPC-web protocol.
1. The current **default proxy** is [Envoy][], which supports gRPC-web out of the box.
```sh
$ docker-compose up -d node-server envoy commonjs-client
```
2. You can also try the [gRPC-web Go proxy][].
```sh
$ docker-compose up -d node-server grpcwebproxy binary-client
```
3. Apache [APISIX](https://apisix.apache.org/) has also added grpc-web support, and more details can be found [here](https://apisix.apache.org/blog/2022/01/25/apisix-grpc-web-integration/).
4. [Nginx](https://www.nginx.com/) has a grpc-web module ([doc](https://nginx.org/en/docs/http/ngx_http_grpc_module.html), [announcement](https://www.nginx.com/blog/nginx-1-13-10-grpc/))), and seems to work with simple configs, according to user [feedback](https://github.com/grpc/grpc-web/discussions/1322).
### Server Frameworks with gRPC-Web support
- [Armeria (JVM)](https://armeria.dev/docs/server-grpc/#grpc-web)
- [Tonic (Rust)](https://docs.rs/tonic-web/latest/tonic_web/)
### Web Frameworks Compatibility
- **Vite** - See this [demo app](https://github.com/a2not/vite-grpc-web), as well as this [comment](https://github.com/grpc/grpc-web/issues/1242#issuecomment-1816249928).
[Envoy]: https://www.envoyproxy.io
[gRPC]: https://grpc.io
[grpc-web-docs]: https://grpc.io/docs/languages/web
[gRPC-web Go Proxy]: https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy
[Hello World example]: net/grpc/gateway/examples/helloworld

View File

@ -1,113 +0,0 @@
# Overview
The purpose of this document is to list all the features that we believe are
useful for gRPC users.
We would like your feedback! Please tell us which features you would most want
to see, so that we can prioritize the work to either publish Google's existing
solutions or develop some of the features directly in the open-source repo. For
the latter case, please mention if you are interested in contributing to any of
the road-map features :)
[Survey link](https://docs.google.com/forms/d/1NjWpyRviohn5jaPntosBHXRXZYkh_Ffi4GxJZFibylM/edit)
# Background
gRPC-Web has been developed internally at Google as part of the future front-end
stacks for Google's Web applications and cloud services. Over time we plan to
open-source and publish most of the features and make them available to gRPC
users.
Like everywhere, Web platforms and technologies are constantly evolving, often
with many inter-dependent ecosystems. As much as we like to open-source
everything, we also need keep the balance between creating a reusable and stable
open-source solution and meeting those requirements unique to Google's Web
ecosystems or their applications (such as search).
# Roadmap Features (in no particular order)
## Non-Binary Message Encoding
The binary protobuf encoding format is not most CPU efficient for browser
clients. Furthermore, the generated code size increases as the total protobuf
definition increases.
For Google's Web applications (e.g. gmail), we use a JSON like format which is
comparable to JSON in efficiency but also very compact in both the message size
and code size.
## Streaming-Friendly Transport Implementation
Currently the gRPC-Web client library uses XHR to ensure cross-browser support
and to support platforms such as React-Native.
We do plan to add fetch/streams support at some point, which is more efficient
for binary streams and incurs less memory overhead on the client-side.
However, fetch still has certain gaps compared to XHR, most notably the lack of
cancellation support. Progressing events, I/O event throttling are other
concerns.
## Bidi Support
As WebSocket over HTTP/2 becomes more available, we may add bidi support over
WebSockets.
At the same time, please tell us your exact use case, and maybe explain why
server-streaming is insufficient.
## Security
We plan to publish a comprehensive guideline doc on how to create secure Web
applications.
Native support such as XSRF, XSS prevention may also be added to the gRPC-Web
protocol.
## Compression
Do you need request compression? Brotli?
## CORS
We plan to support CORS preflight as specified in
[PROTOCOL-WEB.md](https://github.com/grpc/grpc-web/blob/master/PROTOCOL-WEB.md).
## Local Proxies
In-process proxies will eliminate the need to deploy an extra proxy such as
Nginx.
We have plans to add proxy support in Python, Java, Node, C++ etc. Let us know
if you are interested in implementing any language-specific in-process
gRPC-Web proxy.
To minimize maintenance overhead, we don't have any plan to add gRPC-Web support
to any new HTTP reverse proxies other than Nginx and Envoy.
## Web Framework Integration
This is to provide first-class support for gRPC API and gRPC-Web in popular Web
frameworks such as Angular.
Note Dart gRPC will be using gRPC-Web as the underlying implementation on the
Dart Web platform.
## TypeScript Support
We now have experimental TypeScript Support! See the main README for more
information.
## Non-Closure compiler support
With the addition of CommonJS style imports, gRPC-Web client stubs can now be
compiled with various tools such as Browserify, Webpack, etc. Let us know
what else we should try!
## Web UI Support
This allows the user to construct and submit a gRPC request directly using the
browser.
We need define a standard look & feel for creating and rendering nested protobuf
messages.

3
SECURITY.md Normal file
View File

@ -0,0 +1,3 @@
# Security Policy
For information on gRPC Security Policy and reporting potentional security issues, please see [gRPC CVE Process](https://github.com/grpc/proposal/blob/master/P4-grpc-cve-process.md).

View File

@ -1,14 +0,0 @@
workspace(name = "com_github_grpc_grpc_web")
http_archive(
name = "io_bazel_rules_closure",
sha256 = "4463509e8f86c9b7726b6b7c751132f0ca14f907cba00759b21f8577c2dcf710",
strip_prefix = "rules_closure-acad96981d76b60844bf815d03043619714839ad",
urls = [
"https://github.com/bazelbuild/rules_closure/archive/acad96981d76b60844bf815d03043619714839ad.zip",
],
)
load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories")
closure_repositories()

View File

View File

@ -1,197 +0,0 @@
# This rule was inspired by rules_closure`s implementation of
# |closure_proto_library|, licensed under Apache 2.
# https://github.com/bazelbuild/rules_closure/blob/3555e5ba61fdcc17157dd833eaf7d19b313b1bca/closure/protobuf/closure_proto_library.bzl
load(
"@io_bazel_rules_closure//closure/compiler:closure_js_library.bzl",
"closure_js_library_impl",
)
load(
"@io_bazel_rules_closure//closure/private:defs.bzl",
"CLOSURE_WORKER_ATTR",
"CLOSURE_LIBRARY_BASE_ATTR",
"unfurl",
)
load(
"@io_bazel_rules_closure//closure/protobuf:closure_proto_library.bzl",
"closure_proto_aspect",
)
# This was borrowed from Rules Go, licensed under Apache 2.
# https://github.com/bazelbuild/rules_go/blob/67f44035d84a352cffb9465159e199066ecb814c/proto/compiler.bzl#L72
def _proto_path(proto):
path = proto.path
root = proto.root.path
ws = proto.owner.workspace_root
if path.startswith(root):
path = path[len(root):]
if path.startswith("/"):
path = path[1:]
if path.startswith(ws):
path = path[len(ws):]
if path.startswith("/"):
path = path[1:]
return path
def _proto_include_path(proto):
path = proto.path[:-len(_proto_path(proto))]
if not path:
return "."
if path.endswith("/"):
path = path[:-1]
return path
def _proto_include_paths(protos):
return depset([_proto_include_path(proto) for proto in protos])
def _generate_closure_grpc_web_src_progress_message(name):
# TODO(yannic): Add a better message?
return "Generating GRPC Web %s" % name
def _generate_closure_grpc_web_srcs(
actions, protoc, protoc_gen_grpc_web, import_style, mode,
sources, transitive_sources):
all_sources = [src for src in sources] + [src for src in transitive_sources]
proto_include_paths = [
"-I%s" % p for p in _proto_include_paths(
[f for f in all_sources])
]
grpc_web_out_common_options = ",".join([
"import_style={}".format(import_style),
"mode={}".format(mode),
])
files = []
for src in sources:
name = "{}.grpc.js".format(
".".join(src.path.split("/")[-1].split(".")[:-1]))
js = actions.declare_file(name)
files.append(js)
args = proto_include_paths + [
"--plugin=protoc-gen-grpc-web={}".format(protoc_gen_grpc_web.path),
"--grpc-web_out={options},out={out_file}:{path}".format(
options = grpc_web_out_common_options,
out_file = name,
path = js.path[:js.path.rfind("/")],
),
src.path
]
actions.run(
inputs = [protoc_gen_grpc_web] + all_sources,
outputs = [js],
executable = protoc,
arguments = args,
progress_message =
_generate_closure_grpc_web_src_progress_message(name),
)
return files
_error_multiple_deps = "".join([
"'deps' attribute must contain exactly one label ",
"(we didn't name it 'dep' for consistency). ",
"We may revisit this restriction later.",
])
def _closure_grpc_web_library_impl(ctx):
if len(ctx.attr.deps) > 1:
# TODO(yannic): Revisit this restriction.
fail(_error_multiple_deps, "deps");
dep = ctx.attr.deps[0]
srcs = _generate_closure_grpc_web_srcs(
actions = ctx.actions,
protoc = ctx.executable._protoc,
protoc_gen_grpc_web = ctx.executable._protoc_gen_grpc_web,
import_style = ctx.attr.import_style,
mode = ctx.attr.mode,
sources = dep.proto.direct_sources,
transitive_sources = dep.proto.transitive_imports,
)
deps = unfurl(ctx.attr.deps, provider = "closure_js_library")
deps += [
ctx.attr._grpc_web_abstractclientbase,
ctx.attr._grpc_web_clientreadablestream,
ctx.attr._grpc_web_error,
ctx.attr._grpc_web_grpcwebclientbase
]
suppress = [
"misplacedTypeAnnotation",
"unusedPrivateMembers",
"strictDependencies",
]
library = closure_js_library_impl(
actions = ctx.actions,
label = ctx.label,
workspace_name = ctx.workspace_name,
srcs = srcs,
deps = deps,
testonly = ctx.attr.testonly,
suppress = suppress,
lenient = False,
closure_library_base = ctx.files._closure_library_base,
_ClosureWorker = ctx.executable._ClosureWorker,
)
return struct(
exports = library.exports,
closure_js_library = library.closure_js_library,
# The usual suspects are exported as runfiles, in addition to raw source.
runfiles = ctx.runfiles(files = srcs),
)
closure_grpc_web_library = rule(
implementation = _closure_grpc_web_library_impl,
attrs = {
"deps": attr.label_list(
mandatory = True,
providers = ["proto", "closure_js_library"],
# The files generated by this aspect are required dependencies.
aspects = [closure_proto_aspect],
),
"import_style": attr.string(
default = "closure",
values = ["closure"],
),
"mode": attr.string(
default = "grpcwebtext",
values = ["grpcwebtext", "grpcweb"],
),
# Required for closure_js_library_impl
"_ClosureWorker": CLOSURE_WORKER_ATTR,
"_closure_library_base": CLOSURE_LIBRARY_BASE_ATTR,
# internal only
"_protoc": attr.label(
default = Label("@com_google_protobuf//:protoc"),
executable = True,
cfg = "host",
),
"_protoc_gen_grpc_web": attr.label(
default = Label("//javascript/net/grpc/web:protoc-gen-grpc-web"),
executable = True,
cfg = "host",
),
"_grpc_web_abstractclientbase": attr.label(
default = Label("//javascript/net/grpc/web:abstractclientbase"),
),
"_grpc_web_clientreadablestream": attr.label(
default = Label("//javascript/net/grpc/web:clientreadablestream"),
),
"_grpc_web_error": attr.label(
default = Label("//javascript/net/grpc/web:error"),
),
"_grpc_web_grpcwebclientbase": attr.label(
default = Label("//javascript/net/grpc/web:grpcwebclientbase"),
),
},
)

View File

@ -1,20 +1,23 @@
# gRPC-Web features for browser (HTML) clients
Due to browser limitation, gRPC-Web supports a different transport
Due to browser limitation, gRPC-Web uses a different transport
than the [HTTP/2 based gRPC protocol](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md).
The difference between the gRPC-Web
protocol and the HTTP/2 based gRPC protocol is specified in the core gRPC repo as [PROTOCOL-WEB](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md).
In addition to the wire-transport spec, gRPC-Web also supports features that are unique to browser (HTML) clients.
This document is the official spec for those features. As the Web platform evolves,
This document serves as the official spec for those features. As the Web platform evolves,
we expect some of those features will evolve too or become deprecated.
On the server-side, [Envoy](https://www.envoyproxy.io/) is the official proxy with built-in gRPC-Web support. New features will be implemented in Envoy first. For [in-process gRPC-Web support](https://github.com/grpc/grpc-web/blob/master/doc/in-process-proxy.md), we recommend that the gRPC-Web module implement only a minimum set of features, e.g. to enable local development. Those features are identified as mandatory features in this doc.
# CORS support
* Should follow the [CORS spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Server-Side_Access_Control)
* Should follow the [CORS spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Server-Side_Access_Control) (Mandatory)
* Access-Control-Allow-Credentials to allow Authorization headers
* Access-Control-Allow-Methods to allow POST and (preflight) OPTIONS only
* Access-Control-Allow-Headers to whatever the preflight request carries
* Access-Control-Expose-Headers to allow Javascript access to `grpc-status,grpc-message` headers.
* The client library is expected to support header overwrites to avoid preflight
* https://github.com/whatwg/fetch/issues/210
* CSP support to be specified
@ -23,6 +26,12 @@ we expect some of those features will evolve too or become deprecated.
A grpc-web gateway is recommended to overwrite the default 200 status code and map any gateway-generated or server-generated error status to standard HTTP status codes (such as 503) when it is possible. This will help with debugging and may also improve security protection for web apps.
# URL query params
To enable query-param based routing rules in reverse proxies and to avoid CORS preflight, a grpc-web client may "advertise" certain request data or metadata as query params. The grpc-web proxy module should remove the query params before the request is sent to the downstream gRPC server.
The actual data in query params is not interpreted by grpc-web libraries. Standard URL encoding rules shoud be followed to encode those query params.
# Security
* XSRF, XSS policy to be published

30
doc/in-process-proxy.md Normal file
View File

@ -0,0 +1,30 @@
# Overview
In-process proxies allow a browser client to talk to a gRPC server directly without relying on any intermediary process
such as an Envoy proxy. This document provides a high-level design guidelines on how we expect such a "proxy" to work.
# The choice of HTTP stack
We strongly recommend that the gRPC-Web module use the default HTTP stack provided by the language platform, or in the case of Java,
the standard Java Servlet framework. This is to ensure maximum portability and to ease integration between gRPC-Web and existing Web
frameworks.
The actual HTTP version that the HTTP stack supports may include both HTTP/1.1 and HTTP/2. In the runtime, it's up to the user-agent and
intermediaries to negotiate the HTTP version, which is transparent to the gRPC-Web module.
# Request translation
For most languages, the gRPC-Web module will handle the gRPC-Web request, perform the translation, and then proxy the request using a gRPC client
to the gRPC server via a local socket. The gRPC-Web support is fully transparent to the gRPC server.
For some languages, such as Swift, .NET, if the gRPC server implementation uses the same HTTP stack that the gRPC-Web module uses, then gRPC-Web may be supported
directly as part of the gRPC server implementation. The added complexity to the gRPC implementation itself is still a concern.
# HTTP port
We expect that gRPC-Web requests are handled on a separate port. If the HTTP stack supports both HTTP/2 and HTTP/1.1, port sharing could be supported.
However, since CORS is a mandatory feature for gRPC-Web proxies, port sharing should be optional for in-process proxies.
# Core features
The gRPC-Web module should implement only the [core gRPC-Web features](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md) and leave to the HTTP/Web stack provided by the language platform to handle [Web-framework-level features](https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md) such as XSRF, CORS policies. Some of those features may be incompatible with what Envoy supports for gRPC-Web.

View File

@ -0,0 +1,83 @@
gRPC-Web Interop Tests
======================
This document describes the set of tests any gRPC-Web clients or proxies need
to implement. The proto definition for the messages and RPCs we are using for
the tests can be found
[here](https://github.com/grpc/grpc/blob/master/src/proto/grpc/testing/test.proto).
The canonical set of interop tests was defined in the main
[grpc/grpc repo](https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md).
Here in gRPC-Web, we will only implement a subset of tests that's relevant to
gRPC-Web. For example, we will not be implementing any tests involving
client-streaming or bidi-streaming for now. On the other hand, there are
gRPC-Web specific tests that we will add here.
```
gRPC-Web Client <--> Proxy <--> gRPC Service
```
The idea is that we should be able to swap out any of the 3 components above
and all the interop tests should still pass.
This repository will provide a canonical implementation of the interop test
suite using the Javascript client, Envoy and a gRPC service implemented in
Node.
For any new gRPC-Web client implementation, you need to swap out the JS
client and make sure all tests still pass.
For any in-process proxies implementation, you need to swap out the proxy
and the service as a unit and make sure the standard JS client will still
pass the tests.
List of Tests
-------------
| Test Name | grpc-web-text Mode | grpc-web Binary mode |
| --------- |:------------------:|:--------------------:|
| empty_unary | &#10003; | &#10003; |
| cacheable_unary | TBD | TBD |
| large_unary | &#10003; | &#10003; |
| client_compressed_unary | &#10003; | &#10003; |
| server_compressed_unary | &#10003; | &#10003; |
| client_streaming | &#10007; | &#10007; |
| client_compressed_streaming | &#10007; | &#10007; |
| server_streaming | &#10003; | &#10007; |
| server_compressed_streaming | &#10003; | &#10007; |
| ping_pong | &#10007; | &#10007; |
| empty_stream | &#10007; | &#10007; |
| compute_engine_creds | TBD | TBD |
| jwt_token_creds | TBD | TBD |
| oauth2_auth_token | TBD | TBD |
| per_rpc_creds | TBD | TBD |
| google_default_credentials | TBD | TBD |
| compute_engine_channel_credentials | TBD | TBD |
| custom_metadata * | &#10003; | &#10003; |
| status_code_and_message * | &#10003; | &#10003; |
| special_status_message | &#10003; | &#10003; |
| unimplemented_method | &#10003; | &#10003; |
| unimplemented_service | &#10003; | &#10003; |
| cancel_after_begin | &#10007; | &#10007; |
| cancel_after_first_response | &#10007; | &#10007; |
| timeout_on_sleeping_server | &#10007; | &#10007; |
\* only need to implement the UnaryCall RPC
gRPC-Web specific considerations
--------------------------------
### Text vs Binary mode
As mentioned in the table above, client needs to be tested in both the text
format `application/grpc-web-text` and the binary mode
`application/grpc-web+proto`. The latter we don't need to test any streaming
methods.
### CORS and other web specific scenarios
We may add specific tests to account for web-related scenarios like CORS
handling, etc. Mostly these are to test the connection between the browser
client and the proxy.

72
doc/roadmap.md Normal file
View File

@ -0,0 +1,72 @@
# gRPC-Web Roadmap
The purpose of this document is to collect all the features that we believe are
useful for gRPC users.
## Background
gRPC-Web has been developed internally at Google as part of the front-end
stacks for Google's Web applications and cloud services. Over time we plan to
open-source and publish most of the features and make them available to open-source
users.
Like everywhere, Web platforms and technologies are constantly evolving, often
with many inter-dependent ecosystems. As much as we like to open-source
everything, we also need keep the balance between creating a reusable and stable
open-source solution and meeting those requirements unique to Google's Web applications
(such as search).
## Roadmap Features
> NOTE: Due to the status of two of gRPC-Webs core dependencies — [Google
Closure](https://github.com/google/closure-library/issues/1214), which has been
archived, and [Protobuf
JavaScript](https://github.com/protocolbuffers/protobuf-javascript?tab=readme-ov-file#project-status),
which is receiving only minimal updates — the gRPC-Web project is no longer able
to deliver new, modern solutions for the open source community. As a result, we
do not plan to be adding new features going forward.
>
> We recommend you to use [gRPC-Gateway](https://github.com/grpc-ecosystem/grpc-gateway) as an alternative.
### TypeScript Codebase
Migrate the codebase to TypeScript and update the related toolchains (incl. remove
dependency on `closure-compiler`). Enhance overall TypeScript support.
### Streaming Support
Enhance Fetch/streams support (e.g. cancellation support) and improve runtime
support, including service workers.
See streaming roadmap [here](streaming-roadmap.md).
### Non-Binary Message Encoding
The binary protobuf encoding format is not most CPU efficient for browser
clients. Furthermore, the generated code size increases as the total protobuf
definition increases.
For Google's Web applications (e.g. gmail), we use a JSON like format which is
comparable to JSON in efficiency but also very compact in both the message size
and code size.
### Security
We plan to publish a comprehensive guideline doc on how to create secure Web
applications.
Native support such as XSRF, XSS prevention may also be added to the gRPC-Web
protocol.
### Web Framework Integration
This is to provide first-class support for gRPC API and gRPC-Web in popular Web
frameworks such as Angular.
Note: Dart gRPC will use gRPC-Web as the underlying implementation on the
Dart Web platform.
### Non-Closure compiler support
With the addition of CommonJS style imports, gRPC-Web client stubs can now be
compiled with various tools such as Browserify, Webpack, etc. Let us know
what else we should try!

38
doc/streaming-roadmap.md Normal file
View File

@ -0,0 +1,38 @@
# Streaming Roadmap
This document describes the road-map for gRPC-Web to support different streaming features.
* Server-streaming
* Client-streaming and half-duplex streaming
## Server-streaming
We will keep improving server-streaming in the following areas:
* Fetch cancellation support - 2024
* Performance improvements and whatwg Fetch/streams support, including service workers - 2024
* Finalizing keep-alive support (via Envoy) - 2024+
* Addressing runtime behavior gaps between Fetch and XHR - 2024+
## Client-streaming and half-duplex streaming
We dont plan to support client-streaming via Fetch/upload-streams (See [Appendix](#chrome-origin-trial-on-upload-streaming) on backgrounds on the Chrome Origin Trial). As a result, half-duplex bidi streaming wont be supported via Fetch/streams either.
Client-streaming and half-duplex bidi streaming will be addressed when Full-duplex streaming is supported via WebTransport (see below).
## Full-duplex streaming
Not planned.
## Issues with WebSockets
We have no plan to support full-duplex streaming over WebSockets (over TCP or HTTP/2). We will not publish any experimental spec for gRPC over WebSockets either.
The main issue with WebSockets is its incompatibility with HTTP, i.e. the ubiquitous Web infrastructure. This means HTTP fallback is always needed. Recent IETF proposal to tunnel WebSockets over HTTP/2 is not widely implemented either.
## Appendix
### Chrome Origin Trial on `upload-streaming`
We worked on a Chrome [Origin Trial](https://developers.chrome.com/origintrials/#/view_trial/3524066708417413121)
to finalize the fetch/upload stream API spec (whatwg). One of the pending issues that blocks the final spec is to decide whether it is safe to enable
upload-streaming over HTTP/1.1. We believe that upload-streaming should be enabled for both HTTP/2 and HTTP/1.1. Specifically for gRPC-Web, the server can't control the client deployment. As a result, if upload-streaming is only enabled over HTTP/2, a gRPC service will have to implement a non-streaming method
as a fallback for each client-streaming method.

View File

@ -5,19 +5,12 @@ services:
context: ./
dockerfile: ./net/grpc/gateway/docker/prereqs/Dockerfile
image: grpcweb/prereqs
common:
build:
context: ./
dockerfile: ./net/grpc/gateway/docker/common/Dockerfile
depends_on:
- prereqs
image: grpcweb/common
echo-server:
build:
context: ./
dockerfile: ./net/grpc/gateway/docker/echo_server/Dockerfile
depends_on:
- common
- prereqs
image: grpcweb/echo-server
ports:
- "9090:9090"
@ -26,10 +19,17 @@ services:
context: ./
dockerfile: ./net/grpc/gateway/docker/node_server/Dockerfile
depends_on:
- common
- prereqs
image: grpcweb/node-server
ports:
- "9090:9090"
node-interop-server:
build:
context: ./
dockerfile: ./net/grpc/gateway/docker/node_interop_server/Dockerfile
image: grpcweb/node-interop-server
ports:
- "7074:7074"
envoy:
build:
context: ./
@ -39,17 +39,6 @@ services:
- "8080:8080"
links:
- node-server
nginx:
build:
context: ./
dockerfile: ./net/grpc/gateway/docker/nginx/Dockerfile
depends_on:
- common
image: grpcweb/nginx
ports:
- "8080:8080"
links:
- node-server
grpcwebproxy:
build:
context: ./
@ -64,7 +53,7 @@ services:
context: ./
dockerfile: ./net/grpc/gateway/docker/commonjs_client/Dockerfile
depends_on:
- common
- prereqs
image: grpcweb/commonjs-client
ports:
- "8081:8081"
@ -73,7 +62,7 @@ services:
context: ./
dockerfile: ./net/grpc/gateway/docker/closure_client/Dockerfile
depends_on:
- common
- prereqs
image: grpcweb/closure-client
ports:
- "8081:8081"
@ -82,7 +71,7 @@ services:
context: ./
dockerfile: ./net/grpc/gateway/docker/ts_client/Dockerfile
depends_on:
- common
- prereqs
image: grpcweb/ts-client
ports:
- "8081:8081"
@ -91,7 +80,28 @@ services:
context: ./
dockerfile: ./net/grpc/gateway/docker/binary_client/Dockerfile
depends_on:
- common
- prereqs
image: grpcweb/binary-client
ports:
- "8081:8081"
interop-client:
build:
context: ./
dockerfile: ./net/grpc/gateway/docker/interop_client/Dockerfile
depends_on:
- prereqs
image: grpcweb/interop-client
ports:
- "8081:8081"
protoc-plugin:
build:
context: ./
dockerfile: ./net/grpc/gateway/docker/protoc_plugin/Dockerfile
depends_on:
- prereqs
image: grpcweb/protoc-plugin
jsunit-test:
build:
context: ./
dockerfile: ./packages/grpc-web/docker/jsunit-test/Dockerfile
image: grpcweb/jsunit-test

View File

@ -1,163 +0,0 @@
package(default_visibility = ["//visibility:public"])
load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_binary")
load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_library")
load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_test")
cc_binary(
name = "protoc-gen-grpc-web",
srcs = [
"grpc_generator.cc",
],
deps = [
"@com_google_protobuf//:protoc_lib"
],
)
closure_js_library(
name = "abstractclientbase",
srcs = [
"abstractclientbase.js",
],
deps = [
":clientreadablestream",
":error",
],
)
closure_js_library(
name = "clientreadablestream",
srcs = [
"clientreadablestream.js",
],
)
closure_js_library(
name = "error",
srcs = [
"error.js",
],
)
closure_js_library(
name = "generictransportinterface",
srcs = [
"generictransportinterface.js",
],
deps = [
"@io_bazel_rules_closure//closure/library/net/streams:nodereadablestream",
"@io_bazel_rules_closure//closure/library/net:xhrio",
],
)
closure_js_library(
name = "grpcwebclientbase",
srcs = [
"grpcwebclientbase.js",
],
suppress = [
"checkTypes",
"reportUnknownTypes",
],
deps = [
":abstractclientbase",
":grpcwebclientreadablestream",
":statuscode",
"@io_bazel_rules_closure//closure/library/crypt:base64",
"@io_bazel_rules_closure//closure/library/net:xhrio",
"@io_bazel_rules_closure//closure/library/net/rpc:httpcors",
],
)
closure_js_library(
name = "grpcwebclientreadablestream",
srcs = [
"grpcwebclientreadablestream.js",
],
deps = [
":clientreadablestream",
":generictransportinterface",
":grpcwebstreamparser",
":status",
":statuscode",
"@io_bazel_rules_closure//closure/library/crypt:base64",
"@io_bazel_rules_closure//closure/library/events:events",
"@io_bazel_rules_closure//closure/library/net:errorcode",
"@io_bazel_rules_closure//closure/library/net:eventtype",
"@io_bazel_rules_closure//closure/library/net:xhrio",
"@io_bazel_rules_closure//closure/library/net:xmlhttp",
"@io_bazel_rules_closure//closure/library/string",
],
suppress = [
"reportUnknownTypes",
],
)
closure_js_library(
name = "grpcwebstreamparser",
srcs = [
"grpcwebstreamparser.js",
],
suppress = [
"reportUnknownTypes",
],
deps = [
"@io_bazel_rules_closure//closure/library/asserts",
"@io_bazel_rules_closure//closure/library/net/streams:streamparser",
],
)
closure_js_library(
name = "status",
srcs = [
"status.js",
],
)
closure_js_library(
name = "statuscode",
srcs = [
"statuscode.js",
],
)
closure_js_test(
name = "grpcwebclientbase_test",
srcs = [
"grpcwebclientbase_test.js",
],
entry_points = [
"goog:grpc.web.GrpcWebClientBaseTest",
],
suppress = [
"visibility",
"checkTypes",
"deprecated",
"reportUnknownTypes",
"strictCheckTypes",
],
deps = [
"@io_bazel_rules_closure//closure/library:testing",
"@io_bazel_rules_closure//closure/library/crypt:base64",
"@io_bazel_rules_closure//closure/library/events:events",
"@io_bazel_rules_closure//closure/library/structs:map",
":grpcwebclientbase",
],
)
closure_js_test(
name = "grpcwebstreamparser_test",
srcs = [
"grpcwebstreamparser_test.js",
],
entry_points = [
"goog:grpc.web.GrpcWebStreamParserTest",
],
suppress = [
"reportUnknownTypes",
],
deps = [
":grpcwebstreamparser",
"@io_bazel_rules_closure//closure/library:testing",
],
)

View File

@ -28,75 +28,84 @@ goog.module.declareLegacyNamespace();
const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const Error = goog.require('grpc.web.Error');
/**
* This interface represents a grpc-web client
*
* @interface
*/
const AbstractClientBase = function() {};
const MethodDescriptor = goog.require('grpc.web.MethodDescriptor');
const RpcError = goog.require('grpc.web.RpcError');
/**
* @constructor
* @struct
* @template REQUEST, RESPONSE
* @param {function(new: RESPONSE, ...)} responseType
* @param {function(REQUEST): ?} requestSerializeFn
* @param {function(?): RESPONSE} responseDeserializeFn
* @final
*/
AbstractClientBase.MethodInfo = function(
responseType,
requestSerializeFn,
responseDeserializeFn) {
/** @const */
this.responseType = responseType;
/** @const */
this.requestSerializeFn = requestSerializeFn;
/** @const */
this.responseDeserializeFn = responseDeserializeFn;
const PromiseCallOptions = function() {};
/**
* An AbortSignal to abort the call.
* @type {AbortSignal|undefined}
*/
PromiseCallOptions.prototype.signal;
/**
* This interface represents a grpc-web client
* @interface
*/
const AbstractClientBase = class {
constructor() {}
/**
* @abstract
* @template REQUEST, RESPONSE
* @param {string} method The method to invoke
* @param {REQUEST} requestMessage The request proto
* @param {!Object<string, string>} metadata User defined call metadata
* @param {!MethodDescriptor<REQUEST, RESPONSE>}
* methodDescriptor Information of this RPC method
* @param {function(?RpcError, ?)}
* callback A callback function which takes (error, RESPONSE or null)
* @return {!ClientReadableStream<RESPONSE>}
*/
rpcCall(method, requestMessage, metadata, methodDescriptor, callback) {}
/**
* @abstract
* @protected
* @template REQUEST, RESPONSE
* @param {string} method The method to invoke
* @param {REQUEST} requestMessage The request proto
* @param {!Object<string, string>} metadata User defined call metadata
* @param {!MethodDescriptor<REQUEST, RESPONSE>}
* methodDescriptor Information of this RPC method
* @param options Options for the call
* @return {!IThenable<RESPONSE>}
* A promise that resolves to the response message
*/
thenableCall(method, requestMessage, metadata, methodDescriptor, options) {}
/**
* @abstract
* @template REQUEST, RESPONSE
* @param {string} method The method to invoke
* @param {REQUEST} requestMessage The request proto
* @param {!Object<string, string>} metadata User defined call metadata
* @param {!MethodDescriptor<REQUEST, RESPONSE>}
* methodDescriptor Information of this RPC method
* @return {!ClientReadableStream<RESPONSE>} The Client Readable Stream
*/
serverStreaming(method, requestMessage, metadata, methodDescriptor) {}
};
/**
* Get the hostname of the current request.
* @template REQUEST, RESPONSE
* Even with ?RESPONSE the RESPONSE will still be inferred as
* "FooResponse|Null". Use RESPONSE_LEAN to extract out the "FooResponse"
* part. See go/closure-ttl.
* @template RESPONSE_LEAN :=
* cond(isUnknown(RESPONSE), unknown(),
* mapunion(RESPONSE, (X) =>
* cond(eq(X, 'undefined'), none(),
* cond(eq(X, 'null'), none(),
* X))))
* =:
* @param {string} method The method to invoke
* @param {REQUEST} request The request proto
* @param {!Object<string, string>} metadata User defined call metadata
* @param {!AbstractClientBase.MethodInfo<REQUEST, RESPONSE_LEAN>}
* methodInfo Information of this RPC method
* @param {function(?Error, ?RESPONSE)}
* callback A callback function which takes (error, response)
* @return {!ClientReadableStream<RESPONSE_LEAN>|undefined}
* The Client Readable Stream
* @param {string} method
* @param {!MethodDescriptor<REQUEST,RESPONSE>} methodDescriptor
* @return {string}
*/
AbstractClientBase.prototype.rpcCall = goog.abstractMethod;
function getHostname(method, methodDescriptor) {
// method = hostname + methodDescriptor.name(relative path of this method)
return method.substr(0, method.length - methodDescriptor.name.length);
}
/**
* @template REQUEST, RESPONSE
* @param {string} method The method to invoke
* @param {REQUEST} request The request proto
* @param {!Object<string, string>} metadata User defined call metadata
* @param {!AbstractClientBase.MethodInfo<REQUEST, RESPONSE>}
* methodInfo Information of this RPC method
* @return {!ClientReadableStream<RESPONSE>} The Client Readable Stream
*/
AbstractClientBase.prototype.serverStreaming = goog.abstractMethod;
exports = AbstractClientBase;
exports = {AbstractClientBase, PromiseCallOptions, getHostname};

View File

@ -0,0 +1,66 @@
/**
* @fileoverview grpc.web.CallOptions
*/
goog.module('grpc.web.CallOptions');
goog.module.declareLegacyNamespace();
/**
* The collection of runtime options for a new RPC call.
* @unrestricted
*/
class CallOptions {
/**
* @param {!Object<string, !Object>=} options
*/
constructor(options) {
/**
* @const {!Object<string, !Object>}
* @private
*/
this.properties_ = options || {};
}
/**
* Add a new CallOption or override an existing one.
*
* @param {string} name name of the CallOption that should be
* added/overridden.
* @param {VALUE} value value of the CallOption
* @template VALUE
*/
setOption(name, value) {
this.properties_[name] = value;
}
/**
* Get the value of one CallOption.
*
* @param {string} name name of the CallOption.
* @return {!Object} value of the CallOption. If name doesn't exist, will
* return 'undefined'.
*/
get(name) {
return this.properties_[name];
}
/**
* Remove a CallOption.
*
* @param {string} name name of the CallOption that shoud be removed.
*/
removeOption(name) {
delete this.properties_[name];
}
/**
* @return {!Array<string>}
*/
getKeys() {
return Object.keys(this.properties_);
}
}
exports = CallOptions;

View File

@ -0,0 +1,66 @@
goog.module('grpc.web.ClientOptions');
goog.module.declareLegacyNamespace();
const {StreamInterceptor, UnaryInterceptor} = goog.require('grpc.web.Interceptor');
/**
* Options that are available during the client construction.
* @record
*/
class ClientOptions {
constructor() {
/**
* Whether to use the HttpCors library to pack http headers into a special
* url query param $httpHeaders= so that browsers can bypass CORS OPTIONS
* requests.
* @type {boolean|undefined}
*/
this.suppressCorsPreflight;
/**
* Whether to turn on XMLHttpRequest's withCredentials flag.
* @type {boolean|undefined}
*/
this.withCredentials;
/**
* Unary interceptors. Note that they are only available in grpcweb and
* grpcwebtext mode
* @type {!Array<!UnaryInterceptor>|undefined}
*/
this.unaryInterceptors;
/**
* Stream interceptors. Note that they are only available in grpcweb and
* grpcwebtext mode
* @type {!Array<!StreamInterceptor>|undefined}
*/
this.streamInterceptors;
/**
* Protocol buffer format for open source gRPC-Web. This attribute should be
* specified by the gRPC-Web build rule by default.
* @type {string|undefined}
*/
this.format;
/**
* The Worker global scope. Once this option is specified, gRPC-Web will
* also use 'fetch' API as the underlying transport instead of native
* XmlHttpRequest.
* @type {!WorkerGlobalScope|undefined}
*/
this.workerScope;
/**
* This is an experimental feature to reduce memory consumption
* during high throughput server-streaming calls by using
* 'streamBinaryChunks' mode FetchXmlHttpFactory.
* @type {boolean|undefined}
*/
this.useFetchDownloadStreams;
}
}
exports = ClientOptions;

View File

@ -43,10 +43,25 @@ const ClientReadableStream = function() {};
/**
* Register a callback to handle I/O events.
* Register a callback to handle different stream events.
*
* Available event types for gRPC-Web:
* 'data': The 'data' event is emitted when a new response message chunk is
* received and successfully handled by gRPC-Web client.
* 'status': the google RPC status of the response stream.
* 'end': The 'end' event is emitted when all the data have been successfully
* consumed from the stream.
* 'error': typically, this may occur when an underlying internal failure
* happens, or a stream implementation attempts to push an invalid chunk of
* data.
* 'metadata': the response metadata. Response headers should be read via
* 'metadata' callbacks.
*
* For server-streaming calls. the 'data' and 'status' callbacks (if exist)
* will always precede 'metadata', 'error', or 'end' callbacks.
*
* @param {string} eventType The event type
* @param {function(?)} callback The call back to handle the event with
* @param {function(?)} callback The callback to handle the event with
* an optional input object
* @return {!ClientReadableStream} this object
*/
@ -54,6 +69,17 @@ ClientReadableStream.prototype.on = goog.abstractMethod;
/**
* Remove a particular callback.
*
* @param {string} eventType The event type
* @param {function(?)} callback The callback to remove
* @return {!ClientReadableStream} this object
*/
ClientReadableStream.prototype.removeListener = goog.abstractMethod;
/**
* Close the stream.
*/

View File

@ -0,0 +1,51 @@
/**
* @fileoverview This class handles ClientReadableStream returned by unary
* calls.
*/
goog.module('grpc.web.ClientUnaryCallImpl');
goog.module.declareLegacyNamespace();
const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
/**
* @implements {ClientReadableStream<RESPONSE>}
* @template RESPONSE
*/
class ClientUnaryCallImpl {
/**
* @param {!ClientReadableStream<RESPONSE>} stream
*/
constructor(stream) {
this.stream = stream;
}
/**
* @override
*/
on(eventType, callback) {
if (eventType == 'data' || eventType == 'error') {
// unary call responses and errors should be handled by the main
// (err, resp) => ... callback
return this;
}
return this.stream.on(eventType, callback);
}
/**
* @override
*/
removeListener(eventType, callback) {
return this.stream.removeListener(eventType, callback);
}
/**
* @override
*/
cancel() {
this.stream.cancel();
}
}
exports = ClientUnaryCallImpl;

View File

@ -1,39 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
/**
* @fileoverview gRPC-Web Error objects
*
* gRPC-Web Error objects
*
* @author stanleycheung@google.com (Stanley Cheung)
*/
goog.module('grpc.web.Error');
goog.module.declareLegacyNamespace();
/**
* @typedef {{
* code: (number|undefined),
* message: (string|undefined),
* }}
*/
let Error;
exports = Error;

View File

@ -1,188 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
/**
* @fileoverview gRPC browser client library.
*
* Base class for gRPC Web JS clients to be used with the gRPC Gateway
*
* @author stanleycheung@google.com (Stanley Cheung)
*/
goog.module('grpc.web.GatewayClientBase');
goog.module.declareLegacyNamespace();
const AbstractClientBase = goog.require('grpc.web.AbstractClientBase');
const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const GoogleRpcStatus = goog.require('proto.google.rpc.Status');
const NodeReadableStream = goog.require('goog.net.streams.NodeReadableStream');
const Pair = goog.require('proto.grpc.gateway.Pair');
const StatusCode = goog.require('grpc.web.StatusCode');
const StreamBodyClientReadableStream = goog.require('grpc.web.StreamBodyClientReadableStream');
const XhrIo = goog.require('goog.net.XhrIo');
const createXhrNodeReadableStream = goog.require('goog.net.streams.createXhrNodeReadableStream');
const googCrypt = goog.require('goog.crypt');
const {Status} = goog.require('grpc.web.Status');
/**
* Base class for gRPC web client (gRPC Gateway)
* @param {?Object=} opt_options
* @constructor
* @implements {AbstractClientBase}
*/
const GatewayClientBase = function(opt_options) {
};
/**
* @override
*/
GatewayClientBase.prototype.rpcCall = function(
method, request, metadata, methodInfo, callback) {
var xhr = this.newXhr_();
var serialized = methodInfo.requestSerializeFn(request);
xhr.headers.addAll(metadata);
var stream = this.createClientReadableStream_(
xhr,
methodInfo.responseDeserializeFn);
stream.on('data', function(response) {
callback(null, response);
});
stream.on('status', function(status) {
if (status.code != StatusCode.OK) {
callback({
'code': status.code,
'message': status.details
}, null);
}
});
xhr.headers.set('Content-Type', 'application/x-protobuf');
xhr.headers.set('X-User-Agent', 'grpc-web-javascript/0.1');
xhr.headers.set('X-Accept-Content-Transfer-Encoding', 'base64');
xhr.headers.set('X-Accept-Response-Streaming', 'true');
xhr.send(method, 'POST', serialized);
return stream;
};
/**
* @override
*/
GatewayClientBase.prototype.serverStreaming = function(
method, request, metadata, methodInfo) {
var xhr = this.newXhr_();
var serialized = methodInfo.requestSerializeFn(request);
xhr.headers.addAll(metadata);
var stream = this.createClientReadableStream_(
xhr,
methodInfo.responseDeserializeFn);
xhr.headers.set('Content-Type', 'application/x-protobuf');
xhr.headers.set('X-User-Agent', 'grpc-web-javascript/0.1');
xhr.headers.set('X-Accept-Content-Transfer-Encoding', 'base64');
xhr.headers.set('X-Accept-Response-Streaming', 'true');
xhr.send(method, 'POST', serialized);
return stream;
};
/**
* Create a new XhrIo object
*
* @private
* @return {!XhrIo} The created XhrIo object
*/
GatewayClientBase.prototype.newXhr_ = function() {
return new XhrIo();
};
/**
* Create a new XhrNodeReadableStream object
*
* @private
* @param {!XhrIo} xhr The XhrIo object
* @return {?NodeReadableStream} The XHR NodeReadableStream object
*/
GatewayClientBase.prototype.newXhrNodeReadableStream_ = function(xhr) {
return createXhrNodeReadableStream(xhr);
};
/**
* @template RESPONSE
* @private
* @param {!XhrIo} xhr The XhrIo object
* @param {function(?):!RESPONSE} responseDeserializeFn
* The deserialize function for the proto
* @return {!ClientReadableStream<RESPONSE>} The Client Readable Stream
*/
GatewayClientBase.prototype.createClientReadableStream_ = function(
xhr, responseDeserializeFn) {
var xhrNodeReadableStream = this.newXhrNodeReadableStream_(xhr);
var genericTransportInterface = {
xhr: xhr,
nodeReadableStream: xhrNodeReadableStream,
};
var stream = new StreamBodyClientReadableStream(genericTransportInterface);
stream.setResponseDeserializeFn(responseDeserializeFn);
stream.setRpcStatusParseFn(GatewayClientBase.parseRpcStatus_);
return stream;
};
/**
* @private
* @static
* @param {!Uint8Array} data Data returned from underlying stream
* @return {!Status} status The Rpc Status details
*/
GatewayClientBase.parseRpcStatus_ = function(data) {
var rpcStatus = GoogleRpcStatus.deserializeBinary(data);
var metadata = {};
var details = rpcStatus.getDetailsList();
for (var i = 0; i < details.length; i++) {
var pair = Pair.deserializeBinary(
details[i].getValue());
var first = googCrypt.utf8ByteArrayToString(
pair.getFirst_asU8());
var second = googCrypt.utf8ByteArrayToString(
pair.getSecond_asU8());
metadata[first] = second;
}
var status = {
code: rpcStatus.getCode(),
details: rpcStatus.getMessage(),
metadata: metadata
};
return status;
};
exports = GatewayClientBase;

View File

@ -0,0 +1,12 @@
load("@rules_cc//cc:defs.bzl", "cc_binary")
cc_binary(
name = "protoc-gen-grpc-web",
srcs = [
"grpc_generator.cc",
],
visibility = ["//visibility:public"],
deps = [
"@com_google_protobuf//:protoc_lib",
],
)

View File

@ -12,10 +12,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
CXX = g++
CXX ?= g++
CPPFLAGS += -I/usr/local/include -pthread
CXXFLAGS += -std=c++11
LDFLAGS += -L/usr/local/lib -lprotoc -lprotobuf -lpthread -ldl
PREFIX ?= /usr/local
MIN_MACOS_VERSION := 10.7 # Supports OS X Lion
STATIC ?= yes
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
CXXFLAGS += -stdlib=libc++ -mmacosx-version-min=$(MIN_MACOS_VERSION)
else ifeq ($(UNAME_S),Linux)
ifeq ($(STATIC),yes)
LDFLAGS += -static
endif
endif
all: protoc-gen-grpc-web
@ -23,7 +35,8 @@ protoc-gen-grpc-web: grpc_generator.o
$(CXX) $^ $(LDFLAGS) -o $@
install: protoc-gen-grpc-web
install protoc-gen-grpc-web /usr/local/bin/protoc-gen-grpc-web
mkdir -p $(PREFIX)/bin
install protoc-gen-grpc-web $(PREFIX)/bin/protoc-gen-grpc-web
clean:
rm -f *.o protoc-gen-grpc-web

View File

@ -0,0 +1,215 @@
const std = @import("std");
const CrossTarget = std.zig.CrossTarget;
fn format(comptime fmt: []const u8, args: anytype) []const u8 {
return std.fmt.allocPrint(std.testing.allocator, fmt, args) catch unreachable;
}
const BinaryTarget = struct {
name: []const u8,
arch: []const u8,
};
pub fn build(b: *std.build.Builder) void {
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
var version = if (std.os.getenv("VERSION")) |v| v else "unknown";
var targets = [_]BinaryTarget{
// for now, let's only build aarch64 binaries
// .{ .name = format("protoc-gen-grpc-web-{s}-linux-x86_64", .{version}), .arch = "x86_64-linux" },
// .{ .name = format("protoc-gen-grpc-web-{s}-darwin-x86_64", .{version}), .arch = "x86_64-macos" },
// .{ .name = format("protoc-gen-grpc-web-{s}-windows-x86_64", .{version}), .arch = "x86_64-windows" },
.{ .name = format("protoc-gen-grpc-web-{s}-linux-aarch64", .{version}), .arch = "aarch64-linux" },
.{ .name = format("protoc-gen-grpc-web-{s}-darwin-aarch64", .{version}), .arch = "aarch64-macos" },
.{ .name = format("protoc-gen-grpc-web-{s}-windows-aarch64", .{version}), .arch = "aarch64-windows" },
};
for (targets) |target| {
const exe = b.addExecutable(target.name, "grpc_generator.cc");
exe.linkLibCpp();
exe.linkSystemLibrary("pthread");
exe.linkSystemLibrary("dl");
exe.addIncludeDir("../../../../../third_party/protobuf/src");
exe.defineCMacro("HAVE_PTHREAD", "1");
exe.addCSourceFiles(&[_][]const u8{
// libprotobuf_lite source files (copied from third_party/protobuf/cmake/libprotobuf-lite.cmake)
"../../../../../third_party/protobuf/src/google/protobuf/any_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/arena.cc",
"../../../../../third_party/protobuf/src/google/protobuf/arenastring.cc",
"../../../../../third_party/protobuf/src/google/protobuf/extension_set.cc",
"../../../../../third_party/protobuf/src/google/protobuf/generated_enum_util.cc",
"../../../../../third_party/protobuf/src/google/protobuf/generated_message_table_driven_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/generated_message_util.cc",
"../../../../../third_party/protobuf/src/google/protobuf/implicit_weak_message.cc",
"../../../../../third_party/protobuf/src/google/protobuf/io/coded_stream.cc",
"../../../../../third_party/protobuf/src/google/protobuf/io/io_win32.cc",
"../../../../../third_party/protobuf/src/google/protobuf/io/strtod.cc",
"../../../../../third_party/protobuf/src/google/protobuf/io/zero_copy_stream.cc",
"../../../../../third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl.cc",
"../../../../../third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/map.cc",
"../../../../../third_party/protobuf/src/google/protobuf/message_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/parse_context.cc",
"../../../../../third_party/protobuf/src/google/protobuf/repeated_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/bytestream.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/common.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/int128.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/status.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/statusor.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/stringpiece.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/stringprintf.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/structurally_valid.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/strutil.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/time.cc",
"../../../../../third_party/protobuf/src/google/protobuf/wire_format_lite.cc",
// libprotobuf ssource files (copied from third_party/protobuf/cmake/libprotobuf.cmake)
"../../../../../third_party/protobuf/src/google/protobuf/any.cc",
"../../../../../third_party/protobuf/src/google/protobuf/any.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/api.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/importer.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/parser.cc",
"../../../../../third_party/protobuf/src/google/protobuf/descriptor.cc",
"../../../../../third_party/protobuf/src/google/protobuf/descriptor.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/descriptor_database.cc",
"../../../../../third_party/protobuf/src/google/protobuf/duration.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/dynamic_message.cc",
"../../../../../third_party/protobuf/src/google/protobuf/empty.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/extension_set_heavy.cc",
"../../../../../third_party/protobuf/src/google/protobuf/field_mask.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/generated_message_reflection.cc",
"../../../../../third_party/protobuf/src/google/protobuf/generated_message_table_driven.cc",
"../../../../../third_party/protobuf/src/google/protobuf/io/gzip_stream.cc",
"../../../../../third_party/protobuf/src/google/protobuf/io/printer.cc",
"../../../../../third_party/protobuf/src/google/protobuf/io/tokenizer.cc",
"../../../../../third_party/protobuf/src/google/protobuf/map_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/message.cc",
"../../../../../third_party/protobuf/src/google/protobuf/reflection_ops.cc",
"../../../../../third_party/protobuf/src/google/protobuf/service.cc",
"../../../../../third_party/protobuf/src/google/protobuf/source_context.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/struct.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/stubs/substitute.cc",
"../../../../../third_party/protobuf/src/google/protobuf/text_format.cc",
"../../../../../third_party/protobuf/src/google/protobuf/timestamp.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/type.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/unknown_field_set.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/delimited_message_util.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/field_comparator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/field_mask_util.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/datapiece.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/default_value_objectwriter.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/error_listener.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/field_mask_utility.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/json_escaping.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/json_objectwriter.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/json_stream_parser.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/object_writer.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/proto_writer.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/protostream_objectsource.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/protostream_objectwriter.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/type_info.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/type_info_test_helper.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/internal/utility.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/json_util.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/message_differencer.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/time_util.cc",
"../../../../../third_party/protobuf/src/google/protobuf/util/type_resolver_util.cc",
"../../../../../third_party/protobuf/src/google/protobuf/wire_format.cc",
"../../../../../third_party/protobuf/src/google/protobuf/wrappers.pb.cc",
// libprotoc source files (copied from third_party/protobuf/cmake/libprotoc.cmake)
"../../../../../third_party/protobuf/src/google/protobuf/compiler/code_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/command_line_interface.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_enum.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_enum_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_extension.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_file.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_helpers.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_map_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_message.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_message_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_service.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_string_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_enum.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_enum_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_field_base.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_helpers.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_map_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_message.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_message_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_context.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_doc_comment.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_enum.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_enum_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_enum_field_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_enum_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_extension.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_extension_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_file.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_generator_factory.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_helpers.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_map_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_map_field_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_message.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_message_builder.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_message_builder_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_message_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_message_field_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_message_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_name_resolver.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_primitive_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_primitive_field_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_service.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_shared_code_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_string_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/java/java_string_field_lite.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/js/js_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/js/well_known_types_embed.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_enum.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_extension.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_file.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_message.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/php/php_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/plugin.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/plugin.pb.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/python/python_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/ruby/ruby_generator.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/subprocess.cc",
"../../../../../third_party/protobuf/src/google/protobuf/compiler/zip_writer.cc",
}, &[_][]const u8{
"-pthread",
});
exe.setTarget(CrossTarget.parse(.{ .arch_os_abi = target.arch }) catch unreachable);
exe.setBuildMode(mode);
exe.strip = true;
exe.install();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -16,13 +16,10 @@
*
*/
/**
* @fileoverview gRPC web client Readable Stream
* @fileoverview gRPC-Web generic transport interface
*
* This class is being returned after a gRPC streaming call has been
* started. This class provides functionality for user to operates on
* the stream, e.g. set onData callback, etc.
*
* This wraps the underlying goog.net.streams.NodeReadableStream
* This class provides an abstraction for the underlying transport
* implementation underneath the ClientReadableStream layer.
*
* @author stanleycheung@google.com (Stanley Cheung)
*/

File diff suppressed because it is too large Load Diff

View File

@ -28,195 +28,384 @@ goog.module('grpc.web.GrpcWebClientBase');
goog.module.declareLegacyNamespace();
const AbstractClientBase = goog.require('grpc.web.AbstractClientBase');
const ClientOptions = goog.requireType('grpc.web.ClientOptions');
const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const ClientUnaryCallImpl = goog.require('grpc.web.ClientUnaryCallImpl');
const GrpcWebClientReadableStream = goog.require('grpc.web.GrpcWebClientReadableStream');
const HttpCors = goog.require('goog.net.rpc.HttpCors');
const MethodDescriptor = goog.requireType('grpc.web.MethodDescriptor');
const Request = goog.require('grpc.web.Request');
const RpcError = goog.require('grpc.web.RpcError');
const StatusCode = goog.require('grpc.web.StatusCode');
const XhrIo = goog.require('goog.net.XhrIo');
const googCrypt = goog.require('goog.crypt.base64');
const {AbstractClientBase, PromiseCallOptions, getHostname} = goog.require('grpc.web.AbstractClientBase');
const {Status} = goog.require('grpc.web.Status');
const {StreamInterceptor, UnaryInterceptor} = goog.require('grpc.web.Interceptor');
const {toObject} = goog.require('goog.collections.maps');
/**
* Base class for gRPC web client using the application/grpc-web wire format
* @param {?Object=} opt_options
* @constructor
* @implements {AbstractClientBase}
* @unrestricted
*/
const GrpcWebClientBase = function(opt_options) {
class GrpcWebClientBase {
/**
* @const
* @private {string}
* @param {!ClientOptions=} options
* @param {!XhrIo=} xhrIo
*/
this.format_ =
goog.getObjectByName('format', opt_options) || "text";
constructor(options = {}, xhrIo = undefined) {
/**
* @const
* @private {string}
*/
this.format_ =
options.format || goog.getObjectByName('format', options) || 'text';
/**
* @const
* @private {boolean}
*/
this.suppressCorsPreflight_ = options.suppressCorsPreflight ||
goog.getObjectByName('suppressCorsPreflight', options) || false;
/**
* @const
* @private {boolean}
*/
this.withCredentials_ = options.withCredentials ||
goog.getObjectByName('withCredentials', options) || false;
/**
* @const {!Array<!StreamInterceptor>}
* @private
*/
this.streamInterceptors_ = options.streamInterceptors ||
goog.getObjectByName('streamInterceptors', options) || [];
/**
* @const {!Array<!UnaryInterceptor>}
* @private
*/
this.unaryInterceptors_ = options.unaryInterceptors ||
goog.getObjectByName('unaryInterceptors', options) || [];
/** @const @private {?XhrIo} */
this.xhrIo_ = xhrIo || null;
}
/**
* @const
* @private {boolean}
* @override
* @export
*/
this.suppressCorsPreflight_ =
goog.getObjectByName('suppressCorsPreflight', opt_options) || false;
};
rpcCall(method, requestMessage, metadata, methodDescriptor, callback) {
const hostname = getHostname(method, methodDescriptor);
const invoker = GrpcWebClientBase.runInterceptors_(
(request) => this.startStream_(request, hostname),
this.streamInterceptors_);
const stream = /** @type {!ClientReadableStream<?>} */ (invoker.call(
this, methodDescriptor.createRequest(requestMessage, metadata)));
GrpcWebClientBase.setCallback_(stream, callback, false);
return new ClientUnaryCallImpl(stream);
}
/**
* @param {string} method The method to invoke
* @param {REQUEST} requestMessage The request proto
* @param {!Object<string, string>} metadata User defined call metadata
* @param {!MethodDescriptor<REQUEST, RESPONSE>} methodDescriptor
* @param {?PromiseCallOptions=} options Options for the call
* @return {!Promise<RESPONSE>}
* @template REQUEST, RESPONSE
*/
thenableCall(
method, requestMessage, metadata, methodDescriptor, options = {}) {
const hostname = getHostname(method, methodDescriptor);
const signal = options && options.signal;
const initialInvoker = (request) => new Promise((resolve, reject) => {
// If the signal is already aborted, immediately reject the promise
// and don't issue the call.
if (signal && signal.aborted) {
const error = new RpcError(StatusCode.CANCELLED, 'Aborted');
error.cause = signal.reason;
reject(error);
return;
}
/**
* @override
*/
GrpcWebClientBase.prototype.rpcCall = function(
method, request, metadata, methodInfo, callback) {
var xhr = this.newXhr_();
const stream = this.startStream_(request, hostname);
let unaryMetadata;
let unaryStatus;
let unaryMsg;
GrpcWebClientBase.setCallback_(
stream,
(error, response, status, metadata, unaryResponseReceived) => {
if (error) {
reject(error);
} else if (unaryResponseReceived) {
unaryMsg = response;
} else if (status) {
unaryStatus = status;
} else if (metadata) {
unaryMetadata = metadata;
} else {
resolve(request.getMethodDescriptor().createUnaryResponse(
unaryMsg, unaryMetadata, unaryStatus));
}
},
true);
var genericTransportInterface = {
xhr: xhr,
};
var stream = new GrpcWebClientReadableStream(genericTransportInterface);
stream.setResponseDeserializeFn(methodInfo.responseDeserializeFn);
// Wire up cancellation from the abort signal, if any.
if (signal) {
signal.addEventListener('abort', () => {
stream.cancel();
stream.on('data', function(response) {
callback(null, response);
});
const error = new RpcError(StatusCode.CANCELLED, 'Aborted');
error.cause = /** @type {!AbortSignal} */ (signal).reason;
reject(error);
});
}
});
const invoker = GrpcWebClientBase.runInterceptors_(
initialInvoker, this.unaryInterceptors_);
const unaryResponse = /** @type {!Promise<?>} */ (invoker.call(
this, methodDescriptor.createRequest(requestMessage, metadata)));
return unaryResponse.then((response) => response.getResponseMessage());
}
stream.on('status', function(status) {
if (status.code != StatusCode.OK) {
callback({
code: status.code,
message: status.details
}, null);
/**
* @export
* @param {string} method The method to invoke
* @param {REQUEST} requestMessage The request proto
* @param {!Object<string, string>} metadata User defined call metadata
* @param {!MethodDescriptor<REQUEST, RESPONSE>} methodDescriptor Information
* of this RPC method
* @param {?PromiseCallOptions=} options Options for the call
* @return {!Promise<RESPONSE>}
* @template REQUEST, RESPONSE
*/
unaryCall(method, requestMessage, metadata, methodDescriptor, options = {}) {
return /** @type {!Promise<RESPONSE>}*/ (this.thenableCall(
method, requestMessage, metadata, methodDescriptor, options));
}
/**
* @override
* @export
*/
serverStreaming(method, requestMessage, metadata, methodDescriptor) {
const hostname = getHostname(method, methodDescriptor);
const invoker = GrpcWebClientBase.runInterceptors_(
(request) => this.startStream_(request, hostname),
this.streamInterceptors_);
return /** @type {!ClientReadableStream<?>} */ (invoker.call(
this, methodDescriptor.createRequest(requestMessage, metadata)));
}
/**
* @private
* @template REQUEST, RESPONSE
* @param {!Request<REQUEST, RESPONSE>} request
* @param {string} hostname
* @return {!ClientReadableStream<RESPONSE>}
*/
startStream_(request, hostname) {
const methodDescriptor = request.getMethodDescriptor();
let path = hostname + methodDescriptor.getName();
const xhr = this.xhrIo_ ? this.xhrIo_ : new XhrIo();
xhr.setWithCredentials(this.withCredentials_);
const genericTransportInterface = {
xhr: xhr,
};
const stream = new GrpcWebClientReadableStream(genericTransportInterface);
stream.setResponseDeserializeFn(
methodDescriptor.getResponseDeserializeFn());
const metadata = request.getMetadata();
for(const key in metadata) {
xhr.headers.set(key, metadata[key]);
}
});
stream.on('error', function(error) {
if (error.code != StatusCode.OK) {
callback({
code: error.code,
message: error.message
}, null);
this.processHeaders_(xhr);
if (this.suppressCorsPreflight_) {
const headerObject = toObject(xhr.headers);
xhr.headers.clear();
path = GrpcWebClientBase.setCorsOverride_(path, headerObject);
}
});
xhr.headers.addAll(metadata);
this.processHeaders_(xhr);
if (this.suppressCorsPreflight_) {
var headerObject = xhr.headers.toObject();
xhr.headers.clear();
method = GrpcWebClientBase.setCorsOverride_(method, headerObject);
}
var serialized = methodInfo.requestSerializeFn(request);
var payload = this.encodeRequest_(serialized);
if (this.format_ == "text") {
payload = googCrypt.encodeByteArray(payload);
} else if (this.format_ == "binary") {
xhr.setResponseType(XhrIo.ResponseType.ARRAY_BUFFER);
}
xhr.send(method, 'POST', payload);
return stream;
};
/**
* @override
*/
GrpcWebClientBase.prototype.serverStreaming = function(
method, request, metadata, methodInfo) {
var xhr = this.newXhr_();
var genericTransportInterface = {
xhr: xhr,
};
var stream = new GrpcWebClientReadableStream(genericTransportInterface);
stream.setResponseDeserializeFn(methodInfo.responseDeserializeFn);
xhr.headers.addAll(metadata);
this.processHeaders_(xhr);
if (this.suppressCorsPreflight_) {
var headerObject = xhr.headers.toObject();
xhr.headers.clear();
method = GrpcWebClientBase.setCorsOverride_(method, headerObject);
}
var serialized = methodInfo.requestSerializeFn(request);
var payload = this.encodeRequest_(serialized);
if (this.format_ == "text") {
payload = googCrypt.encodeByteArray(payload);
} else if (this.format_ == "binary") {
xhr.setResponseType(XhrIo.ResponseType.ARRAY_BUFFER);
}
xhr.send(method, 'POST', payload);
return stream;
};
/**
* Create a new XhrIo object
*
* @private
* @return {!XhrIo} The created XhrIo object
*/
GrpcWebClientBase.prototype.newXhr_ = function() {
return new XhrIo();
};
/**
* Encode the grpc-web request
*
* @private
* @param {!Uint8Array} serialized The serialized proto payload
* @return {!Uint8Array} The application/grpc-web padded request
*/
GrpcWebClientBase.prototype.encodeRequest_ = function(serialized) {
var len = serialized.length;
var bytesArray = [0, 0, 0, 0];
var payload = new Uint8Array(5 + len);
for (var i = 3; i >= 0; i--) {
bytesArray[i] = (len % 256);
len = len >>> 8;
}
payload.set(new Uint8Array(bytesArray), 1);
payload.set(serialized, 5);
return payload;
};
/**
* @private
* @param {!XhrIo} xhr The xhr object
*/
GrpcWebClientBase.prototype.processHeaders_ = function(xhr) {
if (this.format_ == "text") {
xhr.headers.set('Content-Type', 'application/grpc-web-text');
xhr.headers.set('Accept', 'application/grpc-web-text');
} else {
xhr.headers.set('Content-Type', 'application/grpc-web+proto');
}
xhr.headers.set('X-User-Agent', 'grpc-web-javascript/0.1');
xhr.headers.set('X-Grpc-Web', '1');
if (xhr.headers.containsKey('deadline')) {
var deadline = xhr.headers.get('deadline'); // in ms
var currentTime = (new Date()).getTime();
var timeout = Math.round(deadline - currentTime);
xhr.headers.remove('deadline');
if (timeout > 0) {
xhr.headers.set('grpc-timeout', timeout + 'm');
const requestSerializeFn = methodDescriptor.getRequestSerializeFn();
const serialized = requestSerializeFn(request.getRequestMessage());
let payload = this.encodeRequest_(serialized);
if (this.format_ == 'text') {
payload = googCrypt.encodeByteArray(payload);
} else if (this.format_ == 'binary') {
xhr.setResponseType(XhrIo.ResponseType.ARRAY_BUFFER);
}
}
};
xhr.send(path, 'POST', payload);
return stream;
}
/**
* @private
* @static
* @template RESPONSE
* @param {!ClientReadableStream<RESPONSE>} stream
* @param {function(?RpcError, ?RESPONSE, ?Status=, ?Object<string, string>=, ?boolean)|
* function(?RpcError,?RESPONSE)} callback
* @param {boolean} useUnaryResponse Pass true to have the client make
* multiple calls to the callback, using (error, response, status,
* metadata, unaryResponseReceived) arguments. One of error, status,
* metadata, or unaryResponseReceived will be truthy to indicate which piece
* of information the client is providing in that call. After the stream
* ends, it will call the callback an additional time with all falsy
* arguments. Pass false to have the client make one call to the callback
* using (error, response) arguments.
*/
static setCallback_(stream, callback, useUnaryResponse) {
let isResponseReceived = false;
let responseReceived = null;
let errorEmitted = false;
stream.on('data', function(response) {
isResponseReceived = true;
responseReceived = response;
});
stream.on('error', function(error) {
if (error.code != StatusCode.OK && !errorEmitted) {
errorEmitted = true;
callback(error, null);
}
});
stream.on('status', function(status) {
if (status.code != StatusCode.OK && !errorEmitted) {
errorEmitted = true;
callback(
{
code: status.code,
message: status.details,
metadata: status.metadata
},
null);
} else if (useUnaryResponse) {
callback(null, null, status);
}
});
if (useUnaryResponse) {
stream.on('metadata', function(metadata) {
callback(null, null, null, metadata);
});
}
stream.on('end', function() {
if (!errorEmitted) {
if (!isResponseReceived) {
callback({
code: StatusCode.UNKNOWN,
message: 'Incomplete response',
});
} else if (useUnaryResponse) {
callback(
null, responseReceived, null, null,
/* unaryResponseReceived= */ true);
} else {
callback(null, responseReceived);
}
}
if (useUnaryResponse) {
callback(null, null);
}
});
}
/**
* Encode the grpc-web request
*
* @private
* @param {!Uint8Array} serialized The serialized proto payload
* @return {!Uint8Array} The application/grpc-web padded request
*/
encodeRequest_(serialized) {
let len = serialized.length;
const bytesArray = [0, 0, 0, 0];
const payload = new Uint8Array(5 + len);
for (let i = 3; i >= 0; i--) {
bytesArray[i] = (len % 256);
len = len >>> 8;
}
payload.set(new Uint8Array(bytesArray), 1);
payload.set(serialized, 5);
return payload;
}
/**
* @private
* @param {!XhrIo} xhr The xhr object
*/
processHeaders_(xhr) {
if (this.format_ == 'text') {
xhr.headers.set('Content-Type', 'application/grpc-web-text');
xhr.headers.set('Accept', 'application/grpc-web-text');
} else {
xhr.headers.set('Content-Type', 'application/grpc-web+proto');
}
xhr.headers.set('X-User-Agent', 'grpc-web-javascript/0.1');
xhr.headers.set('X-Grpc-Web', '1');
if (xhr.headers.has('deadline')) {
const deadline = Number(xhr.headers.get('deadline')); // in ms
const currentTime = (new Date()).getTime();
let timeout = Math.ceil(deadline - currentTime);
xhr.headers.delete('deadline');
if (timeout === Infinity) {
// grpc-timeout header defaults to infinity if not set.
timeout = 0;
}
if (timeout > 0) {
xhr.headers.set('grpc-timeout', timeout + 'm');
// Also set timeout on the xhr request to terminate the HTTP request
// if the server doesn't respond within the deadline. We use 110% of
// grpc-timeout for this to allow the server to terminate the connection
// with DEADLINE_EXCEEDED rather than terminating it in the Browser, but
// at least 1 second in case the user is on a high-latency network.
xhr.setTimeoutInterval(Math.max(1000, Math.ceil(timeout * 1.1)));
}
}
}
/**
* @private
* @static
* @param {string} method The method to invoke
* @param {!Object<string,string>} headerObject The xhr headers
* @return {string} The URI object or a string path with headers
*/
static setCorsOverride_(method, headerObject) {
return /** @type {string} */ (HttpCors.setHttpHeadersWithOverwriteParam(
method, HttpCors.HTTP_HEADERS_PARAM_NAME, headerObject));
}
/**
* @private
* @static
* @template REQUEST, RESPONSE
* @param {function(!Request<REQUEST,RESPONSE>):
* (!Promise<RESPONSE>|!ClientReadableStream<RESPONSE>)} invoker
* @param {!Array<!UnaryInterceptor|!StreamInterceptor>}
* interceptors
* @return {function(!Request<REQUEST,RESPONSE>):
* (!Promise<RESPONSE>|!ClientReadableStream<RESPONSE>)}
*/
static runInterceptors_(invoker, interceptors) {
return interceptors.reduce((accumulatedInvoker, interceptor) => {
return (request) => interceptor.intercept(request, accumulatedInvoker);
}, invoker);
}
}
/**
* @private
* @static
* @param {string} method The method to invoke
* @param {!Object<string,string>} headerObject The xhr headers
* @return {string} The URI object or a string path with headers
*/
GrpcWebClientBase.setCorsOverride_ = function(method, headerObject) {
return /** @type {string} */ (HttpCors.setHttpHeadersWithOverwriteParam(
method, HttpCors.HTTP_HEADERS_PARAM_NAME, headerObject));
};
exports = GrpcWebClientBase;

View File

@ -18,179 +18,425 @@
goog.module('grpc.web.GrpcWebClientBaseTest');
goog.setTestOnly('grpc.web.GrpcWebClientBaseTest');
var GrpcWebClientBase = goog.require('grpc.web.GrpcWebClientBase');
var Map = goog.require('goog.structs.Map');
var googCrypt = goog.require('goog.crypt.base64');
var googEvents = goog.require('goog.events');
var testSuite = goog.require('goog.testing.testSuite');
const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const ErrorCode = goog.require('goog.net.ErrorCode');
const GrpcWebClientBase = goog.require('grpc.web.GrpcWebClientBase');
const MethodDescriptor = goog.require('grpc.web.MethodDescriptor');
const ReadyState = goog.require('goog.net.XmlHttp.ReadyState');
const Request = goog.requireType('grpc.web.Request');
const RpcError = goog.require('grpc.web.RpcError');
const StatusCode = goog.require('grpc.web.StatusCode');
const XhrIo = goog.require('goog.testing.net.XhrIo');
const googCrypt = goog.require('goog.crypt.base64');
const testSuite = goog.require('goog.testing.testSuite');
const {StreamInterceptor} = goog.require('grpc.web.Interceptor');
goog.require('goog.testing.jsunit');
var REQUEST_BYTES = [1,2,3];
var FAKE_METHOD = "fake-method";
var PROTO_FIELD_VALUE = "meow";
var EXPECTED_HEADERS;
var EXPECTED_HEADER_VALUES;
var EXPECTED_UNARY_HEADERS = ['Content-Type', 'Accept',
'X-User-Agent', 'X-Grpc-Web'];
var EXPECTED_UNARY_HEADER_VALUES = ['application/grpc-web-text',
'application/grpc-web-text',
'grpc-web-javascript/0.1',
'1'];
var dataCallback;
// This parses to [ { DATA: [4, 5, 6] }, { TRAILER: "a: b" } ]
const DEFAULT_RPC_RESPONSE =
new Uint8Array([0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98]);
const DEFAULT_RPC_RESPONSE_DATA = [4, 5, 6];
const DEFAULT_UNARY_HEADERS =
['Content-Type', 'Accept', 'X-User-Agent', 'X-Grpc-Web'];
const DEFAULT_UNARY_HEADER_VALUES = [
'application/grpc-web-text',
'application/grpc-web-text',
'grpc-web-javascript/0.1',
'1',
];
const DEFAULT_RESPONSE_HEADERS = {
'Content-Type': 'application/grpc-web-text',
};
testSuite({
setUp: function() {
googEvents.listen = function(a, b, listener, d, e) {
dataCallback = listener;
return;
};
},
tearDown: function() {
EXPECTED_HEADERS = null;
EXPECTED_HEADER_VALUES = null;
},
testRpcResponse: function() {
var client = new GrpcWebClientBase();
client.newXhr_ = function() {
return new MockXhr({
// This parses to [ { DATA: [4,5,6] }, { TRAILER: "a: b" } ]
response: googCrypt.encodeByteArray(new Uint8Array([
0, 0, 0, 0, 3, 4, 5, 6, 128, 0, 0, 0, 4, 97, 58, 32, 98
])),
});
};
expectUnaryHeaders();
client.rpcCall(FAKE_METHOD, {}, {}, {
requestSerializeFn : function(request) {
return REQUEST_BYTES;
},
responseDeserializeFn : function(bytes) {
assertElementsEquals([4,5,6], [].slice.call(bytes));
return {"field1": PROTO_FIELD_VALUE};
}
}, function(error, response) {
assertNull(error);
assertEquals(PROTO_FIELD_VALUE, response.field1);
async testRpcResponse() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value');
});
dataCallback();
const response = await new Promise((resolve, reject) => {
client.rpcCall(
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
(error, response) => {
assertNull(error);
resolve(response);
});
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
});
assertEquals('value', response.data);
const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
},
testRpcError: function() {
var client = new GrpcWebClientBase();
client.newXhr_ = function() {
return new MockXhr({
// This decodes to "grpc-status: 3"
response: googCrypt.encodeByteArray(new Uint8Array([
128, 0, 0, 0, 14, 103, 114, 112, 99, 45, 115, 116, 97, 116, 117, 115, 58, 32, 51
])),
});
};
expectUnaryHeaders();
client.rpcCall(FAKE_METHOD, {}, {}, {
requestSerializeFn : function(request) {
return REQUEST_BYTES;
},
responseDeserializeFn : function(bytes) {
return {};
}
}, function(error, response) {
assertNull(response);
assertEquals(3, error.code);
async testRpcFalsyResponse_ForNonProtobufDescriptor() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return 0;
});
dataCallback();
}
const response = await new Promise((resolve, reject) => {
client.rpcCall(
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
(error, response) => {
assertNull(error);
resolve(response);
});
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
});
assertEquals(0, response);
const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
},
async testRpcResponseThenableCall() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value');
});
const responsePromise = client.thenableCall(
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor);
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
const response = await responsePromise;
assertEquals('value', /** @type {!MockReply} */ (response).data);
const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
},
async testRpcFalsyResponseThenableCall_ForNonProtobufDescriptor() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return 0;
});
const responsePromise = client.thenableCall(
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor);
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
const response = await responsePromise;
assertEquals(0, response);
const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
assertElementsEquals(DEFAULT_UNARY_HEADERS, Object.keys(headers));
assertElementsEquals(DEFAULT_UNARY_HEADER_VALUES, Object.values(headers));
},
async testCancelledThenableCall() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return 0;
});
const abortController = new AbortController();
const signal = abortController.signal;
const responsePromise = client.thenableCall(
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
{signal});
abortController.abort();
const error = await assertRejects(responsePromise);
assertTrue(error instanceof RpcError);
assertEquals(StatusCode.CANCELLED, /** @type {!RpcError} */ (error).code);
assertEquals('Aborted', /** @type {!RpcError} */ (error).message);
// Default abort reason if none provided.
const cause = /** @type {!RpcError} */ (error).cause;
assertTrue(cause instanceof Error);
assertEquals('AbortError', /** @type {!Error} */ (cause).name);
assertEquals(ErrorCode.ABORT, xhr.getLastErrorCode());
},
async testCancelledThenableCallWithReason() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return 0;
});
const abortController = new AbortController();
const signal = abortController.signal;
const responsePromise = client.thenableCall(
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
{signal});
abortController.abort('cancelling');
const error = await assertRejects(responsePromise);
assertTrue(error instanceof RpcError);
assertEquals(StatusCode.CANCELLED, /** @type {!RpcError} */ (error).code);
assertEquals('Aborted', /** @type {!RpcError} */ (error).message);
// Abort reason forwarded as cause.
const cause = /** @type {!RpcError} */ (error).cause;
assertEquals('cancelling', cause);
assertEquals(ErrorCode.ABORT, xhr.getLastErrorCode());
},
async testDeadline() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => new MockReply());
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 1);
await new Promise((resolve, reject) => {
client.rpcCall(
'url', new MockRequest(), {'deadline': deadline.getTime().toString()},
methodDescriptor, (error, response) => {
assertNull(error);
resolve();
});
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
});
const headers = /** @type {!Object} */ (xhr.getLastRequestHeaders());
const headersWithDeadline = [...DEFAULT_UNARY_HEADERS, 'grpc-timeout'];
assertElementsEquals(headersWithDeadline, Object.keys(headers));
},
async testRpcError() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => new MockReply());
const error = await new Promise((resolve, reject) => {
client.rpcCall(
'urlurl', new MockRequest(), /* metadata= */ {}, methodDescriptor,
(error, response) => {
assertNull(response);
resolve(error);
});
// This decodes to "grpc-status: 3"
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array([
128, 0, 0, 0, 14, 103, 114, 112, 99, 45,
115, 116, 97, 116, 117, 115, 58, 32, 51,
])),
DEFAULT_RESPONSE_HEADERS);
});
assertTrue(error instanceof RpcError);
assertEquals(3, error.code);
},
async testRpcDeserializationError() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const responseDeserializeFn = () => {
throw new Error('Decoding error :)');
};
const methodDescriptor = createMethodDescriptor(responseDeserializeFn);
const error = await new Promise((resolve, reject) => {
client.rpcCall(
'urlurl', new MockRequest(), /* metadata= */ {}, methodDescriptor,
(error, response) => {
assertNull(response);
resolve(error);
});
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
});
assertTrue(error instanceof RpcError);
assertEquals(StatusCode.INTERNAL, error.code);
},
async testRpcResponseHeader() {
const xhr = new XhrIo();
const client = new GrpcWebClientBase(/* options= */ {}, xhr);
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value');
});
const metadata = await new Promise((resolve, reject) => {
const call = client.rpcCall(
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
(error, response) => {
assertNull(error);
});
call.on('metadata', (metadata) => {
resolve(metadata);
});
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)), {
'Content-Type': 'application/grpc-web-text',
'initial-metadata-key': 'initial-metadata-value',
});
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
});
assertEquals('initial-metadata-value', metadata['initial-metadata-key']);
},
async testStreamInterceptor() {
const xhr = new XhrIo();
const interceptor = new StreamResponseInterceptor();
const methodDescriptor = createMethodDescriptor((bytes) => {
assertElementsEquals(DEFAULT_RPC_RESPONSE_DATA, [].slice.call(bytes));
return new MockReply('value');
});
const client =
new GrpcWebClientBase({'streamInterceptors': [interceptor]}, xhr);
const response = await new Promise((resolve, reject) => {
client.rpcCall(
'url', new MockRequest(), /* metadata= */ {}, methodDescriptor,
(error, response) => {
assertNull(error);
resolve(response);
});
xhr.simulatePartialResponse(
googCrypt.encodeByteArray(new Uint8Array(DEFAULT_RPC_RESPONSE)),
DEFAULT_RESPONSE_HEADERS);
xhr.simulateReadyStateChange(ReadyState.COMPLETE);
});
assertEquals('Intercepted value', response.data);
},
});
/** Mocks a request proto object. */
class MockRequest {
/**
* @param {string=} data
*/
constructor(data = '') {
/** @type {string} */
this.data = data;
}
}
/** Sets expected headers as the unary response headers */
function expectUnaryHeaders() {
EXPECTED_HEADERS = EXPECTED_UNARY_HEADERS;
EXPECTED_HEADER_VALUES = EXPECTED_UNARY_HEADER_VALUES;
/** Mocks a response proto object. */
class MockReply {
/**
* @param {string=} data
*/
constructor(data = '') {
/** @type {string} */
this.data = data;
}
}
/**
* Typedef for allowed response types.
*
* Number is allowed specifically for supporting falsy responses `0`, see:
* https://github.com/grpc/grpc-web/pull/1025
*
* @typedef {!MockReply|number}
*/
let AllowedResponseType;
/**
* @param {function(string): !AllowedResponseType} responseDeSerializeFn
* @return {!MethodDescriptor<!MockRequest, !AllowedResponseType>}
*/
function createMethodDescriptor(responseDeSerializeFn) {
return new MethodDescriptor(
/* name= */ '', /* methodType= */ null, MockRequest, MockReply,
(request) => [1, 2, 3], responseDeSerializeFn);
}
/**
* @constructor
* @param {?Object} mockValues
* Mock XhrIO object to test the outgoing values
* @implements {StreamInterceptor}
* @unrestricted
*/
function MockXhr(mockValues) {
this.mockValues = mockValues;
this.headers = new Map();
class StreamResponseInterceptor {
constructor() {}
/**
* @override
* @template REQUEST, RESPONSE
* @param {!Request<REQUEST, RESPONSE>} request
* @param {function(!Request<REQUEST,RESPONSE>):
* !ClientReadableStream<RESPONSE>} invoker
* @return {!ClientReadableStream<RESPONSE>}
*/
intercept(request, invoker) {
return new InterceptedStream(invoker(request));
}
}
/**
* @param {string} url
* @param {string=} opt_method
* @param {string=} opt_content
* @param {string=} opt_headers
* @implements {ClientReadableStream}
* @template RESPONSE
* @final
*/
MockXhr.prototype.send = function(url, opt_method, opt_content, opt_headers) {
assertEquals(FAKE_METHOD, url);
assertEquals("POST", opt_method);
assertElementsEquals(googCrypt.encodeByteArray(new Uint8Array([0, 0, 0, 0, 3, 1, 2, 3])), opt_content);
assertElementsEquals(EXPECTED_HEADERS, this.headers.getKeys());
assertElementsEquals(EXPECTED_HEADER_VALUES, this.headers.getValues());
};
class InterceptedStream {
/**
* @param {!ClientReadableStream<RESPONSE>} stream
*/
constructor(stream) {
/** @const {!ClientReadableStream<RESPONSE>} */
this.stream = stream;
}
/**
* @override
* @param {string} eventType
* @param {function(?)} callback
* @return {!ClientReadableStream<RESPONSE>}
*/
on(eventType, callback) {
if (eventType == 'data') {
const newCallback = (response) => {
response.data = 'Intercepted ' + response.data;
callback(response);
};
this.stream.on(eventType, newCallback);
} else {
this.stream.on(eventType, callback);
}
return this;
}
/**
* @param {boolean} withCredentials
*/
MockXhr.prototype.setWithCredentials = function(withCredentials) {
return;
};
/**
* @override
* @return {!ClientReadableStream<RESPONSE>}
*/
cancel() {
this.stream.cancel();
return this;
}
/**
* @return {string} response
*/
MockXhr.prototype.getResponseText = function() {
return this.mockValues.response;
};
/**
* @return {string} response
*/
MockXhr.prototype.getResponseHeaders = function() {
return {};
};
/**
* @return {number} xhr state
*/
MockXhr.prototype.getReadyState = function() {
return 0;
};
/**
* @return {number} lastErrorCode
*/
MockXhr.prototype.getLastErrorCode = function() {
return 0;
};
/**
* @return {string} lastError
*/
MockXhr.prototype.getLastError = function() {
return 'server not responding';
};
/**
* @param {string} responseType
*/
MockXhr.prototype.setResponseType = function(responseType) {
return;
};
/**
* @override
* @param {string} eventType
* @param {function(?)} callback
* @return {!ClientReadableStream<RESPONSE>}
*/
removeListener(eventType, callback) {
this.stream.removeListener(eventType, callback);
return this;
}
}

View File

@ -35,9 +35,9 @@ const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const ErrorCode = goog.require('goog.net.ErrorCode');
const EventType = goog.require('goog.net.EventType');
const GrpcWebStreamParser = goog.require('grpc.web.GrpcWebStreamParser');
const RpcError = goog.require('grpc.web.RpcError');
const StatusCode = goog.require('grpc.web.StatusCode');
const XhrIo = goog.require('goog.net.XhrIo');
const XmlHttp = goog.require('goog.net.XmlHttp');
const events = goog.require('goog.events');
const googCrypt = goog.require('goog.crypt.base64');
const googString = goog.require('goog.string');
@ -46,222 +46,402 @@ const {Status} = goog.require('grpc.web.Status');
const GRPC_STATUS = "grpc-status";
const GRPC_STATUS_MESSAGE = "grpc-message";
const GRPC_STATUS = 'grpc-status';
const GRPC_STATUS_MESSAGE = 'grpc-message';
/** @type {!Array<string>} */
const EXCLUDED_RESPONSE_HEADERS =
['content-type', GRPC_STATUS, GRPC_STATUS_MESSAGE];
/**
* A stream that the client can read from. Used for calls that are streaming
* from the server side.
*
* @template RESPONSE
* @constructor
* @implements {ClientReadableStream}
* @final
* @param {!GenericTransportInterface} genericTransportInterface The
* GenericTransportInterface
* @unrestricted
*/
const GrpcWebClientReadableStream = function(genericTransportInterface) {
class GrpcWebClientReadableStream {
/**
* @const
* @private
* @type {?XhrIo} The XhrIo object
* @param {!GenericTransportInterface} genericTransportInterface The
* GenericTransportInterface
*/
this.xhr_ = /** @type {?XhrIo} */ (genericTransportInterface.xhr);
constructor(genericTransportInterface) {
/**
* @const
* @private
* @type {?XhrIo} The XhrIo object
*/
this.xhr_ = /** @type {?XhrIo} */ (genericTransportInterface.xhr);
/**
* @private
* @type {function(?):!RESPONSE|null} The deserialize function for the proto
*/
this.responseDeserializeFn_ = null;
/**
* @private
* @type {function(?):!RESPONSE|null} The deserialize function for the proto
*/
this.responseDeserializeFn_ = null;
/**
* @private
* @type {function(!RESPONSE)|null} The data callback
*/
this.onDataCallback_ = null;
/**
* @const
* @private
* @type {!Array<function(!RESPONSE)>} The list of data callbacks
*/
this.onDataCallbacks_ = [];
/**
* @private
* @type {function(!Status)|null} The status callback
*/
this.onStatusCallback_ = null;
/**
* @const
* @private
* @type {!Array<function(!Status)>} The list of status callbacks
*/
this.onStatusCallbacks_ = [];
/**
* @private
* @type {function(...):?|null} The error callback
*/
this.onErrorCallback_ = null;
/**
* @const
* @private
* @type {!Array<function(!Metadata)>} The list of metadata callbacks
*/
this.onMetadataCallbacks_ = [];
/**
* @private
* @type {function(...):?|null} The stream end callback
*/
this.onEndCallback_ = null;
/**
* @const
* @private
* @type {!Array<function(!RpcError)>} The list of error callbacks
*/
this.onErrorCallbacks_ = [];
/**
* @private
* @type {number} The stream parser position
*/
this.pos_ = 0;
/**
* @const
* @private
* @type {!Array<function(...):?>} The list of stream end callbacks
*/
this.onEndCallbacks_ = [];
/**
* @private
* @type {!GrpcWebStreamParser} The grpc-web stream parser
* @const
*/
this.parser_ = new GrpcWebStreamParser();
/**
* @private
* @type {boolean} Whether the stream has been aborted
*/
this.aborted_ = false;
var self = this;
events.listen(this.xhr_, EventType.READY_STATE_CHANGE,
function(e) {
var contentType = self.xhr_.getStreamingResponseHeader('Content-Type');
if (!contentType) return;
contentType = contentType.toLowerCase();
/**
* @private
* @type {number} The stream parser position
*/
this.pos_ = 0;
if (googString.startsWith(contentType, 'application/grpc-web-text')) {
var responseText = self.xhr_.getResponseText();
var newPos = responseText.length - responseText.length % 4;
var newData = responseText.substr(self.pos_, newPos - self.pos_);
if (newData.length == 0) return;
self.pos_ = newPos;
var byteSource = googCrypt.decodeStringToUint8Array(newData);
} else if (googString.startsWith(contentType, 'application/grpc')) {
var byteSource = new Uint8Array(
/** @type {!ArrayBuffer} */ (self.xhr_.getResponse()));
} else {
return;
}
var messages = self.parser_.parse([].slice.call(byteSource));
if (!messages) return;
/**
* @private
* @type {!GrpcWebStreamParser} The grpc-web stream parser
* @const
*/
this.parser_ = new GrpcWebStreamParser();
var FrameType = GrpcWebStreamParser.FrameType;
for (var i = 0; i < messages.length; i++) {
if (FrameType.DATA in messages[i]) {
var data = messages[i][FrameType.DATA];
if (data) {
var response = self.responseDeserializeFn_(data);
if (response) {
self.onDataCallback_(response);
const self = this;
events.listen(this.xhr_, EventType.READY_STATE_CHANGE, function(e) {
let contentType = self.xhr_.getStreamingResponseHeader('Content-Type');
if (!contentType) return;
contentType = contentType.toLowerCase();
let byteSource;
if (googString.startsWith(contentType, 'application/grpc-web-text')) {
// Ensure responseText is not null
const responseText = self.xhr_.getResponseText() || '';
const newPos = responseText.length - responseText.length % 4;
const newData = responseText.substr(self.pos_, newPos - self.pos_);
if (newData.length == 0) return;
self.pos_ = newPos;
byteSource = googCrypt.decodeStringToUint8Array(newData);
} else if (googString.startsWith(contentType, 'application/grpc')) {
byteSource = new Uint8Array(
/** @type {!ArrayBuffer} */ (self.xhr_.getResponse()));
} else {
self.handleError_(
new RpcError(StatusCode.UNKNOWN, 'Unknown Content-type received.'));
return;
}
let messages = null;
try {
messages = self.parser_.parse(byteSource);
} catch (err) {
self.handleError_(
new RpcError(StatusCode.UNKNOWN, 'Error in parsing response body'));
}
if (messages) {
const FrameType = GrpcWebStreamParser.FrameType;
for (let i = 0; i < messages.length; i++) {
if (FrameType.DATA in messages[i]) {
const data = messages[i][FrameType.DATA];
if (data) {
let isResponseDeserialized = false;
let response;
try {
response = self.responseDeserializeFn_(data);
isResponseDeserialized = true;
} catch (err) {
self.handleError_(new RpcError(
StatusCode.INTERNAL,
`Error when deserializing response data; error: ${err}` +
`, response: ${response}`));
}
if (isResponseDeserialized) {
self.sendDataCallbacks_(response);
}
}
}
if (FrameType.TRAILER in messages[i]) {
if (messages[i][FrameType.TRAILER].length > 0) {
let trailerString = '';
for (let pos = 0; pos < messages[i][FrameType.TRAILER].length;
pos++) {
trailerString +=
String.fromCharCode(messages[i][FrameType.TRAILER][pos]);
}
const trailers = self.parseHttp1Headers_(trailerString);
let grpcStatusCode = StatusCode.OK;
let grpcStatusMessage = '';
if (GRPC_STATUS in trailers) {
grpcStatusCode =
/** @type {!StatusCode} */ (Number(trailers[GRPC_STATUS]));
delete trailers[GRPC_STATUS];
}
if (GRPC_STATUS_MESSAGE in trailers) {
grpcStatusMessage = trailers[GRPC_STATUS_MESSAGE];
delete trailers[GRPC_STATUS_MESSAGE];
}
self.handleError_(
new RpcError(grpcStatusCode, grpcStatusMessage, trailers));
}
}
}
}
if (FrameType.TRAILER in messages[i]) {
if (messages[i][FrameType.TRAILER].length > 0) {
var trailerString = "";
for (var pos = 0; pos < messages[i][FrameType.TRAILER].length;
pos++) {
trailerString += String.fromCharCode(
messages[i][FrameType.TRAILER][pos]);
}
var trailers = self.parseHttp1Headers_(trailerString);
var grpcStatusCode = StatusCode.OK;
var grpcStatusMessage = "";
if (GRPC_STATUS in trailers) {
grpcStatusCode = trailers[GRPC_STATUS];
}
if (GRPC_STATUS_MESSAGE in trailers) {
grpcStatusMessage = trailers[GRPC_STATUS_MESSAGE];
}
if (self.onStatusCallback_) {
self.onStatusCallback_({
code: Number(grpcStatusCode),
details: grpcStatusMessage,
metadata: trailers,
});
}
});
events.listen(this.xhr_, EventType.COMPLETE, function(e) {
const lastErrorCode = self.xhr_.getLastErrorCode();
let grpcStatusCode = StatusCode.UNKNOWN;
let grpcStatusMessage = '';
const initialMetadata = /** @type {!Metadata} */ ({});
// Get response headers with lower case keys.
const rawResponseHeaders = self.xhr_.getResponseHeaders();
const responseHeaders = {};
for (const key in rawResponseHeaders) {
if (rawResponseHeaders.hasOwnProperty(key)) {
responseHeaders[key.toLowerCase()] = rawResponseHeaders[key];
}
}
}
var readyState = self.xhr_.getReadyState();
if (readyState == XmlHttp.ReadyState.COMPLETE) {
if (self.onEndCallback_) {
self.onEndCallback_();
Object.keys(responseHeaders).forEach((header_) => {
if (!(EXCLUDED_RESPONSE_HEADERS.includes(header_))) {
initialMetadata[header_] = responseHeaders[header_];
}
});
self.sendMetadataCallbacks_(initialMetadata);
// There's an XHR level error
let xhrStatusCode = -1;
if (lastErrorCode != ErrorCode.NO_ERROR) {
switch (lastErrorCode) {
case ErrorCode.ABORT:
grpcStatusCode = StatusCode.ABORTED;
break;
case ErrorCode.TIMEOUT:
grpcStatusCode = StatusCode.DEADLINE_EXCEEDED;
break;
case ErrorCode.HTTP_ERROR:
xhrStatusCode = self.xhr_.getStatus();
grpcStatusCode = StatusCode.fromHttpStatus(xhrStatusCode);
break;
default:
grpcStatusCode = StatusCode.UNAVAILABLE;
}
if (grpcStatusCode == StatusCode.ABORTED && self.aborted_) {
return;
}
let errorMessage = ErrorCode.getDebugMessage(lastErrorCode);
if (xhrStatusCode != -1) {
errorMessage += ', http status code: ' + xhrStatusCode;
}
self.handleError_(new RpcError(grpcStatusCode, errorMessage));
return;
}
return;
}
});
events.listen(this.xhr_, EventType.COMPLETE, function(e) {
if (!self.onErrorCallback_) return;
var lastErrorCode = self.xhr_.getLastErrorCode();
if (lastErrorCode != ErrorCode.NO_ERROR) {
self.onErrorCallback_({
code: StatusCode.UNAVAILABLE,
message: ErrorCode.getDebugMessage(lastErrorCode)
});
return;
}
var responseHeaders = self.xhr_.getResponseHeaders();
if (GRPC_STATUS in responseHeaders &&
responseHeaders[GRPC_STATUS] != StatusCode.OK) {
self.onErrorCallback_({
code: Number(responseHeaders[GRPC_STATUS]),
message: responseHeaders[GRPC_STATUS_MESSAGE]
});
}
});
};
let errorEmitted = false;
// Check whethere there are grpc specific response headers
if (GRPC_STATUS in responseHeaders) {
grpcStatusCode = /** @type {!StatusCode} */ (
Number(responseHeaders[GRPC_STATUS]));
if (GRPC_STATUS_MESSAGE in responseHeaders) {
grpcStatusMessage = responseHeaders[GRPC_STATUS_MESSAGE];
}
if (grpcStatusCode != StatusCode.OK) {
self.handleError_(new RpcError(
grpcStatusCode, grpcStatusMessage || '', responseHeaders));
errorEmitted = true;
}
}
/**
* @override
*/
GrpcWebClientReadableStream.prototype.on = function(
eventType, callback) {
// TODO(stanleycheung): change eventType to @enum type
if (eventType == 'data') {
this.onDataCallback_ = callback;
} else if (eventType == 'status') {
this.onStatusCallback_ = callback;
} else if (eventType == 'end') {
this.onEndCallback_ = callback;
} else if (eventType == 'error') {
this.onErrorCallback_ = callback;
if (!errorEmitted) {
self.sendEndCallbacks_();
}
});
}
return this;
};
/**
* Register a callbackl to parse the response
*
* @param {function(?):!RESPONSE} responseDeserializeFn The deserialize
* function for the proto
*/
GrpcWebClientReadableStream.prototype.setResponseDeserializeFn =
function(responseDeserializeFn) {
this.responseDeserializeFn_ = responseDeserializeFn;
};
/**
* @override
*/
GrpcWebClientReadableStream.prototype.cancel = function() {
this.xhr_.abort();
};
/**
* Parse HTTP headers
*
* @private
* @param {string} str The raw http header string
* @return {!Object} The header:value pairs
*/
GrpcWebClientReadableStream.prototype.parseHttp1Headers_ =
function(str) {
var chunks = str.trim().split("\r\n");
var headers = {};
for (var i = 0; i < chunks.length; i++) {
var pos = chunks[i].indexOf(":");
headers[chunks[i].substring(0, pos).trim()] =
chunks[i].substring(pos+1).trim();
/**
* @override
* @export
*/
on(eventType, callback) {
// TODO(stanleycheung): change eventType to @enum type
if (eventType == 'data') {
this.onDataCallbacks_.push(callback);
} else if (eventType == 'status') {
this.onStatusCallbacks_.push(callback);
} else if (eventType == 'metadata') {
this.onMetadataCallbacks_.push(callback);
} else if (eventType == 'end') {
this.onEndCallbacks_.push(callback);
} else if (eventType == 'error') {
this.onErrorCallbacks_.push(callback);
}
return this;
}
return headers;
};
/**
* @private
* @param {!Array<function(?)>} callbacks the internal list of callbacks
* @param {function(?)} callback the callback to remove
*/
removeListenerFromCallbacks_(callbacks, callback) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
/**
* @export
* @override
*/
removeListener(eventType, callback) {
if (eventType == 'data') {
this.removeListenerFromCallbacks_(this.onDataCallbacks_, callback);
} else if (eventType == 'status') {
this.removeListenerFromCallbacks_(this.onStatusCallbacks_, callback);
} else if (eventType == 'metadata') {
this.removeListenerFromCallbacks_(this.onMetadataCallbacks_, callback);
} else if (eventType == 'end') {
this.removeListenerFromCallbacks_(this.onEndCallbacks_, callback);
} else if (eventType == 'error') {
this.removeListenerFromCallbacks_(this.onErrorCallbacks_, callback);
}
return this;
}
/**
* Register a callbackl to parse the response
*
* @param {function(?):!RESPONSE} responseDeserializeFn The deserialize
* function for the proto
*/
setResponseDeserializeFn(responseDeserializeFn) {
this.responseDeserializeFn_ = responseDeserializeFn;
}
/**
* @override
* @export
*/
cancel() {
this.aborted_ = true;
this.xhr_.abort();
}
/**
* Parse HTTP headers
*
* @private
* @param {string} str The raw http header string
* @return {!Object} The header:value pairs
*/
parseHttp1Headers_(str) {
const chunks = str.trim().split('\r\n');
const headers = {};
for (let i = 0; i < chunks.length; i++) {
const pos = chunks[i].indexOf(':');
headers[chunks[i].substring(0, pos).trim()] =
chunks[i].substring(pos + 1).trim();
}
return headers;
}
/**
* A central place to handle errors
*
* @private
* @param {!RpcError} error The error object
*/
handleError_(error) {
if (error.code != StatusCode.OK) {
this.sendErrorCallbacks_(new RpcError(
error.code, decodeURIComponent(error.message || ''), error.metadata));
}
this.sendStatusCallbacks_(/** @type {!Status} */ ({
code: error.code,
details: decodeURIComponent(error.message || ''),
metadata: error.metadata
}));
}
/**
* @private
* @param {!RESPONSE} data The data to send back
*/
sendDataCallbacks_(data) {
for (let i = 0; i < this.onDataCallbacks_.length; i++) {
this.onDataCallbacks_[i](data);
}
}
/**
* @private
* @param {!Status} status The status to send back
*/
sendStatusCallbacks_(status) {
for (let i = 0; i < this.onStatusCallbacks_.length; i++) {
this.onStatusCallbacks_[i](status);
}
}
/**
* @private
* @param {!Metadata} metadata The metadata to send back
*/
sendMetadataCallbacks_(metadata) {
for (let i = 0; i < this.onMetadataCallbacks_.length; i++) {
this.onMetadataCallbacks_[i](metadata);
}
}
/**
* @private
* @param {!RpcError} error The error to send back
*/
sendErrorCallbacks_(error) {
for (let i = 0; i < this.onErrorCallbacks_.length; i++) {
this.onErrorCallbacks_[i](error);
}
}
/**
* @private
*/
sendEndCallbacks_() {
for (let i = 0; i < this.onEndCallbacks_.length; i++) {
this.onEndCallbacks_[i]();
}
}
}

View File

@ -49,71 +49,210 @@ const asserts = goog.require('goog.asserts');
/**
* The default grpc-web stream parser.
*
* @constructor
* @struct
* @implements {StreamParser}
* @final
*/
const GrpcWebStreamParser = function() {
/**
* The current error message, if any.
* @private {?string}
*/
this.errorMessage_ = null;
class GrpcWebStreamParser {
constructor() {
/**
* The current error message, if any.
* @private {?string}
*/
this.errorMessage_ = null;
/**
* The currently buffered result (parsed messages).
* @private {!Array<!Object>}
*/
this.result_ = [];
/**
* The current position in the streamed data.
* @private {number}
*/
this.streamPos_ = 0;
/**
* The current parser state.
* @private {number}
*/
this.state_ = Parser.State_.INIT;
/**
* The current frame byte being parsed
* @private {number}
*/
this.frame_ = 0;
/**
* The length of the proto message being parsed.
* @private {number}
*/
this.length_ = 0;
/**
* Count of processed length bytes.
* @private {number}
*/
this.countLengthBytes_ = 0;
/**
* Raw bytes of the current message. Uses Uint8Array by default. Falls back
* to native array when Uint8Array is unsupported.
* @private {?Uint8Array|?Array<number>}
*/
this.messageBuffer_ = null;
/**
* Count of processed message bytes.
* @private {number}
*/
this.countMessageBytes_ = 0;
}
/**
* The currently buffered result (parsed messages).
* @private {!Array<!Object>}
* @override
*/
this.result_ = [];
isInputValid() {
return this.state_ != Parser.State_.INVALID;
}
/**
* The current position in the streamed data.
* @private {number}
* @override
*/
this.streamPos_ = 0;
getErrorMessage() {
return this.errorMessage_;
}
/**
* The current parser state.
* @private {number}
* @override
* @return {boolean}
*/
this.state_ = Parser.State_.INIT;
acceptsBinaryInput() {
return true;
}
/**
* The current frame byte being parsed
* @private {number}
* Parse the new input.
*
* Note that there is no Parser state to indicate the end of a stream.
*
* @param {string|!ArrayBuffer|!Uint8Array|!Array<number>} input The input
* data
* @throws {!Error} Throws an error message if the input is invalid.
* @return {?Array<string|!Object>} any parsed objects (atomic messages)
* in an array, or null if more data needs be read to parse any new object.
* @override
*/
this.frame_ = 0;
parse(input) {
asserts.assert(
input instanceof Array || input instanceof ArrayBuffer ||
input instanceof Uint8Array);
/**
* The length of the proto message being parsed.
* @private {number}
*/
this.length_ = 0;
var parser = this;
var inputBytes;
var pos = 0;
/**
* Count of processed length bytes.
* @private {number}
*/
this.countLengthBytes_ = 0;
if (input instanceof Uint8Array || input instanceof Array) {
inputBytes = input;
} else {
inputBytes = new Uint8Array(input);
}
/**
* Raw bytes of the current message. Uses Uint8Array by default. Falls back to
* native array when Uint8Array is unsupported.
* @private {?Uint8Array|?Array<number>}
*/
this.messageBuffer_ = null;
while (pos < inputBytes.length) {
switch (parser.state_) {
case Parser.State_.INVALID: {
parser.error_(inputBytes, pos, 'stream already broken');
break;
}
case Parser.State_.INIT: {
processFrameByte(inputBytes[pos]);
break;
}
case Parser.State_.LENGTH: {
processLengthByte(inputBytes[pos]);
break;
}
case Parser.State_.MESSAGE: {
processMessageByte(inputBytes[pos]);
break;
}
default: {
throw new Error('unexpected parser state: ' + parser.state_);
}
}
/**
* Count of processed message bytes.
* @private {number}
*/
this.countMessageBytes_ = 0;
};
parser.streamPos_++;
pos++;
}
var msgs = parser.result_;
parser.result_ = [];
return msgs.length > 0 ? msgs : null;
/**
* @param {number} b A frame byte to process
*/
function processFrameByte(b) {
if (b == FrameType.DATA) {
parser.frame_ = b;
} else if (b == FrameType.TRAILER) {
parser.frame_ = b;
} else {
parser.error_(inputBytes, pos, 'invalid frame byte');
}
parser.state_ = Parser.State_.LENGTH;
parser.length_ = 0;
parser.countLengthBytes_ = 0;
}
/**
* @param {number} b A length byte to process
*/
function processLengthByte(b) {
parser.countLengthBytes_++;
parser.length_ = (parser.length_ << 8) + b;
if (parser.countLengthBytes_ == 4) { // no more length byte
parser.state_ = Parser.State_.MESSAGE;
parser.countMessageBytes_ = 0;
if (typeof Uint8Array !== 'undefined') {
parser.messageBuffer_ = new Uint8Array(parser.length_);
} else {
parser.messageBuffer_ = new Array(parser.length_);
}
if (parser.length_ == 0) { // empty message
finishMessage();
}
}
}
/**
* @param {number} b A message byte to process
*/
function processMessageByte(b) {
parser.messageBuffer_[parser.countMessageBytes_++] = b;
if (parser.countMessageBytes_ == parser.length_) {
finishMessage();
}
}
/**
* Finishes up building the current message and resets parser state
*/
function finishMessage() {
var message = {};
message[parser.frame_] = parser.messageBuffer_;
parser.result_.push(message);
parser.state_ = Parser.State_.INIT;
}
}
}
var Parser = GrpcWebStreamParser;
const Parser = GrpcWebStreamParser;
/**
@ -133,29 +272,14 @@ Parser.State_ = {
* @enum {number}
*/
GrpcWebStreamParser.FrameType = {
DATA: 0x00, // expecting a data frame
TRAILER: 0x80, // expecting a trailer frame
DATA: 0x00, // expecting a data frame
TRAILER: 0x80, // expecting a trailer frame
};
var FrameType = GrpcWebStreamParser.FrameType;
/**
* @override
*/
GrpcWebStreamParser.prototype.isInputValid = function() {
return this.state_ != Parser.State_.INVALID;
};
/**
* @override
*/
GrpcWebStreamParser.prototype.getErrorMessage = function() {
return this.errorMessage_;
};
/**
* @param {!Uint8Array|!Array<number>} inputBytes The current input buffer
@ -174,106 +298,5 @@ Parser.prototype.error_ = function(inputBytes, pos, errorMsg) {
};
/**
* @throws {!Error} Throws an error message if the input is invalid.
* @override
*/
GrpcWebStreamParser.prototype.parse = function(input) {
asserts.assert(input instanceof Array || input instanceof ArrayBuffer);
var parser = this;
var inputBytes = (input instanceof Array) ? input : new Uint8Array(input);
var pos = 0;
while (pos < inputBytes.length) {
switch (parser.state_) {
case Parser.State_.INVALID: {
parser.error_(inputBytes, pos, 'stream already broken');
break;
}
case Parser.State_.INIT: {
processFrameByte(inputBytes[pos]);
break;
}
case Parser.State_.LENGTH: {
processLengthByte(inputBytes[pos]);
break;
}
case Parser.State_.MESSAGE: {
processMessageByte(inputBytes[pos]);
break;
}
default: { throw new Error('unexpected parser state: ' + parser.state_); }
}
parser.streamPos_++;
pos++;
}
var msgs = parser.result_;
parser.result_ = [];
return msgs.length > 0 ? msgs : null;
/**
* @param {number} b A frame byte to process
*/
function processFrameByte(b) {
if (b == FrameType.DATA) {
parser.frame_ = b;
} else if (b == FrameType.TRAILER) {
parser.frame_ = b;
} else {
parser.error_(inputBytes, pos, 'invalid frame byte');
}
parser.state_ = Parser.State_.LENGTH;
parser.length_ = 0;
parser.countLengthBytes_ = 0;
}
/**
* @param {number} b A length byte to process
*/
function processLengthByte(b) {
parser.countLengthBytes_++;
parser.length_ = (parser.length_ << 8) + b;
if (parser.countLengthBytes_ == 4) { // no more length byte
parser.state_ = Parser.State_.MESSAGE;
parser.countMessageBytes_ = 0;
if (typeof Uint8Array !== 'undefined') {
parser.messageBuffer_ = new Uint8Array(parser.length_);
} else {
parser.messageBuffer_ = new Array(parser.length_);
}
if (parser.length_ == 0) { // empty message
finishMessage();
}
}
}
/**
* @param {number} b A message byte to process
*/
function processMessageByte(b) {
parser.messageBuffer_[parser.countMessageBytes_++] = b;
if (parser.countMessageBytes_ == parser.length_) {
finishMessage();
}
}
/**
* Finishes up building the current message and resets parser state
*/
function finishMessage() {
var message = {};
message[parser.frame_] = parser.messageBuffer_;
parser.result_.push(message);
parser.state_ = Parser.State_.INIT;
}
};
exports = GrpcWebStreamParser;

View File

@ -0,0 +1,78 @@
/**
* @fileoverview grpc-web client interceptors.
*
* The type of interceptors is determined by the response type of the RPC call.
* gRPC-Web has two generated clients for one service:
* FooServiceClient and FooServicePromiseClient. The response type of
* FooServiceClient is ClientReadableStream for BOTH unary calls and server
* streaming calls, so StreamInterceptor is expected to be used for intercepting
* FooServiceClient calls. The response type of PromiseClient is Promise, so use
* UnaryInterceptor for PromiseClients.
*/
goog.module('grpc.web.Interceptor');
goog.module.declareLegacyNamespace();
const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const Request = goog.require('grpc.web.Request');
const UnaryResponse = goog.require('grpc.web.UnaryResponse');
/**
* Interceptor for RPC calls with response type `UnaryResponse`.
* An example implementation of UnaryInterceptor
* <pre>
* TestUnaryInterceptor.prototype.intercept = function(request, invoker) {
* const newRequest = ...
* return invoker(newRequest).then((response) => {
* // Do something with response.getMetadata
// Do something with response.getResponseMessage
* return response;
* });
* };
* </pre>
* @interface
*/
const UnaryInterceptor = function() {};
/**
* @export
* @abstract
* @template REQUEST, RESPONSE
* @param {!Request<REQUEST, RESPONSE>} request
* @param {function(!Request<REQUEST,RESPONSE>):!Promise<!UnaryResponse<RESPONSE>>}
* invoker
* @return {!Promise<!UnaryResponse<RESPONSE>>}
*/
UnaryInterceptor.prototype.intercept = function(request, invoker) {};
/**
* Interceptor for RPC calls with response type `ClientReadableStream`.
*
* Two steps to create a stream interceptor:
* <1>Create a new subclass of ClientReadableStream that wraps around the
* original stream and overrides its methods. <2>Create a new subclass of
* StreamInterceptor. While implementing the
* StreamInterceptor.prototype.intercept method, return the wrapped
* ClientReadableStream.
* @interface
*/
const StreamInterceptor = function() {};
/**
* @export
* @abstract
* @template REQUEST, RESPONSE
* @param {!Request<REQUEST, RESPONSE>} request
* @param {function(!Request<REQUEST,RESPONSE>):!ClientReadableStream<RESPONSE>}
* invoker
* @return {!ClientReadableStream<RESPONSE>}
*/
StreamInterceptor.prototype.intercept = function(request, invoker) {};
exports = {
UnaryInterceptor,
StreamInterceptor
};

View File

@ -0,0 +1,15 @@
/**
* @fileoverview grpc-web request/response metadata.
*
* Request and response headers will be included in the Metadata.
*/
goog.module('grpc.web.Metadata');
goog.module.declareLegacyNamespace();
/**
* @typedef {!Object<string,string>}
*/
let Metadata;
exports = Metadata;

View File

@ -0,0 +1,119 @@
/**
* @fileoverview Description of this file.
*
* A templated class that is used to address gRPC Web requests.
*/
goog.module('grpc.web.MethodDescriptor');
goog.module.declareLegacyNamespace();
const CallOptions = goog.require('grpc.web.CallOptions');
const Metadata = goog.requireType('grpc.web.Metadata');
const MethodDescriptorInterface = goog.requireType('grpc.web.MethodDescriptorInterface');
const MethodType = goog.requireType('grpc.web.MethodType');
const Request = goog.requireType('grpc.web.Request');
const RequestInternal = goog.require('grpc.web.RequestInternal');
const UnaryResponse = goog.requireType('grpc.web.UnaryResponse');
const UnaryResponseInternal = goog.require('grpc.web.UnaryResponseInternal');
const {Status} = goog.requireType('grpc.web.Status');
/**
* @final
* @implements {MethodDescriptorInterface<REQUEST, RESPONSE>}
* @template REQUEST, RESPONSE
* @unrestricted
*/
const MethodDescriptor = class {
/**
* @param {string} name
* @param {?MethodType} methodType
* @param {function(new: REQUEST, ...)} requestType
* @param {function(new: RESPONSE, ...)} responseType
* @param {function(REQUEST): ?} requestSerializeFn
* @param {function(?): RESPONSE} responseDeserializeFn
*/
constructor(
name, methodType, requestType, responseType, requestSerializeFn,
responseDeserializeFn) {
/** @const */
this.name = name;
/** @const */
this.methodType = methodType;
/** @const */
this.requestType = requestType;
/** @const */
this.responseType = responseType;
/** @const */
this.requestSerializeFn = requestSerializeFn;
/** @const */
this.responseDeserializeFn = responseDeserializeFn;
}
/**
* @override
* @param {REQUEST} requestMessage
* @param {!Metadata=} metadata
* @param {!CallOptions=} callOptions
* @return {!Request<REQUEST, RESPONSE>}
*/
createRequest(
requestMessage, metadata = {}, callOptions = new CallOptions()) {
return new RequestInternal(requestMessage, this, metadata, callOptions);
}
/**
* @override
* @param {RESPONSE} responseMessage
* @param {!Metadata=} metadata
* @param {?Status=} status
* @return {!UnaryResponse<REQUEST, RESPONSE>}
*/
createUnaryResponse(responseMessage, metadata = {}, status = null) {
return new UnaryResponseInternal(responseMessage, this, metadata, status);
}
/**
* @override
* @export
*/
getName() {
return this.name;
}
/**
* @override
*/
getMethodType() {
return this.methodType;
}
/**
* @override
* @return {function(new: RESPONSE, ...)}
*/
getResponseMessageCtor() {
return this.responseType;
}
/**
* @override
* @return {function(new: REQUEST, ...)}
*/
getRequestMessageCtor() {
return this.requestType;
}
/** @override */
getResponseDeserializeFn() {
return this.responseDeserializeFn;
}
/** @override */
getRequestSerializeFn() {
return this.requestSerializeFn;
}
};
exports = MethodDescriptor;

View File

@ -0,0 +1,60 @@
/**
* @fileoverview Description of this file.
*
* A templated class that is used to address gRPC Web requests.
*/
goog.module('grpc.web.MethodDescriptorInterface');
goog.module.declareLegacyNamespace();
const CallOptions = goog.requireType('grpc.web.CallOptions');
const Metadata = goog.requireType('grpc.web.Metadata');
const MethodType = goog.requireType('grpc.web.MethodType');
const Request = goog.requireType('grpc.web.Request');
const UnaryResponse = goog.requireType('grpc.web.UnaryResponse');
const {Status} = goog.requireType('grpc.web.Status');
/**
* @interface
* @template REQUEST, RESPONSE
*/
const MethodDescriptorInterface = function() {};
/**
* @param {REQUEST} requestMessage
* @param {!Metadata=} metadata
* @param {!CallOptions=} callOptions
* @return {!Request<REQUEST, RESPONSE>}
*/
MethodDescriptorInterface.prototype.createRequest = function(
requestMessage, metadata, callOptions) {};
/**
* @param {RESPONSE} responseMessage
* @param {!Metadata=} metadata
* @param {?Status=} status
* @return {!UnaryResponse<REQUEST, RESPONSE>}
*/
MethodDescriptorInterface.prototype.createUnaryResponse = function(
responseMessage, metadata, status) {};
/** @return {string} */
MethodDescriptorInterface.prototype.getName = function() {};
/** @return {?MethodType} */
MethodDescriptorInterface.prototype.getMethodType = function() {};
/** @return {function(new: RESPONSE, ?Array=)} */
MethodDescriptorInterface.prototype.getResponseMessageCtor = function() {};
/** @return {function(new: REQUEST, ?Array=)} */
MethodDescriptorInterface.prototype.getRequestMessageCtor = function() {};
/** @return {function(?): RESPONSE} */
MethodDescriptorInterface.prototype.getResponseDeserializeFn = function() {};
/** @return {function(REQUEST): ?} */
MethodDescriptorInterface.prototype.getRequestSerializeFn = function() {};
exports = MethodDescriptorInterface;

View File

@ -0,0 +1,24 @@
/**
* @fileoverview gRPC-Web method types.
*/
goog.module('grpc.web.MethodType');
goog.module.declareLegacyNamespace();
/**
* Available method types:
* MethodType.UNARY: unary request and unary response.
* MethodType.SERVER_STREAMING: unary request and streaming responses.
* MethodType.BIDI_STREAMING: streaming requests and streaming responses.
*
* @enum {string}
*/
const MethodType = {
'UNARY': 'unary',
'SERVER_STREAMING': 'server_streaming',
// Bidi streaming is experimental. Do not use.
'BIDI_STREAMING': 'bidi_streaming',
};
exports = MethodType;

View File

@ -0,0 +1,60 @@
/**
* @fileoverview A templated class that is used to address an individual
* gRPC-Web request instance.
*/
goog.module('grpc.web.Request');
goog.module.declareLegacyNamespace();
const CallOptions = goog.require('grpc.web.CallOptions');
const Metadata = goog.require('grpc.web.Metadata');
const MethodDescriptorInterface = goog.requireType('grpc.web.MethodDescriptorInterface');
/**
* @interface
* @template REQUEST, RESPONSE
*/
class Request {
/**
* @export
* @return {REQUEST}
*/
getRequestMessage() {}
/**
* @export
* @return {!MethodDescriptorInterface<REQUEST, RESPONSE>}
*/
getMethodDescriptor() {}
/**
* @export
* @return {!Metadata}
*/
getMetadata() {}
/**
* Client CallOptions. Note that CallOptions has not been implemented in
* grpc.web.AbstractClientbase yet, but will be used in
* grpc.web.GenericClient.
* @export
* @return {!CallOptions|undefined}
*/
getCallOptions() {}
/**
* @param {string} key
* @param {string} value
* @return {!Request<REQUEST, RESPONSE>}
*/
withMetadata(key, value) {}
/**
* @param {string} name
* @param {VALUE} value
* @template VALUE
* @return {!Request<REQUEST, RESPONSE>}
*/
withGrpcCallOption(name, value) {}
}
exports = Request;

View File

@ -0,0 +1,94 @@
/**
* @fileoverview Internal implementation of grpc.web.Request.
*/
goog.module('grpc.web.RequestInternal');
goog.module.declareLegacyNamespace();
const CallOptions = goog.require('grpc.web.CallOptions');
const Metadata = goog.require('grpc.web.Metadata');
const MethodDescriptor = goog.requireType('grpc.web.MethodDescriptor');
const Request = goog.require('grpc.web.Request');
/**
* @template REQUEST, RESPONSE
* @implements {Request<REQUEST, RESPONSE>}
* @final
* @package
*/
class RequestInternal {
/**
* @param {REQUEST} requestMessage
* @param {!MethodDescriptor<REQUEST, RESPONSE>} methodDescriptor
* @param {!Metadata} metadata
* @param {!CallOptions} callOptions
*/
constructor(requestMessage, methodDescriptor, metadata, callOptions) {
/**
* @const {REQUEST}
* @private
*/
this.requestMessage_ = requestMessage;
/**
* @const {!MethodDescriptor<REQUEST, RESPONSE>}
* @private
*/
this.methodDescriptor_ = methodDescriptor;
/** @const @private */
this.metadata_ = metadata;
/** @const @private */
this.callOptions_ = callOptions;
}
/**
* @override
* @return {REQUEST}
*/
getRequestMessage() {
return this.requestMessage_;
}
/**
* @override
* @return {!MethodDescriptor<REQUEST, RESPONSE>}
*/
getMethodDescriptor() {
return this.methodDescriptor_;
}
/**
* @override
* @return {!Metadata}
*/
getMetadata() {
return this.metadata_;
}
/**
* @override
* @return {!CallOptions|undefined}
*/
getCallOptions() {
return this.callOptions_;
}
/**
* @override
*/
withMetadata(key, value) {
this.metadata_[key] = value;
return this;
}
/**
* @override
*/
withGrpcCallOption(name, value) {
this.callOptions_.setOption(name, value);
return this;
}
}
exports = RequestInternal;

View File

@ -0,0 +1,63 @@
/**
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
/**
* @fileoverview gRPC-Web Error objects
*
* gRPC-Web Error objects
*
* @suppress {lintChecks} gRPC-Web is still using default goog.module exports
* right now, and the output of grpc_generator.cc uses goog.provide.
*/
goog.module('grpc.web.RpcError');
const Metadata = goog.require('grpc.web.Metadata');
const StatusCode = goog.require('grpc.web.StatusCode');
/**
* gRPC-Web Error object, contains the {@link StatusCode}, a string message
* and {@link Metadata} contained in the error response.
*/
class RpcError extends Error {
/**
* @param {!StatusCode} code
* @param {string} message
* @param {!Metadata=} metadata
*/
constructor(code, message, metadata = {}) {
super(message);
/** @type {!StatusCode} */
this.code = code;
/** @type {!Metadata} */
this.metadata = metadata;
}
/** @override */
toString() {
const status = StatusCode.statusCodeName(this.code) || String(this.code);
let out = `RpcError(${status})`;
if (this.message) {
out += ': ' + this.message;
}
return out;
}
}
/** @override */
RpcError.prototype.name = 'RpcError';
exports = RpcError;

View File

@ -23,16 +23,19 @@
* @author stanleycheung@google.com (Stanley Cheung)
*/
goog.module('grpc.web.Status');
goog.module.declareLegacyNamespace();
/** @record */
function Status() {}
/**
* @typedef {{
* code: number,
* details: string,
* metadata: (!Object<string, string>|undefined)
* }}
*/
exports.Status;
/** @export {number} */
Status.prototype.code;
/** @export {string} */
Status.prototype.details;
/** @export {(!Object<string, string>|undefined)} */
Status.prototype.metadata;
exports.Status = Status;

View File

@ -24,62 +24,61 @@
*/
goog.module('grpc.web.StatusCode');
goog.module.declareLegacyNamespace();
/**
* gRPC Status Codes
* See: https://github.com/grpc/grpc/blob/master/include/grpc%2B%2B/impl/codegen/status_code_enum.h
* See:
* https://github.com/grpc/grpc/blob/master/include/grpcpp/impl/codegen/status_code_enum.h
* @enum {number}
*/
const StatusCode = {
// LINT.IfChange(status_codes)
// Not an error; returned on success.
OK: 0,
'OK': 0,
// The operation was cancelled (typically by the caller).
CANCELLED: 1,
'CANCELLED': 1,
// Unknown error. An example of where this error may be returned is if a
// Status value received from another address space belongs to an error-space
// that is not known in this address space. Also errors raised by APIs that
// do not return enough error information may be converted to this error.
UNKNOWN: 2,
'UNKNOWN': 2,
// Client specified an invalid argument. Note that this differs from
// FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are
// problematic regardless of the state of the system (e.g., a malformed file
// name).
INVALID_ARGUMENT: 3,
'INVALID_ARGUMENT': 3,
// Deadline expired before operation could complete. For operations that
// change the state of the system, this error may be returned even if the
// operation has completed successfully. For example, a successful response
// from a server could have been delayed long enough for the deadline to
// expire.
DEADLINE_EXCEEDED: 4,
'DEADLINE_EXCEEDED': 4,
// Some requested entity (e.g., file or directory) was not found.
NOT_FOUND: 5,
'NOT_FOUND': 5,
// Some entity that we attempted to create (e.g., file or directory) already
// exists.
ALREADY_EXISTS: 6,
'ALREADY_EXISTS': 6,
// The caller does not have permission to execute the specified operation.
// PERMISSION_DENIED must not be used for rejections caused by exhausting
// some resource (use RESOURCE_EXHAUSTED instead for those errors).
// PERMISSION_DENIED must not be used if the caller can not be identified
// (use UNAUTHENTICATED instead for those errors).
PERMISSION_DENIED: 7,
'PERMISSION_DENIED': 7,
// The request does not have valid authentication credentials for the
// operation.
UNAUTHENTICATED: 16,
'UNAUTHENTICATED': 16,
// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
// entire file system is out of space.
RESOURCE_EXHAUSTED: 8,
'RESOURCE_EXHAUSTED': 8,
// Operation was rejected because the system is not in a state required for
// the operation's execution. For example, directory to be deleted may be
@ -99,14 +98,14 @@ const StatusCode = {
// REST Get/Update/Delete on a resource and the resource on the
// server does not match the condition. E.g., conflicting
// read-modify-write on the same resource.
FAILED_PRECONDITION: 9,
'FAILED_PRECONDITION': 9,
// The operation was aborted, typically due to a concurrency issue like
// sequencer check failures, transaction aborts, etc.
//
// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
// and UNAVAILABLE.
ABORTED: 10,
'ABORTED': 10,
// Operation was attempted past the valid range. E.g., seeking or reading
// past end of file.
@ -121,34 +120,35 @@ const StatusCode = {
// OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error)
// when it applies so that callers who are iterating through a space can
// easily look for an OUT_OF_RANGE error to detect when they are done.
OUT_OF_RANGE: 11,
'OUT_OF_RANGE': 11,
// Operation is not implemented or not supported/enabled in this service.
UNIMPLEMENTED: 12,
'UNIMPLEMENTED': 12,
// Internal errors. Means some invariants expected by underlying System has
// been broken. If you see one of these errors, Something is very broken.
INTERNAL: 13,
'INTERNAL': 13,
// The service is currently unavailable. This is a most likely a transient
// condition and may be corrected by retrying with a backoff.
//
// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
// and UNAVAILABLE.
UNAVAILABLE: 14,
'UNAVAILABLE': 14,
// Unrecoverable data loss or corruption.
DATA_LOSS: 15,
};
'DATA_LOSS': 15,
// LINT.ThenChange(:status_code_name)
};
/**
* Convert HTTP Status code to gRPC Status code
* @param {number} http_status HTTP Status Code
* @return {number} gRPC Status Code
* @param {number} httpStatus HTTP Status Code
* @return {!StatusCode} gRPC Status Code
*/
StatusCode.fromHttpStatus = function(http_status) {
switch (http_status) {
StatusCode.fromHttpStatus = function(httpStatus) {
switch (httpStatus) {
case 200:
return StatusCode.OK;
case 400:
@ -182,4 +182,91 @@ StatusCode.fromHttpStatus = function(http_status) {
};
/**
* Convert a {@link StatusCode} to an HTTP Status code
* @param {!StatusCode} statusCode GRPC Status Code
* @return {number} HTTP Status code
*/
StatusCode.getHttpStatus = function(statusCode) {
switch (statusCode) {
case StatusCode.OK:
return 200;
case StatusCode.INVALID_ARGUMENT:
return 400;
case StatusCode.UNAUTHENTICATED:
return 401;
case StatusCode.PERMISSION_DENIED:
return 403;
case StatusCode.NOT_FOUND:
return 404;
case StatusCode.ABORTED:
return 409;
case StatusCode.FAILED_PRECONDITION:
return 412;
case StatusCode.RESOURCE_EXHAUSTED:
return 429;
case StatusCode.CANCELLED:
return 499;
case StatusCode.UNKNOWN:
return 500;
case StatusCode.UNIMPLEMENTED:
return 501;
case StatusCode.UNAVAILABLE:
return 503;
case StatusCode.DEADLINE_EXCEEDED:
return 504;
/* everything else is unknown */
default:
return 0;
}
};
/**
* Returns the human readable name for a {@link StatusCode}. Useful for logging.
* @param {!StatusCode} statusCode GRPC Status Code
* @return {string} the human readable name for the status code
*/
StatusCode.statusCodeName = function(statusCode) {
switch (statusCode) {
// LINT.IfChange(status_code_name)
case StatusCode.OK:
return 'OK';
case StatusCode.CANCELLED:
return 'CANCELLED';
case StatusCode.UNKNOWN:
return 'UNKNOWN';
case StatusCode.INVALID_ARGUMENT:
return 'INVALID_ARGUMENT';
case StatusCode.DEADLINE_EXCEEDED:
return 'DEADLINE_EXCEEDED';
case StatusCode.NOT_FOUND:
return 'NOT_FOUND';
case StatusCode.ALREADY_EXISTS:
return 'ALREADY_EXISTS';
case StatusCode.PERMISSION_DENIED:
return 'PERMISSION_DENIED';
case StatusCode.UNAUTHENTICATED:
return 'UNAUTHENTICATED';
case StatusCode.RESOURCE_EXHAUSTED:
return 'RESOURCE_EXHAUSTED';
case StatusCode.FAILED_PRECONDITION:
return 'FAILED_PRECONDITION';
case StatusCode.ABORTED:
return 'ABORTED';
case StatusCode.OUT_OF_RANGE:
return 'OUT_OF_RANGE';
case StatusCode.UNIMPLEMENTED:
return 'UNIMPLEMENTED';
case StatusCode.INTERNAL:
return 'INTERNAL';
case StatusCode.UNAVAILABLE:
return 'UNAVAILABLE';
case StatusCode.DATA_LOSS:
return 'DATA_LOSS';
default:
return '';
// LINT.ThenChange(:status_codes)
}
};
exports = StatusCode;

View File

@ -0,0 +1,41 @@
goog.module('grpc.web.StatusCodeTest');
goog.setTestOnly('grpc.web.StatusCodeTest');
const StatusCode = goog.require('grpc.web.StatusCode');
const testSuite = goog.require('goog.testing.testSuite');
/** @type {!Map<number, !StatusCode>} */
const statusMap = new Map([
[200, StatusCode.OK],
[400, StatusCode.INVALID_ARGUMENT],
[401, StatusCode.UNAUTHENTICATED],
[403, StatusCode.PERMISSION_DENIED],
[404, StatusCode.NOT_FOUND],
[409, StatusCode.ABORTED],
[412, StatusCode.FAILED_PRECONDITION],
[429, StatusCode.RESOURCE_EXHAUSTED],
[500, StatusCode.UNKNOWN],
[501, StatusCode.UNIMPLEMENTED],
[503, StatusCode.UNAVAILABLE],
[504, StatusCode.DEADLINE_EXCEEDED],
]);
testSuite({
testFromHttpStatus() {
statusMap.forEach((statusCode, httpStatus) => {
assertEquals(StatusCode.fromHttpStatus(httpStatus), statusCode);
});
},
testGetHttpStatus() {
statusMap.forEach((statusCode, httpStatus) => {
assertEquals(StatusCode.getHttpStatus(statusCode), httpStatus);
});
},
testUnknown() {
assertEquals(StatusCode.getHttpStatus(StatusCode.UNKNOWN), 500);
assertEquals(StatusCode.fromHttpStatus(511), StatusCode.UNKNOWN);
}
});

View File

@ -1,192 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
/**
* @fileoverview gRPC web client Readable Stream
*
* This class is being returned after a gRPC streaming call has been
* started. This class provides functionality for user to operates on
* the stream, e.g. set onData callback, etc.
*
* This wraps the underlying goog.net.streams.NodeReadableStream
*
* @author stanleycheung@google.com (Stanley Cheung)
*/
goog.module('grpc.web.StreamBodyClientReadableStream');
goog.module.declareLegacyNamespace();
const ClientReadableStream = goog.require('grpc.web.ClientReadableStream');
const ErrorCode = goog.require('goog.net.ErrorCode');
const NodeReadableStream = goog.require('goog.net.streams.NodeReadableStream');
const StatusCode = goog.require('grpc.web.StatusCode');
const XhrIo = goog.require('goog.net.XhrIo');
const {GenericTransportInterface} = goog.require('grpc.web.GenericTransportInterface');
const {Status} = goog.require('grpc.web.Status');
/**
* A stream that the client can read from. Used for calls that are streaming
* from the server side.
*
* @template RESPONSE
* @constructor
* @implements {ClientReadableStream}
* @final
* @param {!GenericTransportInterface} genericTransportInterface The
* GenericTransportInterface
*/
const StreamBodyClientReadableStream = function(genericTransportInterface) {
/**
* @const
* @private
* @type {?NodeReadableStream|undefined} The XHR Node Readable Stream
*/
this.xhrNodeReadableStream_ = genericTransportInterface.nodeReadableStream;
/**
* @private
* @type {function(?): RESPONSE|null} The deserialize function for the proto
*/
this.responseDeserializeFn_ = null;
/**
* @const
* @private
* @type {?XhrIo|undefined} The XhrIo object
*/
this.xhr_ = genericTransportInterface.xhr;
/**
* @private
* @type {function(RESPONSE)|null} The data callback
*/
this.onDataCallback_ = null;
/**
* @private
* @type {function(!Status)|null}
* The status callback
*/
this.onStatusCallback_ = null;
/**
* @private
* @type {function(...):?|null}
* The stream end callback
*/
this.onEndCallback_ = null;
/**
* @private
* @type {function(...):?|null}
* The stream error callback
*/
this.onErrorCallback_ = null;
/**
* @private
* @type {function(?):!Status|null}
* A function to parse the Rpc Status response
*/
this.rpcStatusParseFn_ = null;
// Add the callback to the underlying stream
var self = this;
this.xhrNodeReadableStream_.on('data', function(data) {
if ('1' in data && self.onDataCallback_) {
var response = self.responseDeserializeFn_(data['1']);
self.onDataCallback_(response);
}
if ('2' in data && self.onStatusCallback_) {
var status = self.rpcStatusParseFn_(data['2']);
self.onStatusCallback_(status);
}
});
this.xhrNodeReadableStream_.on('end', function() {
if (self.onEndCallback_) {
self.onEndCallback_();
}
});
this.xhrNodeReadableStream_.on('error', function() {
if (!self.onErrorCallback_) return;
var lastErrorCode = self.xhr_.getLastErrorCode();
if (lastErrorCode == ErrorCode.NO_ERROR) return;
self.onErrorCallback_({
code: StatusCode.UNAVAILABLE,
message: ErrorCode.getDebugMessage(lastErrorCode)
});
});
};
/**
* @override
*/
StreamBodyClientReadableStream.prototype.on = function(
eventType, callback) {
// TODO(stanleycheung): change eventType to @enum type
if (eventType == 'data') {
this.onDataCallback_ = callback;
} else if (eventType == 'status') {
this.onStatusCallback_ = callback;
} else if (eventType == 'end') {
this.onEndCallback_ = callback;
} else if (eventType == 'error') {
this.onErrorCallback_ = callback;
}
return this;
};
/**
* Register a callbackl to parse the response
*
* @param {function(?): RESPONSE} responseDeserializeFn The deserialize
* function for the proto
*/
StreamBodyClientReadableStream.prototype.setResponseDeserializeFn =
function(responseDeserializeFn) {
this.responseDeserializeFn_ = responseDeserializeFn;
};
/**
* Register a function to parse RPC status response
*
* @param {function(?):!Status} rpcStatusParseFn A function to parse
* the RPC status response
*/
StreamBodyClientReadableStream.prototype.setRpcStatusParseFn = function(rpcStatusParseFn) {
this.rpcStatusParseFn_ = rpcStatusParseFn;
};
/**
* @override
*/
StreamBodyClientReadableStream.prototype.cancel = function() {
this.xhr_.abort();
};
exports = StreamBodyClientReadableStream;

View File

@ -0,0 +1,44 @@
/**
* @fileoverview gRPC web client UnaryResponse returned by grpc unary calls.
*/
goog.module('grpc.web.UnaryResponse');
goog.module.declareLegacyNamespace();
const Metadata = goog.requireType('grpc.web.Metadata');
const MethodDescriptorInterface = goog.requireType('grpc.web.MethodDescriptorInterface');
const {Status} = goog.requireType('grpc.web.Status');
/**
* @interface
* @template REQUEST, RESPONSE
*/
class UnaryResponse {
/**
* @export
* @return {RESPONSE}
*/
getResponseMessage() {}
/**
* @export
* @return {!Metadata}
*/
getMetadata() {}
/**
* @export
* @return {!MethodDescriptorInterface<REQUEST, RESPONSE>}
*/
getMethodDescriptor() {}
/**
* gRPC status. Trailer metadata returned from a gRPC server is in
* status.metadata.
* @export
* @return {?Status}
*/
getStatus() {}
}
exports = UnaryResponse;

View File

@ -0,0 +1,73 @@
/**
* @fileoverview gRPC-Web UnaryResponse internal implementation.
*/
goog.module('grpc.web.UnaryResponseInternal');
goog.module.declareLegacyNamespace();
const Metadata = goog.requireType('grpc.web.Metadata');
const MethodDescriptor = goog.requireType('grpc.web.MethodDescriptor');
const UnaryResponse = goog.requireType('grpc.web.UnaryResponse');
const {Status} = goog.requireType('grpc.web.Status');
/**
* @template REQUEST, RESPONSE
* @implements {UnaryResponse<REQUEST, RESPONSE>}
* @final
* @package
*/
class UnaryResponseInternal {
/**
* @param {RESPONSE} responseMessage
* @param {!MethodDescriptor<REQUEST, RESPONSE>} methodDescriptor
* @param {!Metadata=} metadata
* @param {?Status=} status
*/
constructor(responseMessage, methodDescriptor, metadata = {}, status = null) {
/**
* @const {RESPONSE}
* @private
*/
this.responseMessage_ = responseMessage;
/**
* @const {!Metadata}
* @private
*/
this.metadata_ = metadata;
/**
* @const {!MethodDescriptor<REQUEST, RESPONSE>}
* @private
*/
this.methodDescriptor_ = methodDescriptor;
/**
* @const {?Status}
* @private
*/
this.status_ = status;
}
/** @override */
getResponseMessage() {
return this.responseMessage_;
}
/** @override */
getMetadata() {
return this.metadata_;
}
/** @override */
getMethodDescriptor() {
return this.methodDescriptor_;
}
/** @override */
getStatus() {
return this.status_;
}
}
exports = UnaryResponseInternal;

View File

@ -1,105 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
/**
* @fileoverview A generic gRPC-Web client customized for Protobuf.js.
*
* (This API is experimental and subject to change.)
*
* @author updogliu@google.com (Zihan Liu)
*/
// TODO(updogliu): add support of server streaming request.
goog.module('grpc.web.util.GenericPbjsClient');
var AbstractClientBase = goog.require('grpc.web.AbstractClientBase');
var Error = goog.require('grpc.web.Error');
var GatewayClientBase = goog.require('grpc.web.GatewayClientBase');
/**
* A generic gRPC-Web client customized for Protobuf.js
*
* @param {string} hostname The hostname of the server
* @constructor
* @struct
* @final
*/
var GenericPbjsClient = function(hostname) {
/**
* The underlying client base
* @private @const {!GatewayClientBase}
*/
this.clientBase_ = new GatewayClientBase();
/**
* The hostname of the server
* @private {string}
*/
this.hostname_ = hostname;
};
/**
* Get the full name (without the leading dot) of the service of the method.
*
* @param {!Object} method The method (a Protobuf.js Method object)
* @return {string} The full name of the service containing the method
*/
function getServiceName(method) {
var fullName = method.parent.fullName;
if (fullName.startsWith('.')) {
fullName = fullName.substring(1);
}
return fullName;
}
/**
* @param {!Object} method
* The method to invoke (an instance of Protobuf.js Method)
* @param {!Object} request
* The request (an instance of Protobuf.js Message or a payload object)
* @param {!Object<string, string>} metadata User defined call metadata
* @param {function(?Error, ?Object)} callback A callback function
* which takes (error, response)
*/
GenericPbjsClient.prototype.rpcCall = function(
method, request, metadata, callback) {
method.resolve();
var requestType = method.resolvedRequestType;
var responseType = method.resolvedResponseType;
var methodInfo = /** @type {!AbstractClientBase.MethodInfo<?, ?>} */ ({
requestSerializeFn: function(request) {
return requestType.encode(request).finish();
},
responseDeserializeFn: function(payload) {
return responseType.decode(payload);
}
});
// Make a gRPC-Web call.
var url = this.hostname_ + '/' + getServiceName(method) + '/' + method.name;
this.clientBase_.rpcCall(url, request, metadata, methodInfo, callback);
};
exports = GenericPbjsClient;

1
kokoro/interop.cfg Normal file
View File

@ -0,0 +1 @@
build_file: "grpc-web/scripts/run_interop_tests.sh"

1
kokoro/master.cfg Normal file
View File

@ -0,0 +1 @@
build_file: "grpc-web/scripts/kokoro.sh"

View File

@ -1 +1 @@
build_file: "grpc-web/scripts/kokoro.sh"
build_file: "grpc-web/scripts/run_basic_tests.sh"

View File

@ -1,29 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/backend/backend.h"
namespace grpc {
namespace gateway {
Backend::Backend() : frontend_(nullptr) {}
Backend::~Backend() {}
} // namespace gateway
} // namespace grpc

View File

@ -1,62 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_BACKEND_BACKEND_H_
#define NET_GRPC_GATEWAY_BACKEND_BACKEND_H_
#include <memory>
#include "net/grpc/gateway/runtime/request.h"
#include "net/grpc/gateway/runtime/tag.h"
#include "third_party/grpc/include/grpcpp/support/status.h"
namespace grpc {
namespace gateway {
class Frontend;
class Backend {
public:
Backend();
virtual ~Backend();
Backend(const Backend&) = delete;
Backend& operator=(const Backend&) = delete;
// Start the backend proxy progress.
virtual void Start() = 0;
// Send request to backend.
virtual void Send(std::unique_ptr<Request> request, Tag* on_done) = 0;
// Cancel the request to backend.
virtual void Cancel(const Status& reason) = 0;
protected:
Frontend* frontend() { return frontend_; }
private:
friend class Frontend;
void set_frontend(Frontend* frontend) { frontend_ = frontend; }
Frontend* frontend_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_BACKEND_BACKEND_H_

View File

@ -1,346 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/backend/grpc_backend.h"
#include <algorithm>
#include <cctype>
#include <iterator>
#include <utility>
#include <vector>
#include "net/grpc/gateway/frontend/frontend.h"
#include "net/grpc/gateway/log.h"
#include "net/grpc/gateway/runtime/runtime.h"
#include "net/grpc/gateway/runtime/types.h"
#include "third_party/grpc/include/grpc/byte_buffer.h"
#include "third_party/grpc/include/grpc/byte_buffer_reader.h"
#include "third_party/grpc/include/grpc/grpc.h"
#include "third_party/grpc/include/grpc/slice.h"
#include "third_party/grpc/include/grpc/support/alloc.h"
#include "third_party/grpc/include/grpc/support/time.h"
#define BACKEND_PREFIX "[addr: %s, host: %s, method: %s] "
#define BACKEND_INFO(f, ...) \
INFO(BACKEND_PREFIX f, address_.c_str(), host_.c_str(), method_.c_str(), \
##__VA_ARGS__)
#define BACKEND_DEBUG(f, ...) \
DEBUG(BACKEND_PREFIX f, address_.c_str(), host_.c_str(), method_.c_str(), \
##__VA_ARGS__)
#define BACKEND_ERROR(f, ...) \
ERROR(BACKEND_PREFIX f, address_.c_str(), host_.c_str(), method_.c_str(), \
##__VA_ARGS__)
namespace grpc {
namespace gateway {
GrpcBackend::GrpcBackend()
: use_shared_channel_pool_(false),
channel_(nullptr),
call_(nullptr),
request_buffer_(nullptr),
response_buffer_(nullptr),
status_code_(grpc_status_code::GRPC_STATUS_OK),
status_details_(grpc_empty_slice()),
is_cancelled_(false) {
BACKEND_DEBUG("Creating GRPC backend proxy.");
grpc_metadata_array_init(&response_initial_metadata_);
grpc_metadata_array_init(&response_trailing_metadata_);
}
GrpcBackend::~GrpcBackend() {
BACKEND_DEBUG("Deleting GRPC backend proxy.");
for (auto& m : request_initial_metadata_) {
grpc_slice_unref(m.key);
grpc_slice_unref(m.value);
}
grpc_metadata_array_destroy(&response_initial_metadata_);
grpc_metadata_array_destroy(&response_trailing_metadata_);
if (request_buffer_ != nullptr) {
grpc_byte_buffer_destroy(request_buffer_);
}
if (response_buffer_ != nullptr) {
grpc_byte_buffer_destroy(response_buffer_);
}
grpc_slice_unref(status_details_);
if (call_ != nullptr) {
BACKEND_DEBUG("Destroying GRPC call.");
grpc_call_unref(call_);
}
if (!use_shared_channel_pool_ && channel_ != nullptr) {
BACKEND_DEBUG("Destroying GRPC channel.");
grpc_channel_destroy(channel_);
}
}
grpc_channel* GrpcBackend::CreateChannel() {
return Runtime::Get().GetBackendChannel(
address_, use_shared_channel_pool_, ssl_, ssl_target_override_,
ssl_pem_root_certs_, ssl_pem_private_key_, ssl_pem_cert_chain_);
}
grpc_call* GrpcBackend::CreateCall() {
BACKEND_DEBUG("Creating GRPC call.");
grpc_slice method_slice = grpc_slice_from_copied_string(method_.c_str());
grpc_slice host_slice = grpc_slice_from_static_string(host_.c_str());
grpc_call* call = grpc_channel_create_call(
channel_, nullptr, 0, Runtime::Get().grpc_event_queue(), method_slice,
host_.empty() ? nullptr : &host_slice, gpr_inf_future(GPR_CLOCK_REALTIME),
nullptr);
grpc_slice_unref(method_slice);
return call;
}
void GrpcBackend::Start() {
channel_ = CreateChannel();
call_ = CreateCall();
// Receives GRPC response initial metadata.
grpc_op ops[1];
ops[0].op = GRPC_OP_RECV_INITIAL_METADATA;
ops[0].data.recv_initial_metadata.recv_initial_metadata =
&response_initial_metadata_;
ops[0].flags = 0;
ops[0].reserved = nullptr;
grpc_call_error error = grpc_call_start_batch(
call_, ops, 1,
BindTo(frontend(), this, &GrpcBackend::OnResponseInitialMetadata),
nullptr);
if (error != GRPC_CALL_OK) {
BACKEND_DEBUG("GRPC batch failed: %s", grpc_call_error_to_string(error));
}
}
void GrpcBackend::OnResponseInitialMetadata(bool result) {
if (!result) {
FinishWhenTagFail(
"Receiving initial metadata for GRPC response from backend failed.");
return;
}
std::unique_ptr<Response> response(new Response());
std::unique_ptr<Headers> response_headers(new Headers());
for (size_t i = 0; i < response_initial_metadata_.count; i++) {
grpc_metadata* metadata = response_initial_metadata_.metadata + i;
response_headers->push_back(Header(
std::string(
reinterpret_cast<char*>(GRPC_SLICE_START_PTR(metadata->key)),
GRPC_SLICE_LENGTH(metadata->key)),
string_ref(
reinterpret_cast<char*>(GRPC_SLICE_START_PTR(metadata->value)),
GRPC_SLICE_LENGTH(metadata->value))));
}
response->set_headers(std::move(response_headers));
frontend()->Send(std::move(response));
// Receives next GRPC response message.
grpc_op ops[1];
ops[0].op = GRPC_OP_RECV_MESSAGE;
ops[0].data.recv_message.recv_message = &response_buffer_;
ops[0].flags = 0;
ops[0].reserved = nullptr;
grpc_call_error error = grpc_call_start_batch(
call_, ops, 1, BindTo(frontend(), this, &GrpcBackend::OnResponseMessage),
nullptr);
if (error != GRPC_CALL_OK) {
BACKEND_DEBUG("GRPC batch failed: %s", grpc_call_error_to_string(error));
}
}
void GrpcBackend::OnResponseMessage(bool result) {
if (!result) {
FinishWhenTagFail("Receiving GRPC response message from backend failed.");
return;
}
if (response_buffer_ == nullptr) {
// Receives the GRPC response status.
grpc_op ops[1];
memset(ops, 0, sizeof(ops));
ops[0].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
ops[0].data.recv_status_on_client.status = &status_code_;
ops[0].data.recv_status_on_client.status_details = &status_details_;
ops[0].data.recv_status_on_client.trailing_metadata =
&response_trailing_metadata_;
ops[0].flags = 0;
ops[0].reserved = nullptr;
grpc_call_error error = grpc_call_start_batch(
call_, ops, 1, BindTo(frontend(), this, &GrpcBackend::OnResponseStatus),
nullptr);
if (error != GRPC_CALL_OK) {
BACKEND_DEBUG("GRPC batch failed: %s", grpc_call_error_to_string(error));
}
return;
}
std::unique_ptr<Response> response(new Response());
std::unique_ptr<Message> message(new Message());
grpc_byte_buffer_reader reader;
grpc_byte_buffer_reader_init(&reader, response_buffer_);
grpc_slice slice;
while (grpc_byte_buffer_reader_next(&reader, &slice)) {
message->push_back(Slice(slice, Slice::STEAL_REF));
}
grpc_byte_buffer_reader_destroy(&reader);
grpc_byte_buffer_destroy(response_buffer_);
response->set_message(std::move(message));
frontend()->Send(std::move(response));
// Receives next GRPC response message.
grpc_op ops[1];
ops[0].op = GRPC_OP_RECV_MESSAGE;
ops[0].data.recv_message.recv_message = &response_buffer_;
ops[0].flags = 0;
ops[0].reserved = nullptr;
grpc_call_error error = grpc_call_start_batch(
call_, ops, 1, BindTo(frontend(), this, &GrpcBackend::OnResponseMessage),
nullptr);
if (error != GRPC_CALL_OK) {
BACKEND_DEBUG("GRPC batch failed: %s", grpc_call_error_to_string(error));
}
}
void GrpcBackend::OnResponseStatus(bool result) {
if (!result) {
FinishWhenTagFail("Receiving GRPC response's status from backend failed.");
return;
}
std::unique_ptr<Response> response(new Response());
grpc::string status_details;
if (!GRPC_SLICE_IS_EMPTY(status_details_)) {
status_details = grpc::string(
reinterpret_cast<char*>(GRPC_SLICE_START_PTR(status_details_)),
GRPC_SLICE_LENGTH(status_details_));
}
response->set_status(std::unique_ptr<grpc::Status>(new grpc::Status(
static_cast<grpc::StatusCode>(status_code_), status_details)));
std::unique_ptr<Trailers> response_trailers(new Trailers());
for (size_t i = 0; i < response_trailing_metadata_.count; i++) {
grpc_metadata* metadata = response_trailing_metadata_.metadata + i;
response_trailers->push_back(Trailer(
std::string(
reinterpret_cast<char*>(GRPC_SLICE_START_PTR(metadata->key)),
GRPC_SLICE_LENGTH(metadata->key)),
string_ref(
reinterpret_cast<char*>(GRPC_SLICE_START_PTR(metadata->value)),
GRPC_SLICE_LENGTH(metadata->value))));
}
response->set_trailers(std::move(response_trailers));
frontend()->Send(std::move(response));
}
void GrpcBackend::Send(std::unique_ptr<Request> request, Tag* on_done) {
grpc_op ops[3] = {};
grpc_op* op = ops;
if (request->headers() != nullptr) {
for (Header& header : *request->headers()) {
std::transform(header.first.begin(), header.first.end(),
header.first.begin(), ::tolower);
if (header.first == kGrpcAcceptEncoding) {
continue;
}
grpc_metadata initial_metadata;
initial_metadata.key =
grpc_slice_from_copied_string(header.first.c_str());
initial_metadata.value = grpc_slice_from_copied_buffer(
header.second.data(), header.second.size());
initial_metadata.flags = 0;
request_initial_metadata_.push_back(initial_metadata);
}
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.metadata = request_initial_metadata_.data();
op->data.send_initial_metadata.count = request_initial_metadata_.size();
op->flags = 0;
op->reserved = nullptr;
op++;
}
if (request->message() != nullptr) {
op->op = GRPC_OP_SEND_MESSAGE;
std::vector<grpc_slice> slices;
for (auto& piece : *request->message()) {
// TODO(fengli): Once I get an API to access the grpc_slice in a Slice,
// the copy can be eliminated.
slices.push_back(grpc_slice_from_copied_buffer(
reinterpret_cast<const char*>(piece.begin()), piece.size()));
}
if (request_buffer_ != nullptr) {
grpc_byte_buffer_destroy(request_buffer_);
}
request_buffer_ = grpc_raw_byte_buffer_create(slices.data(), slices.size());
for (auto& slice : slices) {
grpc_slice_unref(slice);
}
op->data.send_message.send_message = request_buffer_;
op->flags = 0;
op->reserved = nullptr;
op++;
}
if (request->final()) {
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
op->flags = 0;
op->reserved = nullptr;
op++;
}
GPR_ASSERT(op != ops);
if (op != ops) {
grpc_call_error error =
grpc_call_start_batch(call_, ops, op - ops, on_done, nullptr);
BACKEND_DEBUG("grpc_call_start_batch: %s",
grpc_call_error_to_string(error));
}
}
void GrpcBackend::Cancel(const Status& reason) {
if (is_cancelled_) {
BACKEND_DEBUG("GRPC has been cancelled, skip redundant cancellation: %s",
reason.error_message().c_str());
return;
}
is_cancelled_ = true;
BACKEND_DEBUG("Canceling GRPC: %s", reason.error_message().c_str());
cancel_reason_ = reason;
grpc_call_error error = grpc_call_cancel_with_status(
call_, static_cast<grpc_status_code>(cancel_reason_.error_code()),
cancel_reason_.error_message().c_str(), nullptr);
if (error != GRPC_CALL_OK) {
BACKEND_DEBUG("GRPC cancel failed: %s", grpc_call_error_to_string(error));
}
}
void GrpcBackend::FinishWhenTagFail(const char* error) {
BACKEND_DEBUG("%s", error);
std::unique_ptr<Response> response(new Response());
if (is_cancelled_) {
response->set_status(std::unique_ptr<Status>(new Status(cancel_reason_)));
} else {
response->set_status(
std::unique_ptr<Status>(new Status(StatusCode::INTERNAL, error)));
}
frontend()->Send(std::move(response));
}
} // namespace gateway
} // namespace grpc

View File

@ -1,122 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_BACKEND_GRPC_BACKEND_H_
#define NET_GRPC_GATEWAY_BACKEND_GRPC_BACKEND_H_
#include <memory>
#include <string>
#include "net/grpc/gateway/backend/backend.h"
#include "net/grpc/gateway/runtime/request.h"
#include "net/grpc/gateway/runtime/response.h"
#include "net/grpc/gateway/runtime/tag.h"
#include "third_party/grpc/include/grpcpp/support/config.h"
#include "third_party/grpc/include/grpc/grpc.h"
namespace grpc {
namespace gateway {
class GrpcBackend : public Backend {
public:
GrpcBackend();
~GrpcBackend() override;
GrpcBackend(const GrpcBackend&) = delete;
GrpcBackend& operator=(const GrpcBackend&) = delete;
void Start() override;
void Send(std::unique_ptr<Request> request, Tag* on_done) override;
void Cancel(const Status& reason) override;
void set_address(const string& address) { address_ = address; }
void set_host(const string& host) { host_ = host; }
void set_method(const string& method) { method_ = method; }
void set_use_shared_channel_pool(bool use_shared_channel_pool) {
use_shared_channel_pool_ = use_shared_channel_pool;
}
void set_ssl(bool ssl) { ssl_ = ssl; }
void set_ssl_target_override(const string& ssl_target_override) {
ssl_target_override_ = ssl_target_override;
}
void set_ssl_pem_root_certs(const string& ssl_pem_root_certs) {
ssl_pem_root_certs_ = ssl_pem_root_certs;
}
void set_ssl_pem_private_key(const string& ssl_pem_private_key) {
ssl_pem_private_key_ = ssl_pem_private_key;
}
void set_ssl_pem_cert_chain(const string& ssl_pem_cert_chain) {
ssl_pem_cert_chain_ = ssl_pem_cert_chain;
}
private:
// Create a GRPC channel.
grpc_channel* CreateChannel();
// Create a GRPC call.
grpc_call* CreateCall();
void OnResponseInitialMetadata(bool result);
void OnResponseMessage(bool result);
void OnResponseStatus(bool result);
void FinishWhenTagFail(const char* error);
// The backend address we connect to.
string address_;
// The HTTP host header of the request.
string host_;
// The HTTP method of the request.
string method_;
// True if the shared channel pool should be used.
bool use_shared_channel_pool_;
// True if ssl should be used.
bool ssl_;
// The GRPC SSL target override.
string ssl_target_override_;
// The file location which contains the root certs in pem format.
string ssl_pem_root_certs_;
// The file location which contains the client private key in pem format.
string ssl_pem_private_key_;
// The file location which contains the client cert chain in pem format.
string ssl_pem_cert_chain_;
// The GRPC channel.
grpc_channel* channel_;
// The GRPC call.
grpc_call* call_;
// The GRPC request buffer.
Request request_;
// The GRPC request initial metadata.
std::vector<grpc_metadata> request_initial_metadata_;
// The GRPC response initial metadata.
grpc_metadata_array response_initial_metadata_;
// The GRPC request buffer.
grpc_byte_buffer* request_buffer_;
// The GRPC response buffer.
grpc_byte_buffer* response_buffer_;
grpc_status_code status_code_;
grpc_slice status_details_;
grpc_metadata_array response_trailing_metadata_;
// True if the GRPC call has been cancelled by client.
bool is_cancelled_;
// The status which represents why the GRPC call is cancelled.
Status cancel_reason_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_BACKEND_GRPC_BACKEND_H_

View File

@ -1,44 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_B64_PROTO_DECODER_H_
#define NET_GRPC_GATEWAY_CODEC_B64_PROTO_DECODER_H_
#include "net/grpc/gateway/codec/base64.h"
#include "net/grpc/gateway/codec/proto_decoder.h"
#include "third_party/grpc/include/grpcpp/support/status.h"
namespace grpc {
namespace gateway {
class B64ProtoDecoder : public ProtoDecoder {
public:
B64ProtoDecoder();
~B64ProtoDecoder() override;
B64ProtoDecoder(const B64ProtoDecoder&) = delete;
B64ProtoDecoder& operator=(const B64ProtoDecoder&) = delete;
Status Decode() override;
private:
Base64 base64_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_B64_PROTO_DECODER_H_

View File

@ -1,44 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/b64_proto_encoder.h"
namespace grpc {
namespace gateway {
B64ProtoEncoder::B64ProtoEncoder() {}
B64ProtoEncoder::~B64ProtoEncoder() {}
void B64ProtoEncoder::Encode(grpc::ByteBuffer* input,
std::vector<Slice>* result) {
std::vector<Slice> buffer;
ProtoEncoder::Encode(input, &buffer);
base64_.Encode(buffer, result);
}
void B64ProtoEncoder::EncodeStatus(const grpc::Status& status,
const Trailers* trailers,
std::vector<Slice>* result) {
std::vector<Slice> buffer;
ProtoEncoder::EncodeStatus(status, trailers, &buffer);
base64_.Encode(buffer, result);
}
} // namespace gateway
} // namespace grpc

View File

@ -1,47 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_B64_PROTO_ENCODER_H_
#define NET_GRPC_GATEWAY_CODEC_B64_PROTO_ENCODER_H_
#include "net/grpc/gateway/codec/base64.h"
#include "net/grpc/gateway/codec/proto_encoder.h"
namespace grpc {
namespace gateway {
class B64ProtoEncoder : public ProtoEncoder {
public:
B64ProtoEncoder();
~B64ProtoEncoder() override;
// B64ProtoEncoder is neither copyable nor movable.
B64ProtoEncoder(const B64ProtoEncoder&) = delete;
B64ProtoEncoder& operator=(const B64ProtoEncoder&) = delete;
void Encode(grpc::ByteBuffer* input, std::vector<Slice>* result) override;
void EncodeStatus(const grpc::Status& status, const Trailers* trailers,
std::vector<Slice>* result) override;
private:
Base64 base64_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_B64_PROTO_ENCODER_H_

View File

@ -1,49 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/b64_stream_body_decoder.h"
#include <vector>
#include "net/grpc/gateway/codec/decoder.h"
#include "third_party/grpc/include/grpcpp/support/slice.h"
#include "third_party/grpc/include/grpcpp/support/status.h"
namespace grpc {
namespace gateway {
B64StreamBodyDecoder::B64StreamBodyDecoder() {}
B64StreamBodyDecoder::~B64StreamBodyDecoder() {}
Status B64StreamBodyDecoder::Decode() {
std::vector<Slice> buffer;
if (!base64_.Decode(*inputs(), &buffer)) {
return Status(StatusCode::INVALID_ARGUMENT, "Invalid base64 inputs.");
}
inputs()->clear();
for (Slice& s : buffer) {
Append(s);
}
return StreamBodyDecoder::Decode();
}
} // namespace gateway
} // namespace grpc

View File

@ -1,44 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_B64_STREAM_BODY_DECODER_H_
#define NET_GRPC_GATEWAY_CODEC_B64_STREAM_BODY_DECODER_H_
#include "net/grpc/gateway/codec/base64.h"
#include "net/grpc/gateway/codec/stream_body_decoder.h"
#include "third_party/grpc/include/grpcpp/support/status.h"
namespace grpc {
namespace gateway {
class B64StreamBodyDecoder : public StreamBodyDecoder {
public:
B64StreamBodyDecoder();
~B64StreamBodyDecoder() override;
B64StreamBodyDecoder(const B64StreamBodyDecoder&) = delete;
B64StreamBodyDecoder& operator=(const B64StreamBodyDecoder&) = delete;
Status Decode() override;
private:
Base64 base64_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_B64_STREAM_BODY_DECODER_H_

View File

@ -1,44 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/b64_stream_body_encoder.h"
namespace grpc {
namespace gateway {
B64StreamBodyEncoder::B64StreamBodyEncoder() {}
B64StreamBodyEncoder::~B64StreamBodyEncoder() {}
void B64StreamBodyEncoder::Encode(grpc::ByteBuffer* input,
std::vector<Slice>* result) {
std::vector<Slice> buffer;
StreamBodyEncoder::Encode(input, &buffer, true);
base64_.Encode(buffer, result);
}
void B64StreamBodyEncoder::EncodeStatus(const grpc::Status& status,
const Trailers* trailers,
std::vector<Slice>* result) {
std::vector<Slice> buffer;
StreamBodyEncoder::EncodeStatus(status, trailers, &buffer, true);
base64_.Encode(buffer, result);
}
} // namespace gateway
} // namespace grpc

View File

@ -1,51 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_B64_STREAM_BODY_ENCODER_H_
#define NET_GRPC_GATEWAY_CODEC_B64_STREAM_BODY_ENCODER_H_
#include <vector>
#include "net/grpc/gateway/codec/base64.h"
#include "net/grpc/gateway/codec/stream_body_encoder.h"
#include "net/grpc/gateway/runtime/types.h"
#include "third_party/grpc/include/grpcpp/support/byte_buffer.h"
#include "third_party/grpc/include/grpcpp/support/slice.h"
#include "third_party/grpc/include/grpcpp/support/status.h"
namespace grpc {
namespace gateway {
class B64StreamBodyEncoder : public StreamBodyEncoder {
public:
B64StreamBodyEncoder();
~B64StreamBodyEncoder() override;
B64StreamBodyEncoder(const B64StreamBodyEncoder&) = delete;
B64StreamBodyEncoder& operator=(const B64StreamBodyEncoder&) = delete;
void Encode(grpc::ByteBuffer* input, std::vector<Slice>* result) override;
void EncodeStatus(const grpc::Status& status, const Trailers* trailers,
std::vector<Slice>* result) override;
private:
Base64 base64_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_B64_STREAM_BODY_ENCODER_H_

View File

@ -1,274 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/base64.h"
#include <stdint.h>
#include <cstring>
namespace grpc {
namespace gateway {
namespace {
const char kPad = '=';
// Map from base64 encoded char to raw byte.
const int32_t b64_bytes[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 9
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10 - 19
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20 - 29
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 30 - 39
-1, -1, -1, 0x3E, -1, -1, -1, 0x3F, 0x34, 0x35, // 40 - 49
0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, -1, -1, // 50 - 59
-1, 0x7F, -1, -1, -1, 0x00, 0x01, 0x02, 0x03, 0x04, // 60 - 69
0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 70 - 79
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, // 80 - 89
0x19, -1, -1, -1, -1, -1, -1, 0x1A, 0x1B, 0x1C, // 90 - 99
0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, // 100 - 109
0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, // 110 - 119
0x31, 0x32, 0x33, -1, -1, -1, -1, -1, -1, -1, // 120 - 129
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 130 - 139
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 140 - 149
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 150 - 159
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160 - 169
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 170 - 179
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 180 - 189
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 190 - 199
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 200 - 209
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 210 - 219
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 220 - 229
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 230 - 239
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 240 - 249
-1, -1, -1, -1, -1, -1, // 250 - 255
};
const char b64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
} // namespace
bool Base64::IsBase64Char(uint8_t c) { return b64_bytes[c] != -1; }
Base64::Base64() : decode_buffer_{0}, decode_buffer_length_(0) {}
Base64::~Base64() {}
std::unique_ptr<Slice> Base64::Encode(const Slice& input_slice, uint8_t* buffer,
size_t* buffer_length, bool is_last) {
size_t data_size = *buffer_length + input_slice.size();
size_t encoded_size = data_size / 3 * 4;
size_t tail_size = data_size % 3;
if (is_last && tail_size > 0) {
encoded_size += 4;
}
if (encoded_size == 0) {
if (input_slice.size() > 0) {
memcpy(buffer + *buffer_length, input_slice.begin(), input_slice.size());
*buffer_length += input_slice.size();
}
return nullptr;
}
grpc_slice output_slice = grpc_slice_malloc(encoded_size);
uint8_t* output = GRPC_SLICE_START_PTR(output_slice);
const uint8_t* input = input_slice.begin();
// trailers only.
if (data_size == 1) {
if (*buffer_length == 0) {
Encode1CharGroup(*input, output);
} else {
Encode1CharGroup(buffer[0], output);
}
*buffer_length = 0;
} else if (data_size == 2) {
if (*buffer_length == 0) {
Encode2CharGroup(*input, *(input + 1), output);
} else if (*buffer_length == 1) {
Encode2CharGroup(buffer[0], *input, output);
} else if (*buffer_length == 2) {
Encode2CharGroup(buffer[0], buffer[1], output);
}
*buffer_length = 0;
} else if (data_size > 2) {
// Encodes the first group, together with the buffer.
if (*buffer_length == 1) {
Encode3CharGroup(buffer[0], *input, *(input + 1), output);
output += 4;
input += 2;
} else if (*buffer_length == 2) {
Encode3CharGroup(buffer[0], buffer[1], *input, output);
output += 4;
input += 1;
}
// Encodes the other groups, besides the tail.
while (input < input_slice.end() - tail_size) {
Encode3CharGroup(*input, *(input + 1), *(input + 2), output);
output += 4;
input += 3;
}
// Encodes the tail group if current slice is the last one.
if (tail_size > 0) {
if (is_last) {
if (tail_size == 2) {
Encode2CharGroup(*input, *(input + 1), output);
} else if (tail_size == 1) {
Encode1CharGroup(*input, output);
}
*buffer_length = 0;
} else {
memcpy(buffer, input, tail_size);
*buffer_length = tail_size;
}
} else {
*buffer_length = 0;
}
}
return std::unique_ptr<Slice>(new Slice(output_slice, Slice::STEAL_REF));
}
void Base64::Encode1CharGroup(uint8_t input_0, uint8_t* output) {
*output++ = b64_chars[(input_0 >> 2) & 0x3F];
*output++ = b64_chars[(input_0 & 0x03) << 4];
*output++ = kPad;
*output++ = kPad;
}
void Base64::Encode2CharGroup(uint8_t input_0, uint8_t input_1,
uint8_t* output) {
*output++ = b64_chars[(input_0 >> 2) & 0x3F];
*output++ = b64_chars[((input_0 & 0x03) << 4) | ((input_1 >> 4) & 0x0F)];
*output++ = b64_chars[(input_1 & 0x0F) << 2];
*output++ = kPad;
}
void Base64::Encode3CharGroup(uint8_t input_0, uint8_t input_1, uint8_t input_2,
uint8_t* output) {
*output++ = b64_chars[(input_0 >> 2) & 0x3F];
*output++ = b64_chars[((input_0 & 0x03) << 4) | ((input_1 >> 4) & 0x0F)];
*output++ = b64_chars[((input_1 & 0x0F) << 2) | ((input_2 >> 6) & 0x03)];
*output++ = b64_chars[input_2 & 0x3F];
}
bool Base64::Encode(const std::vector<Slice>& input,
std::vector<Slice>* output) {
uint8_t buffer[2] = {0};
size_t buffer_length = 0;
for (size_t i = 0; i < input.size(); i++) {
std::unique_ptr<Slice> encoded_slice =
Encode(input[i], buffer, &buffer_length, (i == input.size() - 1));
if (encoded_slice) {
output->push_back(*encoded_slice);
}
}
return true;
}
bool Base64::Decode(const std::vector<Slice>& input,
std::vector<Slice>* output) {
for (const Slice& slice_in : input) {
size_t base64_length = slice_in.size() + decode_buffer_length_;
size_t binary_length = base64_length / 4 * 3;
size_t base64_leftover_length = base64_length % 4;
if (base64_length < 4) {
// No enough data to form a group for decoding, copy everything to decode
// buffer.
if (slice_in.size() > 0) {
memcpy(decode_buffer_ + decode_buffer_length_, slice_in.begin(),
slice_in.size());
decode_buffer_length_ += slice_in.size();
}
continue;
}
grpc_slice slice_out = grpc_slice_malloc(binary_length);
uint8_t* result_offset = GRPC_SLICE_START_PTR(slice_out);
if (decode_buffer_length_ > 0) {
// Decode the leftover.
memcpy(decode_buffer_ + decode_buffer_length_, slice_in.begin(),
4 - decode_buffer_length_);
int size = DecodeGroup(decode_buffer_, result_offset);
if (size == -1) {
return false;
}
result_offset += size;
if (size != 3) {
binary_length -= (3 - size);
}
}
for (size_t i = (4 - decode_buffer_length_) % 4;
i < slice_in.size() - base64_leftover_length; i = i + 4) {
int size = DecodeGroup(slice_in.begin() + i, result_offset);
if (size == -1) {
return false;
}
result_offset += size;
if (size != 3) {
binary_length -= (3 - size);
}
}
GRPC_SLICE_SET_LENGTH(slice_out, binary_length);
output->push_back(Slice(slice_out, Slice::STEAL_REF));
if (base64_leftover_length > 0) {
memcpy(decode_buffer_, slice_in.end() - base64_leftover_length,
base64_leftover_length);
}
decode_buffer_length_ = base64_leftover_length;
}
if (output->empty()) {
output->push_back(Slice(grpc_empty_slice(), Slice::STEAL_REF));
}
return true;
}
int Base64::DecodeGroup(const uint8_t* input, uint8_t* output) {
int size = 3;
uint8_t byte0 = *input;
uint8_t byte1 = *(input + 1);
uint8_t byte2 = *(input + 2);
uint8_t byte3 = *(input + 3);
int32_t packed0 = b64_bytes[byte0];
int32_t packed1 = b64_bytes[byte1];
int32_t packed2 = b64_bytes[byte2];
int32_t packed3 = b64_bytes[byte3];
if (packed0 == -1 || packed1 == -1 || packed2 == -1 || packed3 == -1) {
return -1;
}
uint32_t packed = packed0 << 18 | packed1 << 12 | packed2 << 6 | packed3;
if ((packed & 0xFF000000) != 0 || byte0 == kPad || byte1 == kPad ||
(byte2 == kPad && byte3 != kPad)) {
return -1;
}
if (byte2 == kPad) {
size--;
}
if (byte3 == kPad) {
size--;
}
*output++ = static_cast<uint8_t>(packed >> 16);
*output++ = static_cast<uint8_t>(packed >> 8);
*output++ = static_cast<uint8_t>(packed);
return size;
}
} // namespace gateway
} // namespace grpc

View File

@ -1,73 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_BASE64_H_
#define NET_GRPC_GATEWAY_CODEC_BASE64_H_
#include <stddef.h>
#include <cstdint>
#include <memory>
#include <vector>
#include "third_party/grpc/include/grpcpp/support/slice.h"
namespace grpc {
namespace gateway {
class Base64 {
public:
// Returns true if the given character is a valid base64 character, includes
// padding.
static bool IsBase64Char(uint8_t c);
Base64();
virtual ~Base64();
Base64(const Base64&) = delete;
Base64& operator=(const Base64&) = delete;
// Encodes the input to base64 encoding, returns true if success.
bool Encode(const std::vector<Slice>& input, std::vector<Slice>* output);
// Decodes the base64 encoded input, returns true if decode success.
bool Decode(const std::vector<Slice>& input, std::vector<Slice>* output);
private:
// Encodes once single slice together with the data remain in last slice to
// base64. Remained data which cannot be encoded will be put back to the
// buffer. Padding applied when the input slice is the last one.
std::unique_ptr<Slice> Encode(const Slice& input, uint8_t* buffer,
size_t* buffer_length, bool is_last);
void Encode1CharGroup(uint8_t input_0, uint8_t* output);
void Encode2CharGroup(uint8_t input_0, uint8_t input_1, uint8_t* output);
void Encode3CharGroup(uint8_t input_0, uint8_t input_1, uint8_t input_2,
uint8_t* output);
// Decodes a base64 group. The input must be a pointer to uint8_t array with
// at least 4 elements. The output must be a pointer to uint8_t array with
// at least 3 elements. Returns the decoded data size if decode success, else
// returns -1.
int DecodeGroup(const uint8_t* input, uint8_t* output);
uint8_t decode_buffer_[4];
size_t decode_buffer_length_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_BASE64_H_

View File

@ -1,66 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_DECODER_H_
#define NET_GRPC_GATEWAY_CODEC_DECODER_H_
#include <deque>
#include <memory>
#include <vector>
#include "third_party/grpc/include/grpcpp/support/byte_buffer.h"
#include "third_party/grpc/include/grpcpp/support/slice.h"
namespace grpc {
namespace gateway {
// Interface for GRPC-Gateway decoders. A decoder instance records the internal
// states during the processing of a request (stream). Decoder decodes different
// front end protocols to GRPC backend.
class Decoder {
public:
Decoder();
virtual ~Decoder();
Decoder(const Decoder&) = delete;
Decoder& operator=(const Decoder&) = delete;
// Appends a piece of data to decode.
virtual void Append(Slice input);
// Decodes the inputs passed to `Append()` since the last call to `Decode()`
// and appends the decoded results to those available from `results()`.
// This method may be invoked multiple times when processing a streamed
// request.
virtual Status Decode() = 0;
// Returns the decoded messages.
std::deque<std::unique_ptr<ByteBuffer>>* results() { return &results_; }
protected:
// Returns the buffered inputs. When the inputs are not enough to be decoded
// into a new message they will be buffered in this field.
std::vector<Slice>* inputs() { return &inputs_; }
private:
std::vector<Slice> inputs_;
std::deque<std::unique_ptr<ByteBuffer>> results_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_DECODER_H_

View File

@ -1,34 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/encoder.h"
namespace grpc {
namespace gateway {
Encoder::Encoder() {}
Encoder::~Encoder() {}
void Encoder::EncodeStatus(const grpc::Status& status,
std::vector<Slice>* result) {
EncodeStatus(status, nullptr, result);
}
} // namespace gateway
} // namespace grpc

View File

@ -1,53 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_ENCODER_H_
#define NET_GRPC_GATEWAY_CODEC_ENCODER_H_
#include "net/grpc/gateway/runtime/types.h"
#include "third_party/grpc/include/grpcpp/support/byte_buffer.h"
namespace grpc {
namespace gateway {
// Interface for GRPC-Gateway encoders. A encoder instance records the internal
// states during the processing of a response (stream). Encoder encodes the GRPC
// response to different front end protocols.
class Encoder {
public:
Encoder();
virtual ~Encoder();
Encoder(const Encoder&) = delete;
Encoder& operator=(const Encoder&) = delete;
// Encodes a GRPC response message to the front end protocol.
virtual void Encode(grpc::ByteBuffer* input, std::vector<Slice>* result) = 0;
// Encodes a GRPC response status to the front end protocol.
virtual void EncodeStatus(const grpc::Status& status,
std::vector<Slice>* result);
// Encodes a GRPC response status and trailers to the frontend protocol.
virtual void EncodeStatus(const grpc::Status& status,
const Trailers* trailers,
std::vector<Slice>* result) = 0;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_ENCODER_H_

View File

@ -1,140 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/grpc_decoder.h"
#include "net/grpc/gateway/log.h"
#include "net/grpc/gateway/utils.h"
#include "third_party/grpc/src/core/lib/compression/message_compress.h"
#include "third_party/grpc/src/core/lib/iomgr/exec_ctx.h"
namespace grpc {
namespace gateway {
GrpcDecoder::GrpcDecoder()
: state_(kExpectingCompressedFlag),
compression_algorithm_(kIdentity),
compressed_flag_(0),
message_length_(0) {}
GrpcDecoder::~GrpcDecoder() {}
Status GrpcDecoder::Decode() {
grpc_core::ExecCtx exec_ctx;
for (const Slice& slice : *inputs()) {
if (slice.size() == 0) {
continue;
}
for (size_t i = 0; i < slice.size(); i++) {
uint8_t c = *(slice.begin() + i);
switch (state_) {
case kExpectingCompressedFlag: {
if (c != CompressedFlag::kUncompressed &&
c != CompressedFlag::kCompressed) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(StatusCode::INVALID_ARGUMENT,
Format("Receives invalid compressed flag: %c.", c));
DEBUG("%s", status.error_message().c_str());
return status;
}
compressed_flag_ = c;
state_ = kExpectingMessageLengthByte0;
continue;
}
case kExpectingMessageLengthByte0: {
message_length_ = c << 24;
state_ = kExpectingMessageLengthByte1;
continue;
}
case kExpectingMessageLengthByte1: {
message_length_ += c << 16;
state_ = kExpectingMessageLengthByte2;
continue;
}
case kExpectingMessageLengthByte2: {
message_length_ += c << 8;
state_ = kExpectingMessageLengthByte3;
continue;
}
case kExpectingMessageLengthByte3: {
message_length_ += c;
if (message_length_ == 0) {
buffer_.reset(new Slice(grpc_empty_slice(), Slice::STEAL_REF));
results()->push_back(
std::unique_ptr<ByteBuffer>(new ByteBuffer(buffer_.get(), 1)));
state_ = kExpectingCompressedFlag;
} else {
buffer_.reset(new Slice(grpc_slice_malloc(message_length_),
Slice::STEAL_REF));
state_ = kExpectingMessageData;
}
continue;
}
case kExpectingMessageData: {
uint8_t* end = const_cast<uint8_t*>(buffer_->end());
*(end - message_length_) = c;
message_length_--;
if (message_length_ == 0) {
if (compressed_flag_ == CompressedFlag::kCompressed &&
compression_algorithm() == kGzip) {
grpc_slice_buffer input;
grpc_slice_buffer_init(&input);
// TODO(fengli): Remove the additional copy.
grpc_slice slice_input = grpc_slice_from_copied_buffer(
reinterpret_cast<const char*>(buffer_->begin()),
buffer_->size());
grpc_slice_buffer_add(&input, slice_input);
grpc_slice_buffer output;
grpc_slice_buffer_init(&output);
if (grpc_msg_decompress(grpc_message_compression_algorithm::
GRPC_MESSAGE_COMPRESS_GZIP,
&input, &output) != 1) {
grpc_slice_buffer_destroy(&input);
grpc_slice_buffer_destroy(&output);
return Status(StatusCode::INTERNAL,
"Failed to uncompress the GRPC data frame.");
}
std::vector<Slice> s;
while (output.count > 0) {
s.push_back(Slice(grpc_slice_buffer_take_first(&output),
Slice::STEAL_REF));
}
results()->push_back(std::unique_ptr<ByteBuffer>(
new ByteBuffer(s.data(), s.size())));
grpc_slice_buffer_destroy(&input);
grpc_slice_buffer_destroy(&output);
} else {
results()->push_back(std::unique_ptr<ByteBuffer>(
new ByteBuffer(buffer_.get(), 1)));
}
buffer_.reset();
state_ = kExpectingCompressedFlag;
}
continue;
}
}
}
}
inputs()->clear();
return Status::OK;
}
} // namespace gateway
} // namespace grpc

View File

@ -1,87 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_GRPC_DECODER_H_
#define NET_GRPC_GATEWAY_CODEC_GRPC_DECODER_H_
#include <cstdint>
#include <memory>
#include "net/grpc/gateway/codec/decoder.h"
#include "third_party/grpc/include/grpcpp/support/slice.h"
namespace grpc {
namespace gateway {
// Decodes the GRPC requests from raw GRPC frames over HTTP2 to a series of
// protobuf messages.
class GrpcDecoder : public Decoder {
public:
enum State {
// The initial decode state, expecting the compression flag (1 byte).
kExpectingCompressedFlag,
// Expecting the 1st byte of message length (4 bytes in total).
kExpectingMessageLengthByte0,
// Expecting the 2nd byte of message length (4 bytes in total).
kExpectingMessageLengthByte1,
// Expecting the 3rd byte of message length (4 bytes in total).
kExpectingMessageLengthByte2,
// Expecting the 4th byte of message length (4 bytes in total).
kExpectingMessageLengthByte3,
// Expecting the message data.
kExpectingMessageData
};
enum CompressionAlgorithm { kIdentity = 0, kGzip = 1, kSnappy = 2 };
enum CompressedFlag { kUncompressed = 0, kCompressed = 1 };
GrpcDecoder();
~GrpcDecoder() override;
GrpcDecoder(const GrpcDecoder&) = delete;
GrpcDecoder& operator=(const GrpcDecoder&) = delete;
Status Decode() override;
// Sets the GRPC compression algorithm to be used when receiving a compressed
// data frame.
void set_compression_algorithm(CompressionAlgorithm compression_algorithm) {
compression_algorithm_ = compression_algorithm;
}
// Returns the GRPC compression algorithm to be used when receiving a
// compressed data frame.
CompressionAlgorithm compression_algorithm() {
return compression_algorithm_;
}
private:
State state_;
// The compression algorithm used to decode the GRPC frame.
CompressionAlgorithm compression_algorithm_;
// The compressed of the current decoding GRPC frame.
uint8_t compressed_flag_;
// The message length of the current decoding GRPC frame.
uint32_t message_length_;
// The data buffered for the current decoding GRPC frame.
std::unique_ptr<Slice> buffer_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_GRPC_DECODER_H_

View File

@ -1,50 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/grpc_encoder.h"
#include "third_party/grpc/include/grpcpp/support/slice.h"
namespace grpc {
namespace gateway {
GrpcEncoder::GrpcEncoder() {}
GrpcEncoder::~GrpcEncoder() {}
void GrpcEncoder::Encode(grpc::ByteBuffer* input, std::vector<Slice>* result) {
std::vector<Slice> input_slices;
input->Dump(&input_slices);
uint32_t message_length = input->Length();
grpc_slice message_slice = grpc_slice_malloc(message_length + 5);
uint8_t* p = GRPC_SLICE_START_PTR(message_slice);
*p++ = 0; // no compression.
*p++ = (message_length & 0xFF000000) >> 24;
*p++ = (message_length & 0x00FF0000) >> 16;
*p++ = (message_length & 0x0000FF00) >> 8;
*p++ = message_length & 0x000000FF;
for (const Slice& input_slice : input_slices) {
memcpy(p, input_slice.begin(), input_slice.size());
p += input_slice.size();
}
result->push_back(Slice(message_slice, Slice::STEAL_REF));
}
void GrpcEncoder::EncodeStatus(const grpc::Status& status,
const Trailers* trailers,
std::vector<Slice>* result) {}
} // namespace gateway
} // namespace grpc

View File

@ -1,47 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_GRPC_ENCODER_H_
#define NET_GRPC_GATEWAY_CODEC_GRPC_ENCODER_H_
#include "net/grpc/gateway/codec/encoder.h"
namespace grpc {
namespace gateway {
// Encoder for GRPC to GRPC traffic. Encodes the response headers, response
// messages, response status and response trailers to the raw GRPC wire format
// base on HTTP2.
class GrpcEncoder : public Encoder {
public:
GrpcEncoder();
~GrpcEncoder() override;
GrpcEncoder(const GrpcEncoder&) = delete;
GrpcEncoder& operator=(const GrpcEncoder&) = delete;
// Encodes a GRPC response message to raw GRPC wire format base on HTTP2.
void Encode(grpc::ByteBuffer* input, std::vector<Slice>* result) override;
// Encodes the GRPC response status and trailers to raw GRPC wire format base
// on HTTP2.
void EncodeStatus(const grpc::Status& status, const Trailers* trailers,
std::vector<Slice>* result) override;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_GRPC_ENCODER_H_

View File

@ -1,103 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/grpc_web_decoder.h"
#include "net/grpc/gateway/log.h"
#include "net/grpc/gateway/utils.h"
namespace grpc {
namespace gateway {
const uint8_t GrpcWebDecoder::kGrpcWebMessage = 0;
GrpcWebDecoder::GrpcWebDecoder()
: state_(kExpectingFlags), message_length_(0) {}
GrpcWebDecoder::~GrpcWebDecoder() {}
Status GrpcWebDecoder::Decode() {
for (const Slice& slice : *inputs()) {
if (slice.size() == 0) {
continue;
}
for (size_t i = 0; i < slice.size(); i++) {
uint8_t c = *(slice.begin() + i);
switch (state_) {
case kExpectingFlags: {
if (c != kGrpcWebMessage) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(StatusCode::INVALID_ARGUMENT,
Format("Receives invalid compressed flag: %X.", c));
DEBUG("%s", status.error_message().c_str());
return status;
}
state_ = kExpectingMessageLengthByte0;
continue;
}
case kExpectingMessageLengthByte0: {
message_length_ = c << 24;
state_ = kExpectingMessageLengthByte1;
continue;
}
case kExpectingMessageLengthByte1: {
message_length_ += c << 16;
state_ = kExpectingMessageLengthByte2;
continue;
}
case kExpectingMessageLengthByte2: {
message_length_ += c << 8;
state_ = kExpectingMessageLengthByte3;
continue;
}
case kExpectingMessageLengthByte3: {
message_length_ += c;
if (message_length_ == 0) {
buffer_.reset(new Slice(grpc_empty_slice(), Slice::STEAL_REF));
results()->push_back(
std::unique_ptr<ByteBuffer>(new ByteBuffer(buffer_.get(), 1)));
state_ = kExpectingFlags;
} else {
buffer_.reset(new Slice(grpc_slice_malloc(message_length_),
Slice::STEAL_REF));
state_ = kExpectingMessageData;
}
continue;
}
case kExpectingMessageData: {
uint8_t* end = const_cast<uint8_t*>(buffer_->end());
*(end - message_length_) = c;
message_length_--;
if (message_length_ == 0) {
results()->push_back(
std::unique_ptr<ByteBuffer>(new ByteBuffer(buffer_.get(), 1)));
buffer_.reset();
state_ = kExpectingFlags;
}
continue;
}
}
}
}
inputs()->clear();
return Status::OK;
}
} // namespace gateway
} // namespace grpc

View File

@ -1,69 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_GRPC_WEB_DECODER_H_
#define NET_GRPC_GATEWAY_CODEC_GRPC_WEB_DECODER_H_
#include <cstdint>
#include <memory>
#include "net/grpc/gateway/codec/decoder.h"
#include "third_party/grpc/include/grpcpp/support/slice.h"
namespace grpc {
namespace gateway {
class GrpcWebDecoder : public Decoder {
public:
static const uint8_t kGrpcWebMessage;
enum State : uint8_t {
// The initial decode state, expecting the flags (1 byte).
kExpectingFlags,
// Expecting the 1st byte of message length (4 bytes in total).
kExpectingMessageLengthByte0,
// Expecting the 2nd byte of message length (4 bytes in total).
kExpectingMessageLengthByte1,
// Expecting the 3rd byte of message length (4 bytes in total).
kExpectingMessageLengthByte2,
// Expecting the 4th byte of message length (4 bytes in total).
kExpectingMessageLengthByte3,
// Expecting the message data.
kExpectingMessageData
};
GrpcWebDecoder();
virtual ~GrpcWebDecoder();
// GrpcWebDecoder is neither copyable nor movable.
GrpcWebDecoder(const GrpcWebDecoder&) = delete;
GrpcWebDecoder& operator=(const GrpcWebDecoder&) = delete;
Status Decode() override;
private:
State state_;
// The message length of the current decoding GRPC-Web frame.
uint32_t message_length_;
// The data buffered for the current decoding GRPC-Web frame.
std::unique_ptr<Slice> buffer_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_GRPC_WEB_DECODER_H_

View File

@ -1,130 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/grpc_web_encoder.h"
#include <cstdint>
#include <cstring>
#include <vector>
#include "net/grpc/gateway/runtime/types.h"
#include "third_party/grpc/include/grpcpp/support/byte_buffer.h"
#include "third_party/grpc/include/grpcpp/support/slice.h"
namespace grpc {
namespace gateway {
namespace {
const char kGrpcStatus[] = "grpc-status: %i\r\n";
const char kGrpcMessage[] = "grpc-message: %s\r\n";
// GRPC Web message frame.
const uint8_t GRPC_WEB_FH_DATA = 0b0u;
// GRPC Web trailer frame.
const uint8_t GRPC_WEB_FH_TRAILER = 0b10000000u;
// Creates a new GRPC data frame with the given flags and length.
// @param flags supplies the GRPC data frame flags.
// @param length supplies the GRPC data frame length.
// @param output the buffer to store the encoded data, it's size must be 5.
void NewFrame(uint8_t flags, uint64_t length, uint8_t* output) {
output[0] = flags;
output[1] = static_cast<uint8_t>(length >> 24);
output[2] = static_cast<uint8_t>(length >> 16);
output[3] = static_cast<uint8_t>(length >> 8);
output[4] = static_cast<uint8_t>(length);
}
} // namespace
GrpcWebEncoder::GrpcWebEncoder() {}
GrpcWebEncoder::~GrpcWebEncoder() {}
void GrpcWebEncoder::Encode(grpc::ByteBuffer* input,
std::vector<Slice>* result) {
uint8_t header[5];
NewFrame(GRPC_WEB_FH_DATA, input->Length(), header);
result->push_back(
Slice(gpr_slice_from_copied_buffer(reinterpret_cast<char*>(header), 5),
Slice::STEAL_REF));
std::vector<Slice> buffer;
// TODO(fengli): Optimize if needed. Today we cannot dump data to the result
// directly since it will clear the target.
input->Dump(&buffer);
for (Slice& s : buffer) {
result->push_back(s);
}
}
void GrpcWebEncoder::EncodeStatus(const grpc::Status& status,
const Trailers* trailers,
std::vector<Slice>* result) {
std::vector<Slice> buffer;
uint64_t length = 0;
// Encodes GRPC status.
size_t grpc_status_size =
snprintf(nullptr, 0, kGrpcStatus, status.error_code());
grpc_slice grpc_status = grpc_slice_malloc(grpc_status_size + 1);
snprintf(reinterpret_cast<char*>(GPR_SLICE_START_PTR(grpc_status)),
grpc_status_size + 1, kGrpcStatus, status.error_code());
GPR_SLICE_SET_LENGTH(grpc_status, grpc_status_size);
buffer.push_back(Slice(grpc_status, Slice::STEAL_REF));
length += grpc_status_size;
// Encodes GRPC message.
if (!status.error_message().empty()) {
size_t grpc_message_size =
snprintf(nullptr, 0, kGrpcMessage, status.error_message().c_str());
grpc_slice grpc_message = grpc_slice_malloc(grpc_message_size + 1);
snprintf(reinterpret_cast<char*>(GPR_SLICE_START_PTR(grpc_message)),
grpc_message_size + 1, kGrpcMessage,
status.error_message().c_str());
GPR_SLICE_SET_LENGTH(grpc_message, grpc_message_size);
buffer.push_back(Slice(grpc_message, Slice::STEAL_REF));
length += grpc_message_size;
}
// Encodes GRPC trailers.
if (trailers != nullptr) {
for (auto& trailer : *trailers) {
size_t grpc_trailer_size =
trailer.first.size() + trailer.second.size() + 4;
grpc_slice grpc_trailer = grpc_slice_malloc(grpc_trailer_size);
uint8_t* p = GPR_SLICE_START_PTR(grpc_trailer);
memcpy(p, trailer.first.c_str(), trailer.first.size());
p += trailer.first.size();
memcpy(p, ": ", 2);
p += 2;
memcpy(p, trailer.second.data(), trailer.second.size());
p += trailer.second.size();
memcpy(p, "\r\n", 2);
buffer.push_back(Slice(grpc_trailer, Slice::STEAL_REF));
length += grpc_trailer_size;
}
}
// Encodes GRPC trailer frame.
grpc_slice header = grpc_slice_malloc(5);
NewFrame(GRPC_WEB_FH_TRAILER, length, GPR_SLICE_START_PTR(header));
result->push_back(Slice(header, Slice::STEAL_REF));
result->insert(result->end(), buffer.begin(), buffer.end());
}
} // namespace gateway
} // namespace grpc

View File

@ -1,44 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_GRPC_WEB_ENCODER_H_
#define NET_GRPC_GATEWAY_CODEC_GRPC_WEB_ENCODER_H_
#include "net/grpc/gateway/codec/encoder.h"
namespace grpc {
namespace gateway {
class GrpcWebEncoder : public Encoder {
public:
GrpcWebEncoder();
virtual ~GrpcWebEncoder();
// GrpcWebEncoder is neither copyable nor movable.
GrpcWebEncoder(const GrpcWebEncoder&) = delete;
GrpcWebEncoder& operator=(const GrpcWebEncoder&) = delete;
void Encode(grpc::ByteBuffer* input, std::vector<Slice>* result) override;
void EncodeStatus(const grpc::Status& status, const Trailers* trailers,
std::vector<Slice>* result) override;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_GRPC_WEB_ENCODER_H_

View File

@ -1,43 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/grpc_web_text_decoder.h"
namespace grpc {
namespace gateway {
GrpcWebTextDecoder::GrpcWebTextDecoder() {}
GrpcWebTextDecoder::~GrpcWebTextDecoder() {}
Status GrpcWebTextDecoder::Decode() {
std::vector<Slice> buffer;
if (!base64_.Decode(*inputs(), &buffer)) {
return Status(StatusCode::INVALID_ARGUMENT, "Invalid base64 inputs.");
}
inputs()->clear();
for (Slice& s : buffer) {
Append(s);
}
return GrpcWebDecoder::Decode();
}
} // namespace gateway
} // namespace grpc

View File

@ -1,44 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_DECODER_H_
#define NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_DECODER_H_
#include "net/grpc/gateway/codec/base64.h"
#include "net/grpc/gateway/codec/grpc_web_decoder.h"
namespace grpc {
namespace gateway {
class GrpcWebTextDecoder : public GrpcWebDecoder {
public:
GrpcWebTextDecoder();
~GrpcWebTextDecoder() override;
GrpcWebTextDecoder(const GrpcWebTextDecoder&) = delete;
GrpcWebTextDecoder& operator=(const GrpcWebTextDecoder&) = delete;
Status Decode() override;
private:
Base64 base64_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_DECODER_H_

View File

@ -1,44 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/grpc_web_text_encoder.h"
namespace grpc {
namespace gateway {
GrpcWebTextEncoder::GrpcWebTextEncoder() {}
GrpcWebTextEncoder::~GrpcWebTextEncoder() {}
void GrpcWebTextEncoder::Encode(grpc::ByteBuffer* input,
std::vector<Slice>* result) {
std::vector<Slice> buffer;
GrpcWebEncoder::Encode(input, &buffer);
base64_.Encode(buffer, result);
}
void GrpcWebTextEncoder::EncodeStatus(const grpc::Status& status,
const Trailers* trailers,
std::vector<Slice>* result) {
std::vector<Slice> buffer;
GrpcWebEncoder::EncodeStatus(status, trailers, &buffer);
base64_.Encode(buffer, result);
}
} // namespace gateway
} // namespace grpc

View File

@ -1,46 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_ENCODER_H_
#define NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_ENCODER_H_
#include "net/grpc/gateway/codec/base64.h"
#include "net/grpc/gateway/codec/grpc_web_encoder.h"
namespace grpc {
namespace gateway {
class GrpcWebTextEncoder : public GrpcWebEncoder {
public:
GrpcWebTextEncoder();
~GrpcWebTextEncoder() override;
GrpcWebTextEncoder(const GrpcWebTextEncoder&) = delete;
GrpcWebTextEncoder& operator=(const GrpcWebTextEncoder&) = delete;
void Encode(grpc::ByteBuffer* input, std::vector<Slice>* result) override;
void EncodeStatus(const grpc::Status& status, const Trailers* trailers,
std::vector<Slice>* result) override;
private:
Base64 base64_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_GRPC_WEB_TEXT_ENCODER_H_

View File

@ -1,258 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/json_decoder.h"
#include <cctype>
#include <cstdint>
#include "net/grpc/gateway/codec/base64.h"
#include "net/grpc/gateway/log.h"
#include "net/grpc/gateway/utils.h"
namespace grpc {
namespace gateway {
const char JSON_ARRAY_LEFT_BRACKET = '[';
const char JSON_ARRAY_RIGHT_BRACKET = ']';
const char JSON_OBJECT_LEFT_BRACKET = '{';
const char JSON_OBJECT_RIGHT_BRACKET = '}';
const char JSON_OBJECT_DELIMITER = ',';
const char JSON_DOUBLE_QUOTE = '"';
const char JSON_MESSAGE_TAG = '1';
const char JSON_TAG_VALUE_DELIMITER = ':';
JsonDecoder::JsonDecoder() : state_(EXPECTING_JSON_ARRAY_LEFT_BRACKET) {}
JsonDecoder::~JsonDecoder() {}
Status JsonDecoder::Decode() {
for (Slice& slice : *inputs()) {
int start = -1;
for (size_t i = 0; i < slice.size(); i++) {
char c = *(slice.begin() + i);
if (isblank(c)) {
// Ignore blank characters.
continue;
}
switch (state_) {
case EXPECTING_JSON_ARRAY_LEFT_BRACKET: {
if (c != JSON_ARRAY_LEFT_BRACKET) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(
StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when expecting "
"left bracket of the JSON array.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
state_ = EXPECTING_JSON_OBJECT_LEFT_BRACKET;
continue;
}
case EXPECTING_JSON_OBJECT_LEFT_BRACKET: {
if (c != JSON_OBJECT_LEFT_BRACKET) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(
StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when expecting "
"left bracket of the JSON object.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
state_ = EXPECTING_JSON_MESSAGE_TAG_LEFT_QUOTE;
continue;
}
case EXPECTING_JSON_MESSAGE_TAG_LEFT_QUOTE: {
if (c != JSON_DOUBLE_QUOTE) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(
StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when expecting "
"left double quote of the JSON message tag.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
state_ = EXPECTING_JSON_MESSAGE_TAG;
continue;
}
case EXPECTING_JSON_MESSAGE_TAG: {
if (c != JSON_MESSAGE_TAG) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when "
"expecting the message tag.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
state_ = EXPECTING_JSON_MESSAGE_TAG_RIGHT_QUOTE;
continue;
}
case EXPECTING_JSON_MESSAGE_TAG_RIGHT_QUOTE: {
if (c != JSON_DOUBLE_QUOTE) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(
StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when expecting "
"right double quote of the JSON message tag.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
state_ = EXPECTING_JSON_MESSAGE_TAG_VALUE_DELIMITER;
continue;
}
case EXPECTING_JSON_MESSAGE_TAG_VALUE_DELIMITER: {
if (c != JSON_TAG_VALUE_DELIMITER) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(
StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when expecting "
"colon after the JSON message tag.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
state_ = EXPECTING_JSON_MESSAGE_VALUE_LEFT_QUOTE;
continue;
}
case EXPECTING_JSON_MESSAGE_VALUE_LEFT_QUOTE: {
if (c != JSON_DOUBLE_QUOTE) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(
StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when expecting "
"double quote of the JSON message value.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
start = i + 1;
state_ = EXPECTING_JSON_MESSAGE_VALUE_OR_RIGHT_QUOTE;
continue;
}
case EXPECTING_JSON_MESSAGE_VALUE_OR_RIGHT_QUOTE: {
if (c == JSON_DOUBLE_QUOTE) {
if (start == -1) {
start = 0;
}
if (static_cast<size_t>(start) == i) {
base64_buffer_.push_back(
Slice(grpc_empty_slice(), Slice::STEAL_REF));
} else {
base64_buffer_.push_back(Slice(
grpc_slice_from_copied_buffer(
reinterpret_cast<const char*>(slice.begin() + start),
i - start),
Slice::STEAL_REF));
}
std::vector<Slice> decoded;
base64_.Decode(base64_buffer_, &decoded);
results()->push_back(std::unique_ptr<ByteBuffer>(
new ByteBuffer(&decoded[0], decoded.size())));
base64_buffer_.clear();
start = -1;
state_ = EXPECTING_JSON_OBJECT_RIGHT_BRACKET;
continue;
}
if (!Base64::IsBase64Char(c)) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(
StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when expecting "
"base64 characters for the JSON message value.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
if (start == -1) {
start = i;
}
continue;
}
case EXPECTING_JSON_OBJECT_RIGHT_BRACKET: {
if (c != JSON_OBJECT_RIGHT_BRACKET) {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(
StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when expecting "
"right bracket of the JSON object.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
state_ = EXPECTING_JSON_OBJECT_DELIMITER_OR_JSON_ARRAY_RIGHT_BRACKET;
continue;
}
case EXPECTING_JSON_OBJECT_DELIMITER_OR_JSON_ARRAY_RIGHT_BRACKET: {
if (c == JSON_OBJECT_DELIMITER) {
state_ = EXPECTING_JSON_OBJECT_LEFT_BRACKET;
continue;
}
if (c == JSON_ARRAY_RIGHT_BRACKET) {
state_ = FINISH;
continue;
}
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when expecting "
"right bracket of the JSON array.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
case FINISH: {
// TODO(fengli): The following code is repeated 12 times. Extract it
// into a function or a macro.
Status status(StatusCode::INVALID_ARGUMENT,
Format("Receives invalid character: %c when "
"the JSON array already completed.",
c));
DEBUG("%s", status.error_message().c_str());
return status;
}
}
}
if (start >= 0) {
base64_buffer_.push_back(
Slice(grpc_slice_from_copied_buffer(
reinterpret_cast<const char*>(slice.begin() + start),
slice.size() - start),
Slice::STEAL_REF));
}
}
inputs()->clear();
return Status::OK;
}
} // namespace gateway
} // namespace grpc

View File

@ -1,64 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_JSON_DECODER_H_
#define NET_GRPC_GATEWAY_CODEC_JSON_DECODER_H_
#include <memory>
#include <vector>
#include "net/grpc/gateway/codec/base64.h"
#include "net/grpc/gateway/codec/decoder.h"
#include "third_party/grpc/include/grpcpp/support/byte_buffer.h"
#include "third_party/grpc/include/grpcpp/support/slice.h"
namespace grpc {
namespace gateway {
class JsonDecoder : public Decoder {
public:
enum State {
EXPECTING_JSON_ARRAY_LEFT_BRACKET,
EXPECTING_JSON_OBJECT_LEFT_BRACKET,
EXPECTING_JSON_OBJECT_RIGHT_BRACKET,
EXPECTING_JSON_OBJECT_DELIMITER_OR_JSON_ARRAY_RIGHT_BRACKET,
EXPECTING_JSON_MESSAGE_TAG_LEFT_QUOTE,
EXPECTING_JSON_MESSAGE_TAG,
EXPECTING_JSON_MESSAGE_TAG_RIGHT_QUOTE,
EXPECTING_JSON_MESSAGE_TAG_VALUE_DELIMITER,
EXPECTING_JSON_MESSAGE_VALUE_LEFT_QUOTE,
EXPECTING_JSON_MESSAGE_VALUE_OR_RIGHT_QUOTE,
FINISH
};
JsonDecoder();
~JsonDecoder() override;
JsonDecoder(const JsonDecoder&) = delete;
JsonDecoder& operator=(const JsonDecoder&) = delete;
Status Decode() override;
private:
State state_;
std::vector<Slice> base64_buffer_;
Base64 base64_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_JSON_DECODER_H_

View File

@ -1,98 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/json_encoder.h"
#include <string>
#include "google/protobuf/any.pb.h"
#include "net/grpc/gateway/protos/pair.pb.h"
#include "net/grpc/gateway/protos/stream_body.pb.h"
#include "net/grpc/gateway/runtime/constants.h"
#include "third_party/grpc/include/grpcpp/support/config.h"
#include "third_party/grpc/include/grpc/slice.h"
namespace grpc {
namespace gateway {
const char kJsonArrayFirstMessagePrefix[] = "[{\"1\":\"";
const char kJsonArrayMessagePrefix[] = ",{\"1\":\"";
const char kDoubleQuote[] = "\"}";
const char kJsonArrayStatusPrefix[] = ",{\"2\":\"";
const char kJsonArrayStatusOnlyPrefix[] = "[{\"2\":\"";
const char kJsonArrayStatusSurfix[] = "\"}]";
void do_nothing() {}
JsonEncoder::JsonEncoder() : is_first_message_(true) {}
JsonEncoder::~JsonEncoder() {}
void JsonEncoder::Encode(grpc::ByteBuffer* input, std::vector<Slice>* result) {
std::vector<Slice> input_slices;
input->Dump(&input_slices);
if (is_first_message_) {
is_first_message_ = false;
grpc_slice s = grpc_slice_from_static_string(kJsonArrayFirstMessagePrefix);
result->push_back(Slice(s, Slice::STEAL_REF));
} else {
grpc_slice s = grpc_slice_from_static_string(kJsonArrayMessagePrefix);
result->push_back(Slice(s, Slice::STEAL_REF));
}
base64_.Encode(input_slices, result);
grpc_slice s = grpc_slice_from_static_string(kDoubleQuote);
result->push_back(Slice(s, Slice::STEAL_REF));
}
void JsonEncoder::EncodeStatus(const grpc::Status& status,
const Trailers* trailers,
std::vector<Slice>* result) {
if (is_first_message_) {
grpc_slice prefix =
grpc_slice_from_static_string(kJsonArrayStatusOnlyPrefix);
result->push_back(Slice(prefix, Slice::STEAL_REF));
} else {
grpc_slice prefix = grpc_slice_from_static_string(kJsonArrayStatusPrefix);
result->push_back(Slice(prefix, Slice::STEAL_REF));
}
std::vector<Slice> input_slices;
google::rpc::Status status_proto;
status_proto.set_code(status.error_code());
status_proto.set_message(status.error_message());
if (trailers != nullptr) {
for (auto& trailer : *trailers) {
::google::protobuf::Any* any = status_proto.add_details();
any->set_type_url(kTypeUrlPair);
Pair pair;
pair.set_first(trailer.first);
pair.set_second(trailer.second.data(), trailer.second.length());
// TODO(fengli): Change to open source protobuf.
pair.SerializeToString(any->mutable_value());
}
}
std::string serialized_status_proto;
status_proto.SerializeToString(&serialized_status_proto);
grpc_slice status_slice =
grpc_slice_from_copied_string(serialized_status_proto.c_str());
input_slices.push_back(Slice(status_slice, Slice::STEAL_REF));
base64_.Encode(input_slices, result);
grpc_slice surfix = grpc_slice_from_static_string(kJsonArrayStatusSurfix);
result->push_back(Slice(surfix, Slice::STEAL_REF));
}
} // namespace gateway
} // namespace grpc

View File

@ -1,50 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_JSON_ENCODER_H_
#define NET_GRPC_GATEWAY_CODEC_JSON_ENCODER_H_
#include <vector>
#include "net/grpc/gateway/codec/base64.h"
#include "net/grpc/gateway/codec/encoder.h"
#include "third_party/grpc/include/grpcpp/support/byte_buffer.h"
#include "third_party/grpc/include/grpcpp/support/slice.h"
namespace grpc {
namespace gateway {
class JsonEncoder : public Encoder {
public:
JsonEncoder();
~JsonEncoder() override;
JsonEncoder(const JsonEncoder&) = delete;
JsonEncoder& operator=(const JsonEncoder&) = delete;
void Encode(grpc::ByteBuffer* input, std::vector<Slice>* result) override;
void EncodeStatus(const grpc::Status& status, const Trailers* trailers,
std::vector<Slice>* result) override;
private:
bool is_first_message_;
Base64 base64_;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_JSON_ENCODER_H_

View File

@ -1,45 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#include "net/grpc/gateway/codec/proto_decoder.h"
#include "third_party/grpc/include/grpcpp/support/byte_buffer.h"
#include "third_party/grpc/include/grpcpp/support/status.h"
namespace grpc {
namespace gateway {
ProtoDecoder::ProtoDecoder() {}
ProtoDecoder::~ProtoDecoder() {}
Status ProtoDecoder::Decode() {
if (inputs()->empty()) {
Slice slice(grpc_empty_slice(), Slice::STEAL_REF);
ByteBuffer* buffer = new ByteBuffer(&slice, 1);
results()->push_back(std::unique_ptr<ByteBuffer>(buffer));
} else {
ByteBuffer* buffer = new ByteBuffer(inputs()->data(), inputs()->size());
results()->push_back(std::unique_ptr<ByteBuffer>(buffer));
}
inputs()->clear();
return Status::OK;
}
} // namespace gateway
} // namespace grpc

View File

@ -1,39 +0,0 @@
/**
*
* Copyright 2018 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://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.
*
*/
#ifndef NET_GRPC_GATEWAY_CODEC_PROTO_DECODER_H_
#define NET_GRPC_GATEWAY_CODEC_PROTO_DECODER_H_
#include "net/grpc/gateway/codec/decoder.h"
namespace grpc {
namespace gateway {
class ProtoDecoder : public Decoder {
public:
ProtoDecoder();
~ProtoDecoder() override;
ProtoDecoder(const ProtoDecoder&) = delete;
ProtoDecoder& operator=(const ProtoDecoder&) = delete;
Status Decode() override;
};
} // namespace gateway
} // namespace grpc
#endif // NET_GRPC_GATEWAY_CODEC_PROTO_DECODER_H_

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