Coder Social home page Coder Social logo

improbable-eng / grpc-web Goto Github PK

View Code? Open in Web Editor NEW
4.3K 65.0 435.0 7.56 MB

gRPC Web implementation for Golang and TypeScript

License: Apache License 2.0

Go 29.82% Shell 3.38% JavaScript 20.67% TypeScript 46.10% HTML 0.03%
grpc grpc-web browser golang typescript

grpc-web's People

Contributors

0hm avatar a-krause avatar anuraaga avatar crisbodnar avatar danilvpetrov avatar dependabot-preview[bot] avatar dependabot[bot] avatar easycz avatar ishitatsuyuki avatar jcgrant avatar johanbrandhorst avatar jonny-improbable avatar jonnyreeves avatar mangas avatar marcuslongmuir avatar midan888 avatar mohsen1 avatar mrwinstead avatar nathanb21 avatar nevi-me avatar pbsf avatar philipithomas avatar runarheggset avatar sam-parsons avatar sandersaares avatar seanwatters avatar shreyas-samsung avatar sschuberth avatar vdods avatar zolkin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

grpc-web's Issues

Websocket transport

Thanks very much for this project. Just a thought - it would be nice to see the project designed in such a way that the transport can be swapped out. I think a WS/WSS transport would work quite well for gRPC-Web with full streaming support and even better browser compatibility than what this current project supports.

Can the grpc-web-client run on node?

Our website use the Universal Rendering, I succeeded to run the standalone grpcwebproxy with client side rendering, but when it comes to server side rendering, I got the error

ReferenceError: XMLHttpRequest is not defined
     at getXHR (/**/node_modules/grpc-web-client/src/transports/Transport.ts:27:7)
     at xhrSupportsResponseType (/**/node_modules/grpc-web-client/src/transports/Transport.ts:37:15)
     at Function.DefaultTransportFactory.detectTransport (/**/node_modules/grpc-web-client/src/transports/Transport.ts:62:9)
     at Function.DefaultTransportFactory.getTransport (/**/node_modules/grpc-web-client/src/transports/Transport.ts:52:47)
     at Object.invoke (/**/node_modules/grpc-web-client/src/grpc.ts:160:43)
     at /**/src/helpers/books.js:11:10
     at F (/**/node_modules/core-js/library/modules/_export.js:35:28)
     at batchGetBook (/**/src/helpers/books.js:10:10)
     at /**/src/redux/modules/full.js:96:12
     at Object.promise (/**/src/containers/Book/Book.js:17:19)
     at /**/node_modules/redux-async-connect/lib/ReduxAsyncConnect.js:118:34
     at Array.reduce (native)
     at /**/node_modules/redux-async-connect/lib/ReduxAsyncConnect.js:117:35
     at Array.map (native)
     at loadAsyncConnect (/**/node_modules/redux-async-connect/lib/ReduxAsyncConnect.js:114:68)
     at loadOnServer (/**/node_modules/redux-async-connect/lib/ReduxAsyncConnect.js:142:16)

it's because XMLHttpRequest is a built-in object in web browsers but not distributed with Node.
Are there any chance to let the grpc-web works on the Universal Rendering pattern of our website?

501 response...grpcwebproxy debug flag?

Hello,

After solving the TLS certs related erros, I'm now sending a fetch request to the grpcwebproxy using:

fetch('https://localhost:8443/package.service/method', {
        method: 'POST',
        mode: 'no-cors',
        headers: {
          'Content-Type': 'application/grpc-web',
          'X-GRPC-WEB': '1',
        },
        body: Message.encode(message).finish(),
      })

But I get a 501 status reply, and no reaction in my grpc server log.
screenshot from 2017-04-08 19-57-08

So is there a way to show the debug log from the grpcwebproxy in the console? Or what are the debug capabilities of grpcwebproxy?

Thank you :)

Inconsistent multi-value Headers and Trailers handling

Headers and Trailers with multiple values for the same key are being handled differently. Headers are being concatenated into a single string, whereas Trailers retain the individual values.

Setting response headers with:

grpc.SendHeader(ctx, metadata.Pairs("HeaderTestKey1", "ServerValue1A", "HeaderTestKey1", "ServerValue1B", "HeaderTestKey2", "ServerValue2"))

and response trailers with:

grpc.SetTrailer(ctx, metadata.Pairs("TrailerTestKey1", "ServerValue1A", "TrailerTestKey1", "ServerValue1B", "TrailerTestKey2", "ServerValue2"))

Produces inconsistent handling in Chrome 57:

