Skip to content

Commit

Permalink
Added multiple proxies interoperability
Browse files Browse the repository at this point in the history
  • Loading branch information
stanley-cheung committed Aug 4, 2018
1 parent 712d784 commit a9aa7df
Show file tree
Hide file tree
Showing 17 changed files with 271 additions and 38 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ plugin:
example: plugin
cd "$(ROOT_DIR)"/net/grpc/gateway/examples/echo && make

standalone-proxy: package
cd "$(ROOT_DIR)"/net/grpc/gateway/examples/echo && make standalone-proxy

echo_server:
cd "$(ROOT_DIR)"/net/grpc/gateway/examples/echo && make echo_server

Expand Down
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ Try gRPC-Web and run a quick Echo example from the browser!
From the repo root directory:

```sh
$ docker-compose up
$ docker-compose up echo-server envoy commonjs-client-example
```

Open a browser tab, and inspect

```
http://localhost/net/grpc/gateway/examples/echo/echotest.html
http://localhost:8081/echo_commonjs_test.html
```

## How it works
Expand Down Expand Up @@ -99,3 +99,24 @@ stream.on('data', function(response) {
console.log(response.getMessage());
});
```

## Proxy interoperability

Multiple proxies supports the gRPC-Web protocol. Currently, the default proxy
is [Envoy](https://www.envoyproxy.io).

```
$ docker-compose up echo-server envoy commonjs-client-example
```

An alternative is to build Nginx that comes with this repository.

```
$ docker-compose up echo-server nginx commonjs-client-example
```

Finally, you can also try this [gRPC-Web Go Proxy](https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy).

```
$ docker-compose up echo-server grpcwebproxy binary-client-example
```
27 changes: 27 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ services:
- "8080:8080"
links:
- echo-server
grpcwebproxy:
build:
context: ./
dockerfile: ./net/grpc/gateway/docker/grpcwebproxy/Dockerfile
image: grpc-web:grpcwebproxy
ports:
- "8080:8080"
links:
- echo-server
nginx:
build:
context: ./
dockerfile: ./net/grpc/gateway/docker/nginx/Dockerfile
image: grpc-web:nginx
ports:
- "8080:8080"
links:
- echo-server
static-assets:
build:
context: ./
Expand All @@ -41,3 +59,12 @@ services:
image: grpc-web:commonjs-client-example
ports:
- "8081:8081"
binary-client-example:
build:
context: ./
dockerfile: ./net/grpc/gateway/docker/binary_client_example/Dockerfile
depends_on:
- prereqs
image: grpc-web:binary-client-example
ports:
- "8081:8081"
18 changes: 18 additions & 0 deletions etc/localhost.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC9jCCAd4CCQCfXxHXagE8mjANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJV
UzELMAkGA1UECAwCQ0ExITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDAeFw0xODA4MDMyMTE2NDdaFw0yMTA1MjMyMTE2NDdaMD0xCzAJBgNVBAYTAlVT
MQswCQYDVQQIDAJDQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2/MlKj+OtIgJm/7DywOR
POypfvGHXqHTpg/ZbZgflx2vMwgoAhdun2e//AlssouStadnkevPEr+uFfxkEzH3
80iYDtcZKXY8E6692hFrp7hKnA7gcBbb7ZQ1FwG/SfKLtLcderLcQb51P7IsQkfh
nB8hSosV9nHhdfVtsMW7L/caqB5lUHIbRsHhSw3+hzg0r0HuKxXd5HlyRXzf9cQX
4xc5B8Ldxo3QmXDOUHDw9quuxvUn5VWppWCGn2J+f9L/5iwgciApbiMBv/CkiVrt
iYwZY+TZY5u8lmL4FtLd2tj2vNXl5ESWcL1SRGSiaYmxX1B5rg4fSAAXmcNOzZHo
8wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCHko8eFag++9knWV8KlRRi+IdGeatU
TejdBVnCPFc7sJf1lkUQSb0mZMv0QEO51aXGVvU46pIjTAwtzcVgPc6ZHqcZY4r3
xscrmECThbhsEQCHqDD55OB2a06bx+ylfbBnLh+F18W+/rI+HlRxSBGclyfVto1P
aCuYvYc0qKK90Ft1joZh1tXpho/D52B4CTa0Ax/5UqSVjTt0uPDhkCZJKnoENVgh
6hF8ehYTC6Kf6ZtbB6+GuaLXf6F96CROLifW219qxrKmGbMyJXolOxLatufnWwwv
Hw7z1FUzulJUkSRmgPJ9hFeyTjCS1BJ18glFjleLykYOtQi8kvnpFm6C
-----END CERTIFICATE-----
27 changes: 27 additions & 0 deletions etc/localhost.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA2/MlKj+OtIgJm/7DywORPOypfvGHXqHTpg/ZbZgflx2vMwgo
Ahdun2e//AlssouStadnkevPEr+uFfxkEzH380iYDtcZKXY8E6692hFrp7hKnA7g
cBbb7ZQ1FwG/SfKLtLcderLcQb51P7IsQkfhnB8hSosV9nHhdfVtsMW7L/caqB5l
UHIbRsHhSw3+hzg0r0HuKxXd5HlyRXzf9cQX4xc5B8Ldxo3QmXDOUHDw9quuxvUn
5VWppWCGn2J+f9L/5iwgciApbiMBv/CkiVrtiYwZY+TZY5u8lmL4FtLd2tj2vNXl
5ESWcL1SRGSiaYmxX1B5rg4fSAAXmcNOzZHo8wIDAQABAoIBAFPnJL5BEIb9fezr
+nRvH/BFt0KdkC4hPUOTuDV+Wk6jHDozWk+x8JkOUsYqMjTJ2WVCPtgDRDK6vAXX
CbXo0dUUVC0VEJwoZjJ77iBJlO+d9ZgidKtNjQfMCZSFLhtfUrvVPoGXyT2rEb8C
kK+YDBAqL+DnvbENMBx3SyirxQQ+YetAUSxiZagtjKlax1bhXF/JCj816ezDqOzR
ZVx4MJiJg3oD+zKlwP+IaFlANIuW2W7+LbNzPpdXER4gafRzyjRy5ksO9NFO11Bu
2srJbAMZEr0MCEBjf5rD7CPuvhTTEcgk6CNPsIEt8zzMSZUMeS0xaIv0W1FKiw+R
BENntsECgYEA84fSMRApKaweeBrthPKR1ucnv3EHxn1l1mz45bp+2euHB6jUul9D
629mMv8J9PVdcPF5ck7YpCjslWm6oOhtKMemuwJm61cRF44Lz7jtLm3zNJMGhpbh
6Q0GoIVcyMrW79BODbEIU5SlWp0Usyjo/4CZEP5adho1JNTDHCyvEVUCgYEA5zY6
tbfu+YYgICBnaRkGgdCRBxMurxdPrgbwvrKSMerYE9fufpgvZc5wwx02rJX0psuo
JNGLAkPJyimZCYhY/hxWwXQX8X4BhkKK2aFyBMaDIBA0h+unOwTxHrebQNprot3h
YVT8+tfZf8bGPl+dBs3Qf+WOESjRSyO9jr5BMScCgYBfI4mPF1Qtbot8unBeRvGI
tkeF999kwOp/CZV3EhOqiOP4rxFkOgFrwdp4Q8CdDRpTHFMov/rMrxw2BtcdM5Ap
pU3Ss06H1DzeKeUdYo5uXA/uUx3yiJF7HVagcVldLDkp+QP1P1sUY/bxXnqOv4W/
A3tI80Vd7EEkwWXz5NUD/QKBgGXhYXFdQTI2RcWiQa7v1gwxqRYi/7krXnLioAaH
jR/tyZTE21RxHsGPe+Sd5M+brBgrOUYwBz7SPAKW3dZzfDNMrXXFAB/rVCSjAafw
Gdu81V61hVA3KJM7FDxiz0h+dltnxb4rwuWNY0uIfSZS31B2NF+G+VjaUY74irhx
YSyVAoGANrn70s5+cJDWcmDhaFNU/J/X2Q8GgTyBRd02FIvuv7BXgd9TZ7bUVTws
1nsQCCEVJqj4ksddVRq33NvsnjrgetGYe1LGl3uLakqaXd7iJXFCice3ZuFFrp9o
Iq2sUgG+9K8WFqNhANRKBVd32IpQzjIMAAJSbuG3EFZDLZJqxDs=
-----END RSA PRIVATE KEY-----
13 changes: 11 additions & 2 deletions javascript/net/grpc/web/grpc_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ void PrintFileHeader(Printer* printer, const std::map<string, string>& vars) {
}

void PrintServiceConstructor(Printer* printer,
const std::map<string, string>& vars) {
std::map<string, string>& vars) {
printer->Print(
vars,
"/**\n"
Expand All @@ -181,6 +181,14 @@ void PrintServiceConstructor(Printer* printer,
" /**\n"
" * @private @const {!grpc.web.$mode$ClientBase} The client\n"
" */\n"
" if (!options) options = {};\n");
if (vars["mode"] == GetModeVar(Mode::GRPCWEB)) {
printer->Print(
vars,
" options['format'] = '$format$';\n\n");
}
printer->Print(
vars,
" this.client_ = new grpc.web.$mode$ClientBase(options);\n\n"
" /**\n"
" * @private @const {string} The hostname\n"
Expand Down Expand Up @@ -357,8 +365,9 @@ class GrpcCodeGenerator : public CodeGenerator {
vars["mode"] = GetModeVar(Mode::OP);
} else if (mode == "base64") {
vars["mode"] = GetModeVar(Mode::GATEWAY);
} else if (mode == "grpcweb") {
} else if (mode == "grpcweb" || mode == "grpcwebtext") {
vars["mode"] = GetModeVar(Mode::GRPCWEB);
vars["format"] = (mode == "grpcweb") ? "binary" : "text";
} else if (mode == "jspb") {
vars["mode"] = GetModeVar(Mode::OPJSPB);
} else if (mode == "frameworks") {
Expand Down
55 changes: 39 additions & 16 deletions javascript/net/grpc/web/grpcwebclientbase.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ const googCrypt = goog.require('goog.crypt.base64');
* @implements {AbstractClientBase}
*/
const GrpcWebClientBase = function(opt_options) {
/**
* @const
* @private {string}
*/
this.format_ =
goog.getObjectByName('format', opt_options) || "text";

/**
* @const
* @private {boolean}
Expand All @@ -58,8 +65,6 @@ const GrpcWebClientBase = function(opt_options) {
GrpcWebClientBase.prototype.rpcCall = function(
method, request, metadata, methodInfo, callback) {
var xhr = this.newXhr_();
var serialized = methodInfo.requestSerializeFn(request);
xhr.headers.addAll(metadata);

var genericTransportInterface = {
xhr: xhr,
Expand Down Expand Up @@ -89,18 +94,28 @@ GrpcWebClientBase.prototype.rpcCall = function(
}
});

xhr.headers.set('Content-Type', 'application/grpc-web-text');
xhr.headers.addAll(metadata);
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('Accept', 'application/grpc-web-text');

var payload = this.encodeRequest_(serialized);
payload = googCrypt.encodeByteArray(payload);

xhr.headers.set('X-Grpc-Web', '1');
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;
};
Expand All @@ -112,27 +127,35 @@ GrpcWebClientBase.prototype.rpcCall = function(
GrpcWebClientBase.prototype.serverStreaming = function(
method, request, metadata, methodInfo) {
var xhr = this.newXhr_();
var serialized = methodInfo.requestSerializeFn(request);
xhr.headers.addAll(metadata);

var genericTransportInterface = {
xhr: xhr,
};
var stream = new GrpcWebClientReadableStream(genericTransportInterface);
stream.setResponseDeserializeFn(methodInfo.responseDeserializeFn);

xhr.headers.set('Content-Type', 'application/grpc-web-text');
xhr.headers.addAll(metadata);
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('Accept', 'application/grpc-web-text');

var payload = this.encodeRequest_(serialized);
payload = googCrypt.encodeByteArray(payload);

xhr.headers.set('X-Grpc-Web', '1');
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;
Expand Down
27 changes: 18 additions & 9 deletions javascript/net/grpc/web/grpcwebclientreadablestream.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ 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');
const {GenericTransportInterface} = goog.require('grpc.web.GenericTransportInterface');
const {Status} = goog.require('grpc.web.Status');

Expand Down Expand Up @@ -114,18 +115,26 @@ const GrpcWebClientReadableStream = function(genericTransportInterface) {
var self = this;
events.listen(this.xhr_, EventType.READY_STATE_CHANGE,
function(e) {
var FrameType = GrpcWebStreamParser.FrameType;

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);
var contentType = self.xhr_.getStreamingResponseHeader('Content-Type');
if (!contentType) return;
contentType = contentType.toLowerCase();

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(self.xhr_.getResponse());
} else {
return;
}
var messages = self.parser_.parse([].slice.call(byteSource));
if (!messages) return;

var FrameType = GrpcWebStreamParser.FrameType;
for (var i = 0; i < messages.length; i++) {
if (FrameType.DATA in messages[i]) {
var data = messages[i][FrameType.DATA];
Expand Down
40 changes: 40 additions & 0 deletions net/grpc/gateway/docker/binary_client_example/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 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.

FROM grpc-web:prereqs

ARG EXAMPLE_DIR=/github/grpc-web/net/grpc/gateway/examples/echo

RUN cd /github/grpc-web/packages/grpc-web && \
rm -rf node_modules && \
npm install && \
npm run build && \
npm link

RUN protoc -I=$EXAMPLE_DIR echo.proto \
--plugin=protoc-gen-grpc-web=\
/github/grpc-web/javascript/net/grpc/web/protoc-gen-grpc-web \
--js_out=import_style=commonjs:\
$EXAMPLE_DIR/commonjs-example \
--grpc-web_out=import_style=commonjs,mode=grpcweb,out=echo_grpc_pb.js:\
$EXAMPLE_DIR/commonjs-example

RUN cd $EXAMPLE_DIR/commonjs-example && \
rm -rf node_modules && \
npm install && \
npm link grpc-web && \
./node_modules/.bin/browserify client.js > out.js

EXPOSE 8081
CMD ["nginx"]
9 changes: 8 additions & 1 deletion net/grpc/gateway/docker/commonjs_client_example/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,24 @@ FROM grpc-web:prereqs

ARG EXAMPLE_DIR=/github/grpc-web/net/grpc/gateway/examples/echo

RUN cd /github/grpc-web/packages/grpc-web && \
rm -rf node_modules && \
npm install && \
npm run build && \
npm link

RUN protoc -I=$EXAMPLE_DIR echo.proto \
--plugin=protoc-gen-grpc-web=\
/github/grpc-web/javascript/net/grpc/web/protoc-gen-grpc-web \
--js_out=import_style=commonjs:\
$EXAMPLE_DIR/commonjs-example \
--grpc-web_out=import_style=commonjs,mode=grpcweb,out=echo_grpc_pb.js:\
--grpc-web_out=import_style=commonjs,mode=grpcwebtext,out=echo_grpc_pb.js:\
$EXAMPLE_DIR/commonjs-example

RUN cd $EXAMPLE_DIR/commonjs-example && \
rm -rf node_modules && \
npm install && \
npm link grpc-web && \
./node_modules/.bin/browserify client.js > out.js

EXPOSE 8081
Expand Down
Loading

0 comments on commit a9aa7df

Please sign in to comment.