├── .bazelci └── presubmit.yml ├── .bazelignore ├── .bazelrc ├── .dockerignore ├── .github └── workflows │ ├── make-plugin-arm.yml │ ├── make-plugin-linux.yml │ ├── make-plugin-mac-os.yml │ ├── make-plugin-windows.yml │ └── release-source-archive.yml ├── .gitignore ├── .gitmodules ├── AUTHORS ├── CHANGELOG.md ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── GOVERNANCE.md ├── LICENSE ├── MAINTAINERS.md ├── MODULE.bazel ├── MODULE.bazel.lock ├── Makefile ├── PATENTS ├── README.md ├── SECURITY.md ├── doc ├── browser-features.md ├── in-process-proxy.md ├── interop-test-descriptions.md ├── roadmap.md └── streaming-roadmap.md ├── docker-compose.yml ├── etc ├── localhost.crt └── localhost.key ├── javascript └── net │ └── grpc │ └── web │ ├── abstractclientbase.js │ ├── calloptions.js │ ├── clientoptions.js │ ├── clientreadablestream.js │ ├── clientunarycallimpl.js │ ├── generator │ ├── BUILD.bazel │ ├── Makefile │ ├── build.zig │ └── grpc_generator.cc │ ├── generictransportinterface.js │ ├── grpcwebclientbase.js │ ├── grpcwebclientbase_test.js │ ├── grpcwebclientreadablestream.js │ ├── grpcwebstreamparser.js │ ├── grpcwebstreamparser_test.js │ ├── interceptor.js │ ├── metadata.js │ ├── methoddescriptor.js │ ├── methoddescriptorinterface.js │ ├── methodtype.js │ ├── request.js │ ├── requestinternal.js │ ├── rpcerror.js │ ├── status.js │ ├── statuscode.js │ ├── statuscode_test.js │ ├── unaryresponse.js │ └── unaryresponseinternal.js ├── kokoro ├── interop.cfg ├── master.cfg └── presubmit.cfg ├── net └── grpc │ └── gateway │ ├── docker │ ├── binary_client │ │ └── Dockerfile │ ├── closure_client │ │ └── Dockerfile │ ├── commonjs_client │ │ └── Dockerfile │ ├── echo_server │ │ └── Dockerfile │ ├── envoy │ │ └── Dockerfile │ ├── grpcwebproxy │ │ └── Dockerfile │ ├── interop_client │ │ └── Dockerfile │ ├── node_interop_server │ │ └── Dockerfile │ ├── node_server │ │ └── Dockerfile │ ├── prereqs │ │ └── Dockerfile │ ├── protoc_plugin │ │ └── Dockerfile │ └── ts_client │ │ └── Dockerfile │ └── examples │ ├── echo │ ├── .gitignore │ ├── BUILD.bazel │ ├── Makefile │ ├── README.md │ ├── commonjs-example │ │ ├── .gitignore │ │ ├── client.js │ │ ├── echotest.html │ │ ├── package.json │ │ └── webpack.config.js │ ├── echo.proto │ ├── echo_chat.js │ ├── echo_server.cc │ ├── echo_service_impl.cc │ ├── echo_service_impl.h │ ├── echoapp.js │ ├── echotest.html │ ├── envoy.yaml │ ├── node-server │ │ ├── .gitignore │ │ ├── package.json │ │ └── server.js │ ├── package.json │ ├── ts-example │ │ ├── .gitignore │ │ ├── README.md │ │ ├── client.ts │ │ ├── echotest.html │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── webpack.config.js │ └── tutorial.md │ └── helloworld │ ├── .gitignore │ ├── README.md │ ├── client.js │ ├── debugging │ └── node-client.js │ ├── envoy.yaml │ ├── helloworld.proto │ ├── index.html │ ├── package.json │ └── server.js ├── packages └── grpc-web │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── docker │ └── jsunit-test │ │ └── Dockerfile │ ├── exports.js │ ├── externs.js │ ├── gulpfile.js │ ├── index.d.ts │ ├── package.json │ ├── protractor.conf.js │ ├── protractor_spec.js │ ├── scripts │ ├── build.js │ ├── common.py │ ├── gen_all_tests_js.py │ ├── gen_test_htmls.py │ ├── generate_test_files.sh │ ├── run_jsunit_tests.sh │ ├── template_all_tests_js.txt │ └── template_test_html.txt │ └── test │ ├── closure_client.js │ ├── common.js │ ├── eval_test.js │ ├── export_test.js │ ├── generated_code_test.js │ ├── gulpfile.js │ ├── plugin_test.js │ ├── protos │ ├── echo.proto │ ├── foo.proto │ ├── models.proto │ ├── myapi │ │ └── v1 │ │ │ ├── myapi-two.proto │ │ │ └── myapi.proto │ ├── nopackage.proto │ ├── otherapi │ │ └── v1 │ │ │ └── otherapi.proto │ ├── test01.proto │ ├── test02.proto │ └── test03.proto │ ├── tsc-tests │ ├── client01.ts │ ├── client02.ts │ ├── client03.ts │ ├── client04.ts │ ├── client05.ts │ └── client06.ts │ └── tsc_test.js ├── scripts ├── README.md ├── docker-run-build-tests.sh ├── docker-run-interop-tests.sh ├── docker-run-jsunit-tests.sh ├── docker-run-mocha-tests.sh ├── init_submodules.sh ├── kokoro.sh ├── release_notes.py ├── run_basic_tests.sh ├── run_interop_tests.sh └── test-proxy.sh ├── src └── proto │ └── grpc │ └── testing │ ├── empty.proto │ ├── messages.proto │ └── test.proto └── test └── interop ├── .gitignore ├── README.md ├── envoy.yaml ├── index.html ├── interop_client.js ├── package.json └── webpack.config.js /.bazelci/presubmit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # TODO(yannic): Enable buildifier and test on Windows and RBE (both unsupported by rules_closure). 3 | platforms: 4 | macos: 5 | build_targets: 6 | - //... 7 | test_targets: 8 | - //... 9 | 10 | ubuntu1804: 11 | build_targets: 12 | - //... 13 | test_targets: 14 | - //... 15 | -------------------------------------------------------------------------------- /.bazelignore: -------------------------------------------------------------------------------- 1 | # //third_party conatins git submodules. 2 | third_party/ 3 | -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | build --copt=-Wno-error=deprecated-declarations --host_copt=-Wno-error=deprecated-declarations 2 | 3 | # Required until this is the default; expected in Bazel 7 4 | common --enable_bzlmod 5 | 6 | # Load any settings specific to the current user. 7 | # .bazelrc.user should appear in .gitignore so that settings are not shared with team members 8 | # This needs to be last statement in this 9 | # config, as the user configuration should be able to overwrite flags from this file. 10 | # See https://docs.bazel.build/versions/master/best-practices.html#bazelrc 11 | # (Note that we use .bazelrc.user so the file appears next to .bazelrc in directory listing, 12 | # rather than user.bazelrc as suggested in the Bazel docs) 13 | try-import %workspace%/.bazelrc.user 14 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/dist 2 | **/node_modules 3 | packages/grpc-web/generated 4 | -------------------------------------------------------------------------------- /.github/workflows/make-plugin-arm.yml: -------------------------------------------------------------------------------- 1 | name: Make ARM Plugins (Windows/macOS/Linux) 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version_number: 7 | description: 'Version number' 8 | required: true 9 | default: '1.x.x' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | with: 17 | submodules: 'recursive' 18 | - name: Setup Zig 19 | run: | 20 | mkdir -p $HOME/.local/bin $HOME/.local/zig 21 | 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 22 | ln -s $HOME/.local/zig/zig $HOME/.local/bin/zig 23 | echo "PATH=$PATH:$HOME/.local/bin" >> $GITHUB_ENV 24 | - name: Build plugin 25 | env: 26 | VERSION: ${{ github.event.inputs.version_number }} 27 | run: | 28 | cd javascript/net/grpc/web/generator 29 | zig build -Drelease-fast 30 | - name: gen and verify sha256 31 | run: | 32 | cd javascript/net/grpc/web/generator/zig-out/bin 33 | for exe in $(ls) 34 | do 35 | openssl dgst -sha256 -r -out $exe'.sha256' $exe 36 | sha256sum -c $exe'.sha256' 37 | done 38 | - name: Upload artifacts 39 | uses: actions/upload-artifact@v3 40 | with: 41 | name: plugin 42 | path: javascript/net/grpc/web/generator/zig-out/bin/ 43 | -------------------------------------------------------------------------------- /.github/workflows/make-plugin-linux.yml: -------------------------------------------------------------------------------- 1 | name: Make Linux Plugin 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version_number: 7 | description: 'Version number' 8 | required: true 9 | default: '1.x.x' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Build plugin docker image 17 | run: docker-compose build prereqs protoc-plugin 18 | - name: Copy binary from Docker image 19 | run: | 20 | docker cp $(docker create grpcweb/protoc-plugin):/github/grpc-web/javascript/net/grpc/web/generator/protoc-gen-grpc-web \ 21 | ./protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-linux-x86_64 22 | - name: gen sha256 23 | run: | 24 | openssl dgst -sha256 -r -out protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-linux-x86_64.sha256 \ 25 | protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-linux-x86_64 26 | - name: verify sha256 27 | run: sha256sum -c protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-linux-x86_64.sha256 28 | - name: Upload artifacts 29 | uses: actions/upload-artifact@v3 30 | with: 31 | name: plugin 32 | path: protoc-gen-grpc-web* 33 | -------------------------------------------------------------------------------- /.github/workflows/make-plugin-mac-os.yml: -------------------------------------------------------------------------------- 1 | name: Make MacOS Plugin 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version_number: 7 | description: 'Version number' 8 | required: true 9 | default: '1.x.x' 10 | 11 | jobs: 12 | build: 13 | runs-on: macos-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Install build utils 17 | run: brew install coreutils automake 18 | - name: Checkout protobuf code 19 | run: | 20 | ./scripts/init_submodules.sh 21 | # Protobuf build instructions from: 22 | # https://github.com/protocolbuffers/protobuf/blob/master/src/README.md 23 | - name: Build protobuf (configure & make) 24 | run: | 25 | cd ./third_party/protobuf 26 | ./autogen.sh 27 | ./configure 28 | make -j$(nproc) 29 | make install 30 | - name: Remove dynamite dependencies (similar to `-static` on linux) 31 | run: rm /usr/local/lib/libproto*.dylib 32 | - name: make 33 | run: make clean && make plugin 34 | - name: move 35 | run: | 36 | mv javascript/net/grpc/web/generator/protoc-gen-grpc-web \ 37 | ./protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-darwin-x86_64 38 | - name: gen sha256 39 | run: | 40 | openssl dgst -sha256 -r -out protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-darwin-x86_64.sha256 \ 41 | protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-darwin-x86_64 42 | - name: verify sha256 43 | run: sha256sum -c protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-darwin-x86_64.sha256 44 | - name: Upload artifacts 45 | uses: actions/upload-artifact@v3 46 | with: 47 | name: plugin 48 | path: protoc-gen-grpc-web* 49 | -------------------------------------------------------------------------------- /.github/workflows/make-plugin-windows.yml: -------------------------------------------------------------------------------- 1 | name: Make Windows Plugin 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version_number: 7 | description: 'Version number' 8 | required: true 9 | default: '1.x.x' 10 | 11 | jobs: 12 | build: 13 | runs-on: windows-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Print Bazel version 17 | run: | 18 | bazel version 19 | - name: build 20 | run: bazel build javascript/net/grpc/web/generator:protoc-gen-grpc-web 21 | - name: move 22 | run: | 23 | mv bazel-bin/javascript/net/grpc/web/generator/protoc-gen-grpc-web.exe \ 24 | ./protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-windows-x86_64.exe 25 | shell: bash 26 | - name: gen sha256 27 | run: | 28 | openssl dgst -sha256 -r -out protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-windows-x86_64.exe.sha256 \ 29 | protoc-gen-grpc-web-${{ github.event.inputs.version_number }}-windows-x86_64.exe 30 | shell: bash 31 | # TODO: Check sha256 (sha256sum not available for now. ) 32 | #- name: verify sha256 33 | - name: Upload artifacts 34 | uses: actions/upload-artifact@v3 35 | with: 36 | name: plugin 37 | path: protoc-gen-grpc-web* 38 | -------------------------------------------------------------------------------- /.github/workflows/release-source-archive.yml: -------------------------------------------------------------------------------- 1 | name: Publish Stable Source Archive 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | # Whenever a release is published, this uploads an accompanying stable source archive. 9 | # 10 | # Github doesn't guarantee stability of source archives for more than 6 months[1]. 11 | # More stability is required by projects like Bazel Central Registry[2][3]. 12 | # 13 | # [1]: https://github.blog/open-source/git/update-on-the-future-stability-of-source-code-archives-and-hashes/ 14 | # [2]: https://github.com/bazelbuild/bazel-central-registry/blob/main/docs/README.md#validations 15 | # [3]: https://blog.bazel.build/2023/02/15/github-archive-checksum.html 16 | bazel-release-archive: 17 | defaults: 18 | run: 19 | # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ 20 | shell: /usr/bin/bash -euxo pipefail {0} 21 | env: 22 | # github.ref_name is defined here: 23 | # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context 24 | TAG: ${{github.ref_name}} 25 | runs-on: ubuntu-latest 26 | permissions: 27 | contents: write 28 | steps: 29 | - uses: actions/checkout 30 | # GITHUB_REF is defined here: 31 | # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables 32 | - run: git archive --format zip --prefix "grpc-web-$TAG/" --output "grpc-web-source-${TAG}.zip" "$GITHUB_REF" 33 | - run: git archive --format tar.gz --prefix "grpc-web-$TAG/" --output "grpc-web-source-${TAG}.tar.gz" "$GITHUB_REF" 34 | - run: gh release upload "${TAG}" "grpc-web-source-${TAG}.zip" "grpc-web-source-${TAG}.tar.gz" 35 | env: 36 | GH_TOKEN: ${{ github.token }} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | bazel-bin 3 | bazel-genfiles 4 | bazel-grpc-web 5 | bazel-out 6 | bazel-testlogs 7 | *.o 8 | protoc-gen-* 9 | .DS_Store 10 | target 11 | .project 12 | .classpath 13 | .settings 14 | zig-out 15 | zig-cache 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/protobuf"] 2 | path = third_party/protobuf 3 | url = https://github.com/protocolbuffers/protobuf.git 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Google Inc. 2 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Community Code of Conduct 2 | 3 | gRPC-Web follows the 4 | [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We definitely welcome patches and contribution to gRPC-Web! Here is some guideline 4 | and information about how to do so. 5 | 6 | Please read the gRPC 7 | organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md) 8 | and [contribution guidelines](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md) before proceeding. 9 | 10 | ## Getting started 11 | 12 | ### Legal requirements 13 | 14 | In order to protect both you and ourselves, you will need to sign the 15 | [Contributor License Agreement](https://cla.developers.google.com/clas). 16 | 17 | ### Technical requirements 18 | 19 | The basic build script should run to completion. 20 | 21 | ```sh 22 | $ ./scripts/kokoro.sh 23 | ``` 24 | 25 | More details to come. 26 | -------------------------------------------------------------------------------- /GOVERNANCE.md: -------------------------------------------------------------------------------- 1 | This repository is governed by the gRPC organization's [governance rules](https://github.com/grpc/grpc-community/blob/master/governance.md). 2 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | This page lists all active maintainers of this repository. If you were a 2 | maintainer and would like to add your name to the Emeritus list, please send us a 3 | PR. 4 | 5 | See [GOVERNANCE.md](https://github.com/grpc/grpc-community/blob/master/governance.md) 6 | for governance guidelines and how to become a maintainer. 7 | See [CONTRIBUTING.md](https://github.com/grpc/grpc-community/blob/master/CONTRIBUTING.md) 8 | for general contribution guidelines. 9 | 10 | ## Maintainers (in alphabetical order) 11 | - [sampajano](https://github.com/sampajano), Google Inc. 12 | - [wenbozhu](https://github.com/wenbozhu), Google Inc. 13 | 14 | ## Emeritus Maintainers (in alphabetical order) 15 | - [fengli79](https://github.com/fengli79) 16 | - [stanley-cheung](https://github.com/stanley-cheung) 17 | -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | """ 2 | A bazel module for the grpc-web project. 3 | 4 | Visit https://grpc.io/ and https://github.com/grpc/grpc-web for 5 | more information about the project. 6 | """ 7 | 8 | module( 9 | name = "grpc-web", 10 | version = "1.6.0", 11 | compatibility_level = 1, 12 | repo_name = "com_github_grpc_grpc_web", 13 | ) 14 | 15 | bazel_dep(name = "protobuf", version = "27.1", repo_name = "com_google_protobuf") 16 | bazel_dep(name = "grpc", version = "1.65.0", repo_name = "com_github_grpc_grpc") 17 | bazel_dep(name = "rules_cc", version = "0.0.2") 18 | bazel_dep(name = "rules_proto", version = "6.0.2") 19 | 20 | # Needed to resolve https://github.com/bazelbuild/bazel-central-registry/issues/2538. 21 | single_version_override( 22 | module_name = "grpc-java", 23 | version = "1.64.0", 24 | ) 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ROOT_DIR := $(shell pwd) 2 | 3 | all: clean 4 | 5 | plugin: 6 | cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make 7 | 8 | install-plugin: 9 | cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make install 10 | 11 | clean: 12 | cd "$(ROOT_DIR)"/javascript/net/grpc/web/generator && make clean 13 | cd "$(ROOT_DIR)" 14 | -------------------------------------------------------------------------------- /PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the GRPC project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of GRPC, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of GRPC. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of GRPC or any code incorporated within this 19 | implementation of GRPC constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of GRPC 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | 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). 4 | -------------------------------------------------------------------------------- /doc/browser-features.md: -------------------------------------------------------------------------------- 1 | # gRPC-Web features for browser (HTML) clients 2 | 3 | Due to browser limitation, gRPC-Web uses a different transport 4 | than the [HTTP/2 based gRPC protocol](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md). 5 | The difference between the gRPC-Web 6 | 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). 7 | 8 | In addition to the wire-transport spec, gRPC-Web also supports features that are unique to browser (HTML) clients. 9 | This document serves as the official spec for those features. As the Web platform evolves, 10 | we expect some of those features will evolve too or become deprecated. 11 | 12 | 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. 13 | 14 | # CORS support 15 | 16 | * Should follow the [CORS spec](https://developer.mozilla.org/en-US/docs/Web/HTTP/Server-Side_Access_Control) (Mandatory) 17 | * Access-Control-Allow-Credentials to allow Authorization headers 18 | * Access-Control-Allow-Methods to allow POST and (preflight) OPTIONS only 19 | * Access-Control-Allow-Headers to whatever the preflight request carries 20 | * Access-Control-Expose-Headers to allow Javascript access to `grpc-status,grpc-message` headers. 21 | * The client library is expected to support header overwrites to avoid preflight 22 | * https://github.com/whatwg/fetch/issues/210 23 | * CSP support to be specified 24 | 25 | # HTTP status code mapping 26 | 27 | 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. 28 | 29 | # URL query params 30 | 31 | 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. 32 | 33 | 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. 34 | 35 | # Security 36 | 37 | * XSRF, XSS policy to be published 38 | 39 | # Compression 40 | 41 | * Full-body compression is supported and expected for all unary 42 | requests/responses. The compression/decompression will be done 43 | by browsers, using standard Content-Encoding headers 44 | * “grpc-encoding” header is not used 45 | * SDCH, Brotli will be supported 46 | * Message-level compression for streamed requests/responses is not supported 47 | because manual compression/decompression is prohibitively expensive using JS 48 | -------------------------------------------------------------------------------- /doc/in-process-proxy.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | In-process proxies allow a browser client to talk to a gRPC server directly without relying on any intermediary process 4 | such as an Envoy proxy. This document provides a high-level design guidelines on how we expect such a "proxy" to work. 5 | 6 | # The choice of HTTP stack 7 | 8 | We strongly recommend that the gRPC-Web module use the default HTTP stack provided by the language platform, or in the case of Java, 9 | the standard Java Servlet framework. This is to ensure maximum portability and to ease integration between gRPC-Web and existing Web 10 | frameworks. 11 | 12 | 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 13 | intermediaries to negotiate the HTTP version, which is transparent to the gRPC-Web module. 14 | 15 | # Request translation 16 | 17 | 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 18 | to the gRPC server via a local socket. The gRPC-Web support is fully transparent to the gRPC server. 19 | 20 | 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 21 | directly as part of the gRPC server implementation. The added complexity to the gRPC implementation itself is still a concern. 22 | 23 | # HTTP port 24 | 25 | 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. 26 | However, since CORS is a mandatory feature for gRPC-Web proxies, port sharing should be optional for in-process proxies. 27 | 28 | # Core features 29 | 30 | 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. 31 | -------------------------------------------------------------------------------- /doc/interop-test-descriptions.md: -------------------------------------------------------------------------------- 1 | gRPC-Web Interop Tests 2 | ====================== 3 | 4 | This document describes the set of tests any gRPC-Web clients or proxies need 5 | to implement. The proto definition for the messages and RPCs we are using for 6 | the tests can be found 7 | [here](https://github.com/grpc/grpc/blob/master/src/proto/grpc/testing/test.proto). 8 | 9 | The canonical set of interop tests was defined in the main 10 | [grpc/grpc repo](https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md). 11 | 12 | Here in gRPC-Web, we will only implement a subset of tests that's relevant to 13 | gRPC-Web. For example, we will not be implementing any tests involving 14 | client-streaming or bidi-streaming for now. On the other hand, there are 15 | gRPC-Web specific tests that we will add here. 16 | 17 | ``` 18 | gRPC-Web Client <--> Proxy <--> gRPC Service 19 | ``` 20 | 21 | The idea is that we should be able to swap out any of the 3 components above 22 | and all the interop tests should still pass. 23 | 24 | This repository will provide a canonical implementation of the interop test 25 | suite using the Javascript client, Envoy and a gRPC service implemented in 26 | Node. 27 | 28 | For any new gRPC-Web client implementation, you need to swap out the JS 29 | client and make sure all tests still pass. 30 | 31 | For any in-process proxies implementation, you need to swap out the proxy 32 | and the service as a unit and make sure the standard JS client will still 33 | pass the tests. 34 | 35 | 36 | List of Tests 37 | ------------- 38 | 39 | | Test Name | grpc-web-text Mode | grpc-web Binary mode | 40 | | --------- |:------------------:|:--------------------:| 41 | | empty_unary | ✓ | ✓ | 42 | | cacheable_unary | TBD | TBD | 43 | | large_unary | ✓ | ✓ | 44 | | client_compressed_unary | ✓ | ✓ | 45 | | server_compressed_unary | ✓ | ✓ | 46 | | client_streaming | ✗ | ✗ | 47 | | client_compressed_streaming | ✗ | ✗ | 48 | | server_streaming | ✓ | ✗ | 49 | | server_compressed_streaming | ✓ | ✗ | 50 | | ping_pong | ✗ | ✗ | 51 | | empty_stream | ✗ | ✗ | 52 | | compute_engine_creds | TBD | TBD | 53 | | jwt_token_creds | TBD | TBD | 54 | | oauth2_auth_token | TBD | TBD | 55 | | per_rpc_creds | TBD | TBD | 56 | | google_default_credentials | TBD | TBD | 57 | | compute_engine_channel_credentials | TBD | TBD | 58 | | custom_metadata * | ✓ | ✓ | 59 | | status_code_and_message * | ✓ | ✓ | 60 | | special_status_message | ✓ | ✓ | 61 | | unimplemented_method | ✓ | ✓ | 62 | | unimplemented_service | ✓ | ✓ | 63 | | cancel_after_begin | ✗ | ✗ | 64 | | cancel_after_first_response | ✗ | ✗ | 65 | | timeout_on_sleeping_server | ✗ | ✗ | 66 | 67 | \* only need to implement the UnaryCall RPC 68 | 69 | gRPC-Web specific considerations 70 | -------------------------------- 71 | 72 | ### Text vs Binary mode 73 | 74 | As mentioned in the table above, client needs to be tested in both the text 75 | format `application/grpc-web-text` and the binary mode 76 | `application/grpc-web+proto`. The latter we don't need to test any streaming 77 | methods. 78 | 79 | ### CORS and other web specific scenarios 80 | 81 | We may add specific tests to account for web-related scenarios like CORS 82 | handling, etc. Mostly these are to test the connection between the browser 83 | client and the proxy. 84 | -------------------------------------------------------------------------------- /doc/roadmap.md: -------------------------------------------------------------------------------- 1 | # gRPC-Web Roadmap 2 | 3 | The purpose of this document is to collect all the features that we believe are 4 | useful for gRPC users. 5 | 6 | ## Background 7 | 8 | gRPC-Web has been developed internally at Google as part of the front-end 9 | stacks for Google's Web applications and cloud services. Over time we plan to 10 | open-source and publish most of the features and make them available to open-source 11 | users. 12 | 13 | Like everywhere, Web platforms and technologies are constantly evolving, often 14 | with many inter-dependent ecosystems. As much as we like to open-source 15 | everything, we also need keep the balance between creating a reusable and stable 16 | open-source solution and meeting those requirements unique to Google's Web applications 17 | (such as search). 18 | 19 | ## Roadmap Features 20 | 21 | ### TypeScript Codebase (2024) 22 | Migrate the codebase to TypeScript and update the related toolchains (incl. remove 23 | dependency on `closure-compiler`). Enhance overall TypeScript support. 24 | 25 | ### Streaming Support 26 | 27 | Enhance Fetch/streams support (e.g. cancellation support) and improve runtime 28 | support, including service workers. 29 | 30 | See streaming roadmap [here](streaming-roadmap.md). 31 | 32 | ### Bidi Streaming 33 | 34 | We plan to leverage [WebTransport](https://web.dev/webtransport/) for bi-directional streaming. 35 | 36 | Also see the dedicate road-map doc on [bidi streaming](streaming-roadmap.md). 37 | 38 | ### Non-Binary Message Encoding 39 | 40 | The binary protobuf encoding format is not most CPU efficient for browser 41 | clients. Furthermore, the generated code size increases as the total protobuf 42 | definition increases. 43 | 44 | For Google's Web applications (e.g. gmail), we use a JSON like format which is 45 | comparable to JSON in efficiency but also very compact in both the message size 46 | and code size. 47 | 48 | ### Security 49 | 50 | We plan to publish a comprehensive guideline doc on how to create secure Web 51 | applications. 52 | 53 | Native support such as XSRF, XSS prevention may also be added to the gRPC-Web 54 | protocol. 55 | 56 | ### In-process Proxies 57 | 58 | [In-process proxies](https://github.com/grpc/grpc-web/blob/master/doc/in-process-proxy.md) 59 | will eliminate the need to deploy Envoy to use gRPC-Web. 60 | 61 | We have plans to add in-process proxy support in Python, Java, Node, C++ etc. Let us know 62 | if you are interested in implementing any language-specific in-process 63 | gRPC-Web proxy. 64 | 65 | To minimize maintenance overhead, we will only support Envoy as the official proxy for gRPC-Web. 66 | 67 | ### Web Framework Integration 68 | 69 | This is to provide first-class support for gRPC API and gRPC-Web in popular Web 70 | frameworks such as Angular. 71 | 72 | Note: Dart gRPC will use gRPC-Web as the underlying implementation on the 73 | Dart Web platform. 74 | 75 | ### Non-Closure compiler support 76 | 77 | With the addition of CommonJS style imports, gRPC-Web client stubs can now be 78 | compiled with various tools such as Browserify, Webpack, etc. Let us know 79 | what else we should try! 80 | 81 | ### Web UI Support 82 | 83 | This allows the user to construct and submit a gRPC request directly using the 84 | browser. 85 | 86 | We need define a standard look & feel for creating and rendering nested protobuf 87 | messages. 88 | -------------------------------------------------------------------------------- /doc/streaming-roadmap.md: -------------------------------------------------------------------------------- 1 | # Streaming Roadmap 2 | 3 | This document describes the road-map for gRPC-Web to support different streaming features. 4 | * Server-streaming 5 | * Client-streaming and half-duplex streaming 6 | * Full-duplex streaming over WebTransport 7 | 8 | ## Server-streaming 9 | 10 | We will keep improving server-streaming in the following areas: 11 | * Fetch cancellation support - 2024 12 | * Performance improvements and whatwg Fetch/streams support, including service workers - 2024 13 | * Finalizing keep-alive support (via Envoy) - 2024+ 14 | * Addressing runtime behavior gaps between Fetch and XHR - 2024+ 15 | 16 | ## Client-streaming and half-duplex streaming 17 | 18 | We don’t 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 won’t be supported via Fetch/streams either. 19 | 20 | Client-streaming and half-duplex bidi streaming will be addressed when Full-duplex streaming is supported via WebTransport (see below). 21 | 22 | ## Full-duplex streaming over WebTransport 23 | 24 | We will be leveraging [WebTransport](https://web.dev/webtransport/) to enable full-duplex (bi-directional) streaming. Planned for 2023+. 25 | 26 | ## Issues with WebSockets 27 | 28 | 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. 29 | 30 | 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. 31 | 32 | ## Appendix 33 | 34 | ### Chrome Origin Trial on `upload-streaming` 35 | 36 | We worked on a Chrome [Origin Trial](https://developers.chrome.com/origintrials/#/view_trial/3524066708417413121) 37 | 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 38 | 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 39 | as a fallback for each client-streaming method. -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | prereqs: 4 | build: 5 | context: ./ 6 | dockerfile: ./net/grpc/gateway/docker/prereqs/Dockerfile 7 | image: grpcweb/prereqs 8 | echo-server: 9 | build: 10 | context: ./ 11 | dockerfile: ./net/grpc/gateway/docker/echo_server/Dockerfile 12 | depends_on: 13 | - prereqs 14 | image: grpcweb/echo-server 15 | ports: 16 | - "9090:9090" 17 | node-server: 18 | build: 19 | context: ./ 20 | dockerfile: ./net/grpc/gateway/docker/node_server/Dockerfile 21 | depends_on: 22 | - prereqs 23 | image: grpcweb/node-server 24 | ports: 25 | - "9090:9090" 26 | node-interop-server: 27 | build: 28 | context: ./ 29 | dockerfile: ./net/grpc/gateway/docker/node_interop_server/Dockerfile 30 | image: grpcweb/node-interop-server 31 | ports: 32 | - "7074:7074" 33 | envoy: 34 | build: 35 | context: ./ 36 | dockerfile: ./net/grpc/gateway/docker/envoy/Dockerfile 37 | image: grpcweb/envoy 38 | ports: 39 | - "8080:8080" 40 | links: 41 | - node-server 42 | grpcwebproxy: 43 | build: 44 | context: ./ 45 | dockerfile: ./net/grpc/gateway/docker/grpcwebproxy/Dockerfile 46 | image: grpcweb/grpcwebproxy 47 | ports: 48 | - "8080:8080" 49 | links: 50 | - node-server 51 | commonjs-client: 52 | build: 53 | context: ./ 54 | dockerfile: ./net/grpc/gateway/docker/commonjs_client/Dockerfile 55 | depends_on: 56 | - prereqs 57 | image: grpcweb/commonjs-client 58 | ports: 59 | - "8081:8081" 60 | closure-client: 61 | build: 62 | context: ./ 63 | dockerfile: ./net/grpc/gateway/docker/closure_client/Dockerfile 64 | depends_on: 65 | - prereqs 66 | image: grpcweb/closure-client 67 | ports: 68 | - "8081:8081" 69 | ts-client: 70 | build: 71 | context: ./ 72 | dockerfile: ./net/grpc/gateway/docker/ts_client/Dockerfile 73 | depends_on: 74 | - prereqs 75 | image: grpcweb/ts-client 76 | ports: 77 | - "8081:8081" 78 | binary-client: 79 | build: 80 | context: ./ 81 | dockerfile: ./net/grpc/gateway/docker/binary_client/Dockerfile 82 | depends_on: 83 | - prereqs 84 | image: grpcweb/binary-client 85 | ports: 86 | - "8081:8081" 87 | interop-client: 88 | build: 89 | context: ./ 90 | dockerfile: ./net/grpc/gateway/docker/interop_client/Dockerfile 91 | depends_on: 92 | - prereqs 93 | image: grpcweb/interop-client 94 | ports: 95 | - "8081:8081" 96 | protoc-plugin: 97 | build: 98 | context: ./ 99 | dockerfile: ./net/grpc/gateway/docker/protoc_plugin/Dockerfile 100 | depends_on: 101 | - prereqs 102 | image: grpcweb/protoc-plugin 103 | jsunit-test: 104 | build: 105 | context: ./ 106 | dockerfile: ./packages/grpc-web/docker/jsunit-test/Dockerfile 107 | image: grpcweb/jsunit-test 108 | -------------------------------------------------------------------------------- /etc/localhost.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC9jCCAd4CCQCfXxHXagE8mjANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECAwCQ0ExITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 4 | ZDAeFw0xODA4MDMyMTE2NDdaFw0yMTA1MjMyMTE2NDdaMD0xCzAJBgNVBAYTAlVT 5 | MQswCQYDVQQIDAJDQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk 6 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2/MlKj+OtIgJm/7DywOR 7 | POypfvGHXqHTpg/ZbZgflx2vMwgoAhdun2e//AlssouStadnkevPEr+uFfxkEzH3 8 | 80iYDtcZKXY8E6692hFrp7hKnA7gcBbb7ZQ1FwG/SfKLtLcderLcQb51P7IsQkfh 9 | nB8hSosV9nHhdfVtsMW7L/caqB5lUHIbRsHhSw3+hzg0r0HuKxXd5HlyRXzf9cQX 10 | 4xc5B8Ldxo3QmXDOUHDw9quuxvUn5VWppWCGn2J+f9L/5iwgciApbiMBv/CkiVrt 11 | iYwZY+TZY5u8lmL4FtLd2tj2vNXl5ESWcL1SRGSiaYmxX1B5rg4fSAAXmcNOzZHo 12 | 8wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCHko8eFag++9knWV8KlRRi+IdGeatU 13 | TejdBVnCPFc7sJf1lkUQSb0mZMv0QEO51aXGVvU46pIjTAwtzcVgPc6ZHqcZY4r3 14 | xscrmECThbhsEQCHqDD55OB2a06bx+ylfbBnLh+F18W+/rI+HlRxSBGclyfVto1P 15 | aCuYvYc0qKK90Ft1joZh1tXpho/D52B4CTa0Ax/5UqSVjTt0uPDhkCZJKnoENVgh 16 | 6hF8ehYTC6Kf6ZtbB6+GuaLXf6F96CROLifW219qxrKmGbMyJXolOxLatufnWwwv 17 | Hw7z1FUzulJUkSRmgPJ9hFeyTjCS1BJ18glFjleLykYOtQi8kvnpFm6C 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /etc/localhost.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEA2/MlKj+OtIgJm/7DywORPOypfvGHXqHTpg/ZbZgflx2vMwgo 3 | Ahdun2e//AlssouStadnkevPEr+uFfxkEzH380iYDtcZKXY8E6692hFrp7hKnA7g 4 | cBbb7ZQ1FwG/SfKLtLcderLcQb51P7IsQkfhnB8hSosV9nHhdfVtsMW7L/caqB5l 5 | UHIbRsHhSw3+hzg0r0HuKxXd5HlyRXzf9cQX4xc5B8Ldxo3QmXDOUHDw9quuxvUn 6 | 5VWppWCGn2J+f9L/5iwgciApbiMBv/CkiVrtiYwZY+TZY5u8lmL4FtLd2tj2vNXl 7 | 5ESWcL1SRGSiaYmxX1B5rg4fSAAXmcNOzZHo8wIDAQABAoIBAFPnJL5BEIb9fezr 8 | +nRvH/BFt0KdkC4hPUOTuDV+Wk6jHDozWk+x8JkOUsYqMjTJ2WVCPtgDRDK6vAXX 9 | CbXo0dUUVC0VEJwoZjJ77iBJlO+d9ZgidKtNjQfMCZSFLhtfUrvVPoGXyT2rEb8C 10 | kK+YDBAqL+DnvbENMBx3SyirxQQ+YetAUSxiZagtjKlax1bhXF/JCj816ezDqOzR 11 | ZVx4MJiJg3oD+zKlwP+IaFlANIuW2W7+LbNzPpdXER4gafRzyjRy5ksO9NFO11Bu 12 | 2srJbAMZEr0MCEBjf5rD7CPuvhTTEcgk6CNPsIEt8zzMSZUMeS0xaIv0W1FKiw+R 13 | BENntsECgYEA84fSMRApKaweeBrthPKR1ucnv3EHxn1l1mz45bp+2euHB6jUul9D 14 | 629mMv8J9PVdcPF5ck7YpCjslWm6oOhtKMemuwJm61cRF44Lz7jtLm3zNJMGhpbh 15 | 6Q0GoIVcyMrW79BODbEIU5SlWp0Usyjo/4CZEP5adho1JNTDHCyvEVUCgYEA5zY6 16 | tbfu+YYgICBnaRkGgdCRBxMurxdPrgbwvrKSMerYE9fufpgvZc5wwx02rJX0psuo 17 | JNGLAkPJyimZCYhY/hxWwXQX8X4BhkKK2aFyBMaDIBA0h+unOwTxHrebQNprot3h 18 | YVT8+tfZf8bGPl+dBs3Qf+WOESjRSyO9jr5BMScCgYBfI4mPF1Qtbot8unBeRvGI 19 | tkeF999kwOp/CZV3EhOqiOP4rxFkOgFrwdp4Q8CdDRpTHFMov/rMrxw2BtcdM5Ap 20 | pU3Ss06H1DzeKeUdYo5uXA/uUx3yiJF7HVagcVldLDkp+QP1P1sUY/bxXnqOv4W/ 21 | A3tI80Vd7EEkwWXz5NUD/QKBgGXhYXFdQTI2RcWiQa7v1gwxqRYi/7krXnLioAaH 22 | jR/tyZTE21RxHsGPe+Sd5M+brBgrOUYwBz7SPAKW3dZzfDNMrXXFAB/rVCSjAafw 23 | Gdu81V61hVA3KJM7FDxiz0h+dltnxb4rwuWNY0uIfSZS31B2NF+G+VjaUY74irhx 24 | YSyVAoGANrn70s5+cJDWcmDhaFNU/J/X2Q8GgTyBRd02FIvuv7BXgd9TZ7bUVTws 25 | 1nsQCCEVJqj4ksddVRq33NvsnjrgetGYe1LGl3uLakqaXd7iJXFCice3ZuFFrp9o 26 | Iq2sUgG+9K8WFqNhANRKBVd32IpQzjIMAAJSbuG3EFZDLZJqxDs= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/abstractclientbase.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | /** 19 | * @fileoverview gRPC browser client library. 20 | * 21 | * Base interface for gRPC Web JS clients 22 | * 23 | * @author stanleycheung@google.com (Stanley Cheung) 24 | */ 25 | goog.module('grpc.web.AbstractClientBase'); 26 | 27 | goog.module.declareLegacyNamespace(); 28 | 29 | 30 | const ClientReadableStream = goog.require('grpc.web.ClientReadableStream'); 31 | const MethodDescriptor = goog.require('grpc.web.MethodDescriptor'); 32 | const RpcError = goog.require('grpc.web.RpcError'); 33 | 34 | 35 | /** 36 | * @constructor 37 | * @struct 38 | * @final 39 | */ 40 | const PromiseCallOptions = function() {}; 41 | 42 | /** 43 | * An AbortSignal to abort the call. 44 | * @type {AbortSignal|undefined} 45 | */ 46 | PromiseCallOptions.prototype.signal; 47 | 48 | 49 | /** 50 | * This interface represents a grpc-web client 51 | * @interface 52 | */ 53 | const AbstractClientBase = class { 54 | constructor() {} 55 | 56 | /** 57 | * @abstract 58 | * @template REQUEST, RESPONSE 59 | * @param {string} method The method to invoke 60 | * @param {REQUEST} requestMessage The request proto 61 | * @param {!Object} metadata User defined call metadata 62 | * @param {!MethodDescriptor} 63 | * methodDescriptor Information of this RPC method 64 | * @param {function(?RpcError, ?)} 65 | * callback A callback function which takes (error, RESPONSE or null) 66 | * @return {!ClientReadableStream} 67 | */ 68 | rpcCall(method, requestMessage, metadata, methodDescriptor, callback) {} 69 | 70 | /** 71 | * @abstract 72 | * @protected 73 | * @template REQUEST, RESPONSE 74 | * @param {string} method The method to invoke 75 | * @param {REQUEST} requestMessage The request proto 76 | * @param {!Object} metadata User defined call metadata 77 | * @param {!MethodDescriptor} 78 | * methodDescriptor Information of this RPC method 79 | * @param options Options for the call 80 | * @return {!IThenable} 81 | * A promise that resolves to the response message 82 | */ 83 | thenableCall(method, requestMessage, metadata, methodDescriptor, options) {} 84 | 85 | /** 86 | * @abstract 87 | * @template REQUEST, RESPONSE 88 | * @param {string} method The method to invoke 89 | * @param {REQUEST} requestMessage The request proto 90 | * @param {!Object} metadata User defined call metadata 91 | * @param {!MethodDescriptor} 92 | * methodDescriptor Information of this RPC method 93 | * @return {!ClientReadableStream} The Client Readable Stream 94 | */ 95 | serverStreaming(method, requestMessage, metadata, methodDescriptor) {} 96 | }; 97 | 98 | /** 99 | * Get the hostname of the current request. 100 | * @template REQUEST, RESPONSE 101 | * @param {string} method 102 | * @param {!MethodDescriptor} methodDescriptor 103 | * @return {string} 104 | */ 105 | function getHostname(method, methodDescriptor) { 106 | // method = hostname + methodDescriptor.name(relative path of this method) 107 | return method.substr(0, method.length - methodDescriptor.name.length); 108 | } 109 | 110 | 111 | exports = {AbstractClientBase, PromiseCallOptions, getHostname}; 112 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/calloptions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview grpc.web.CallOptions 3 | */ 4 | 5 | goog.module('grpc.web.CallOptions'); 6 | goog.module.declareLegacyNamespace(); 7 | 8 | /** 9 | * The collection of runtime options for a new RPC call. 10 | * @unrestricted 11 | */ 12 | class CallOptions { 13 | /** 14 | * @param {!Object=} options 15 | */ 16 | constructor(options) { 17 | /** 18 | * @const {!Object} 19 | * @private 20 | */ 21 | this.properties_ = options || {}; 22 | } 23 | 24 | /** 25 | * Add a new CallOption or override an existing one. 26 | * 27 | * @param {string} name name of the CallOption that should be 28 | * added/overridden. 29 | * @param {VALUE} value value of the CallOption 30 | * @template VALUE 31 | */ 32 | setOption(name, value) { 33 | this.properties_[name] = value; 34 | } 35 | 36 | /** 37 | * Get the value of one CallOption. 38 | * 39 | * @param {string} name name of the CallOption. 40 | * @return {!Object} value of the CallOption. If name doesn't exist, will 41 | * return 'undefined'. 42 | */ 43 | get(name) { 44 | return this.properties_[name]; 45 | } 46 | 47 | /** 48 | * Remove a CallOption. 49 | * 50 | * @param {string} name name of the CallOption that shoud be removed. 51 | */ 52 | removeOption(name) { 53 | delete this.properties_[name]; 54 | } 55 | 56 | /** 57 | * @return {!Array} 58 | */ 59 | getKeys() { 60 | return Object.keys(this.properties_); 61 | } 62 | } 63 | 64 | 65 | 66 | exports = CallOptions; 67 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/clientoptions.js: -------------------------------------------------------------------------------- 1 | goog.module('grpc.web.ClientOptions'); 2 | goog.module.declareLegacyNamespace(); 3 | 4 | const {StreamInterceptor, UnaryInterceptor} = goog.require('grpc.web.Interceptor'); 5 | 6 | 7 | /** 8 | * Options that are available during the client construction. 9 | * @record 10 | */ 11 | class ClientOptions { 12 | constructor() { 13 | /** 14 | * Whether to use the HttpCors library to pack http headers into a special 15 | * url query param $httpHeaders= so that browsers can bypass CORS OPTIONS 16 | * requests. 17 | * @type {boolean|undefined} 18 | */ 19 | this.suppressCorsPreflight; 20 | 21 | /** 22 | * Whether to turn on XMLHttpRequest's withCredentials flag. 23 | * @type {boolean|undefined} 24 | */ 25 | this.withCredentials; 26 | 27 | /** 28 | * Unary interceptors. Note that they are only available in grpcweb and 29 | * grpcwebtext mode 30 | * @type {!Array|undefined} 31 | */ 32 | this.unaryInterceptors; 33 | 34 | /** 35 | * Stream interceptors. Note that they are only available in grpcweb and 36 | * grpcwebtext mode 37 | * @type {!Array|undefined} 38 | */ 39 | this.streamInterceptors; 40 | 41 | /** 42 | * Protocol buffer format for open source gRPC-Web. This attribute should be 43 | * specified by the gRPC-Web build rule by default. 44 | * @type {string|undefined} 45 | */ 46 | this.format; 47 | 48 | /** 49 | * The Worker global scope. Once this option is specified, gRPC-Web will 50 | * also use 'fetch' API as the underlying transport instead of native 51 | * XmlHttpRequest. 52 | * @type {!WorkerGlobalScope|undefined} 53 | */ 54 | this.workerScope; 55 | 56 | /** 57 | * This is an experimental feature to reduce memory consumption 58 | * during high throughput server-streaming calls by using 59 | * 'streamBinaryChunks' mode FetchXmlHttpFactory. 60 | * @type {boolean|undefined} 61 | */ 62 | this.useFetchDownloadStreams; 63 | } 64 | } 65 | 66 | exports = ClientOptions; 67 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/clientreadablestream.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | /** 19 | * @fileoverview gRPC web client Readable Stream 20 | * 21 | * This class is being returned after a gRPC streaming call has been 22 | * started. This class provides functionality for user to operates on 23 | * the stream, e.g. set onData callback, etc. 24 | * 25 | * This wraps the underlying goog.net.streams.NodeReadableStream 26 | * 27 | * @author stanleycheung@google.com (Stanley Cheung) 28 | */ 29 | goog.module('grpc.web.ClientReadableStream'); 30 | 31 | goog.module.declareLegacyNamespace(); 32 | 33 | 34 | 35 | /** 36 | * A stream that the client can read from. Used for calls that are streaming 37 | * from the server side. 38 | * 39 | * @template RESPONSE 40 | * @interface 41 | */ 42 | const ClientReadableStream = function() {}; 43 | 44 | 45 | /** 46 | * Register a callback to handle different stream events. 47 | * 48 | * Available event types for gRPC-Web: 49 | * 'data': The 'data' event is emitted when a new response message chunk is 50 | * received and successfully handled by gRPC-Web client. 51 | * 'status': the google RPC status of the response stream. 52 | * 'end': The 'end' event is emitted when all the data have been successfully 53 | * consumed from the stream. 54 | * 'error': typically, this may occur when an underlying internal failure 55 | * happens, or a stream implementation attempts to push an invalid chunk of 56 | * data. 57 | * 'metadata': the response metadata. Response headers should be read via 58 | * 'metadata' callbacks. 59 | * 60 | * For server-streaming calls. the 'data' and 'status' callbacks (if exist) 61 | * will always precede 'metadata', 'error', or 'end' callbacks. 62 | * 63 | * @param {string} eventType The event type 64 | * @param {function(?)} callback The callback to handle the event with 65 | * an optional input object 66 | * @return {!ClientReadableStream} this object 67 | */ 68 | ClientReadableStream.prototype.on = goog.abstractMethod; 69 | 70 | 71 | 72 | /** 73 | * Remove a particular callback. 74 | * 75 | * @param {string} eventType The event type 76 | * @param {function(?)} callback The callback to remove 77 | * @return {!ClientReadableStream} this object 78 | */ 79 | ClientReadableStream.prototype.removeListener = goog.abstractMethod; 80 | 81 | 82 | 83 | /** 84 | * Close the stream. 85 | */ 86 | ClientReadableStream.prototype.cancel = goog.abstractMethod; 87 | 88 | 89 | 90 | exports = ClientReadableStream; 91 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/clientunarycallimpl.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview This class handles ClientReadableStream returned by unary 3 | * calls. 4 | */ 5 | 6 | goog.module('grpc.web.ClientUnaryCallImpl'); 7 | 8 | goog.module.declareLegacyNamespace(); 9 | 10 | const ClientReadableStream = goog.require('grpc.web.ClientReadableStream'); 11 | 12 | /** 13 | * @implements {ClientReadableStream} 14 | * @template RESPONSE 15 | */ 16 | class ClientUnaryCallImpl { 17 | /** 18 | * @param {!ClientReadableStream} stream 19 | */ 20 | constructor(stream) { 21 | this.stream = stream; 22 | } 23 | 24 | /** 25 | * @override 26 | */ 27 | on(eventType, callback) { 28 | if (eventType == 'data' || eventType == 'error') { 29 | // unary call responses and errors should be handled by the main 30 | // (err, resp) => ... callback 31 | return this; 32 | } 33 | return this.stream.on(eventType, callback); 34 | } 35 | 36 | /** 37 | * @override 38 | */ 39 | removeListener(eventType, callback) { 40 | return this.stream.removeListener(eventType, callback); 41 | } 42 | 43 | /** 44 | * @override 45 | */ 46 | cancel() { 47 | this.stream.cancel(); 48 | } 49 | } 50 | 51 | exports = ClientUnaryCallImpl; 52 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/generator/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_binary") 2 | 3 | cc_binary( 4 | name = "protoc-gen-grpc-web", 5 | srcs = [ 6 | "grpc_generator.cc", 7 | ], 8 | visibility = ["//visibility:public"], 9 | deps = [ 10 | "@com_google_protobuf//:protoc_lib", 11 | ], 12 | ) 13 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/generator/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | CXX ?= g++ 16 | CPPFLAGS += -I/usr/local/include -pthread 17 | CXXFLAGS += -std=c++11 18 | LDFLAGS += -L/usr/local/lib -lprotoc -lprotobuf -lpthread -ldl 19 | PREFIX ?= /usr/local 20 | MIN_MACOS_VERSION := 10.7 # Supports OS X Lion 21 | STATIC ?= yes 22 | 23 | UNAME_S := $(shell uname -s) 24 | ifeq ($(UNAME_S),Darwin) 25 | CXXFLAGS += -stdlib=libc++ -mmacosx-version-min=$(MIN_MACOS_VERSION) 26 | else ifeq ($(UNAME_S),Linux) 27 | ifeq ($(STATIC),yes) 28 | LDFLAGS += -static 29 | endif 30 | endif 31 | 32 | all: protoc-gen-grpc-web 33 | 34 | protoc-gen-grpc-web: grpc_generator.o 35 | $(CXX) $^ $(LDFLAGS) -o $@ 36 | 37 | install: protoc-gen-grpc-web 38 | mkdir -p $(PREFIX)/bin 39 | install protoc-gen-grpc-web $(PREFIX)/bin/protoc-gen-grpc-web 40 | 41 | clean: 42 | rm -f *.o protoc-gen-grpc-web 43 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/generictransportinterface.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | /** 19 | * @fileoverview gRPC-Web generic transport interface 20 | * 21 | * This class provides an abstraction for the underlying transport 22 | * implementation underneath the ClientReadableStream layer. 23 | * 24 | * @author stanleycheung@google.com (Stanley Cheung) 25 | */ 26 | goog.module('grpc.web.GenericTransportInterface'); 27 | 28 | goog.module.declareLegacyNamespace(); 29 | 30 | 31 | const NodeReadableStream = goog.require('goog.net.streams.NodeReadableStream'); 32 | const XhrIo = goog.require('goog.net.XhrIo'); 33 | 34 | 35 | /** 36 | * @typedef {{ 37 | * nodeReadableStream: (?NodeReadableStream|undefined), 38 | * xhr: (?XhrIo|undefined), 39 | * }} 40 | */ 41 | exports.GenericTransportInterface; 42 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/interceptor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview grpc-web client interceptors. 3 | * 4 | * The type of interceptors is determined by the response type of the RPC call. 5 | * gRPC-Web has two generated clients for one service: 6 | * FooServiceClient and FooServicePromiseClient. The response type of 7 | * FooServiceClient is ClientReadableStream for BOTH unary calls and server 8 | * streaming calls, so StreamInterceptor is expected to be used for intercepting 9 | * FooServiceClient calls. The response type of PromiseClient is Promise, so use 10 | * UnaryInterceptor for PromiseClients. 11 | */ 12 | 13 | goog.module('grpc.web.Interceptor'); 14 | goog.module.declareLegacyNamespace(); 15 | 16 | 17 | const ClientReadableStream = goog.require('grpc.web.ClientReadableStream'); 18 | const Request = goog.require('grpc.web.Request'); 19 | const UnaryResponse = goog.require('grpc.web.UnaryResponse'); 20 | 21 | /** 22 | * Interceptor for RPC calls with response type `UnaryResponse`. 23 | * An example implementation of UnaryInterceptor 24 | *
25 |  * TestUnaryInterceptor.prototype.intercept = function(request, invoker) {
26 |  *   const newRequest = ...
27 |  *   return invoker(newRequest).then((response) => {
28 |  *     // Do something with response.getMetadata
29 |        // Do something with response.getResponseMessage
30 |  *     return response;
31 |  *   });
32 |  * };
33 |  * 
34 | * @interface 35 | */ 36 | const UnaryInterceptor = function() {}; 37 | 38 | /** 39 | * @export 40 | * @abstract 41 | * @template REQUEST, RESPONSE 42 | * @param {!Request} request 43 | * @param {function(!Request):!Promise>} 44 | * invoker 45 | * @return {!Promise>} 46 | */ 47 | UnaryInterceptor.prototype.intercept = function(request, invoker) {}; 48 | 49 | 50 | /** 51 | * Interceptor for RPC calls with response type `ClientReadableStream`. 52 | * 53 | * Two steps to create a stream interceptor: 54 | * <1>Create a new subclass of ClientReadableStream that wraps around the 55 | * original stream and overrides its methods. <2>Create a new subclass of 56 | * StreamInterceptor. While implementing the 57 | * StreamInterceptor.prototype.intercept method, return the wrapped 58 | * ClientReadableStream. 59 | * @interface 60 | */ 61 | const StreamInterceptor = function() {}; 62 | 63 | /** 64 | * @export 65 | * @abstract 66 | * @template REQUEST, RESPONSE 67 | * @param {!Request} request 68 | * @param {function(!Request):!ClientReadableStream} 69 | * invoker 70 | * @return {!ClientReadableStream} 71 | */ 72 | StreamInterceptor.prototype.intercept = function(request, invoker) {}; 73 | 74 | 75 | exports = { 76 | UnaryInterceptor, 77 | StreamInterceptor 78 | }; 79 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/metadata.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview grpc-web request/response metadata. 3 | * 4 | * Request and response headers will be included in the Metadata. 5 | */ 6 | 7 | goog.module('grpc.web.Metadata'); 8 | goog.module.declareLegacyNamespace(); 9 | 10 | /** 11 | * @typedef {!Object} 12 | */ 13 | let Metadata; 14 | 15 | exports = Metadata; 16 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/methoddescriptor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Description of this file. 3 | * 4 | * A templated class that is used to address gRPC Web requests. 5 | */ 6 | 7 | goog.module('grpc.web.MethodDescriptor'); 8 | goog.module.declareLegacyNamespace(); 9 | 10 | const CallOptions = goog.require('grpc.web.CallOptions'); 11 | const Metadata = goog.requireType('grpc.web.Metadata'); 12 | const MethodDescriptorInterface = goog.requireType('grpc.web.MethodDescriptorInterface'); 13 | const MethodType = goog.requireType('grpc.web.MethodType'); 14 | const Request = goog.requireType('grpc.web.Request'); 15 | const RequestInternal = goog.require('grpc.web.RequestInternal'); 16 | const UnaryResponse = goog.requireType('grpc.web.UnaryResponse'); 17 | const UnaryResponseInternal = goog.require('grpc.web.UnaryResponseInternal'); 18 | const {Status} = goog.requireType('grpc.web.Status'); 19 | 20 | /** 21 | * @final 22 | * @implements {MethodDescriptorInterface} 23 | * @template REQUEST, RESPONSE 24 | * @unrestricted 25 | */ 26 | const MethodDescriptor = class { 27 | /** 28 | * @param {string} name 29 | * @param {?MethodType} methodType 30 | * @param {function(new: REQUEST, ...)} requestType 31 | * @param {function(new: RESPONSE, ...)} responseType 32 | * @param {function(REQUEST): ?} requestSerializeFn 33 | * @param {function(?): RESPONSE} responseDeserializeFn 34 | */ 35 | constructor( 36 | name, methodType, requestType, responseType, requestSerializeFn, 37 | responseDeserializeFn) { 38 | /** @const */ 39 | this.name = name; 40 | /** @const */ 41 | this.methodType = methodType; 42 | /** @const */ 43 | this.requestType = requestType; 44 | /** @const */ 45 | this.responseType = responseType; 46 | /** @const */ 47 | this.requestSerializeFn = requestSerializeFn; 48 | /** @const */ 49 | this.responseDeserializeFn = responseDeserializeFn; 50 | } 51 | 52 | /** 53 | * @override 54 | * @param {REQUEST} requestMessage 55 | * @param {!Metadata=} metadata 56 | * @param {!CallOptions=} callOptions 57 | * @return {!Request} 58 | */ 59 | createRequest( 60 | requestMessage, metadata = {}, callOptions = new CallOptions()) { 61 | return new RequestInternal(requestMessage, this, metadata, callOptions); 62 | } 63 | 64 | /** 65 | * @override 66 | * @param {RESPONSE} responseMessage 67 | * @param {!Metadata=} metadata 68 | * @param {?Status=} status 69 | * @return {!UnaryResponse} 70 | */ 71 | createUnaryResponse(responseMessage, metadata = {}, status = null) { 72 | return new UnaryResponseInternal(responseMessage, this, metadata, status); 73 | } 74 | 75 | /** 76 | * @override 77 | * @export 78 | */ 79 | getName() { 80 | return this.name; 81 | } 82 | 83 | /** 84 | * @override 85 | */ 86 | getMethodType() { 87 | return this.methodType; 88 | } 89 | 90 | /** 91 | * @override 92 | * @return {function(new: RESPONSE, ...)} 93 | */ 94 | getResponseMessageCtor() { 95 | return this.responseType; 96 | } 97 | 98 | /** 99 | * @override 100 | * @return {function(new: REQUEST, ...)} 101 | */ 102 | getRequestMessageCtor() { 103 | return this.requestType; 104 | } 105 | 106 | /** @override */ 107 | getResponseDeserializeFn() { 108 | return this.responseDeserializeFn; 109 | } 110 | 111 | /** @override */ 112 | getRequestSerializeFn() { 113 | return this.requestSerializeFn; 114 | } 115 | }; 116 | 117 | 118 | 119 | exports = MethodDescriptor; 120 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/methoddescriptorinterface.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Description of this file. 3 | * 4 | * A templated class that is used to address gRPC Web requests. 5 | */ 6 | 7 | goog.module('grpc.web.MethodDescriptorInterface'); 8 | goog.module.declareLegacyNamespace(); 9 | 10 | const CallOptions = goog.requireType('grpc.web.CallOptions'); 11 | const Metadata = goog.requireType('grpc.web.Metadata'); 12 | const MethodType = goog.requireType('grpc.web.MethodType'); 13 | const Request = goog.requireType('grpc.web.Request'); 14 | const UnaryResponse = goog.requireType('grpc.web.UnaryResponse'); 15 | const {Status} = goog.requireType('grpc.web.Status'); 16 | 17 | /** 18 | * @interface 19 | * @template REQUEST, RESPONSE 20 | */ 21 | const MethodDescriptorInterface = function() {}; 22 | 23 | /** 24 | * @param {REQUEST} requestMessage 25 | * @param {!Metadata=} metadata 26 | * @param {!CallOptions=} callOptions 27 | * @return {!Request} 28 | */ 29 | MethodDescriptorInterface.prototype.createRequest = function( 30 | requestMessage, metadata, callOptions) {}; 31 | 32 | 33 | /** 34 | * @param {RESPONSE} responseMessage 35 | * @param {!Metadata=} metadata 36 | * @param {?Status=} status 37 | * @return {!UnaryResponse} 38 | */ 39 | MethodDescriptorInterface.prototype.createUnaryResponse = function( 40 | responseMessage, metadata, status) {}; 41 | 42 | /** @return {string} */ 43 | MethodDescriptorInterface.prototype.getName = function() {}; 44 | 45 | /** @return {?MethodType} */ 46 | MethodDescriptorInterface.prototype.getMethodType = function() {}; 47 | 48 | /** @return {function(new: RESPONSE, ?Array=)} */ 49 | MethodDescriptorInterface.prototype.getResponseMessageCtor = function() {}; 50 | 51 | /** @return {function(new: REQUEST, ?Array=)} */ 52 | MethodDescriptorInterface.prototype.getRequestMessageCtor = function() {}; 53 | 54 | /** @return {function(?): RESPONSE} */ 55 | MethodDescriptorInterface.prototype.getResponseDeserializeFn = function() {}; 56 | 57 | /** @return {function(REQUEST): ?} */ 58 | MethodDescriptorInterface.prototype.getRequestSerializeFn = function() {}; 59 | 60 | exports = MethodDescriptorInterface; 61 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/methodtype.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gRPC-Web method types. 3 | */ 4 | 5 | goog.module('grpc.web.MethodType'); 6 | 7 | goog.module.declareLegacyNamespace(); 8 | 9 | /** 10 | * Available method types: 11 | * MethodType.UNARY: unary request and unary response. 12 | * MethodType.SERVER_STREAMING: unary request and streaming responses. 13 | * MethodType.BIDI_STREAMING: streaming requests and streaming responses. 14 | * 15 | * @enum {string} 16 | */ 17 | const MethodType = { 18 | 'UNARY': 'unary', 19 | 'SERVER_STREAMING': 'server_streaming', 20 | // Bidi streaming is experimental. Do not use. 21 | 'BIDI_STREAMING': 'bidi_streaming', 22 | }; 23 | 24 | exports = MethodType; 25 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/request.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview A templated class that is used to address an individual 3 | * gRPC-Web request instance. 4 | */ 5 | goog.module('grpc.web.Request'); 6 | goog.module.declareLegacyNamespace(); 7 | 8 | const CallOptions = goog.require('grpc.web.CallOptions'); 9 | const Metadata = goog.require('grpc.web.Metadata'); 10 | const MethodDescriptorInterface = goog.requireType('grpc.web.MethodDescriptorInterface'); 11 | 12 | /** 13 | * @interface 14 | * @template REQUEST, RESPONSE 15 | */ 16 | class Request { 17 | /** 18 | * @export 19 | * @return {REQUEST} 20 | */ 21 | getRequestMessage() {} 22 | 23 | /** 24 | * @export 25 | * @return {!MethodDescriptorInterface} 26 | */ 27 | getMethodDescriptor() {} 28 | 29 | /** 30 | * @export 31 | * @return {!Metadata} 32 | */ 33 | getMetadata() {} 34 | 35 | /** 36 | * Client CallOptions. Note that CallOptions has not been implemented in 37 | * grpc.web.AbstractClientbase yet, but will be used in 38 | * grpc.web.GenericClient. 39 | * @export 40 | * @return {!CallOptions|undefined} 41 | */ 42 | getCallOptions() {} 43 | 44 | /** 45 | * @param {string} key 46 | * @param {string} value 47 | * @return {!Request} 48 | */ 49 | withMetadata(key, value) {} 50 | 51 | /** 52 | * @param {string} name 53 | * @param {VALUE} value 54 | * @template VALUE 55 | * @return {!Request} 56 | */ 57 | withGrpcCallOption(name, value) {} 58 | } 59 | 60 | exports = Request; 61 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/requestinternal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Internal implementation of grpc.web.Request. 3 | */ 4 | goog.module('grpc.web.RequestInternal'); 5 | goog.module.declareLegacyNamespace(); 6 | 7 | const CallOptions = goog.require('grpc.web.CallOptions'); 8 | const Metadata = goog.require('grpc.web.Metadata'); 9 | const MethodDescriptor = goog.requireType('grpc.web.MethodDescriptor'); 10 | const Request = goog.require('grpc.web.Request'); 11 | 12 | /** 13 | * @template REQUEST, RESPONSE 14 | * @implements {Request} 15 | * @final 16 | * @package 17 | */ 18 | class RequestInternal { 19 | /** 20 | * @param {REQUEST} requestMessage 21 | * @param {!MethodDescriptor} methodDescriptor 22 | * @param {!Metadata} metadata 23 | * @param {!CallOptions} callOptions 24 | */ 25 | constructor(requestMessage, methodDescriptor, metadata, callOptions) { 26 | /** 27 | * @const {REQUEST} 28 | * @private 29 | */ 30 | this.requestMessage_ = requestMessage; 31 | 32 | /** 33 | * @const {!MethodDescriptor} 34 | * @private 35 | */ 36 | this.methodDescriptor_ = methodDescriptor; 37 | 38 | /** @const @private */ 39 | this.metadata_ = metadata; 40 | 41 | /** @const @private */ 42 | this.callOptions_ = callOptions; 43 | } 44 | 45 | /** 46 | * @override 47 | * @return {REQUEST} 48 | */ 49 | getRequestMessage() { 50 | return this.requestMessage_; 51 | } 52 | 53 | /** 54 | * @override 55 | * @return {!MethodDescriptor} 56 | */ 57 | getMethodDescriptor() { 58 | return this.methodDescriptor_; 59 | } 60 | 61 | /** 62 | * @override 63 | * @return {!Metadata} 64 | */ 65 | getMetadata() { 66 | return this.metadata_; 67 | } 68 | 69 | /** 70 | * @override 71 | * @return {!CallOptions|undefined} 72 | */ 73 | getCallOptions() { 74 | return this.callOptions_; 75 | } 76 | 77 | /** 78 | * @override 79 | */ 80 | withMetadata(key, value) { 81 | this.metadata_[key] = value; 82 | return this; 83 | } 84 | 85 | /** 86 | * @override 87 | */ 88 | withGrpcCallOption(name, value) { 89 | this.callOptions_.setOption(name, value); 90 | return this; 91 | } 92 | } 93 | 94 | exports = RequestInternal; 95 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/rpcerror.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2021 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | /** 19 | * @fileoverview gRPC-Web Error objects 20 | * 21 | * gRPC-Web Error objects 22 | * 23 | * @suppress {lintChecks} gRPC-Web is still using default goog.module exports 24 | * right now, and the output of grpc_generator.cc uses goog.provide. 25 | */ 26 | goog.module('grpc.web.RpcError'); 27 | 28 | const Metadata = goog.require('grpc.web.Metadata'); 29 | const StatusCode = goog.require('grpc.web.StatusCode'); 30 | 31 | /** 32 | * gRPC-Web Error object, contains the {@link StatusCode}, a string message 33 | * and {@link Metadata} contained in the error response. 34 | */ 35 | class RpcError extends Error { 36 | /** 37 | * @param {!StatusCode} code 38 | * @param {string} message 39 | * @param {!Metadata=} metadata 40 | */ 41 | constructor(code, message, metadata = {}) { 42 | super(message); 43 | /** @type {!StatusCode} */ 44 | this.code = code; 45 | /** @type {!Metadata} */ 46 | this.metadata = metadata; 47 | } 48 | 49 | /** @override */ 50 | toString() { 51 | const status = StatusCode.statusCodeName(this.code) || String(this.code); 52 | let out = `RpcError(${status})`; 53 | if (this.message) { 54 | out += ': ' + this.message; 55 | } 56 | return out; 57 | } 58 | } 59 | 60 | /** @override */ 61 | RpcError.prototype.name = 'RpcError'; 62 | 63 | exports = RpcError; 64 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/status.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | /** 19 | * @fileoverview gRPC Web Status codes and mapping. 20 | * 21 | * gRPC Web Status codes and mapping. 22 | * 23 | * @author stanleycheung@google.com (Stanley Cheung) 24 | */ 25 | goog.module('grpc.web.Status'); 26 | goog.module.declareLegacyNamespace(); 27 | 28 | 29 | /** @record */ 30 | function Status() {} 31 | 32 | /** @export {number} */ 33 | Status.prototype.code; 34 | 35 | /** @export {string} */ 36 | Status.prototype.details; 37 | 38 | /** @export {(!Object|undefined)} */ 39 | Status.prototype.metadata; 40 | 41 | exports.Status = Status; 42 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/statuscode_test.js: -------------------------------------------------------------------------------- 1 | goog.module('grpc.web.StatusCodeTest'); 2 | goog.setTestOnly('grpc.web.StatusCodeTest'); 3 | 4 | const StatusCode = goog.require('grpc.web.StatusCode'); 5 | const testSuite = goog.require('goog.testing.testSuite'); 6 | 7 | 8 | /** @type {!Map} */ 9 | const statusMap = new Map([ 10 | [200, StatusCode.OK], 11 | [400, StatusCode.INVALID_ARGUMENT], 12 | [401, StatusCode.UNAUTHENTICATED], 13 | [403, StatusCode.PERMISSION_DENIED], 14 | [404, StatusCode.NOT_FOUND], 15 | [409, StatusCode.ABORTED], 16 | [412, StatusCode.FAILED_PRECONDITION], 17 | [429, StatusCode.RESOURCE_EXHAUSTED], 18 | [500, StatusCode.UNKNOWN], 19 | [501, StatusCode.UNIMPLEMENTED], 20 | [503, StatusCode.UNAVAILABLE], 21 | [504, StatusCode.DEADLINE_EXCEEDED], 22 | ]); 23 | 24 | testSuite({ 25 | testFromHttpStatus() { 26 | statusMap.forEach((statusCode, httpStatus) => { 27 | assertEquals(StatusCode.fromHttpStatus(httpStatus), statusCode); 28 | }); 29 | }, 30 | 31 | testGetHttpStatus() { 32 | statusMap.forEach((statusCode, httpStatus) => { 33 | assertEquals(StatusCode.getHttpStatus(statusCode), httpStatus); 34 | }); 35 | }, 36 | 37 | testUnknown() { 38 | assertEquals(StatusCode.getHttpStatus(StatusCode.UNKNOWN), 500); 39 | assertEquals(StatusCode.fromHttpStatus(511), StatusCode.UNKNOWN); 40 | } 41 | }); -------------------------------------------------------------------------------- /javascript/net/grpc/web/unaryresponse.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gRPC web client UnaryResponse returned by grpc unary calls. 3 | */ 4 | 5 | goog.module('grpc.web.UnaryResponse'); 6 | goog.module.declareLegacyNamespace(); 7 | 8 | const Metadata = goog.requireType('grpc.web.Metadata'); 9 | const MethodDescriptorInterface = goog.requireType('grpc.web.MethodDescriptorInterface'); 10 | const {Status} = goog.requireType('grpc.web.Status'); 11 | 12 | /** 13 | * @interface 14 | * @template REQUEST, RESPONSE 15 | */ 16 | class UnaryResponse { 17 | /** 18 | * @export 19 | * @return {RESPONSE} 20 | */ 21 | getResponseMessage() {} 22 | 23 | /** 24 | * @export 25 | * @return {!Metadata} 26 | */ 27 | getMetadata() {} 28 | 29 | /** 30 | * @export 31 | * @return {!MethodDescriptorInterface} 32 | */ 33 | getMethodDescriptor() {} 34 | 35 | /** 36 | * gRPC status. Trailer metadata returned from a gRPC server is in 37 | * status.metadata. 38 | * @export 39 | * @return {?Status} 40 | */ 41 | getStatus() {} 42 | } 43 | 44 | exports = UnaryResponse; 45 | -------------------------------------------------------------------------------- /javascript/net/grpc/web/unaryresponseinternal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gRPC-Web UnaryResponse internal implementation. 3 | */ 4 | 5 | goog.module('grpc.web.UnaryResponseInternal'); 6 | goog.module.declareLegacyNamespace(); 7 | 8 | const Metadata = goog.requireType('grpc.web.Metadata'); 9 | const MethodDescriptor = goog.requireType('grpc.web.MethodDescriptor'); 10 | const UnaryResponse = goog.requireType('grpc.web.UnaryResponse'); 11 | const {Status} = goog.requireType('grpc.web.Status'); 12 | 13 | /** 14 | * @template REQUEST, RESPONSE 15 | * @implements {UnaryResponse} 16 | * @final 17 | * @package 18 | */ 19 | class UnaryResponseInternal { 20 | /** 21 | * @param {RESPONSE} responseMessage 22 | * @param {!MethodDescriptor} methodDescriptor 23 | * @param {!Metadata=} metadata 24 | * @param {?Status=} status 25 | */ 26 | constructor(responseMessage, methodDescriptor, metadata = {}, status = null) { 27 | /** 28 | * @const {RESPONSE} 29 | * @private 30 | */ 31 | this.responseMessage_ = responseMessage; 32 | 33 | /** 34 | * @const {!Metadata} 35 | * @private 36 | */ 37 | this.metadata_ = metadata; 38 | 39 | /** 40 | * @const {!MethodDescriptor} 41 | * @private 42 | */ 43 | this.methodDescriptor_ = methodDescriptor; 44 | 45 | /** 46 | * @const {?Status} 47 | * @private 48 | */ 49 | this.status_ = status; 50 | } 51 | 52 | /** @override */ 53 | getResponseMessage() { 54 | return this.responseMessage_; 55 | } 56 | 57 | /** @override */ 58 | getMetadata() { 59 | return this.metadata_; 60 | } 61 | 62 | /** @override */ 63 | getMethodDescriptor() { 64 | return this.methodDescriptor_; 65 | } 66 | 67 | /** @override */ 68 | getStatus() { 69 | return this.status_; 70 | } 71 | } 72 | 73 | exports = UnaryResponseInternal; 74 | -------------------------------------------------------------------------------- /kokoro/interop.cfg: -------------------------------------------------------------------------------- 1 | build_file: "grpc-web/scripts/run_interop_tests.sh" 2 | -------------------------------------------------------------------------------- /kokoro/master.cfg: -------------------------------------------------------------------------------- 1 | build_file: "grpc-web/scripts/kokoro.sh" 2 | -------------------------------------------------------------------------------- /kokoro/presubmit.cfg: -------------------------------------------------------------------------------- 1 | build_file: "grpc-web/scripts/run_basic_tests.sh" 2 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/binary_client/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM grpcweb/prereqs 16 | 17 | WORKDIR /github/grpc-web/net/grpc/gateway/examples/echo 18 | 19 | RUN protoc -I=. echo.proto \ 20 | --js_out=import_style=commonjs:./commonjs-example \ 21 | --grpc-web_out=import_style=commonjs,mode=grpcweb:./commonjs-example 22 | 23 | WORKDIR /github/grpc-web/net/grpc/gateway/examples/echo/commonjs-example 24 | 25 | RUN npm install && \ 26 | npm link grpc-web && \ 27 | npx webpack && \ 28 | cp echotest.html /var/www/html && \ 29 | cp dist/main.js /var/www/html/dist 30 | 31 | WORKDIR /var/www/html 32 | 33 | EXPOSE 8081 34 | CMD ["python", "-m", "SimpleHTTPServer", "8081"] 35 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/closure_client/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM grpcweb/prereqs 16 | 17 | WORKDIR /github/grpc-web/net/grpc/gateway/examples/echo 18 | 19 | RUN npm install 20 | 21 | RUN make client && make install 22 | 23 | WORKDIR /var/www/html 24 | 25 | EXPOSE 8081 26 | CMD ["python", "-m", "SimpleHTTPServer", "8081"] 27 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/commonjs_client/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM grpcweb/prereqs 16 | 17 | WORKDIR /github/grpc-web/net/grpc/gateway/examples/echo 18 | 19 | RUN protoc -I=. echo.proto \ 20 | --js_out=import_style=commonjs:./commonjs-example \ 21 | --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./commonjs-example 22 | 23 | WORKDIR /github/grpc-web/net/grpc/gateway/examples/echo/commonjs-example 24 | 25 | RUN npm install && \ 26 | npm link grpc-web && \ 27 | npx webpack && \ 28 | cp echotest.html /var/www/html && \ 29 | cp dist/main.js /var/www/html/dist 30 | 31 | WORKDIR /var/www/html 32 | 33 | EXPOSE 8081 34 | CMD ["python", "-m", "SimpleHTTPServer", "8081"] 35 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/echo_server/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM grpcweb/prereqs 16 | 17 | WORKDIR /github/grpc-web 18 | 19 | RUN bazel build net/grpc/gateway/examples/echo:server 20 | 21 | WORKDIR /github/grpc-web/bazel-bin/net/grpc/gateway/examples/echo 22 | 23 | EXPOSE 9090 24 | CMD ["./server"] 25 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/envoy/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM envoyproxy/envoy:v1.22.0 16 | 17 | COPY net/grpc/gateway/examples/echo/envoy.yaml /etc/envoy/envoy.yaml 18 | 19 | ENTRYPOINT [ "/usr/local/bin/envoy" ] 20 | CMD [ "-c /etc/envoy/envoy.yaml", "-l trace", "--log-path /tmp/envoy_info.log" ] 21 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/grpcwebproxy/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM golang:1.16-alpine3.13 16 | 17 | RUN apk add --no-cache curl git ca-certificates && \ 18 | rm -rf /var/lib/apt/lists/* 19 | 20 | ARG VERSION=0.14.0 21 | 22 | WORKDIR /tmp 23 | 24 | RUN curl -sS https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 25 | RUN wget https://github.com/improbable-eng/grpc-web/archive/v$VERSION.tar.gz 26 | 27 | WORKDIR /go/src/github.com/improbable-eng/ 28 | 29 | RUN tar -zxf /tmp/v$VERSION.tar.gz -C . 30 | RUN mv grpc-web-$VERSION grpc-web 31 | 32 | WORKDIR /go/src/github.com/improbable-eng/grpc-web 33 | 34 | RUN dep ensure && \ 35 | go env -w GO111MODULE=auto && \ 36 | go install ./go/grpcwebproxy 37 | 38 | ADD ./etc/localhost.crt /etc 39 | ADD ./etc/localhost.key /etc 40 | 41 | ENTRYPOINT [ "/bin/sh", "-c", "exec /go/bin/grpcwebproxy \ 42 | --backend_addr=node-server:9090 \ 43 | --run_tls_server=false \ 44 | --allow_all_origins " ] 45 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/interop_client/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM grpcweb/prereqs 16 | 17 | WORKDIR /github/grpc-web/test/interop 18 | 19 | COPY ./test/interop . 20 | 21 | RUN protoc -I=../.. src/proto/grpc/testing/test.proto \ 22 | src/proto/grpc/testing/empty.proto src/proto/grpc/testing/messages.proto \ 23 | --js_out=import_style=commonjs:. \ 24 | --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. 25 | 26 | RUN npm install && \ 27 | npm link grpc-web && \ 28 | npx webpack && \ 29 | cp index.html /var/www/html && \ 30 | cp dist/main.js /var/www/html/dist 31 | 32 | WORKDIR /var/www/html 33 | 34 | EXPOSE 8081 35 | CMD ["python", "-m", "SimpleHTTPServer", "8081"] 36 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/node_interop_server/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM node:20.0.0-bullseye 16 | 17 | WORKDIR /github/grpc-node 18 | 19 | RUN git clone https://github.com/grpc/grpc-node . && \ 20 | git submodule --quiet update --init --recursive 21 | 22 | RUN cd packages/grpc-native-core && \ 23 | npm install --build-from-source --unsafe-perm && \ 24 | npm link 25 | 26 | RUN cd packages/proto-loader && \ 27 | npm install @types/mocha@7.0.2&& \ 28 | npm install --unsafe-perm 29 | 30 | WORKDIR /github/grpc-node/test 31 | 32 | RUN npm install node-pre-gyp && \ 33 | npm install && \ 34 | npm link grpc 35 | 36 | EXPOSE 7074 37 | CMD ["node", "--require", "./fixtures/native_native", "./interop/interop_server.js", "--port=7074"] 38 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/node_server/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM grpcweb/prereqs 16 | 17 | WORKDIR /github/grpc-web/net/grpc/gateway/examples/echo/node-server 18 | 19 | RUN npm install 20 | 21 | EXPOSE 9090 22 | CMD ["node", "server.js"] 23 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/prereqs/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # Using multi-stage buid (See: https://docs.docker.com/develop/develop-images/multistage-build/) 17 | 18 | ###################################### 19 | # Stage 1: Fetch binaries 20 | ###################################### 21 | # node:... Docker image is based on buildpack-deps:bullseye (11) 22 | FROM buildpack-deps:bullseye AS prepare 23 | 24 | ARG BUILDIFIER_VERSION=1.0.0 25 | ARG PROTOBUF_VERSION=3.19.4 26 | 27 | RUN apt-get -qq update && apt-get -qq install -y curl unzip 28 | 29 | WORKDIR /tmp 30 | 31 | RUN curl -sSL https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOBUF_VERSION/\ 32 | protoc-$PROTOBUF_VERSION-linux-x86_64.zip -o protoc.zip && \ 33 | unzip -qq protoc.zip && \ 34 | cp ./bin/protoc /usr/local/bin/protoc 35 | 36 | RUN wget -nv -O buildifier \ 37 | https://github.com/bazelbuild/buildtools/releases/download/$BUILDIFIER_VERSION/buildifier && \ 38 | chmod +x ./buildifier && \ 39 | cp ./buildifier /usr/local/bin/buildifier 40 | 41 | # Download third_party modules to be used for the next stage 42 | WORKDIR /github/grpc-web 43 | 44 | RUN git clone https://github.com/grpc/grpc-web . 45 | COPY ./scripts/init_submodules.sh ./scripts/ 46 | RUN ./scripts/init_submodules.sh 47 | 48 | 49 | ###################################### 50 | # Stage 2: Copy source files and build 51 | ###################################### 52 | FROM node:20.0.0-bullseye AS copy-and-build 53 | 54 | ARG MAKEFLAGS=-j8 55 | ARG BAZEL_VERSION=7.3.0 56 | 57 | RUN apt-get -qq update && apt-get -qq install -y python 58 | 59 | RUN mkdir -p /var/www/html/dist 60 | RUN echo "\nloglevel=error\n" >> $HOME/.npmrc 61 | 62 | COPY --from=prepare /usr/local/bin/protoc /usr/local/bin/ 63 | COPY --from=prepare /usr/local/bin/buildifier /usr/local/bin/ 64 | COPY --from=prepare /github/grpc-web/third_party /github/grpc-web/third_party 65 | 66 | RUN wget -nv -O bazel-installer.sh \ 67 | https://github.com/bazelbuild/bazel/releases/download/$BAZEL_VERSION/\ 68 | bazel-$BAZEL_VERSION-installer-linux-x86_64.sh && \ 69 | chmod +x ./bazel-installer.sh && \ 70 | ./bazel-installer.sh && \ 71 | rm ./bazel-installer.sh 72 | 73 | WORKDIR /github/grpc-web 74 | 75 | # Copy only files necessary to build the protoc-gen-grpc-web first as an optimization because they 76 | # are rarely updated compared with the javascript files. 77 | COPY ./MODULE.bazel ./MODULE.bazel 78 | COPY ./MODULE.bazel.lock ./MODULE.bazel.lock 79 | COPY ./.bazelrc ./.bazelrc 80 | COPY ./javascript/net/grpc/web/generator javascript/net/grpc/web/generator 81 | 82 | RUN bazel build javascript/net/grpc/web/generator:protoc-gen-grpc-web && \ 83 | cp $(bazel info bazel-genfiles)/javascript/net/grpc/web/generator/protoc-gen-grpc-web \ 84 | /usr/local/bin/protoc-gen-grpc-web 85 | 86 | COPY ./javascript ./javascript 87 | COPY ./packages ./packages 88 | 89 | RUN cd ./packages/grpc-web && \ 90 | npm install && \ 91 | npm run build && \ 92 | npm link 93 | 94 | COPY ./Makefile ./Makefile 95 | COPY ./net ./net 96 | COPY ./scripts ./scripts 97 | COPY ./src ./src 98 | COPY ./test ./test 99 | 100 | RUN /usr/local/bin/buildifier \ 101 | --mode=check --lint=warn --warnings=all -r ./MODULE.bazel javascript net 102 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/protoc_plugin/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM grpcweb/prereqs 16 | 17 | ARG MAKEFLAGS=-j8 18 | 19 | RUN apt-get -qq update && apt-get -qq install -y \ 20 | autoconf \ 21 | automake \ 22 | build-essential \ 23 | curl \ 24 | git \ 25 | libtool \ 26 | libssl-dev \ 27 | make \ 28 | zip 29 | 30 | WORKDIR /github/grpc-web 31 | 32 | RUN cd ./third_party/protobuf && \ 33 | ./autogen.sh && \ 34 | ./configure && \ 35 | make && \ 36 | make install && \ 37 | ldconfig 38 | 39 | RUN cd ./javascript/net/grpc/web/generator && \ 40 | make protoc-gen-grpc-web 41 | -------------------------------------------------------------------------------- /net/grpc/gateway/docker/ts_client/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM grpcweb/prereqs 16 | 17 | WORKDIR /github/grpc-web/net/grpc/gateway/examples/echo 18 | 19 | RUN protoc -I=. echo.proto \ 20 | --js_out=import_style=commonjs:./ts-example \ 21 | --grpc-web_out=import_style=commonjs+dts,mode=grpcwebtext:./ts-example 22 | 23 | WORKDIR /github/grpc-web/net/grpc/gateway/examples/echo/ts-example 24 | 25 | RUN npm install && \ 26 | npm link grpc-web && \ 27 | npx tsc && \ 28 | # Since typescript@4.5.2, tsc has apparently stopped moving dependent js files into dist/ dir, so 29 | # we'll move them manually. 30 | mv *_pb.js dist/ && \ 31 | npx webpack && \ 32 | cp echotest.html /var/www/html && \ 33 | cp dist/main.js /var/www/html/dist 34 | 35 | WORKDIR /var/www/html 36 | 37 | EXPOSE 8081 38 | CMD ["python", "-m", "SimpleHTTPServer", "8081"] 39 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/.gitignore: -------------------------------------------------------------------------------- 1 | /package-lock.json 2 | /node_modules 3 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") 2 | load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_proto_library") 3 | load("@rules_proto//proto:defs.bzl", "proto_library") 4 | 5 | proto_library( 6 | name = "echo_proto", 7 | srcs = [ 8 | "echo.proto", 9 | ], 10 | ) 11 | 12 | # Server 13 | 14 | cc_proto_library( 15 | name = "echo_cc_proto", 16 | deps = [ 17 | ":echo_proto", 18 | ], 19 | ) 20 | 21 | cc_grpc_library( 22 | name = "echo_cc_grpc", 23 | srcs = [ 24 | ":echo_proto", 25 | ], 26 | grpc_only = True, 27 | deps = [ 28 | ":echo_cc_proto", 29 | ], 30 | ) 31 | 32 | cc_binary( 33 | name = "server", 34 | srcs = [ 35 | "echo_server.cc", 36 | "echo_service_impl.cc", 37 | "echo_service_impl.h", 38 | ], 39 | deps = [ 40 | ":echo_cc_grpc", 41 | ":echo_cc_proto", 42 | "@com_github_grpc_grpc//:grpc++", 43 | ], 44 | ) 45 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ROOT_DIR = ../../../../.. 16 | NPM_DIR = ./node_modules 17 | PROTOC = protoc 18 | PROTOS_PATH = ../.. 19 | HTML_DIR = /var/www/html 20 | JS_IMPORT_STYLE = import_style=closure,binary 21 | JS_PATH = javascript/net/grpc/web 22 | OUT_DIR = ./out 23 | PROTOBUF_PATH = third_party/protobuf 24 | GRPC_WEB_PLUGIN_PATH = /usr/local/bin/protoc-gen-grpc-web 25 | 26 | all: client 27 | 28 | client: proto-js compiled-js 29 | 30 | compiled-js: 31 | ./node_modules/.bin/google-closure-compiler \ 32 | --js=*.js \ 33 | --js=$(OUT_DIR)/*.js \ 34 | --js=$(ROOT_DIR)/javascript \ 35 | --js=$(ROOT_DIR)/$(PROTOBUF_PATH)/js \ 36 | --js='!$(ROOT_DIR)/$(PROTOBUF_PATH)/js/**/*_test.js' \ 37 | --js=$(NPM_DIR)/google-closure-library \ 38 | --entry_point=goog:proto.grpc.gateway.testing.EchoServiceClient \ 39 | --dependency_mode=PRUNE \ 40 | --js_output_file compiled.js 41 | 42 | proto-js: 43 | mkdir -p $(OUT_DIR) 44 | $(PROTOC) -I=$(ROOT_DIR)/$(PROTOBUF_PATH)/src/google/protobuf \ 45 | --js_out=$(JS_IMPORT_STYLE):$(OUT_DIR) \ 46 | $(ROOT_DIR)/$(PROTOBUF_PATH)/src/google/protobuf/any.proto 47 | $(PROTOC) -I=. --js_out=$(JS_IMPORT_STYLE):$(OUT_DIR) ./echo.proto 48 | $(PROTOC) -I=. --plugin=protoc-gen-grpc-web=$(GRPC_WEB_PLUGIN_PATH) \ 49 | --grpc-web_out=import_style=closure,mode=grpcwebtext:. ./echo.proto 50 | 51 | install: 52 | mkdir -p $(HTML_DIR) 53 | cp ./echotest.html $(HTML_DIR) 54 | cp ./echoapp.js $(HTML_DIR) 55 | cp ./compiled.js $(HTML_DIR)/echo_js_bin_dev.js 56 | 57 | clean: 58 | rm -f compiled.js 59 | rm -f *_pb.js 60 | rm -rf $(OUT_DIR) 61 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/README.md: -------------------------------------------------------------------------------- 1 | ## Build and Run an Echo example 2 | 3 | This page will show you how to quickly build and run an end-to-end Echo 4 | example. The example has 3 key components: 5 | 6 | - Front-end JS client 7 | - Envoy proxy 8 | - gRPC backend server (written in Node) 9 | 10 | 11 | From the repo root directory: 12 | 13 | ## Build pre-requisites 14 | 15 | This step downloads the necessary pre-requisites, and serves as the base docker 16 | image for the subsequent docker images. 17 | 18 | ```sh 19 | $ docker build -t grpcweb/prereqs \ 20 | -f net/grpc/gateway/docker/prereqs/Dockerfile . 21 | ``` 22 | 23 | ## Run the gRPC Backend server 24 | 25 | This compiles the gRPC backend server, written in Node, and listens on port 26 | 9090. 27 | 28 | ```sh 29 | $ docker build -t grpcweb/node-server \ 30 | -f net/grpc/gateway/docker/node_server/Dockerfile . 31 | $ docker run -d -p 9090:9090 --name node-server grpcweb/node-server 32 | ``` 33 | 34 | ## Run the Envoy proxy 35 | 36 | This step runs the Envoy proxy, and listens on port 8080. Any gRPC-Web browser 37 | requests will be forwarded to port 9090. 38 | 39 | ```sh 40 | $ docker build -t grpcweb/envoy \ 41 | -f net/grpc/gateway/docker/envoy/Dockerfile . 42 | $ docker run -d -p 8080:8080 --link node-server:node-server grpcweb/envoy 43 | ``` 44 | 45 | ## Serve static JS/HTML contents 46 | 47 | This steps compiles the front-end gRPC-Web client into a static .JS file, and 48 | we use a simple server to serve up the JS/HTML static contents. 49 | 50 | ```sh 51 | $ docker build -t grpcweb/commonjs-client \ 52 | -f net/grpc/gateway/docker/commonjs_client/Dockerfile . 53 | $ docker run -d -p 8081:8081 grpcweb/commonjs-client 54 | ``` 55 | 56 | ## Run the example from your browser 57 | 58 | Finally, open a browser tab, and inspect 59 | 60 | ``` 61 | http://localhost:8081/echotest.html 62 | ``` 63 | 64 | ## What's next? 65 | 66 | For more details about how you can run your own gRPC service and access it 67 | from the browser, please see this [tutorial](tutorial.md) 68 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/commonjs-example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/commonjs-example/client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | const {EchoRequest, 20 | ServerStreamingEchoRequest} = require('./echo_pb.js'); 21 | const {EchoServiceClient} = require('./echo_grpc_web_pb.js'); 22 | const {EchoApp} = require('../echoapp.js'); 23 | const grpc = {}; 24 | grpc.web = require('grpc-web'); 25 | 26 | /** Sample interceptor implementation */ 27 | const StreamResponseInterceptor = function() {}; 28 | 29 | /** 30 | * @template REQUEST, RESPONSE 31 | * @param {!Request} request 32 | * @param {function(!Request):!ClientReadableStream} 33 | * invoker 34 | * @return {!ClientReadableStream} 35 | */ 36 | StreamResponseInterceptor.prototype.intercept = function(request, invoker) { 37 | const InterceptedStream = function(stream) { 38 | this.stream = stream; 39 | }; 40 | InterceptedStream.prototype.on = function(eventType, callback) { 41 | if (eventType == 'data') { 42 | const newCallback = (response) => { 43 | response.setMessage('[Intcpt Resp1]'+response.getMessage()); 44 | callback(response); 45 | }; 46 | this.stream.on(eventType, newCallback); 47 | } else { 48 | this.stream.on(eventType, callback); 49 | } 50 | return this; 51 | }; 52 | var reqMsg = request.getRequestMessage(); 53 | reqMsg.setMessage('[Intcpt Req1]'+reqMsg.getMessage()); 54 | return new InterceptedStream(invoker(request)); 55 | }; 56 | 57 | var opts = {'streamInterceptors' : [new StreamResponseInterceptor()]}; 58 | var echoService = new EchoServiceClient('http://'+window.location.hostname+':8080', null, 59 | null); 60 | // opts); 61 | 62 | var echoApp = new EchoApp( 63 | echoService, 64 | { 65 | EchoRequest: EchoRequest, 66 | ServerStreamingEchoRequest: ServerStreamingEchoRequest 67 | } 68 | ); 69 | 70 | echoApp.load(); 71 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/commonjs-example/echotest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Echo Example 20 | 21 | 22 | 28 | 29 | 30 |
31 |
32 |
33 |
34 | 35 | 36 | 38 | 39 |
40 |

Example: "Hello", "4 Hello", "err Hello"

41 |
42 |
43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/commonjs-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc-web-commonjs-example", 3 | "version": "0.1.0", 4 | "description": "gRPC-Web CommonJS client example", 5 | "license": "Apache-2.0", 6 | "dependencies": { 7 | "google-protobuf": "~3.21.4", 8 | "grpc-web": "~1.5.0" 9 | }, 10 | "devDependencies": { 11 | "webpack": "~5.82.1", 12 | "webpack-cli": "~5.1.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/commonjs-example/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: "production", 3 | entry: "./client.js", 4 | }; 5 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/echo.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package grpc.gateway.testing; 18 | 19 | message Empty {} 20 | 21 | message EchoRequest { 22 | string message = 1; 23 | } 24 | 25 | message EchoResponse { 26 | string message = 1; 27 | int32 message_count = 2; 28 | } 29 | 30 | // Request type for server side streaming echo. 31 | message ServerStreamingEchoRequest { 32 | // Message string for server streaming request. 33 | string message = 1; 34 | 35 | // The total number of messages to be generated before the server 36 | // closes the stream; default is 10. 37 | int32 message_count = 2; 38 | 39 | // The interval (ms) between two server messages. The server implementation 40 | // may enforce some minimum interval (e.g. 100ms) to avoid message overflow. 41 | int32 message_interval = 3; 42 | } 43 | 44 | // Response type for server streaming response. 45 | message ServerStreamingEchoResponse { 46 | // Response message. 47 | string message = 1; 48 | } 49 | 50 | // Request type for client side streaming echo. 51 | message ClientStreamingEchoRequest { 52 | // A special value "" indicates that there's no further messages. 53 | string message = 1; 54 | } 55 | 56 | // Response type for client side streaming echo. 57 | message ClientStreamingEchoResponse { 58 | // Total number of client messages that have been received. 59 | int32 message_count = 1; 60 | } 61 | 62 | // A simple echo service. 63 | service EchoService { 64 | // One request followed by one response 65 | // The server returns the client message as-is. 66 | rpc Echo(EchoRequest) returns (EchoResponse); 67 | 68 | // Sends back abort status. 69 | rpc EchoAbort(EchoRequest) returns (EchoResponse) {} 70 | 71 | // One empty request, ZERO processing, followed by one empty response 72 | // (minimum effort to do message serialization). 73 | rpc NoOp(Empty) returns (Empty); 74 | 75 | // One request followed by a sequence of responses (streamed download). 76 | // The server will return the same client message repeatedly. 77 | rpc ServerStreamingEcho(ServerStreamingEchoRequest) 78 | returns (stream ServerStreamingEchoResponse); 79 | 80 | // One request followed by a sequence of responses (streamed download). 81 | // The server abort directly. 82 | rpc ServerStreamingEchoAbort(ServerStreamingEchoRequest) 83 | returns (stream ServerStreamingEchoResponse) {} 84 | 85 | // A sequence of requests followed by one response (streamed upload). 86 | // The server returns the total number of messages as the result. 87 | // Notice: Client side streaming and Bidi streaming are not supported at the moment. 88 | rpc ClientStreamingEcho(stream ClientStreamingEchoRequest) 89 | returns (ClientStreamingEchoResponse); 90 | 91 | // A sequence of requests with each message echoed by the server immediately. 92 | // The server returns the same client messages in order. 93 | // E.g. this is how the speech API works. 94 | // Notice: Client side streaming and Bidi streaming are not supported at the moment. 95 | rpc FullDuplexEcho(stream EchoRequest) returns (stream EchoResponse); 96 | 97 | // A sequence of requests followed by a sequence of responses. 98 | // The server buffers all the client messages and then returns the same 99 | // client messages one by one after the client half-closes the stream. 100 | // This is how an image recognition API may work. 101 | // Notice: Client side streaming and Bidi streaming are not supported at the moment. 102 | rpc HalfDuplexEcho(stream EchoRequest) returns (stream EchoResponse); 103 | } 104 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/echo_chat.js: -------------------------------------------------------------------------------- 1 | // TODO: Implement simple chat client using ClosureJS (similar to echoapp.js). 2 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/echo_server.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "net/grpc/gateway/examples/echo/echo.grpc.pb.h" 24 | #include "net/grpc/gateway/examples/echo/echo_service_impl.h" 25 | 26 | using grpc::Server; 27 | using grpc::ServerBuilder; 28 | 29 | void RunServer() { 30 | std::string server_address("0.0.0.0:9090"); 31 | EchoServiceImpl service; 32 | ServerBuilder builder; 33 | builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); 34 | builder.RegisterService(&service); 35 | std::unique_ptr server(builder.BuildAndStart()); 36 | server->Wait(); 37 | } 38 | 39 | int main(int argc, char** argv) { 40 | RunServer(); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/echo_service_impl.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | #include "net/grpc/gateway/examples/echo/echo_service_impl.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "net/grpc/gateway/examples/echo/echo.grpc.pb.h" 26 | 27 | using grpc::ServerContext; 28 | using grpc::ServerWriter; 29 | using grpc::Status; 30 | using grpc::gateway::testing::EchoRequest; 31 | using grpc::gateway::testing::EchoResponse; 32 | using grpc::gateway::testing::EchoService; 33 | using grpc::gateway::testing::Empty; 34 | using grpc::gateway::testing::ServerStreamingEchoRequest; 35 | using grpc::gateway::testing::ServerStreamingEchoResponse; 36 | 37 | 38 | EchoServiceImpl::EchoServiceImpl() {} 39 | EchoServiceImpl::~EchoServiceImpl() {} 40 | 41 | void EchoServiceImpl::CopyClientMetadataToResponse(ServerContext* context) { 42 | for (auto& client_metadata : context->client_metadata()) { 43 | context->AddInitialMetadata(std::string(client_metadata.first.data(), 44 | client_metadata.first.length()), 45 | std::string(client_metadata.second.data(), 46 | client_metadata.second.length())); 47 | context->AddTrailingMetadata( 48 | std::string(client_metadata.first.data(), 49 | client_metadata.first.length()), 50 | std::string(client_metadata.second.data(), 51 | client_metadata.second.length())); 52 | } 53 | } 54 | 55 | Status EchoServiceImpl::Echo(ServerContext* context, const EchoRequest* request, 56 | EchoResponse* response) { 57 | CopyClientMetadataToResponse(context); 58 | response->set_message(request->message()); 59 | return Status::OK; 60 | } 61 | 62 | Status EchoServiceImpl::EchoAbort(ServerContext* context, 63 | const EchoRequest* request, 64 | EchoResponse* response) { 65 | CopyClientMetadataToResponse(context); 66 | response->set_message(request->message()); 67 | return Status(grpc::StatusCode::ABORTED, 68 | "Aborted from server side."); 69 | } 70 | 71 | Status EchoServiceImpl::NoOp(ServerContext* context, const Empty* request, 72 | Empty* response) { 73 | CopyClientMetadataToResponse(context); 74 | return Status::OK; 75 | } 76 | 77 | Status EchoServiceImpl::ServerStreamingEcho( 78 | ServerContext* context, const ServerStreamingEchoRequest* request, 79 | ServerWriter* writer) { 80 | CopyClientMetadataToResponse(context); 81 | for (int i = 0; i < request->message_count(); i++) { 82 | if (context->IsCancelled()) { 83 | return Status::CANCELLED; 84 | } 85 | ServerStreamingEchoResponse response; 86 | response.set_message(request->message()); 87 | usleep(request->message_interval() * 1000); 88 | writer->Write(response); 89 | } 90 | return Status::OK; 91 | } 92 | 93 | Status EchoServiceImpl::ServerStreamingEchoAbort( 94 | ServerContext* context, const ServerStreamingEchoRequest* request, 95 | ServerWriter* writer) { 96 | CopyClientMetadataToResponse(context); 97 | ServerStreamingEchoResponse response; 98 | response.set_message(request->message()); 99 | writer->Write(response); 100 | return Status(grpc::StatusCode::ABORTED, 101 | "Aborted from server side."); 102 | } 103 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/echo_service_impl.h: -------------------------------------------------------------------------------- 1 | #ifndef NET_GRPC_GATEWAY_EXAMPLES_ECHO_ECHO_SERVICE_IMPL_H_ 2 | #define NET_GRPC_GATEWAY_EXAMPLES_ECHO_ECHO_SERVICE_IMPL_H_ 3 | 4 | /** 5 | * 6 | * Copyright 2018 Google LLC 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * https://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "net/grpc/gateway/examples/echo/echo.grpc.pb.h" 27 | 28 | class EchoServiceImpl final : 29 | public grpc::gateway::testing::EchoService::Service { 30 | public: 31 | EchoServiceImpl(); 32 | ~EchoServiceImpl() override; 33 | 34 | void CopyClientMetadataToResponse(grpc::ServerContext* context); 35 | grpc::Status Echo( 36 | grpc::ServerContext* context, 37 | const grpc::gateway::testing::EchoRequest* request, 38 | grpc::gateway::testing::EchoResponse* response) override; 39 | grpc::Status EchoAbort( 40 | grpc::ServerContext* context, 41 | const grpc::gateway::testing::EchoRequest* request, 42 | grpc::gateway::testing::EchoResponse* response) override; 43 | grpc::Status NoOp( 44 | grpc::ServerContext* context, 45 | const grpc::gateway::testing::Empty* request, 46 | grpc::gateway::testing::Empty* response) override; 47 | grpc::Status ServerStreamingEcho( 48 | grpc::ServerContext* context, 49 | const grpc::gateway::testing::ServerStreamingEchoRequest* request, 50 | grpc::ServerWriter< 51 | grpc::gateway::testing::ServerStreamingEchoResponse>* writer) override; 52 | grpc::Status ServerStreamingEchoAbort( 53 | grpc::ServerContext* context, 54 | const grpc::gateway::testing::ServerStreamingEchoRequest* request, 55 | grpc::ServerWriter< 56 | grpc::gateway::testing::ServerStreamingEchoResponse>* writer) override; 57 | }; 58 | 59 | #endif // NET_GRPC_GATEWAY_EXAMPLES_ECHO_ECHO_SERVICE_IMPL_H_ 60 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/echotest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Echo Example 20 | 21 | 22 | 25 | 26 | 27 | 39 | 40 | 41 |
42 |
43 |
44 |
45 | 46 | 47 | 49 | 50 |
51 |