onHeaders: function(headers: BrowserHeaders) {
    console.log("headers", JSON.stringify(headers));
    // ...
    // "headertestkey1": [
    //   "ServerValue1A, ServerValue1B"
    // ],
    // "headertestkey2": [
    //   "ServerValue2"
    // ]
onEnd: function(code: grpc.Code, msg: string, trailers: BrowserHeaders) {
    console.log("trailers", JSON.stringify(trailers));
    // ...
    // "trailertestkey1": [
    //   "ServerValue1A",
    //   "ServerValue1B"
    // ],
    // "trailertestkey2": [
    //   "ServerValue2"
    // ]

Typings do not match generated javascript

message validationReq {
    string accessToken = 1;
}

Produces a typings file with definitions like setAccessToken(...).

However the produced javascript does not honor the camelcase and creates the prototype function setAccesstoken(...).

Is this a .proto convention? A bug? It's late so I'm filing a short bug to see if there is a simple answer. Please let me know if more information is needed and I'd be happy to oblige.

abort on the client only propagates to the upstream gRPC server if at least one package was already received

OBSERVATION

Right now, cancelling a request only propagates to the upstream gRPC server on a "streaming call" after one result was received (because then the reader is available). Cancellation of "unary calls" doesn't propagate to the upstream gRPC server at all.

REASON

Currently, the only away to abort a "fetch" is to cancel the reader, which is only possible after at least one FRAME has been received by the browser (since then you get the reader instance).

(The "fetch" standard will introduce "abort" soon. Some browser already have it, but it might take a while for this to be implemented in all browser.)

PROPOSAL

The grpc-web-proxy should send an empty frame downstream to the client at the beginning in order to trigger the reader being created at the client so that "cancel" is propagated.

Because sending an empty frame might be overhead, I would propose that grpc-web-proxy sends an empty frame with some delay (500ms), i.e. only if the upstream server isn't responding within a certain timeframe, an empty frame is send downstream to the web client.

Not using stream.SendHeader in Server Streaming call yields error

I've been playing around with gRPC-Web for my GopherJS bindings, and I noticed that if I remove the stream.SendHeader call in QueryBooks I get the following error:

Response closed without grpc-status (Headers only)

The onEnd headers look like so:

access-control-allow-credentials:[true]
access-control-allow-origin:[https://localhost:10000]
content-type:[application/octet-stream]
date:[Fri, 23 Jun 2017 08:46:36 GMT]
status:[200]
strict-transport-security:[max-age=31536000; includeSubDomains; preload]
vary:[Origin]

Indeed, the grpc-status header is missing. When the SendHeaders call is added back the onEnd headers look like so instead:

grpc-status:[0]

I've tried debugging this a little but It'd be cool to know if this is reproducible on your end of if it's a problem with my setup. The source repo I'm using is https://github.com/johanbrandhorst/grpcweb-example.

Python backend

I'm trying to use grpc-web with a python backend. I keep getting the following output from the grpc-web-client:

status 13 Response closed without headers

Perhaps someone can point me in the right direction

Debug output from the grpc-server:
localhost:4200..User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36..Access-Control-Request-Headers: content-type,x-grpc-web..Accept: */*..Referer: http://localhost:4200/login..Accept-Encoding: gzip, deflate, br..Accept-Language: en-US,en;q=0.8,en-GB;q=0.6,sv;q=0.4....' D1013 11:20:41.749662000 123145419489280 chttp2_transport.c:2546] set connectivity_state=4 D1013 11:20:41.749700000 123145419489280 connectivity_state.c:171] SET: 0x7fa8f5072850 server_transport: READY --> SHUTDOWN [close_transport] error=0x7fa8f4e89030 {"created":"@1507886441.749660000","description":"Failed parsing HTTP/2","file":"src/core/ext/transport/chttp2/transport/chttp2_transport.c","file_line":2342,"grpc_status":14,"referenced_errors":[{"created":"@1507886441.749656000","description":"Connect string mismatch: expected 'P' (80) got 'O' (79) at byte 0","file":"src/core/ext/transport/chttp2/transport/parsing.c","file_line":104}]}

//Jonas

Passing metadata through proxy?

I am using grpcwebproxy. My backend server is a node js grpc server. I want pass metadata through grpc-web-client.

  const testRequest = new TestRequest();
  testRequest.setUserId('testuserid');

  grpc.unary(MeetService.testHistory, {
    request: testRequest,
    host: host,
    debug: true,
    metadata: {test_token: 'xxxxxxxxxxx'},
    onEnd: res => {
      const { status, statusMessage, headers, message, trailers } = res;
      if (status === Code.OK && message) {
        // console.log('meetHistory.onEnd.message', message.toObject());
      }
      // console.log('meetHistory.onEnd.trailers', trailers);
    }
  });

But my node server cannot get metadata. It only can get a user-agent metadata.

Metadata { _internal_repr: { 'user-agent': [ 'grpc-go/1.7.0-dev' ] } }
ServerUnaryCall {
  domain: null,
  _events: { error: [Function] },
  _eventsCount: 1,
  _maxListeners: undefined,
  call: Call {},
  cancelled: false,
  metadata: Metadata { _internal_repr: { 'user-agent': [Array] } },
  request: 
   { wrappers_: null,
     messageId_: undefined,
     arrayIndexOffset_: -1,
     array: [ 'testuserid' ],
     pivot_: 1.7976931348623157e+308,
     convertedFloatingPointFields_: {} } }

Option to set xhr.withCredentials

I'm facing a scenario where I have to talk to a gRPC server that sits behind a reverse proxy which redirects to ADFS for login and sets a http-only session cookie which it will pick up on subsequent requests and attaches an Authorization header. The problem is that the transports don't set the cookies with the gRPC invocation, so each POST results in a redirect.

What do you think about adding an option somewhere which could set the withCredentials property of the XMLHttpRequest before sending the request?

ServeHTTP should support HTTP2 / vanilla gRPC

It looks like the Serve HTTP does not currently support vanilla HTTP2 gRPC requests.

I propose changing the ServeHTTP to check defer to grpcServer.ServeHTTP when the request is HTTP2.

It should look something like this:

func (w *WrappedGrpcServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
	if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
			w.grpcServer.ServeHTTP(w, r)
        } else if w.IsAcceptableGrpcCorsRequest(req) || w.IsGrpcWebRequest(req) {
		w.corsWrapper.Handler(http.HandlerFunc(w.HandleGrpcWebRequest)).ServeHTTP(resp, req)
		return
	}
	w.server.ServeHTTP(resp, req)
}

(source parent)

So - is this approach ok? If I code it, will it get merged?

Cancel server streaming from client (browser)

Is there any way to cancel server push from client after method was invoked?

E.g. i want to be able to cancel current request and invoke new one with different parameters (filters change on page). But i do not see how i can get to connection.

Unclear how to stop a client.invoke on a stream

I have a working example of accessing a stream using grpc.invoke however there seems to be no way to cancel. Would think there would be something like setInterval ala...

const cancelId = grpc.invoke(...)
grpc.cancelnvoke(cancelId)

thoughts?

Auth Tokens

How would one go about including authentication tokens in the grpc-web-client? I see the metadata in the RpcOptions type however when I try to use it TypeScript says that property isn't valid.

    grpc.invoke(BookService.GetBooks, {
      request: new BookFilter(),
      host: 'http://localhost:9090',
      onEnd: (code: grpc.Code, msg: string, trailers: BrowserHeaders) => {
        console.log('getBooks.onEnd', code, msg, trailers);
      },
    });

This is an example of how I am trying to include/use it.

Changing directory structure of /proto causes failing import statements in generated code

I tried to clean up the project structure a little bit as a means of groking the project and found that changing the structure of the /proto directory caused a failing build due to an improper import statement in the generated typescript code.

Changing /proto/examplecom/library/book_service.proto

To /proto/book_service.proto

And changing the package name accordingly from examplecom.library to books in the proto file.

Caused the statement

import * as examplecom_library_book_service_pb from "../../examplecom/library/book_service_pb";

to be changed to

import * as book_service_pb from "book_service_pb";

which causes webpack to fail.

Manually changing the import to "./book_service_pb" caused the error to go away but I understand
you aren't supposed to manually change the generated code.

Thoughts? Let me know if you want me to give more examples.

Aborted request should call onEnd with Status.CANCELLED

As per discussion in #88, onEnd() callback on a grpc-web request should be called when a request is cancelled.

Requires:

  • Implement a call to onEnd() when a request is aborted
  • Update documentation to make it clear checking the status of onEnd is important

Wrap server without cors

Is there any possibility to wrap the the gRPC proxy server without allowing CORS? We already have a gateway that serves all preflight requests, however, the headers are doubled as grpc web proxy also adds headers to the response.

Node gRPC server using GCP Cloud Endpoint

Hi,

This library looks super useful, but a question:

I have a gRPC server running (node.js), deployed onto Google Cloud with GKE and Cloud Endpoints. Another node.js client is capable of calling this endpoint from inside or outside of GCP using an JWT token and that works fine. Is it possible to use this library with such a cloud endpoint and call my service from a web application?

Thanks!

Codec support

Love where this is going. Are you thinking jsonpb codec as an option or default for generated deserializer? IMHO this would be much better than protobufjs as browser decode speed is way better for json than the base64 hackery used in protobufjs.

Either way if it could support multiple codecs as per spec someone else can add.

Headless Chrome issue

Hi there,

If I run grpc-web on angular2 everything works well. If I am testing the gui with protractor/selenium and chrome everything works as intended too, but when adding the --headless flag to chrome it reports an grpc error code 13 on every grpc.invoke onEnd() handler. Is it possible that it does not work in headless chrome somehow?

Thank you very much for your help!

How to return grpc message results

I'm trying to create an Angular 4 webapp that displays the data retrieved in the grpc call. But, I'm having a heck of a time figuring out how to do it -- likely because I'm not very familiar with typescript/js. I'd appreciate a pointer on how I can get this to work.

Given the getBook example:

function getBook() {
  const getBookRequest = new GetBookRequest();
  getBookRequest.setIsbn(60929871);
  grpc.unary(BookService.GetBook, {
    request: getBookRequest,
    host: host,
    onEnd: res => {
      const { status, statusMessage, headers, message, trailers } = res;
      console.log("getBook.onEnd.status", status, statusMessage);
      console.log("getBook.onEnd.headers", headers);
      if (status === Code.OK && message) {
        console.log("getBook.onEnd.message", message.toObject());
        //document.write(message.toString());
      }
      console.log("getBook.onEnd.trailers", trailers);
      queryBooks();
    }
  });
}

I can see the json object being displayed in the console log, but I can't figure out how to return that data so I can bind it to an Angular component and display it in the browser. I've tried something like this:

function getBook(): object {
  var result: object = {};
  const getBookRequest = new GetBookRequest();
  getBookRequest.setIsbn(60929871);
  grpc.unary(BookService.GetBook, {
    request: getBookRequest,
    host: host,
    onEnd: res => {
      const { status, statusMessage, headers, message, trailers } = res;
      console.log("getBook.onEnd.status", status, statusMessage);
      console.log("getBook.onEnd.headers", headers);
      if (status === Code.OK && message) {
        console.log("getBook.onEnd.message", message.toObject());
        result = message.toObject()
      }
      console.log("getBook.onEnd.trailers", trailers);
      queryBooks();
    }
  });
  return result
}

But, result is always empty, which I gather is because the grpc.unary call is asynchronous. I've also tried creating an Observable, but I still ended up with an empty value.

I've been trying to figure this out for three days and I am at a loss. Any pointers on how to return the retrieved data so I can bind it in my browser would be much appreciated.

Debug tools in browser side?

Are there any handy debug tools which could be used to check data in req/res at browser side?

Chrome developer tools can't resolve gprc message, instead it always show "This request has no response data available".

And can't find any Chrome extension to resolve grpc message.

Websockets ?

Does this handle streaming over websockets for doing push upwards from a golang server ?

Dart support

I'll be that guy ;-)

A code generator for Dart web clients would be really nice...

General question about grpc-web

I understand that the browser is lacking a feature to fully support grpc protocol (that may arrive in 2 years). I deduce that my only way to request data from grpc server using a browser is to go through a gateway...

So the gateway needs to talk HTTP on one side, and GPRC on the other...correct?

So the MAJOR barrier to bring grpc to the web is to have a robust gateway (would it be written in go, or c++...), the browser library to send the requests would be as easy as using fetch in recent browsers...

If I'm correct on these points, I don't understand how the official grpc-web-gateway (not open-sourced yet) could be still not following the last grpc-web specs, and why this present grpcwebproxy seems to not be the "killer program" of this current repository?

I'm just trying to get some insights from the people who have a full picture of the whole situation, As I'm struggling to get a simple grpc unaryCall working over http....

Many thanks!

go get fails

go get -u github.com/improbable-eng/grpc-web/go/grpcwebproxy

../github.com/improbable-eng/grpc-web/go/grpcwebproxy/main.go:91: cannot use logger (type *"github.com/sirupsen/logrus".Entry) as type "github.com/grpc-ecosystem/go-grpc-middleware/vendor/github.com/sirupsen/logrus".Entry in argument to grpc_logrus.ReplaceGrpcLogger
../github.com/improbable-eng/grpc-web/go/grpcwebproxy/main.go:101: cannot use director (type func("golang.org/x/net/context".Context, string) (
"google.golang.org/grpc".ClientConn, error)) as type proxy.StreamDirector in argument to proxy.TransparentHandler
../github.com/improbable-eng/grpc-web/go/grpcwebproxy/main.go:103: cannot use logger (type *"github.com/sirupsen/logrus".Entry) as type *"github.com/grpc-ecosystem/go-grpc-middleware/vendor/github.com/sirupsen/logrus".Entry in argument to grpc_logrus.UnaryServerInterceptor
../github.com/improbable-eng/grpc-web/go/grpcwebproxy/main.go:103: cannot use grpc_prometheus.UnaryServerInterceptor (type func("golang.org/x/net/context".Context, interface {}, *"google.golang.org/grpc".UnaryServerInfo, "google.golang.org/grpc".UnaryHandler) (interface {}, error)) as type "github.com/grpc-ecosystem/go-grpc-middleware/vendor/google.golang.org/grpc".UnaryServerInterceptor in argument to grpc_middleware.WithUnaryServerChain
../github.com/improbable-eng/grpc-web/go/grpcwebproxy/main.go:105: cannot use grpc_middleware.WithUnaryServerChain(grpc_logrus.UnaryServerInterceptor(logger), grpc_prometheus.UnaryServerInterceptor) (type "github.com/grpc-ecosystem/go-grpc-middleware/vendor/google.golang.org/grpc".ServerOption) as type "google.golang.org/grpc".ServerOption in argument to "google.golang.org/grpc".NewServer
../github.com/improbable-eng/grpc-web/go/grpcwebproxy/main.go:107: cannot use logger (type *"github.com/sirupsen/logrus".Entry) as type *"github.com/grpc-ecosystem/go-grpc-middleware/vendor/github.com/sirupsen/logrus".Entry in argument to grpc_logrus.StreamServerInterceptor
../github.com/improbable-eng/grpc-web/go/grpcwebproxy/main.go:107: cannot use grpc_prometheus.StreamServerInterceptor (type func(interface {}, "google.golang.org/grpc".ServerStream, *"google.golang.org/grpc".StreamServerInfo, "google.golang.org/grpc".StreamHandler) error) as type "github.com/grpc-ecosystem/go-grpc-middleware/vendor/google.golang.org/grpc".StreamServerInterceptor in argument to grpc_middleware.WithStreamServerChain
../github.com/improbable-eng/grpc-web/go/grpcwebproxy/main.go:109: cannot use grpc_middleware.WithStreamServerChain(grpc_logrus.StreamServerInterceptor(logger), grpc_prometheus.StreamServerInterceptor) (type "github.com/grpc-ecosystem/go-grpc-middleware/vendor/google.golang.org/grpc".ServerOption) as type "google.golang.org/grpc".ServerOption in argument to "google.golang.org/grpc".NewServer

Server streaming not received on stream.send() but on return nil

Here is my proto:

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHelloStream (HelloRequest) returns (stream HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

And here is the server implementation:

func (s *server) SayHelloStream(in *proto.HelloRequest, stream proto.Greeter_SayHelloStreamServer) error {
	for j := 0; j <= 10; j++ {
		if err := stream.Send(&proto.HelloReply{Message: "Hello " + in.Name}); err != nil {
			return err
		}
		time.Sleep(1000 * time.Millisecond)
	}
	return nil
}
func main() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	grpcServer := grpc.NewServer()
	proto.RegisterGreeterServer(grpcServer, &server{})
	wrappedGrpc := grpcweb.WrapServer(grpcServer)

	srv := &http.Server{
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			wrappedGrpc.ServeHttp(resp, req)
		}),
	}
        // tls certs
        srv.Serve(lis)
}

Problem/Question:

  • In the browser (grpcweb), I receive the 10 messages from the server all at once, after 10 seconds.
  • In my go client (grpc), I correctly receive 1 message every 1 second.

Any idea where this problem could come from? Many thanks!
screenshot from 2017-05-05 22-29-11

Proxy HTTP only serving

Hello guys,
is the proxy capable of serving a HTTP only endpoint (no TLS) and route that to a classic no-TLS Java gRPC server?

Unable to run http:// (HTTP1.1) tests through BrowserStack

Currently the http:// tests (which test HTTP1.1) are flaky when run through BrowserStack on all browsers. The same tests locally work as expected. Hypothesis of the root cause is the BrowserStack tunnel that is used to proxy requests.

https:// tests (which test HTTP2) are unaffected

server_tls_client_ca_files flag supportted?

Is there already support for this flag?

my grpc server is running on localhost with a self-signed certificate (issued for localhost) but giving the rootCA.pem to grpcwebproxy doesn't work:

./bin/grpcwebproxy --server_tls_cert_file=/home/me/crt/localhost.crt --server_tls_key_file=/home/me/crt/localhost.key  --server_tls_client_ca_files=/home/me/crt/rootCA.pem --backend_addr=localhost:50052

complains:

Failed to dial localhost:50052: connection error: desc = "transport: x509: certificate signed by unknown authority"; please retry

Many thanks

Getting remote IP from a load balancer

Here's a fairly common thing I'm trying to do, but can't find docs to describe.

I'm running this gRPC app behind a load balancer, which sets an HTTP header of the remote IP address. How can I fetch that in the gRPC server for logging purposes? Will that still work with streaming?

Client compatible with grpc-java? Any example ?

Hi,

Is it possible to use the grpc-web client with the Java impl grpc-java (instead of the go server, basically) ?

Has anyone successfully managed to make both talk to each other ?

If so, would there be an example anywhere ?

Thanks a lot, kinda lost here.

Is Typescript totally necessary?

I am wondering if this can be made to use with plain ES5 javascript without the typescript bits. I tried for a while to integrate it in a normal ReactJS workflow but I could not do it without TS

Invalid buffer / grpc: received message length 105465393 exceeding the max size 4194304

Hello,

my preflight request is OK, but then I'm POSTing my proto message buffer using fetch to the grpcwebproxy using:

fetch('https://localhost:8443/package.service/Method', {
        method: 'POST',
        headers: new Headers({
          'Content-Type': 'application/grpc-web',
        }),
        body: GetInstrumentsRequest.encode(message).finish(),
      })

And the grpcwebproxy throws:

ERRO[0121] finished streaming call  error="rpc error: code = Internal desc = failed proxying s2c: rpc error: code = Internal desc = grpc: received message length 105465393 exceeding the max size 4194304" grpc.code=Internal grpc.method=GetInstruments grpc.service=package.service grpc.time_ns=352.723µs system=grpc

And my node-grpc logs:

D0411 17:19:02.735121065   20308 chttp2_transport.c:1212]    perform_stream_op[s=0x7f8594013828/3]: [COVERED] SEND_INITIAL_METADATA{key=3a 73 74 61 74 75 73 ':status' value=32 30 30 '200', key=63 6f 6e 74 65 6e 74 2d 74 79 70 65 'content-type' value=61 70 70 6c 69 63 61 74 69 6f 6e 2f 67 72 70 63 'application/grpc', key=67 72 70 63 2d 65 6e 63 6f 64 69 6e 67 'grpc-encoding' value=69 64 65 6e 74 69 74 79 'identity', key=67 72 70 63 2d 61 63 63 65 70 74 2d 65 6e 63 6f 64 69 6e 67 'grpc-accept-encoding' value=69 64 65 6e 74 69 74 79 2c 64 65 66 6c 61 74 65 2c 67 7a 69 70 'identity,deflate,gzip'} SEND_TRAILING_METADATA{key=67 72 70 63 2d 73 74 61 74 75 73 'grpc-status' value=31 33 '13', key=67 72 70 63 2d 6d 65 73 73 61 67 65 'grpc-message' value=49 6c 6c 65 67 61 6c 20 62 75 66 66 65 72 'Illegal buffer'}
I0411 17:19:02.735298783   20334 chttp2_transport.c:983]      HTTP:3:HDR:SVR: :status: 200
I0411 17:19:02.735306943   20334 chttp2_transport.c:983]     HTTP:3:HDR:SVR: content-type: application/grpc
I0411 17:19:02.735314917   20334 chttp2_transport.c:983]     HTTP:3:HDR:SVR: grpc-encoding: identity
I0411 17:19:02.735323738   20334 chttp2_transport.c:983]     HTTP:3:TRL:SVR: grpc-status: 13
D0411 17:19:02.735351624   20334 chttp2_transport.c:863]     complete_closure_step: 0x7f8594012680 refs=1 flags=0x0002 desc=send_initial_metadata_finished err={"created":"@1491902342.735336170","description":"Attempt to send initial metadata after stream was closed","file":"../src/core/ext/transport/chttp2/transport/chttp2_transport.c","file_line":1084}

Any idea where the problem could come from?
Many thanks

Debugger Interface Proposal

This is a proposal for a debugger interface in grpc-web-client. The intent is to make the current console based debugger extensible and allow clients to implement their own version of the debugger. The driving factor is to improve the currently tedious debugging experience where grpc-web requests shown in the Network tab are not properly serialized and hard to inspect. The proposed interface allows for an extension to be build for Chrome & Firefox to expose grpc-web requests similarly to the Network tab in Chrome.

This document is an aimed athelping shape the debugger interface and get feedback on the proposed changes.

Interface

The interface proposed consists of a DebuggerProvider and a Debugger.

DebuggerProvider

The provider is responsible for supplying an instance of a debugger to be used in a particular request. Additionally, it can implement supplemental functionality such as tracking of requests or recording request history.

export interface DebuggerProvider {
    // Obtain an instance of a debugger.
    // A convenience proxy allowing the provider to implement additional functionality
    // such as tracking history of requests
    getInstanceForRequest(id: number): Debugger;
}

Debugger

The Debugger implements callback methods for the lifecycle of a grpc-web request.

// Implements callbacks for grpc-web request/response lifecycle events
export interface Debugger {
    // Called just before a request is fired, called only once
    onRequestStart(host: string, method: MethodDefinition): void;

    // Called just before a request is fired, called only once
    onRequestHeaders(headers: BrowserHeaders): void;

    // Called with the request payload, potentially called multiple times with request streams
    onRequestMessage(payload: Message): void;

    // Called when response headers are available, called once multiple times
    // This is a low level method intended to debug byte serialization
    onResponseHeaders(headers: BrowserHeaders, httpStatus: number): void;

    // Called with each received chunk
    onResponseChunk?(chunk: Chunk[], chunkBytes: Uint8Array): void;

    // Called with each response message, called multiple times with response streams
    onResponseMessage(payload: Message): void;

    // Called with response trailers, called once
    onResponseTrailers(metadata: BrowserHeaders): void;

    // Called when a request completes, called once
    onResponseEnd(grpcStatus: Code | null): void;

    // Called with any error occuring in the flow, potentially called multiple times
    onError(code: Code, err: Error): void;
}

Example

export class ConsoleDebuggerProvider implements DebuggerProvider {

    getInstanceForRequest(id: number): Debugger {
        return new ConsoleDebugger(id);
    }

}

export class ConsoleDebugger implements Debugger {

    readonly id: number;

    constructor(id: number) {
        this.id = id;
    }

    onRequestStart(host: string, method: MethodDefinition): void {
        debug(`gRPC-Web #${this.id}: Making request to ${host} for ${method.service.serviceName}.${method.methodName}`, method);
    }

    onRequestHeaders(headers: BrowserHeaders): void {
        debug(`gRPC-Web #${this.id}: Headers:`, headers);
    }

    onRequestMessage(payload: Message): void {
        debug(`gRPC-Web #${this.id}: Request message:`, payload.toObject());
    }

    onResponseHeaders(headers: BrowserHeaders, httpStatus: number) {
        debug(`gRPC-Web #${this.id}: Response headers:`, headers, 'HTTP Status:', httpStatus);
    }

    onResponseMessage(payload: Message): void {
        debug(`gRPC-Web #${this.id}: Response message:`, payload.toObject());
    }

    onResponseTrailers(metadata: BrowserHeaders): void {
        debug(`gRPC-Web #${this.id}: Response trailers:`, metadata);
    }

    onResponseEnd(grpcStatus: Code | any): void {
        debug(`gRPC-Web #${this.id}: Finished with status:`, grpcStatus);
    }

    onError(code: Code, err: Error): void {
        debug(`gRPC-Web #${this.id}: Error. Code:`, code, 'Error:', err);
    }

}

Registration of Debuggers

In order to allow clients a custom implementation of a debugger, an API must be exposed to allow the registration, retrieval and de-registration of debuggers. The following section discusses possible scenarios of achieving this. When addressing this problem, it is important to consider a scenario where an implementation is not immediately available when an application executes, ie. extension loaded when a user opens developer tools.

Explict registration in client code

In this option, the client is required to explictly include any required debuggers in user code. An example might look as follows:

grpc.registerDebugger(new ConsoleDebuggerProvider());
// extension debugger exposes itself on the window as window.__GRPC_WEB_DEVTOOLS__
if ((window as any).__GRPC_WEB_DEVTOOLS__) {
    grpc.registerDebugger((window as any).__GRPC_WEB_DEVTOOLS__);
}
  • (+) The user is direct control of what extension runs in a given application
  • (-) An extension loaded at some point in the future will not be able to register itself (can be solved by exposing grpc on the window object)

Expose a global with a list of Debuggers

Grpc-web-client reads from a specific global variable containing a list of debuggers to use. This can be made to work in NodeJS as well.

(function (debuggers) {
    debuggers.push(new ShinyDebuggerProvider());
    window.__GRPC_WEB_DEBUGGERS__ = debuggers;
})(window.__GRPC_WEB_DEBUGGERS__ || []);
  • (+) An extension loaded at a later stage can attach itself without the need to have grpc on the global scope
  • (+) No need for client code to attach an extension debugger
  • (-) Pollutes global scope
  • (-) Is stringly typed

Lifecycle callback

In order to implement the debugger callbacks, we dispatch individual callbacks inside grpc-web-client's invoke method. For example:

const transportOptions: TransportOptions = {
    // ...
    onHeaders: (headers: Metadata, status: number) => {
        detach(() => debugger.onRequestHeaders(headers));
        // rest omitted
    }
    // ...
}

To simplify execution of callbacks for multiple debuggers we can utilize a proxy implementation of the Debugger interface which dispatches to all registered debuggers. Example here

Debug property

Currently, we use a debug property on a request invocation to determine if we should log debug information. This flag would become obsolete and could be removed/depracted.

Working examples

What do you think? Suggestions & improvements are extremely welcome.
@jonnyreeves @MarcusLongmuir @mwitkow

question: streaming

thanks for doing this.

I noticed that it supports the 1 request, n responses pattern.
So, if i want to notify a client for whatever reason, i can use this pattern. Its basically exactly like a web socket in terms of patterns at least ?

What would be a use case example of what this library does not support in terms of "client streaming" ?

grpcwebproxy problems - CORS and Go panic

Hi,
I created a normal gRPC service (not wrapped) based on the exampleserver to test the grpcwebproxy.
(Here's the source for the gRPC service: https://gist.github.com/pieterlouw/b773b16bd488ac3441ca32a739edb4e9)

I then run the grpcwebproxy with flag --backend_addr=localhost:9090

I start the example typescript and then get the following in the console.log (which look like it's not handling the CORS and there's a HTTP 500):

dev-server.js:49 [HMR] Waiting for update signal from WDS...
:8443/examplecom.library.BookService/GetBook Failed to load resource: the server responded with a status of 500 ()
:8443/examplecom.library.BookService/GetBook Failed to load resource: the server responded with a status of 500 ()
localhost/:1 Fetch API cannot load https://localhost:8443/examplecom.library.BookService/GetBook. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:8082' is therefore not allowed access. The response had HTTP status code 500. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
index.ts:22 getBook.onEnd 13 Response closed without headers BrowserHeaders
:8443/examplecom.library.BookService/QueryBooks Failed to load resource: the server responded with a status of 500 ()
:8443/examplecom.library.BookService/QueryBooks Failed to load resource: the server responded with a status of 500 ()
localhost/:1 Fetch API cannot load https://localhost:8443/examplecom.library.BookService/QueryBooks. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://localhost:8082' is therefore not allowed access. The response had HTTP status code 500. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
bundle.js:4889 queryBooks.onEnd 13 Response closed without headers BrowserHeaders
bundle.js:4939 [WDS] Hot Module Replacement enabled.
bundle.js:4943 [WDS] Disconnected!

I looked around in the main.go and changed the servingServer Handler to this:

servingServer := http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			wrappedGrpc.ServeHTTP(resp, req)
			//if wrappedGrpc.IsGrpcWebRequest(req) || wrappedGrpc.IsAcceptableGrpcCorsRequest(req) {
			//	wrappedGrpc.HandleGrpcWebRequest(resp, req)
			//}
			//resp.WriteHeader(http.StatusNotImplemented)
		}),
	}

When I rebuild and ran the grpcwebproxy and ran the typescript I received the following panic from grpcwebproxy:
panic: interface conversion: *proxy.frame is not proto.Message: missing method ProtoMessage

Full trace:

INFO[0000] listening for http_tls on: [::]:8443
INFO[0000] listening for http on: [::]:8080
panic: interface conversion: *proxy.frame is not proto.Message: missing method ProtoMessage

goroutine 46 [running]:
google.golang.org/grpc.protoCodec.marshal(0x867520, 0xc4201baa00, 0xc4201be5b0, 0x934a4, 0x0, 0x1, 0x5fdd7cfcac48351c, 0xb70ce0)
        $GOPATH/src/google.golang.org/grpc/codec.go:73 +0x45
google.golang.org/grpc.protoCodec.Marshal(0x867520, 0xc4201baa00, 0x410dc2, 0xc4201baa60, 0x20, 0x18, 0x8dcec0)
        $GOPATH/src/google.golang.org/grpc/codec.go:88 +0x73
google.golang.org/grpc.(*protoCodec).Marshal(0xbcd258, 0x867520, 0xc4201baa00, 0x8a1980, 0xc4201baa60, 0x0, 0x7f4c6f920960, 0x0)
        <autogenerated>:35 +0x59
google.golang.org/grpc.encode(0xb74c20, 0xbcd258, 0x867520, 0xc4201baa00, 0x0, 0x0, 0x0, 0x0, 0xc4201dce11, 0x1e, ...)
        $GOPATH/src/google.golang.org/grpc/rpc_util.go:254 +0x2f9
google.golang.org/grpc.(*clientStream).SendMsg(0xc4201d4b00, 0x867520, 0xc4201baa00, 0x0, 0x0)
        $GOPATH/src/google.golang.org/grpc/stream.go:352 +0x11a
github.com/mwitkow/grpc-proxy/proxy.(*handler).forwardServerToClient.func1(0xb77020, 0xc4201ba8c0, 0xc4201ac9c0, 0xb77080, 0xc4201d4b00)
        $GOPATH/src/github.com/mwitkow/grpc-proxy/proxy/handler.go:155 +0xb3
created by github.com/mwitkow/grpc-proxy/proxy.(*handler).forwardServerToClient
        $GOPATH/src/github.com/mwitkow/grpc-proxy/proxy/handler.go:160 +0x89

Pieter

multiple concurrent requests

As described in README:

The gRPC semantics encourage you to make multiple requests at once.

What's the recommended approach to make multiple concurrent requests with grpc-web client?

For example, axios provide a way to deal with multipe concurrent requests like:

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // Both requests are now complete
  }));

I checked gprc.d.ts and didn't find a clue to add a handler function for when all request are complete. So for make multiple requests at once in grpc-web client, does it mean to invoke grpc.invoke() inside of some other framework functions? Could you share your ideas for the best practise of making multiple requests at once in grpc-web client. Thank you very much.

Passing token based auth through proxy?

Hello, I'm working with @pjm17971 and this is a follow up to #79. Thanks for grpc-web -- I think it's fantastic idea and we'd really love to use it in our project.

We're trying to use the grpc-web proxy to talk to an gRPC server that is fronted by Google Cloud Endpoints -- I've attached a diagram. We are using JWT tokens for authentication. It seems like the token isn't making it through the grpcwebproxy. I've looked at the code and I can't see why that isn't getting passed through.

Do you think the JWT token should be making it through to the backend server? Is there someplace I'd need to add something to help with that? I'm totally fine adding some code but I'm not sure I fully get how the proxy works and so I'm not sure if that would be an adjustment to the grpcwebproxy code or perhaps to the grpc-proxy library. Any pointer you could give would be greatly appreciated!

esp

How to do user authentication with github?

I need to authenticate user with Github. But I am having difficult time in understanding how to do it in grpc-web. I google it for several times and asked in stackoverflow, but no luck. I will appreciate if anyone can give me an example or any sort of help.

Example not work. Cannot find package

Hi. I try run example.
I do:

git clone https://github.com/improbable-eng/grpc-web
cd grpc-web/example/

# then I install go from brew and set:
# export GOPATH=$HOME/work
# export GOBIN=$HOME/work/bin

npm install
npm run get_go_deps
npm start

and I get the error:

[0] go/exampleserver/exampleserver.go:18:2: cannot find package "github.com/improbable-eng/grpc-web/go/grpcweb" in any of:
[0] 	/usr/local/Cellar/go/1.9.2/libexec/src/github.com/improbable-eng/grpc-web/go/grpcweb (from $GOROOT)
[0] 	/Users/ezheg/work/src/github.com/improbable-eng/grpc-web/go/grpcweb (from $GOPATH)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.