Example: "Hello", "4 Hello", "err Hello"

52 |
53 |
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/envoy.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | access_log_path: /tmp/admin_access.log 3 | address: 4 | socket_address: { address: 0.0.0.0, port_value: 9901 } 5 | 6 | static_resources: 7 | listeners: 8 | - name: listener_0 9 | address: 10 | socket_address: { address: 0.0.0.0, port_value: 8080 } 11 | filter_chains: 12 | - filters: 13 | - name: envoy.filters.network.http_connection_manager 14 | typed_config: 15 | "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 16 | codec_type: auto 17 | stat_prefix: ingress_http 18 | route_config: 19 | name: local_route 20 | virtual_hosts: 21 | - name: local_service 22 | domains: ["*"] 23 | routes: 24 | - match: { prefix: "/" } 25 | route: 26 | cluster: echo_service 27 | timeout: 0s 28 | max_stream_duration: 29 | grpc_timeout_header_max: 0s 30 | cors: 31 | allow_origin_string_match: 32 | - prefix: "*" 33 | allow_methods: GET, PUT, DELETE, POST, OPTIONS 34 | 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 35 | max_age: "1728000" 36 | expose_headers: custom-header-1,grpc-status,grpc-message 37 | http_filters: 38 | - name: envoy.filters.http.grpc_web 39 | typed_config: 40 | "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb 41 | - name: envoy.filters.http.cors 42 | typed_config: 43 | "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors 44 | - name: envoy.filters.http.router 45 | typed_config: 46 | "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router 47 | clusters: 48 | - name: echo_service 49 | connect_timeout: 0.25s 50 | type: logical_dns 51 | # HTTP/2 support 52 | typed_extension_protocol_options: 53 | envoy.extensions.upstreams.http.v3.HttpProtocolOptions: 54 | "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions 55 | explicit_http_config: 56 | http2_protocol_options: {} 57 | lb_policy: round_robin 58 | load_assignment: 59 | cluster_name: cluster_0 60 | endpoints: 61 | - lb_endpoints: 62 | - endpoint: 63 | address: 64 | socket_address: 65 | address: node-server 66 | port_value: 9090 67 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/node-server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/node-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc-web-node-server-example", 3 | "version": "0.1.0", 4 | "main": "server.js", 5 | "dependencies": { 6 | "@grpc/grpc-js": "~1.1.8", 7 | "@grpc/proto-loader": "~0.5.0", 8 | "async": "~3.2.3", 9 | "google-protobuf": "~3.21.4", 10 | "lodash": "~4.17.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/node-server/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | var PROTO_PATH = __dirname + '/../echo.proto'; 20 | 21 | var assert = require('assert'); 22 | var async = require('async'); 23 | var _ = require('lodash'); 24 | var grpc = require('@grpc/grpc-js'); 25 | var protoLoader = require('@grpc/proto-loader'); 26 | var packageDefinition = protoLoader.loadSync( 27 | PROTO_PATH, 28 | {keepCase: true, 29 | longs: String, 30 | enums: String, 31 | defaults: true, 32 | oneofs: true 33 | }); 34 | var protoDescriptor = grpc.loadPackageDefinition(packageDefinition); 35 | var echo = protoDescriptor.grpc.gateway.testing; 36 | 37 | /** 38 | * @param {!Object} call 39 | * @return {!Object} metadata 40 | */ 41 | function copyMetadata(call) { 42 | var metadata = call.metadata.getMap(); 43 | var response_metadata = new grpc.Metadata(); 44 | for (var key in metadata) { 45 | response_metadata.set(key, metadata[key]); 46 | } 47 | return response_metadata; 48 | } 49 | 50 | /** 51 | * @param {!Object} call 52 | * @param {function():?} callback 53 | */ 54 | function doEcho(call, callback) { 55 | callback(null, { 56 | message: call.request.message 57 | }, copyMetadata(call)); 58 | } 59 | 60 | /** 61 | * @param {!Object} call 62 | * @param {function():?} callback 63 | */ 64 | function doEchoAbort(call, callback) { 65 | callback({ 66 | code: grpc.status.ABORTED, 67 | message: 'Aborted from server side.' 68 | }); 69 | } 70 | 71 | /** 72 | * @param {!Object} call 73 | */ 74 | function doServerStreamingEcho(call) { 75 | var senders = []; 76 | function sender(message, interval) { 77 | return (callback) => { 78 | call.write({ 79 | message: message 80 | }); 81 | _.delay(callback, interval); 82 | }; 83 | } 84 | for (var i = 0; i < call.request.message_count; i++) { 85 | senders[i] = sender(call.request.message, call.request.message_interval); 86 | } 87 | async.series(senders, () => { 88 | call.end(copyMetadata(call)); 89 | }); 90 | } 91 | 92 | /** 93 | * Get a new server with the handler functions in this file bound to the 94 | * methods it serves. 95 | * @return {!Server} The new server object 96 | */ 97 | function getServer() { 98 | var server = new grpc.Server(); 99 | server.addService(echo.EchoService.service, { 100 | echo: doEcho, 101 | echoAbort: doEchoAbort, 102 | serverStreamingEcho: doServerStreamingEcho, 103 | }); 104 | return server; 105 | } 106 | 107 | if (require.main === module) { 108 | var echoServer = getServer(); 109 | echoServer.bindAsync( 110 | '0.0.0.0:9090', grpc.ServerCredentials.createInsecure(), (err, port) => { 111 | assert.ifError(err); 112 | echoServer.start(); 113 | }); 114 | } 115 | 116 | exports.getServer = getServer; 117 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "echo-closure-example", 3 | "version": "0.1.0", 4 | "description": "gRPC-Web Closure JS client example", 5 | "license": "Apache-2.0", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "google-closure-compiler": "~20200224.0.0", 9 | "google-closure-library": "~20210808.0.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/ts-example/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/ts-example/README.md: -------------------------------------------------------------------------------- 1 | # Instructions to run the Typescript example 2 | 3 | ## Docker run 4 | 5 | ```bash 6 | # From root dir 7 | docker-compose up --build node-server envoy ts-client 8 | ``` 9 | 10 | Visit http://localhost:8081/echotest.html 11 | 12 | ## Manual run 13 | 14 | ### Step 1 - Run servers 15 | 16 | ```bash 17 | # From root dir 18 | docker-compose up --build node-server envoy 19 | ``` 20 | 21 | ### Step 2 - Codegen 22 | ```bash 23 | cd net/grpc/gateway/examples/echo 24 | ``` 25 | 26 | #### Option 1: `import_style=commonjs+dts` 27 | 28 | ``` 29 | RUN protoc -I=. echo.proto \ 30 | --js_out=import_style=commonjs:./ts-example \ 31 | --grpc-web_out=import_style=commonjs+dts,mode=grpcwebtext:./ts-example 32 | ``` 33 | 34 | #### Option 2: `import_style=typescript` 35 | 36 | ``` 37 | RUN protoc -I=. echo.proto \ 38 | --js_out=import_style=commonjs:./ts-example \ 39 | --grpc-web_out=import_style=typescript,mode=grpcwebtext:./ts-example 40 | ``` 41 | 42 | ### Step 3 - (Optional) Update import style 43 | 44 | Change `client.ts` to use import style Option 2 if you had chosen `import_style=typescript` above: 45 | 46 | https://github.com/grpc/grpc-web/blob/60caece15489787662ebac6167572eecd5bfa568/net/grpc/gateway/examples/echo/ts-example/client.ts#L26-L27 47 | 48 | ### Step 4 - Build JS files. 49 | 50 | ```bash 51 | cd net/grpc/gateway/examples/echo/ts-example 52 | npm install 53 | npx tsc 54 | mv *_pb.js dist/ 55 | npx webpack 56 | ``` 57 | 58 | ### Step 5 - Host and visit page 59 | 60 | ```bash 61 | # In the ./ts-example folder 62 | python3 -m http.server 8081 63 | ``` 64 | 65 | Visit http://localhost:8081/echotest.html -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/ts-example/echotest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Echo Example 20 | 21 | 22 | 28 | 29 | 30 |
31 |
32 |
33 |
34 | 35 | 36 | 38 | 39 |
40 |

Example: "Hello", "4 Hello", "err Hello"

41 |
42 |
43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/ts-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@types/google-protobuf": "~3.15.12", 4 | "@types/jquery": "~3.3.6", 5 | "@types/node": "~10.17.0", 6 | "google-protobuf": "~3.21.4", 7 | "grpc-web": "~1.5.0", 8 | "jquery": "~3.5.1", 9 | "mock-xmlhttprequest": "~2.0.0", 10 | "typescript": "latest", 11 | "webpack": "~5.82.1", 12 | "webpack-cli": "~5.1.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/ts-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "strict": true, 6 | "allowJs": true, 7 | "outDir": "./dist", 8 | "types": ["node"], 9 | }, 10 | "include": [ 11 | "client.ts", 12 | "echo_pb.js", 13 | "echo_pb.d.ts", 14 | "echo_grpc_web_pb.js", 15 | "echo_grpc_web_pb.d.ts", 16 | "EchoServiceClientPb.ts" 17 | ], 18 | "exclude": [ 19 | "node_modules" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/echo/ts-example/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'production', 3 | entry: './dist/client.js', 4 | }; 5 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/helloworld/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | *_pb.js 3 | node_modules/ 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/helloworld/client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | const {HelloRequest, RepeatHelloRequest, 20 | HelloReply} = require('./helloworld_pb.js'); 21 | const {GreeterClient} = require('./helloworld_grpc_web_pb.js'); 22 | 23 | var client = new GreeterClient('http://' + window.location.hostname + ':8080', 24 | null, null); 25 | 26 | // simple unary call 27 | var request = new HelloRequest(); 28 | request.setName('World'); 29 | 30 | client.sayHello(request, {}, (err, response) => { 31 | if (err) { 32 | console.log(`Unexpected error for sayHello: code = ${err.code}` + 33 | `, message = "${err.message}"`); 34 | } else { 35 | console.log(response.getMessage()); 36 | } 37 | }); 38 | 39 | 40 | // server streaming call 41 | var streamRequest = new RepeatHelloRequest(); 42 | streamRequest.setName('World'); 43 | streamRequest.setCount(5); 44 | 45 | var stream = client.sayRepeatHello(streamRequest, {}); 46 | stream.on('data', (response) => { 47 | console.log(response.getMessage()); 48 | }); 49 | stream.on('error', (err) => { 50 | console.log(`Unexpected stream error: code = ${err.code}` + 51 | `, message = "${err.message}"`); 52 | }); 53 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/helloworld/debugging/node-client.js: -------------------------------------------------------------------------------- 1 | // NOTE: This client is used for debugging the node gRPC server WITHOUT the 2 | // Envoy proxy. It does not use the gRPC-Web protocol. 3 | 4 | var PROTO_PATH = __dirname + '/helloworld.proto'; 5 | 6 | var async = require('async'); 7 | var grpc = require('@grpc/grpc-js'); 8 | var protoLoader = require('@grpc/proto-loader'); 9 | var packageDefinition = protoLoader.loadSync( 10 | PROTO_PATH, 11 | {keepCase: true, 12 | longs: String, 13 | enums: String, 14 | defaults: true, 15 | oneofs: true 16 | }); 17 | var protoDescriptor = grpc.loadPackageDefinition(packageDefinition); 18 | var helloworld = protoDescriptor.helloworld; 19 | var client = new helloworld.Greeter('localhost:9090', 20 | grpc.credentials.createInsecure()); 21 | 22 | /** 23 | * @param {function():?} callback 24 | */ 25 | function runSayHello(callback) { 26 | client.sayHello({name: 'John'}, {}, (err, response) => { 27 | console.log(response.message); 28 | callback(); 29 | }); 30 | } 31 | 32 | /** 33 | * @param {function():?} callback 34 | */ 35 | function runSayRepeatHello(callback) { 36 | var stream = client.sayRepeatHello({name: 'John', count: 5}, {}); 37 | stream.on('data', (response) => { 38 | console.log(response.message); 39 | }); 40 | stream.on('end', () => { 41 | callback(); 42 | }); 43 | } 44 | 45 | 46 | /** 47 | * Run all of the demos in order 48 | */ 49 | function main() { 50 | async.series([ 51 | runSayHello, 52 | runSayRepeatHello, 53 | ]); 54 | } 55 | 56 | if (require.main === module) { 57 | main(); 58 | } 59 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/helloworld/envoy.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | access_log_path: /tmp/admin_access.log 3 | address: 4 | socket_address: { address: 0.0.0.0, port_value: 9901 } 5 | 6 | static_resources: 7 | listeners: 8 | - name: listener_0 9 | address: 10 | socket_address: { address: 0.0.0.0, port_value: 8080 } 11 | filter_chains: 12 | - filters: 13 | - name: envoy.filters.network.http_connection_manager 14 | typed_config: 15 | "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 16 | codec_type: auto 17 | stat_prefix: ingress_http 18 | route_config: 19 | name: local_route 20 | virtual_hosts: 21 | - name: local_service 22 | domains: ["*"] 23 | routes: 24 | - match: { prefix: "/" } 25 | route: 26 | cluster: greeter_service 27 | timeout: 0s 28 | max_stream_duration: 29 | grpc_timeout_header_max: 0s 30 | cors: 31 | allow_origin_string_match: 32 | - prefix: "*" 33 | allow_methods: GET, PUT, DELETE, POST, OPTIONS 34 | 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 35 | max_age: "1728000" 36 | expose_headers: custom-header-1,grpc-status,grpc-message 37 | http_filters: 38 | - name: envoy.filters.http.grpc_web 39 | typed_config: 40 | "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb 41 | - name: envoy.filters.http.cors 42 | typed_config: 43 | "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors 44 | - name: envoy.filters.http.router 45 | typed_config: 46 | "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router 47 | clusters: 48 | - name: greeter_service 49 | connect_timeout: 0.25s 50 | type: logical_dns 51 | # HTTP/2 support 52 | typed_extension_protocol_options: 53 | envoy.extensions.upstreams.http.v3.HttpProtocolOptions: 54 | "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions 55 | explicit_http_config: 56 | http2_protocol_options: {} 57 | lb_policy: round_robin 58 | # win/mac hosts: Use address: host.docker.internal instead of address: localhost in the line below 59 | load_assignment: 60 | cluster_name: cluster_0 61 | endpoints: 62 | - lb_endpoints: 63 | - endpoint: 64 | address: 65 | socket_address: 66 | address: 0.0.0.0 67 | port_value: 9090 68 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/helloworld/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package helloworld; 18 | 19 | service Greeter { 20 | // unary call 21 | rpc SayHello(HelloRequest) returns (HelloReply); 22 | // server streaming call 23 | rpc SayRepeatHello(RepeatHelloRequest) returns (stream HelloReply); 24 | } 25 | 26 | message HelloRequest { 27 | string name = 1; 28 | } 29 | 30 | message RepeatHelloRequest { 31 | string name = 1; 32 | int32 count = 2; 33 | } 34 | 35 | message HelloReply { 36 | string message = 1; 37 | } 38 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/helloworld/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | gRPC-Web Example 6 | 7 | 8 | 9 |

Open up the developer console and see the logs for the output.

10 | 11 | 12 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/helloworld/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc-web-simple-example", 3 | "version": "0.1.0", 4 | "description": "gRPC-Web simple example", 5 | "main": "server.js", 6 | "devDependencies": { 7 | "@grpc/grpc-js": "~1.1.8", 8 | "@grpc/proto-loader": "~0.5.4", 9 | "async": "~3.2.3", 10 | "google-protobuf": "~3.21.4", 11 | "grpc-web": "~1.5.0", 12 | "lodash": "~4.17.0", 13 | "webpack": "~5.82.1", 14 | "webpack-cli": "~5.1.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /net/grpc/gateway/examples/helloworld/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | var PROTO_PATH = __dirname + '/helloworld.proto'; 20 | 21 | var assert = require('assert'); 22 | var async = require('async'); 23 | var _ = require('lodash'); 24 | var grpc = require('@grpc/grpc-js'); 25 | var protoLoader = require('@grpc/proto-loader'); 26 | var packageDefinition = protoLoader.loadSync( 27 | PROTO_PATH, 28 | {keepCase: true, 29 | longs: String, 30 | enums: String, 31 | defaults: true, 32 | oneofs: true 33 | }); 34 | var protoDescriptor = grpc.loadPackageDefinition(packageDefinition); 35 | var helloworld = protoDescriptor.helloworld; 36 | 37 | /** 38 | * @param {!Object} call 39 | * @param {function():?} callback 40 | */ 41 | function doSayHello(call, callback) { 42 | callback(null, {message: 'Hello! '+ call.request.name}); 43 | } 44 | 45 | /** 46 | * @param {!Object} call 47 | */ 48 | function doSayRepeatHello(call) { 49 | var senders = []; 50 | function sender(name) { 51 | return (callback) => { 52 | call.write({ 53 | message: 'Hey! ' + name 54 | }); 55 | _.delay(callback, 500); // in ms 56 | }; 57 | } 58 | for (var i = 0; i < call.request.count; i++) { 59 | senders[i] = sender(call.request.name + i); 60 | } 61 | async.series(senders, () => { 62 | call.end(); 63 | }); 64 | } 65 | 66 | /** 67 | * @return {!Object} gRPC server 68 | */ 69 | function getServer() { 70 | var server = new grpc.Server(); 71 | server.addService(helloworld.Greeter.service, { 72 | sayHello: doSayHello, 73 | sayRepeatHello: doSayRepeatHello, 74 | }); 75 | return server; 76 | } 77 | 78 | if (require.main === module) { 79 | var server = getServer(); 80 | server.bindAsync( 81 | '0.0.0.0:9090', grpc.ServerCredentials.createInsecure(), (err, port) => { 82 | assert.ifError(err); 83 | server.start(); 84 | }); 85 | } 86 | 87 | exports.getServer = getServer; 88 | -------------------------------------------------------------------------------- /packages/grpc-web/.gitignore: -------------------------------------------------------------------------------- 1 | /index.js 2 | /generated/ 3 | 4 | package-lock.json 5 | node_modules/ 6 | __pycache__/ 7 | -------------------------------------------------------------------------------- /packages/grpc-web/.npmignore: -------------------------------------------------------------------------------- 1 | /scripts 2 | /package-lock.json 3 | /.gitignore 4 | -------------------------------------------------------------------------------- /packages/grpc-web/docker/jsunit-test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM selenium/standalone-chrome:112.0.5615.165 2 | 3 | # Matching the node version used in the node:20.0.0-bullseye image. 4 | ARG NODE_VERSION=20.0.0 5 | 6 | USER root 7 | 8 | RUN apt-get update && \ 9 | apt-get install -y nodejs npm 10 | 11 | # Install nvm (See https://github.com/creationix/nvm#install-script) and nodejs version per 12 | # specified in NODE_VERSION 13 | RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash 14 | ENV NVM_DIR=$HOME/.nvm 15 | RUN . $NVM_DIR/nvm.sh \ 16 | && nvm install $NODE_VERSION \ 17 | && nvm alias default $NODE_VERSION \ 18 | && nvm use default 19 | ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH 20 | 21 | WORKDIR /grpc-web 22 | 23 | COPY ./packages ./packages 24 | RUN cd ./packages/grpc-web && \ 25 | npm install 26 | 27 | COPY ./javascript ./javascript 28 | COPY ./scripts ./scripts 29 | -------------------------------------------------------------------------------- /packages/grpc-web/exports.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Export symbols needed by generated code in CommonJS style. 3 | * 4 | * Note that public methods called by generated code are exposed 5 | * using Closure Compiler's @export annotation 6 | */ 7 | goog.module('grpc.web.Exports'); 8 | 9 | const CallOptions = goog.require('grpc.web.CallOptions'); 10 | const MethodDescriptor = goog.require('grpc.web.MethodDescriptor'); 11 | const GrpcWebClientBase = goog.require('grpc.web.GrpcWebClientBase'); 12 | const RpcError = goog.require('grpc.web.RpcError'); 13 | const StatusCode = goog.require('grpc.web.StatusCode'); 14 | const MethodType = goog.require('grpc.web.MethodType'); 15 | 16 | module['exports']['CallOptions'] = CallOptions; 17 | module['exports']['MethodDescriptor'] = MethodDescriptor; 18 | module['exports']['GrpcWebClientBase'] = GrpcWebClientBase; 19 | module['exports']['RpcError'] = RpcError; 20 | module['exports']['StatusCode'] = StatusCode; 21 | module['exports']['MethodType'] = MethodType; 22 | 23 | // Temporary hack to fix https://github.com/grpc/grpc-web/issues/1153, which is 24 | // caused by `goog.global` not pointing to the global scope when grpc-web is 25 | // being imported as a CommonJS module. 26 | // TODO: Remove this hack after `goog.global` is fixed. 27 | goog.Timer.defaultTimerObject = (typeof globalThis !== "undefined" && globalThis) || self; 28 | -------------------------------------------------------------------------------- /packages/grpc-web/externs.js: -------------------------------------------------------------------------------- 1 | var module; 2 | 3 | /** 4 | * List of functions we want to preserve when running the closure compiler 5 | * with --compilation_level=ADVANCED_OPTIMIZATIONS. 6 | */ 7 | module.ClientReadableStream = function() {}; 8 | module.ClientReadableStream.prototype.on = function(eventType, callback) {}; 9 | module.ClientReadableStream.prototype.removeListener = 10 | function(eventType, callback) {}; 11 | module.ClientReadableStream.prototype.cancel = function() {}; 12 | 13 | module.GenericClient = function() {}; 14 | module.GenericClient.prototype.unaryCall = function(request) {}; 15 | module.GenericClient.prototype.call = function(requestMessage, 16 | methodDescriptor) {}; 17 | 18 | module.UnaryInterceptor = function() {}; 19 | module.UnaryInterceptor.prototype.intercept = function(request, invoker) {}; 20 | 21 | module.StreamInterceptor = function() {}; 22 | module.StreamInterceptor.prototype.intercept = function(request, invoker) {}; 23 | 24 | module.Request = function() {}; 25 | module.Request.prototype.getRequestMessage = function() {}; 26 | module.Request.prototype.getMethodDescriptor = function() {}; 27 | module.Request.prototype.getMetadata = function() {}; 28 | module.Request.prototype.getCallOptions = function() {}; 29 | 30 | module.UnaryResponse = function() {}; 31 | module.UnaryResponse.prototype.getResponseMessage = function() {}; 32 | module.UnaryResponse.prototype.getMetadata = function() {}; 33 | module.UnaryResponse.prototype.getMethodDescriptor = function() {}; 34 | module.UnaryResponse.prototype.getStatus = function() {}; 35 | 36 | module.MethodDescriptor = function() {}; 37 | module.MethodDescriptor.getName = function() {}; 38 | -------------------------------------------------------------------------------- /packages/grpc-web/gulpfile.js: -------------------------------------------------------------------------------- 1 | const connect = require('gulp-connect'); 2 | const gulp = require('gulp'); 3 | 4 | gulp.task('serve', () => { 5 | connect.server({ 6 | // Serves the root of github repo so tests an access javascript files. 7 | root: '../../', 8 | port: 4000 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/grpc-web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc-web", 3 | "version": "1.5.0", 4 | "author": "Google Inc.", 5 | "description": "gRPC-Web Client Runtime Library", 6 | "homepage": "https://grpc.io/", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/grpc/grpc-web.git" 10 | }, 11 | "bugs": "https://github.com/grpc/grpc-web/issues", 12 | "files": [ 13 | "index.js", 14 | "index.d.ts" 15 | ], 16 | "main": "index.js", 17 | "typings": "index.d.ts", 18 | "scripts": { 19 | "build": "node scripts/build.js", 20 | "prepare": "npm run build", 21 | "prepublishOnly": "npm run build", 22 | "test": "npm run test-jsunit && npm run test-mocha", 23 | "test-mocha": "mocha --timeout 10000 \"./test/**/*_test.js\"", 24 | "test-jsunit": "./scripts/generate_test_files.sh && ./scripts/run_jsunit_tests.sh && rm -rf ./generated" 25 | }, 26 | "license": "Apache-2.0", 27 | "devDependencies": { 28 | "@types/google-protobuf": "~3.15.12", 29 | "command-exists": "~1.2.8", 30 | "google-closure-compiler": "~20200224.0.0", 31 | "google-closure-deps": "~20210601.0.0", 32 | "google-closure-library": "~20210808.0.0", 33 | "google-protobuf": "~3.21.4", 34 | "gulp": "~4.0.2", 35 | "gulp-connect": "~5.7.0", 36 | "gulp-eval": "~1.0.0", 37 | "mocha": "~5.2.0", 38 | "mock-xmlhttprequest": "~2.0.0", 39 | "protractor": "~7.0.0", 40 | "typescript": "latest" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/grpc-web/protractor.conf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Stores the configuration of Protractor. It is loaded by protractor to run 19 | * tests. 20 | * 21 | * Intended to be used through ./run_jsunit_test.sh 22 | */ 23 | 24 | // Common configuration. 25 | config = { 26 | // Using jasmine to wrap Closure JSUnit tests. 27 | framework: 'jasmine', 28 | // The jasmine specs to run. 29 | specs: ['protractor_spec.js'], 30 | // Jasmine options. Increase the timeout to 5min instead of the default 30s. 31 | jasmineNodeOpts: { 32 | // Default time to wait in ms before a test fails. 33 | defaultTimeoutInterval: 5 * 60 * 1000 // 5 minutes 34 | } 35 | }; 36 | 37 | // Configuration for headless chrome. 38 | config.directConnect = true; 39 | config.multiCapabilities = [{ 40 | browserName: 'chrome', 41 | chromeOptions: { 42 | args: [ "--headless", "--disable-gpu", "--window-size=800,600", 43 | "--no-sandbox", "--disable-dev-shm-usage" ] 44 | } 45 | }]; 46 | 47 | exports.config = config; 48 | -------------------------------------------------------------------------------- /packages/grpc-web/protractor_spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | var allTests = require('./generated/all_tests'); 18 | 19 | var TEST_SERVER = 'http://localhost:4000'; 20 | 21 | describe('Run all Closure unit tests', function() { 22 | /** 23 | * Waits for current tests to be executed. 24 | * @param {function(!Object)} done The function called when the test is finished. 25 | * @param {function(!Error)} fail The function called when an unrecoverable error 26 | * happened during the test. 27 | */ 28 | var waitForTest = function(done, fail) { 29 | // executeScript runs the passed method in the "window" context of 30 | // the current test. JSUnit exposes hooks into the test's status through 31 | // the "G_testRunner" global object. 32 | browser.executeScript(function() { 33 | if (window['G_testRunner'] && window['G_testRunner']['isFinished']()) { 34 | return { 35 | isFinished: true, 36 | isSuccess: window['G_testRunner']['isSuccess'](), 37 | report: window['G_testRunner']['getReport']() 38 | }; 39 | } else { 40 | return {'isFinished': false}; 41 | } 42 | }).then(function(status) { 43 | if (status && status.isFinished) { 44 | done(status); 45 | } else { 46 | waitForTest(done, fail); 47 | } 48 | }, function(err) { 49 | // This can happen if the webdriver had an issue executing the script. 50 | fail(err); 51 | }); 52 | }; 53 | 54 | /** 55 | * Executes the test cases for the file at the given testPath. 56 | * @param {!string} testPath The path of the current test suite to execute. 57 | */ 58 | var executeTest = function(testPath) { 59 | it('runs ' + testPath + ' with success', function(done) { 60 | /** 61 | * Runs the test routines for a given test path. 62 | * @param {function()} done The function to run on completion. 63 | */ 64 | var runRoutine = function(done) { 65 | browser.navigate() 66 | .to(TEST_SERVER + '/' + testPath) 67 | .then(function() { 68 | waitForTest(function(status) { 69 | expect(status).toBeSuccess(); 70 | done(); 71 | }, function(err) { 72 | done.fail(err); 73 | }); 74 | }, function(err) { 75 | done.fail(err); 76 | }); 77 | }; 78 | // Run the test routine. 79 | runRoutine(done); 80 | }); 81 | }; 82 | 83 | beforeEach(function() { 84 | jasmine.addMatchers({ 85 | // This custom matcher allows for cleaner reports. 86 | toBeSuccess: function() { 87 | return { 88 | // Checks that the status report is successful, otherwise displays 89 | // the report as error message. 90 | compare: function(status) { 91 | return { 92 | pass: status.isSuccess, 93 | message: 'Some test cases failed!\n\n' + status.report 94 | }; 95 | } 96 | }; 97 | } 98 | }); 99 | }); 100 | 101 | if (!allTests.length) { 102 | throw new Error('Cannot find any JsUnit tests!!'); 103 | } 104 | 105 | // Run all tests. 106 | for (var i = 0; i < allTests.length; i++) { 107 | var testPath = allTests[i]; 108 | executeTest(testPath); 109 | } 110 | }); 111 | -------------------------------------------------------------------------------- /packages/grpc-web/scripts/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | const fs = require("fs"); 20 | const path = require("path"); 21 | const {exec} = require("child_process"); 22 | 23 | const cwd = process.cwd(); 24 | 25 | const indexPath = path.relative(cwd, path.resolve(__dirname, "../index.js")); 26 | 27 | const jsPaths = [ 28 | "../exports.js", 29 | "../../../javascript", 30 | "../node_modules/google-closure-library", 31 | ].map(jsPath => path.relative(cwd, path.resolve(__dirname, jsPath))); 32 | 33 | const closureArgs = [].concat( 34 | jsPaths.map(jsPath => `--js=${jsPath}`), 35 | [ 36 | `--entry_point=grpc.web.Exports`, 37 | `--externs=externs.js`, 38 | `--dependency_mode=PRUNE`, 39 | `--compilation_level=ADVANCED_OPTIMIZATIONS`, 40 | `--generate_exports`, 41 | `--export_local_property_definitions`, 42 | `--js_output_file=${indexPath}`, 43 | ] 44 | ); 45 | 46 | const closureCompilerBin = 47 | path.resolve(__dirname, "../node_modules/.bin/google-closure-compiler"); 48 | const closureCommand = closureCompilerBin + " " + closureArgs.join(' '); 49 | 50 | console.log(closureCommand); 51 | let child = exec(closureCommand); 52 | 53 | child.stdout.pipe(process.stdout); 54 | child.stderr.pipe(process.stderr); 55 | 56 | function createSymlink(target, path) { 57 | fs.symlink(target, path, (err) => { 58 | if (err && err.code != 'EEXIST') { 59 | throw err; 60 | } 61 | }); 62 | } 63 | 64 | createSymlink(path.resolve(__dirname, "../index.js"), 65 | path.resolve(__dirname, "../node_modules/grpc-web.js")); 66 | createSymlink(path.resolve(__dirname, "../index.d.ts"), 67 | path.resolve(__dirname, "../node_modules/grpc-web.d.ts")); 68 | -------------------------------------------------------------------------------- /packages/grpc-web/scripts/common.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Common methods and constants for generating test files.""" 15 | 16 | import os 17 | from typing import Iterator 18 | 19 | # The directory in which test HTML files are generated. 20 | GENERATED_TEST_BASE_PATH = "generated/test_htmls/" 21 | 22 | 23 | def read_file(path: str) -> str: 24 | """Reads the content of a file.""" 25 | with open(path) as f: 26 | return f.read() 27 | 28 | 29 | def write_file(path: str, content: str): 30 | """Writes a string to file, overwriting existing content; intermediate 31 | directories are created if not present.""" 32 | dir_name = os.path.dirname(path) 33 | if not os.path.exists(dir_name): 34 | os.makedirs(dir_name) 35 | 36 | with open(path, "w") as f: 37 | f.write(content) 38 | 39 | 40 | def get_files_with_suffix(root_dir: str, suffix: str) -> Iterator[str]: 41 | """Yields file names under a directory with a given suffix.""" 42 | for dir_path, _, file_names in os.walk(root_dir): 43 | for file_name in file_names: 44 | if file_name.endswith(suffix): 45 | yield os.path.join(dir_path, file_name) 46 | -------------------------------------------------------------------------------- /packages/grpc-web/scripts/gen_all_tests_js.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Generates the all_tests.js file for consumption by Protractor. 15 | 16 | Usage: 17 | $ cd packages/grpc-web 18 | $ python3 ./scripts/gen_test_htmls.py # Prerequisite 19 | $ python3 ./scripts/gen_all_tests_js.py 20 | """ 21 | 22 | from string import Template 23 | 24 | import common 25 | 26 | ALL_TESTS_TEMPLATE_FILE = './scripts/template_all_tests_js.txt' 27 | 28 | # The path of the generated all_tests.js file 29 | GENERATED_ALL_TESTS_JS_PATH = './generated/all_tests.js' 30 | 31 | # File paths needs to be prepended by the relative path of the grpc-web package 32 | # because web server is hosting the root of github repo for tests to access the 33 | # javascript files. 34 | GRPC_WEB_BASE_DIR = 'packages/grpc-web' 35 | 36 | 37 | def main(): 38 | template_data = common.read_file(ALL_TESTS_TEMPLATE_FILE) 39 | template = Template(template_data) 40 | 41 | test_html_paths = [] 42 | for file_name in common.get_files_with_suffix( 43 | common.GENERATED_TEST_BASE_PATH, '_test.html'): 44 | test_html_paths.append(" '%s/%s'," % (GRPC_WEB_BASE_DIR, file_name)) 45 | # Example output paths: 46 | # 'packages/grpc-web/generated/test_htmls/javascript__net__grpc__web__grpcwebclientbase_test.html', 47 | # 'packages/grpc-web/generated/test_htmls/javascript__net__grpc__web__grpcwebstreamparser_test.html', 48 | test_html_paths_str = "\n".join(test_html_paths) 49 | 50 | # Writes the generated output to the all_tests.js file. 51 | common.write_file(GENERATED_ALL_TESTS_JS_PATH, 52 | template.substitute(test_html_paths=test_html_paths_str)) 53 | 54 | 55 | if __name__ == "__main__": 56 | main() 57 | -------------------------------------------------------------------------------- /packages/grpc-web/scripts/gen_test_htmls.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Generates *_test.html files from *_test.js files. 15 | 16 | Usage: 17 | $ cd packages/grpc-web 18 | $ python3 ./scripts/gen_test_htmls.py 19 | """ 20 | 21 | import os 22 | import re 23 | from string import Template 24 | 25 | import common 26 | 27 | # The directories containing JS tests. 28 | DIRECTORIES_WITH_TESTS = ["../../javascript"] 29 | 30 | TEST_HTML_TEMPLATE_FILE = './scripts/template_test_html.txt' 31 | 32 | 33 | def main(): 34 | template_data = common.read_file(TEST_HTML_TEMPLATE_FILE) 35 | template = Template(template_data) 36 | for directory in DIRECTORIES_WITH_TESTS: 37 | for js_file_path in common.get_files_with_suffix( 38 | directory, "_test.js"): 39 | _gen_test_html(js_file_path, template) 40 | 41 | 42 | def _gen_test_html(js_file_path: str, template: Template): 43 | """Generates a Closure test wrapper HTML and saves it to the filesystem.""" 44 | # Generates the test_file_name so that: 45 | # ../../javascript/net/grpc/web/grpcwebclientbase_test.js 46 | # will now be named: 47 | # javascript__net__grpc__web__grpcwebclientbase_test.html 48 | test_file_name = js_file_path 49 | while test_file_name.startswith('../'): 50 | test_file_name = test_file_name[3:] 51 | test_file_name = test_file_name.replace('/', '__') 52 | test_file_name = os.path.splitext(test_file_name)[0] + '.html' 53 | 54 | # Generates the test HTML using the package name of the test file 55 | package_name = _extract_closure_package(js_file_path) 56 | generated_html = template.substitute(package=package_name) 57 | 58 | # Writes the test HTML files 59 | common.write_file(common.GENERATED_TEST_BASE_PATH + test_file_name, 60 | generated_html) 61 | 62 | 63 | def _extract_closure_package(js_file_path) -> str: 64 | """Extracts the package name from goog.provide() or goog.module() in the 65 | JS file.""" 66 | js_data = common.read_file(js_file_path) 67 | matches = re.search(r"goog\.(provide|module)\([\n\s]*'(.+)'\);", js_data) 68 | 69 | if matches is None: 70 | raise ValueError("goog.provide() or goog.module() not found in file") 71 | 72 | return matches.group(2) 73 | 74 | 75 | if __name__ == "__main__": 76 | main() 77 | -------------------------------------------------------------------------------- /packages/grpc-web/scripts/generate_test_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2021 Google Inc. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Generates the temporary files needed for tests to run, putting them in the 17 | # generated/ directory. 18 | # 19 | # Usage: 20 | # $ cd packages/grpc-web 21 | # $ ./scripts/generate_test_files.sh 22 | 23 | set -ex 24 | 25 | SCRIPT_DIR=$(dirname "$0") 26 | REPO_DIR=$(realpath "${SCRIPT_DIR}/../") 27 | JAVASCRIPT_DIR=$(realpath "${SCRIPT_DIR}/../../../javascript") 28 | GEN_DIR="$REPO_DIR/generated" 29 | 30 | cd "$REPO_DIR" 31 | 32 | mkdir -p "$GEN_DIR" 33 | 34 | echo "Generating dependency file..." 35 | npx closure-make-deps \ 36 | --closure-path="node_modules/google-closure-library/closure/goog" \ 37 | --file="node_modules/google-closure-library/closure/goog/deps.js" \ 38 | --root="$JAVASCRIPT_DIR" \ 39 | --exclude="$GEN_DIR/all_tests.js" \ 40 | --exclude="$GEN_DIR/deps.js" \ 41 | > "$GEN_DIR/deps.js" 42 | 43 | echo "Generating test HTML files..." 44 | python3 ./scripts/gen_test_htmls.py 45 | python3 ./scripts/gen_all_tests_js.py 46 | 47 | echo "Done." 48 | -------------------------------------------------------------------------------- /packages/grpc-web/scripts/run_jsunit_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2021 Google Inc. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # This script starts a local HTTP server to serve test files, starts a Selenium Webdriver, and 17 | # runs the unit tests using Protractor. 18 | # Run locally with Pratractor: 19 | # 20 | # Usage (under ./packages/grpc-web): 21 | # $ ./scripts/generate_test_files.sh # Required first step 22 | # $ ./scripts/run_jsunit_tests.sh 23 | # 24 | # Or (preferred use): 25 | # $ npm run test-jsunit 26 | 27 | set -e 28 | 29 | cd "$(dirname $(dirname "$0"))" 30 | PROTRACTOR_BIN_PATH="./node_modules/protractor/bin" 31 | 32 | function cleanup () { 33 | echo "Killing HTTP Server..." 34 | kill $serverPid 35 | } 36 | 37 | # Start the local webserver. 38 | echo "Starting local HTP Server..." 39 | npx gulp serve & 40 | serverPid=$! 41 | echo "Local HTTP Server started with PID $serverPid." 42 | 43 | trap cleanup EXIT 44 | 45 | echo "Using Headless Chrome." 46 | # Updates Selenium Webdriver. 47 | echo "$PROTRACTOR_BIN_PATH/webdriver-manager update --versions.chrome=112.0.5615.165 --gecko=false" 48 | $PROTRACTOR_BIN_PATH/webdriver-manager update --versions.chrome=112.0.5615.165 --gecko=false 49 | 50 | # Run the tests using Protractor! (Protractor should run selenium automatically) 51 | $PROTRACTOR_BIN_PATH/protractor protractor.conf.js 52 | -------------------------------------------------------------------------------- /packages/grpc-web/scripts/template_all_tests_js.txt: -------------------------------------------------------------------------------- 1 | var allTests = [ 2 | $test_html_paths 3 | ]; 4 | if (typeof module !== 'undefined' && module.exports) { 5 | module.exports = allTests; 6 | } 7 | -------------------------------------------------------------------------------- /packages/grpc-web/scripts/template_test_html.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/grpc-web/test/closure_client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | goog.provide('proto.grpc.gateway.testing.EchoAppClient'); 20 | 21 | goog.require('proto.grpc.gateway.testing.EchoServiceClient'); 22 | goog.require('proto.grpc.gateway.testing.EchoRequest'); 23 | 24 | 25 | proto.grpc.gateway.testing.EchoAppClient = function() { 26 | this.client = 27 | new proto.grpc.gateway.testing.EchoServiceClient('MyHostname', null, null); 28 | } 29 | 30 | proto.grpc.gateway.testing.EchoAppClient.prototype.echo = function(msg, cb) { 31 | var request = new proto.grpc.gateway.testing.EchoRequest(); 32 | request.setMessage(msg); 33 | this.client.echo(request, {}, cb); 34 | } 35 | -------------------------------------------------------------------------------- /packages/grpc-web/test/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | const fs = require('fs'); 20 | const GENERATED_CODE_PATH = './generated'; 21 | 22 | var removeDirectory = function(path) { 23 | if (fs.existsSync(path)) { 24 | fs.readdirSync(path).forEach(function(file, index) { 25 | var curPath = path + "/" + file; 26 | if (fs.lstatSync(curPath).isDirectory()) { 27 | removeDirectory(curPath); 28 | } else { 29 | fs.unlinkSync(curPath); 30 | } 31 | }); 32 | fs.rmdirSync(path); 33 | } 34 | } 35 | 36 | exports.removeDirectory = removeDirectory; 37 | exports.GENERATED_CODE_PATH = GENERATED_CODE_PATH; 38 | -------------------------------------------------------------------------------- /packages/grpc-web/test/eval_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | 19 | const assert = require('assert'); 20 | const execSync = require('child_process').execSync; 21 | const commandExists = require('command-exists').sync; 22 | const fs = require('fs'); 23 | const path = require('path'); 24 | const removeDirectory = require('./common.js').removeDirectory; 25 | const GENERATED_CODE_PATH = require('./common.js').GENERATED_CODE_PATH; 26 | 27 | describe('grpc-web generated code eval test (commonjs+dts)', function() { 28 | const genCodeCmd = 29 | 'protoc -I=./test/protos foo.proto models.proto ' + 30 | '--js_out=import_style=commonjs:./test/generated ' + 31 | '--grpc-web_out=import_style=commonjs+dts,mode=grpcwebtext:' + 32 | './test/generated'; 33 | 34 | before(function() { 35 | ['protoc', 'protoc-gen-grpc-web'].map(prog => { 36 | if (!commandExists(prog)) { 37 | assert.fail(`${prog} is not installed`); 38 | } 39 | }); 40 | }); 41 | 42 | beforeEach(function() { 43 | removeDirectory(path.resolve(__dirname, GENERATED_CODE_PATH)); 44 | fs.mkdirSync(path.resolve(__dirname, GENERATED_CODE_PATH)); 45 | }); 46 | 47 | afterEach(function() { 48 | removeDirectory(path.resolve(__dirname, GENERATED_CODE_PATH)); 49 | }); 50 | 51 | it('should eval', function() { 52 | execSync(genCodeCmd); 53 | execSync(`npx gulp --gulpfile ./test/gulpfile.js gen-code-eval-test`); 54 | }) 55 | }); 56 | 57 | 58 | describe('grpc-web generated code eval test (typescript)', function() { 59 | const genTsCodePath = path.resolve(__dirname, 60 | './generated/FooServiceClientPb.ts'); 61 | 62 | const genCodeCmd = 63 | 'protoc -I=./test/protos foo.proto models.proto ' + 64 | '--js_out=import_style=commonjs:./test/generated ' + 65 | '--grpc-web_out=import_style=typescript,mode=grpcwebtext:./test/generated'; 66 | 67 | before(function() { 68 | ['protoc', 'protoc-gen-grpc-web'].map(prog => { 69 | if (!commandExists(prog)) { 70 | assert.fail(`${prog} is not installed`); 71 | } 72 | }); 73 | }); 74 | 75 | beforeEach(function() { 76 | removeDirectory(path.resolve(__dirname, GENERATED_CODE_PATH)); 77 | fs.mkdirSync(path.resolve(__dirname, GENERATED_CODE_PATH)); 78 | }); 79 | 80 | afterEach(function() { 81 | removeDirectory(path.resolve(__dirname, GENERATED_CODE_PATH)); 82 | }); 83 | 84 | it('should eval', function() { 85 | execSync(genCodeCmd); 86 | // --skipLibCheck is needed because some of our node_modules/ targets es6 87 | // but our test doesn't pass with `--target es6` 88 | // TODO: Find out how we can enable --target es6! 89 | execSync(`tsc --strict --skipLibCheck ${genTsCodePath}`); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /packages/grpc-web/test/export_test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const grpc = {}; 3 | grpc.web = require('grpc-web'); 4 | 5 | describe('grpc-web export test', function() { 6 | it('should have MethodDescriptor exported', function() { 7 | assert.equal(typeof grpc.web.MethodDescriptor, 'function'); 8 | }); 9 | 10 | it('should have GrpcWebClientBase#rpcCall() exported', function() { 11 | assert.equal(typeof grpc.web.GrpcWebClientBase.prototype.rpcCall, 'function'); 12 | }); 13 | 14 | it('should have GrpcWebClientBase#serverStreaming() exported', function() { 15 | assert.equal(typeof grpc.web.GrpcWebClientBase.prototype.serverStreaming, 'function'); 16 | }); 17 | 18 | it('should have RpcError properties exported', function() { 19 | const rpcError = new grpc.web.RpcError(/* code= */ 0, 'message'); 20 | assert.equal(typeof rpcError.code, 'number'); 21 | assert.equal(typeof rpcError.message, 'string'); 22 | assert.equal(typeof rpcError.metadata, 'object'); 23 | }); 24 | 25 | it('should have StatusCode exported', function() { 26 | assert.deepEqual(grpc.web.StatusCode, { 27 | ABORTED: 10, 28 | ALREADY_EXISTS: 6, 29 | CANCELLED: 1, 30 | DATA_LOSS: 15, 31 | DEADLINE_EXCEEDED: 4, 32 | FAILED_PRECONDITION: 9, 33 | INTERNAL: 13, 34 | INVALID_ARGUMENT: 3, 35 | NOT_FOUND: 5, 36 | OK: 0, 37 | OUT_OF_RANGE: 11, 38 | PERMISSION_DENIED: 7, 39 | RESOURCE_EXHAUSTED: 8, 40 | UNAUTHENTICATED: 16, 41 | UNAVAILABLE: 14, 42 | UNIMPLEMENTED: 12, 43 | UNKNOWN: 2 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /packages/grpc-web/test/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const gulpEval = require('gulp-eval'); 3 | 4 | gulp.task('gen-code-eval-test', () => 5 | gulp.src('./generated/foo_grpc_web_pb.js') 6 | .pipe(gulpEval()) 7 | ); 8 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/echo.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package grpc.gateway.testing; 18 | 19 | message EchoRequest { 20 | string message = 1; 21 | int32 value = 2; 22 | } 23 | 24 | message EchoResponse { 25 | string message = 1; 26 | // to test Request and Response having a different shape 27 | string value = 2; 28 | } 29 | 30 | message ServerStreamingEchoRequest { 31 | string message = 1; 32 | int32 message_count = 2; 33 | int32 message_interval = 3; 34 | } 35 | 36 | message ServerStreamingEchoResponse { 37 | string message = 1; 38 | } 39 | 40 | enum Status { 41 | UNKNOWN = 0; 42 | SUCCESS = 1; 43 | } 44 | 45 | message EchoStatusRequest { 46 | Status status = 1; 47 | } 48 | 49 | message EchoStatusResponse { 50 | enum InternalStatus { 51 | UNKNOWN = 0; 52 | SUCCESS = 1; 53 | } 54 | InternalStatus status = 1; 55 | } 56 | 57 | 58 | service EchoService { 59 | rpc Echo(EchoRequest) returns (EchoResponse); 60 | rpc ServerStreamingEcho(ServerStreamingEchoRequest) 61 | returns (stream ServerStreamingEchoResponse); 62 | rpc EchoStatus(EchoStatusRequest) returns (EchoStatusResponse); 63 | } 64 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/foo.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package Foo; 4 | 5 | import "models.proto"; 6 | 7 | message SimpleRequest { 8 | string message = 1; 9 | } 10 | 11 | service FooService { 12 | rpc EchoEmpty(SimpleRequest) returns (models.SimpleMessage); 13 | } 14 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/models.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package models; 4 | 5 | message SimpleMessage { 6 | } 7 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/myapi/v1/myapi-two.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package myproject.myapi.v1; 18 | 19 | import "otherapi/v1/otherapi.proto"; 20 | 21 | service MyServiceB { 22 | rpc DoThat(myproject.otherapi.v1.OtherThing) returns (myproject.otherapi.v1.OtherThing); 23 | } 24 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/myapi/v1/myapi.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package myproject.myapi.v1; 18 | 19 | import "otherapi/v1/otherapi.proto"; 20 | 21 | message MyThing { 22 | string message = 1; 23 | myproject.otherapi.v1.OtherThing other_thing = 2; 24 | } 25 | 26 | service MyService { 27 | rpc DoThis(myproject.otherapi.v1.OtherThing) returns (myproject.otherapi.v1.OtherThing); 28 | } 29 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/nopackage.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | service Greeter { 18 | rpc SayHello (HelloRequest) returns (HelloReply); 19 | } 20 | 21 | message HelloRequest { 22 | string name = 1; 23 | } 24 | 25 | message HelloReply { 26 | string message = 1; 27 | } 28 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/otherapi/v1/otherapi.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package myproject.otherapi.v1; 18 | 19 | message OtherThing { 20 | string value = 1; 21 | } 22 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/test01.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message MessageOuter { 4 | message MessageInner { 5 | int32 value = 1; 6 | } 7 | 8 | enum EnumInner { 9 | DEFAULT = 0; 10 | } 11 | 12 | repeated MessageInner someProp = 1; 13 | EnumOuter someEnum = 2; 14 | EnumInner anotherEnum = 3; 15 | } 16 | 17 | enum EnumOuter { 18 | DEFAULT = 0; 19 | } 20 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/test02.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | import "test03.proto"; 3 | 4 | service MyService { 5 | rpc addOne(Integer) returns (Integer); 6 | } 7 | -------------------------------------------------------------------------------- /packages/grpc-web/test/protos/test03.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message Integer { 4 | } 5 | -------------------------------------------------------------------------------- /packages/grpc-web/test/tsc-tests/client01.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | import * as grpcWeb from 'grpc-web'; 19 | 20 | import {MessageOuter} from './generated/test01_pb'; 21 | 22 | let inner1 = new MessageOuter.MessageInner(); 23 | inner1.setValue(123); 24 | let msgOuter = new MessageOuter(); 25 | msgOuter.setSomepropList([inner1]); 26 | 27 | export {msgOuter} 28 | -------------------------------------------------------------------------------- /packages/grpc-web/test/tsc-tests/client02.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | import * as grpcWeb from 'grpc-web'; 19 | 20 | import {Integer} from './generated/test03_pb'; 21 | import {MyServiceClient} from './generated/Test02ServiceClientPb'; 22 | 23 | const service = new MyServiceClient('http://mydummy.com', null, null); 24 | const req = new Integer(); 25 | 26 | service.addOne(req, {}, (err: grpcWeb.RpcError, resp: Integer) => { 27 | }); 28 | -------------------------------------------------------------------------------- /packages/grpc-web/test/tsc-tests/client03.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | import * as grpcWeb from 'grpc-web'; 19 | 20 | import {EchoRequest, EchoResponse} from './generated/echo_pb'; 21 | import {EchoServiceClient} from './generated/echo_grpc_web_pb'; 22 | 23 | // The StreamInterceptor interface is for the callback-based client. 24 | class MyStreamInterceptor implements grpcWeb.StreamInterceptor< 25 | EchoRequest, EchoResponse> { 26 | intercept( 27 | request: grpcWeb.Request, 28 | invoker: (request: grpcWeb.Request) => 29 | grpcWeb.ClientReadableStream) { 30 | class InterceptedStream implements grpcWeb.ClientReadableStream< 31 | EchoResponse> { 32 | stream: grpcWeb.ClientReadableStream; 33 | constructor(stream: grpcWeb.ClientReadableStream) { 34 | this.stream = stream; 35 | }; 36 | on(eventType: string, callback: any) { 37 | if (eventType == 'data') { 38 | const newCallback = (response: EchoResponse) => { 39 | response.setMessage('[-in-]'+response.getMessage()); 40 | callback(response); 41 | }; 42 | this.stream.on(eventType, newCallback); 43 | } else if (eventType == 'error') { 44 | this.stream.on('error', callback); 45 | } else if (eventType == 'metadata') { 46 | this.stream.on('metadata', callback); 47 | } else if (eventType == 'status') { 48 | this.stream.on('status', callback); 49 | } else if (eventType == 'end') { 50 | this.stream.on('end', callback); 51 | } 52 | return this; 53 | }; 54 | removeListener(eventType: string, callback: any) { 55 | } 56 | cancel() {} 57 | } 58 | var reqMsg = request.getRequestMessage(); 59 | reqMsg.setMessage('[-out-]'+reqMsg.getMessage()); 60 | return new InterceptedStream(invoker(request)); 61 | }; 62 | } 63 | 64 | var opts = {'streamInterceptors' : [new MyStreamInterceptor()]}; 65 | 66 | const echoService = new EchoServiceClient('http://localhost:8080', null, opts); 67 | 68 | export {echoService, EchoRequest} 69 | -------------------------------------------------------------------------------- /packages/grpc-web/test/tsc-tests/client04.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | import * as grpcWeb from 'grpc-web'; 19 | 20 | import {EchoRequest, EchoResponse} from './generated/echo_pb'; 21 | import {EchoServicePromiseClient} from './generated/echo_grpc_web_pb'; 22 | 23 | // The UnaryInterceptor interface is for the promise-based client. 24 | class MyUnaryInterceptor implements grpcWeb.UnaryInterceptor< 25 | EchoRequest, EchoResponse> { 26 | intercept(request: grpcWeb.Request, 27 | invoker: (request: grpcWeb.Request) => 28 | Promise>) { 29 | const reqMsg = request.getRequestMessage(); 30 | reqMsg.setMessage('[-out-]' + reqMsg.getMessage()); 31 | return invoker(request).then((response: grpcWeb.UnaryResponse< 32 | EchoRequest, EchoResponse>) => { 33 | let result = '<-InitialMetadata->'; 34 | let initialMetadata = response.getMetadata(); 35 | for (let i in initialMetadata) { 36 | result += i + ': ' + initialMetadata[i]; 37 | } 38 | result += '<-TrailingMetadata->'; 39 | let trailingMetadata = response.getStatus().metadata; 40 | for (let i in trailingMetadata) { 41 | result += i + ': ' + trailingMetadata[i]; 42 | } 43 | const responseMsg = response.getResponseMessage(); 44 | result += '[-in-]' + responseMsg.getMessage(); 45 | responseMsg.setMessage(result); 46 | return response; 47 | }); 48 | } 49 | } 50 | 51 | var opts = {'unaryInterceptors' : [new MyUnaryInterceptor()]}; 52 | 53 | const echoService = new EchoServicePromiseClient('http://localhost:8080', 54 | null, opts); 55 | 56 | export {echoService, EchoRequest} 57 | -------------------------------------------------------------------------------- /packages/grpc-web/test/tsc-tests/client05.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | import * as grpcWeb from 'grpc-web'; 19 | 20 | import {EchoRequest, EchoResponse, 21 | ServerStreamingEchoRequest, 22 | ServerStreamingEchoResponse} from './generated/echo_pb'; 23 | import {EchoServiceClient} from './generated/echo_grpc_web_pb'; 24 | 25 | const echoService = new EchoServiceClient('http://localhost:8080', null, null); 26 | 27 | let req = new EchoRequest(); 28 | req.setMessage('aaa'); 29 | 30 | // this test tries to make sure that these types are as accurate as possible 31 | 32 | let call1 : grpcWeb.ClientReadableStream = 33 | echoService.echo(req, {}, (err: grpcWeb.RpcError, 34 | response: EchoResponse) => { 35 | }); 36 | 37 | call1 38 | .on('status', (status: grpcWeb.Status) => { 39 | }) 40 | .on('metadata', (metadata: grpcWeb.Metadata) => { 41 | }); 42 | 43 | let call2 : grpcWeb.ClientReadableStream = 44 | echoService.serverStreamingEcho(new ServerStreamingEchoRequest(), {}); 45 | 46 | call2 47 | .on('data', (response: ServerStreamingEchoResponse) => { 48 | }) 49 | .on('error', (error: grpcWeb.RpcError) => { 50 | }); 51 | -------------------------------------------------------------------------------- /packages/grpc-web/test/tsc-tests/client06.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright 2018 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | */ 18 | import * as grpcWeb from 'grpc-web'; 19 | 20 | import {EchoRequest, EchoResponse} from './generated/echo_pb'; 21 | import {EchoServicePromiseClient} from './generated/echo_grpc_web_pb'; 22 | 23 | const echoService = new EchoServicePromiseClient( 24 | 'http://localhost:8080', null, null); 25 | 26 | let req = new EchoRequest(); 27 | req.setMessage('aaa'); 28 | 29 | // this test tries to make sure that these types are as accurate as possible 30 | 31 | let p1 : Promise = echoService.echo(req, {}); 32 | 33 | // why does the .then() add this extra 'void' type to the returned Promise? 34 | let p2 : Promise = p1.then((response: EchoResponse) => { 35 | }); 36 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts 2 | 3 | A collection of scripts (mostly test related). 4 | 5 | 6 | ## Troubleshooting - Bazel Crashes (OOM) 7 | 8 | [Bazel](https://github.com/bazelbuild/bazel) can be memory hungry and often 9 | crashes while building on Mac with default Docker memory settings (2GB) 10 | (similar reports: [here](https://github.com/tensorflow/models/issues/3647) and 11 | [here](https://stackoverflow.com/questions/65605663/cannot-build-with-error-server-terminated-abruptly)). 12 | 13 | 14 | Bump the [memory settings](https://docs.docker.com/docker-for-mac/#resources) 15 | in Docker Desktop for Mac (e.g. to 4 - 6GB) if you see the following errors: 16 | 17 | 18 | ``` 19 | $ bazel build //javascript/net/grpc/web/generator/... //net/grpc/gateway/examples/echo/... 20 | 21 | ... 22 | 23 | Server terminated abruptly (error code: 14, error message: 'Socket closed', log file: '/root/.cache/bazel/_bazel_root/.../server/jvm.out') 24 | 25 | ------ 26 | 27 | failed to solve: rpc error: code = Unknown desc = executor failed running [/bin/sh -c bazel build javascript/net/grpc/web/... && cp $(bazel info bazel-genfiles)/javascript/net/grpc/web/protoc-gen-grpc-web /usr/local/bin/protoc-gen-grpc-web]: exit code: 37 28 | ``` 29 | -------------------------------------------------------------------------------- /scripts/docker-run-build-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2021 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -ex 16 | 17 | # This script is intended to be run within the base image from 18 | # net/grpc/gateway/docker/prereqs/Dockerfile 19 | 20 | # Ensures all Bazel targets builds 21 | cd /github/grpc-web && \ 22 | bazel clean && \ 23 | bazel build \ 24 | //javascript/net/grpc/web/generator/... \ 25 | //net/grpc/gateway/examples/echo/... 26 | -------------------------------------------------------------------------------- /scripts/docker-run-interop-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -ex 16 | 17 | # This script is intended to be run within the base image from 18 | # net/grpc/gateway/docker/prereqs/Dockerfile 19 | 20 | cd /github/grpc-web/test/interop && \ 21 | npm install && \ 22 | npm link grpc-web 23 | 24 | # Test grpc-web-text mode 25 | protoc -I=../.. src/proto/grpc/testing/test.proto \ 26 | src/proto/grpc/testing/empty.proto \ 27 | src/proto/grpc/testing/messages.proto \ 28 | --js_out=import_style=commonjs:. \ 29 | --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. && \ 30 | npm test 31 | 32 | # Test grpc-web mode (binary) 33 | protoc -I=../.. src/proto/grpc/testing/test.proto \ 34 | src/proto/grpc/testing/empty.proto \ 35 | src/proto/grpc/testing/messages.proto \ 36 | --js_out=import_style=commonjs:. \ 37 | --grpc-web_out=import_style=commonjs,mode=grpcweb:. && \ 38 | npm test -- --mode=binary 39 | -------------------------------------------------------------------------------- /scripts/docker-run-jsunit-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2021 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -ex 16 | 17 | # This script is intended to be run within the base image from 18 | # packages/grpc-web/docker/jsunit-test/Dockerfile 19 | 20 | cd /grpc-web/packages/grpc-web 21 | 22 | npm run test-jsunit 23 | -------------------------------------------------------------------------------- /scripts/docker-run-mocha-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2021 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -ex 16 | 17 | # This script is intended to be run within the base image from 18 | # net/grpc/gateway/docker/prereqs/Dockerfile 19 | 20 | cd /github/grpc-web/packages/grpc-web 21 | npm run prepare && \ 22 | npm run test-mocha 23 | -------------------------------------------------------------------------------- /scripts/init_submodules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -ex 16 | 17 | cd "$(dirname "$0")"/.. 18 | git submodule --quiet update --init --recursive 19 | (cd third_party/protobuf && git checkout tags/v3.15.6) 20 | -------------------------------------------------------------------------------- /scripts/kokoro.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -ex 16 | 17 | SCRIPT_DIR=$(dirname "$0") 18 | cd "${SCRIPT_DIR}" 19 | 20 | export MASTER=1 21 | 22 | ./run_basic_tests.sh 23 | 24 | ./run_interop_tests.sh 25 | -------------------------------------------------------------------------------- /scripts/run_basic_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -ex 16 | 17 | SCRIPT_DIR=$(dirname "$0") 18 | REPO_DIR=$(realpath "${SCRIPT_DIR}/..") 19 | 20 | # Set up 21 | cd "${REPO_DIR}" 22 | 23 | 24 | # These programs need to be already installed 25 | progs=(docker docker-compose curl) 26 | for p in "${progs[@]}" 27 | do 28 | command -v "$p" > /dev/null 2>&1 || \ 29 | { echo >&2 "$p is required but not installed. Aborting."; exit 1; } 30 | done 31 | 32 | ########################################################## 33 | # Step 1: Run all unit tests 34 | ########################################################## 35 | echo -e "\n[Running] Basic test #1 - Runnning unit tests" 36 | # Run jsunit tests 37 | docker-compose build jsunit-test 38 | docker run --rm grpcweb/jsunit-test /bin/bash \ 39 | /grpc-web/scripts/docker-run-jsunit-tests.sh 40 | 41 | # Run (mocha) unit tests 42 | docker-compose build prereqs 43 | docker run --rm grpcweb/prereqs /bin/bash \ 44 | /github/grpc-web/scripts/docker-run-mocha-tests.sh 45 | 46 | 47 | ########################################################## 48 | # Step 2: Test echo server 49 | ########################################################## 50 | echo -e "\n[Running] Basic test #2 - Testing echo server" 51 | docker-compose build prereqs envoy node-server 52 | 53 | # Bring up the Echo server and the Envoy proxy (in background). 54 | # The 'sleep' seems necessary for the docker containers to be fully up 55 | # and listening before we test the with curl requests 56 | docker-compose up -d node-server envoy && sleep 5; 57 | 58 | # Run a curl request and verify the output 59 | source ./scripts/test-proxy.sh 60 | 61 | # Remove all docker containers 62 | docker-compose down 63 | 64 | 65 | ########################################################## 66 | # Step 3: Test all Dockerfile and Bazel targets can build! 67 | ########################################################## 68 | echo -e "\n[Running] Basic test #3 - Testing everything buids" 69 | if [[ "$MASTER" == "1" ]]; then 70 | # Build all for continuous_integration 71 | # docker-compose build 72 | 73 | # Temporary fix `protoc-plugin` build failure (introduced in 74 | # https://github.com/grpc/grpc-web/pull/1445) by building 75 | # everything but it. 76 | # TODO: Revert to building all targets. 77 | docker-compose build prereqs echo-server node-server node-interop-server envoy grpcwebproxy commonjs-client closure-client ts-client binary-client interop-client jsunit-test 78 | else 79 | # Only build a subset of docker images for presubmit runs 80 | docker-compose build commonjs-client closure-client ts-client 81 | fi 82 | 83 | # Run build tests to ensure all Bazel targets can build. 84 | docker run --rm grpcweb/prereqs /bin/bash \ 85 | /github/grpc-web/scripts/docker-run-build-tests.sh 86 | 87 | # Clean up 88 | git clean -f -d -x 89 | echo 'Basic tests completed successfully!' 90 | -------------------------------------------------------------------------------- /scripts/run_interop_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -ex 16 | 17 | run_tests () { 18 | docker run --network=host --rm grpcweb/prereqs /bin/bash \ 19 | /github/grpc-web/scripts/docker-run-interop-tests.sh 20 | } 21 | 22 | SCRIPT_DIR=$(dirname "$0") 23 | REPO_DIR=$(realpath "${SCRIPT_DIR}/..") 24 | 25 | # Set up 26 | cd "${REPO_DIR}" 27 | 28 | # These programs need to be already installed 29 | progs=(docker docker-compose npm) 30 | for p in "${progs[@]}" 31 | do 32 | command -v "$p" > /dev/null 2>&1 || \ 33 | { echo >&2 "$p is required but not installed. Aborting."; exit 1; } 34 | done 35 | 36 | function cleanup () { 37 | echo "Killing lingering Docker servers..." 38 | docker rm -f "$pid1" 39 | docker rm -f "$pid2" 40 | } 41 | 42 | trap cleanup EXIT 43 | 44 | # Build all relevant docker images. They should all build successfully. 45 | docker-compose build prereqs node-interop-server 46 | 47 | ########################################################## 48 | # Run interop tests (against Envoy) 49 | ########################################################## 50 | echo -e "\n[Running] Interop test (Envoy)" 51 | pid1=$(docker run -d \ 52 | -v "$(pwd)"/test/interop/envoy.yaml:/etc/envoy/envoy.yaml:ro \ 53 | --network=host envoyproxy/envoy:v1.22.0) 54 | pid2=$(docker run -d --network=host grpcweb/node-interop-server) 55 | 56 | run_tests 57 | 58 | docker rm -f "$pid1" 59 | docker rm -f "$pid2" -------------------------------------------------------------------------------- /scripts/test-proxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | set -ex 16 | 17 | # Run a curl request to test the output of the proxy and the backend server. 18 | # This is a simple unary call with "hello" as the protobuf message 19 | out=$(curl -s 'http://localhost:8080/grpc.gateway.testing.EchoService/Echo' \ 20 | -H 'Content-Type: application/grpc-web-text' \ 21 | -H 'Accept: application/grpc-web-text' \ 22 | -H 'Connection: keep-alive' \ 23 | -H 'X-Grpc-Web: 1' \ 24 | -H 'X-User-Agent: grpc-web-javascript/0.1' \ 25 | --data-binary 'AAAAAAcKBWhlbGxv') 26 | 27 | # Cut out a few parts of the response that we are reasonably sure that should 28 | # not change. 29 | # 30 | # Take the first 13 bytes: 31 | # First byte: 00 (data marker) 32 | # Next 4 bytes: 00 00 00 07 (length of payload) 33 | # Next 7 bytes: 0a 05 68 65 6c 6c 6f (binary proto of "1: hello") 34 | # Next 1 byte: 80 (trailer marker) 35 | # Skip the next 4 bytes: 36 | # This represents the length of the trailer frame, which could be unreliable. 37 | # Take the next 15 bytes: 38 | # This is the beginning of the trailer frame, which we are reasonably sure 39 | # that it will begin with: 40 | # grpc-status:0\r\n 41 | s1=$(echo "$out" | base64 -d | \ 42 | { dd bs=1 count=13 ; dd skip=4 bs=1 count=15 ; } 2>/dev/null | \ 43 | base64) 44 | 45 | echo "$s1" | base64 -d | xxd 46 | 47 | # Take the 28 bytes we cut out above, the base64-encoded string should be this 48 | if [[ "$s1" != "AAAAAAcKBWhlbGxvgGdycGMtc3RhdHVzOjANCg==" ]]; then 49 | exit 1; 50 | else 51 | echo "Envoy proxy test successful!" 52 | fi 53 | -------------------------------------------------------------------------------- /src/proto/grpc/testing/empty.proto: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2015 gRPC authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | syntax = "proto3"; 17 | 18 | package grpc.testing; 19 | 20 | // An empty message that you can re-use to avoid defining duplicated empty 21 | // messages in your project. A typical example is to use it as argument or the 22 | // return value of a service API. For instance: 23 | // 24 | // service Foo { 25 | // rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; 26 | // }; 27 | // 28 | message Empty {} 29 | -------------------------------------------------------------------------------- /src/proto/grpc/testing/test.proto: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2015-2016 gRPC authors. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // An integration test service that covers all the method signature permutations 17 | // of unary/streaming requests/responses. 18 | 19 | syntax = "proto3"; 20 | 21 | import "src/proto/grpc/testing/empty.proto"; 22 | import "src/proto/grpc/testing/messages.proto"; 23 | 24 | package grpc.testing; 25 | 26 | // A simple service to test the various types of RPCs and experiment with 27 | // performance with various types of payload. 28 | service TestService { 29 | // One empty request followed by one empty response. 30 | rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); 31 | 32 | // One request followed by one response. 33 | rpc UnaryCall(SimpleRequest) returns (SimpleResponse); 34 | 35 | // One request followed by one response. Response has cache control 36 | // headers set such that a caching HTTP proxy (such as GFE) can 37 | // satisfy subsequent requests. 38 | rpc CacheableUnaryCall(SimpleRequest) returns (SimpleResponse); 39 | 40 | // One request followed by a sequence of responses (streamed download). 41 | // The server returns the payload with client desired type and sizes. 42 | rpc StreamingOutputCall(StreamingOutputCallRequest) 43 | returns (stream StreamingOutputCallResponse); 44 | 45 | // A sequence of requests followed by one response (streamed upload). 46 | // The server returns the aggregated size of client payload as the result. 47 | rpc StreamingInputCall(stream StreamingInputCallRequest) 48 | returns (StreamingInputCallResponse); 49 | 50 | // A sequence of requests with each request served by the server immediately. 51 | // As one request could lead to multiple responses, this interface 52 | // demonstrates the idea of full duplexing. 53 | rpc FullDuplexCall(stream StreamingOutputCallRequest) 54 | returns (stream StreamingOutputCallResponse); 55 | 56 | // A sequence of requests followed by a sequence of responses. 57 | // The server buffers all the client requests and then serves them in order. A 58 | // stream of responses are returned to the client when the server starts with 59 | // first request. 60 | rpc HalfDuplexCall(stream StreamingOutputCallRequest) 61 | returns (stream StreamingOutputCallResponse); 62 | 63 | // The test server will not implement this method. It will be used 64 | // to test the behavior when clients call unimplemented methods. 65 | rpc UnimplementedCall(grpc.testing.Empty) returns (grpc.testing.Empty); 66 | } 67 | 68 | // A simple service NOT implemented at servers so clients can test for 69 | // that case. 70 | service UnimplementedService { 71 | // A call that no server should implement 72 | rpc UnimplementedCall(grpc.testing.Empty) returns (grpc.testing.Empty); 73 | } 74 | 75 | // A service used to control reconnect server. 76 | service ReconnectService { 77 | rpc Start(grpc.testing.ReconnectParams) returns (grpc.testing.Empty); 78 | rpc Stop(grpc.testing.Empty) returns (grpc.testing.ReconnectInfo); 79 | } 80 | 81 | // A service used to obtain stats for verifying LB behavior. 82 | service LoadBalancerStatsService { 83 | // Gets the backend distribution for RPCs sent by a test client. 84 | rpc GetClientStats(LoadBalancerStatsRequest) 85 | returns (LoadBalancerStatsResponse) {} 86 | } 87 | -------------------------------------------------------------------------------- /test/interop/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | src/ 4 | -------------------------------------------------------------------------------- /test/interop/README.md: -------------------------------------------------------------------------------- 1 | gRPC-Web Interop Tests 2 | ====================== 3 | 4 | See the 5 | [main doc](https://github.com/grpc/grpc-web/blob/master/doc/interop-test-descriptions.md) 6 | for details about gRPC interop tests in general and the list of test cases. 7 | 8 | 9 | Run interop tests 10 | ----------------- 11 | 12 | ### Build some docker images 13 | 14 | ```sh 15 | $ cd grpc-web 16 | $ docker-compose build prereqs node-interop-server interop-client 17 | ``` 18 | 19 | 20 | ### Run the Node interop server 21 | 22 | An interop server implemented in Node is hosted in the `grpc/grpc-node` repo. 23 | 24 | ```sh 25 | $ docker run -d --network=host grpcweb/node-interop-server 26 | ``` 27 | 28 | 29 | ### Run the Envoy proxy 30 | 31 | An `envoy.yaml` file is provided in this directory to direct traffic for these 32 | tests. 33 | 34 | ```sh 35 | $ docker run -d -v $(pwd)/test/interop/envoy.yaml:/etc/envoy/envoy.yaml:ro \ 36 | --network=host envoyproxy/envoy:v1.22.0 37 | ``` 38 | 39 | 40 | ### Run the gRPC-Web browser client 41 | 42 | You can either run the interop client as `npm test`, like this: 43 | 44 | ```sh 45 | $ docker run --network=host --rm grpcweb/prereqs /bin/bash \ 46 | /github/grpc-web/scripts/docker-run-interop-tests.sh 47 | ``` 48 | 49 | Or from the browser: 50 | 51 | ```sh 52 | $ docker-compose up interop-client 53 | ``` 54 | 55 | Open up the browser and go to `http://localhost:8081/index.html` and open up 56 | the console. 57 | -------------------------------------------------------------------------------- /test/interop/envoy.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | access_log_path: /tmp/admin_access.log 3 | address: 4 | socket_address: { address: 0.0.0.0, port_value: 9901 } 5 | 6 | static_resources: 7 | listeners: 8 | - name: listener_0 9 | address: 10 | socket_address: { address: 0.0.0.0, port_value: 8080 } 11 | filter_chains: 12 | - filters: 13 | - name: envoy.filters.network.http_connection_manager 14 | typed_config: 15 | "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager 16 | codec_type: auto 17 | stat_prefix: ingress_http 18 | route_config: 19 | name: local_route 20 | virtual_hosts: 21 | - name: local_service 22 | domains: ["*"] 23 | routes: 24 | - match: { prefix: "/" } 25 | route: 26 | cluster: interop_service 27 | timeout: 0s 28 | max_stream_duration: 29 | grpc_timeout_header_max: 0s 30 | cors: 31 | allow_origin_string_match: 32 | - prefix: "*" 33 | allow_methods: GET, PUT, DELETE, POST, OPTIONS 34 | allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,x-grpc-test-echo-initial,x-grpc-test-echo-trailing-bin,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout 35 | max_age: "1728000" 36 | expose_headers: x-grpc-test-echo-initial,x-grpc-test-echo-trailing-bin,grpc-status,grpc-message 37 | http_filters: 38 | - name: envoy.filters.http.grpc_web 39 | typed_config: 40 | "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb 41 | - name: envoy.filters.http.cors 42 | typed_config: 43 | "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors 44 | - name: envoy.filters.http.router 45 | typed_config: 46 | "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router 47 | clusters: 48 | - name: interop_service 49 | connect_timeout: 0.25s 50 | type: logical_dns 51 | # HTTP/2 support 52 | typed_extension_protocol_options: 53 | envoy.extensions.upstreams.http.v3.HttpProtocolOptions: 54 | "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions 55 | explicit_http_config: 56 | http2_protocol_options: {} 57 | lb_policy: round_robin 58 | load_assignment: 59 | cluster_name: cluster_0 60 | endpoints: 61 | - lb_endpoints: 62 | - endpoint: 63 | address: 64 | socket_address: 65 | address: localhost 66 | port_value: 7074 67 | -------------------------------------------------------------------------------- /test/interop/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Interop Test 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |

Please open up the console to see the test results.

28 |
29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /test/interop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grpc-web-interop-test", 3 | "version": "0.1.0", 4 | "description": "gRPC-Web Interop Test Client", 5 | "license": "Apache-2.0", 6 | "scripts": { 7 | "test": "mocha -b interop_client.js" 8 | }, 9 | "dependencies": { 10 | "google-protobuf": "~3.21.4", 11 | "grpc-web": "~1.5.0" 12 | }, 13 | "devDependencies": { 14 | "assert": "^2.0.0", 15 | "minimist": "~1.2.5", 16 | "mocha": "~7.1.1", 17 | "webpack": "~5.82.1", 18 | "webpack-cli": "~5.1.1", 19 | "xhr2": "~0.2.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/interop/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: "production", 3 | entry: "./interop_client.js", 4 | }; 5 | --------------------------------------------------------------------------------