Giter VIP home page Giter VIP logo

protobuf's Introduction

GopherJS Bindings for ProtobufJS and gRPC-Web

Circle CI Go Report Card GoDoc Gitter chat

gRPC-Web radio operator Gopher by Egon Elbre (@egonelbre) gRPC-Web radio operator Gopher by Egon Elbre (@egonelbre)

Users

A list of some of the users of the library. Send me a message on @jbrandhorst on Gophers Slack if you wish to be added to this list

Getting started

The easiest way to get started with gRPC-Web for Go is to clone the boilerplate repo and start playing around with it.

Components

This is a GopherJS client code generator for the Google Protobuf format. It generates code for interfacing with any gRPC services exposing a gRPC-Web spec compatible interface. It uses jspb and grpcweb. It is the main entrypoint for using the protobuf/gRPC GopherJS bindings.

This is a simple GopherJS binding around the npm google-protobuf package. Importing it into any GopherJS source allows usage of ProtobufJS functionality.

This is a GopherJS binding around the Improbable gRPC-Web client. It is not intended for public use.

Contributions

Contributions are very welcome, please submit issues or PRs for review.

Demo

See the example repo and the demo website for an example use of the Protobuf and gRPC-Web bindings.

protobuf's People

Contributors

johanbrandhorst avatar mame82 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

protobuf's Issues

Might be worth updating this now that caddy 2 is stable and tinygo can compile to WASM

I use envoy, but have projects where i need pure baremetal golang and browsers.

Tinygo is now working with go modules and JSON via a hack that will be sorted soon i think.
Caddy v2 is pretty powerful now.
QUIC Is also taking off. Safari beta has it turned on finally :)
Then there is gioui.org:https://gioui.org/

So it feels like its good timing to revive this project. Sort of right time and place.

Tinygo working, instead of gopherjs. Did require quite some hacks.. But i think the tinygo team are getting it to the point that these hacks wont be needed
https://github.com/vugu/vugu
https://github.com/vugu-examples/tinygo
cd /github.com/vugu-examples/tinygo && go run devserver.go

GIO running using gopherjs WASM
https://git.sr.ht/~eliasnaur/gio/tree/master/example/kitchen/kitchen.go

go get -u github.com/shurcooL/goexec
go install: git.sr.ht/~eliasnaur/gio/cmd/gogio/main.go 
cd $(GOPATH)/src/git.sr.ht/~eliasnaur/gio/example/kitchen && gogio -target js gioui.org/example/kitchen .
cd $(GOPATH)/src/git.sr.ht/~eliasnaur/gio/example/kitchen && goexec -quiet 'http.ListenAndServe(":8080", http.FileServer(http.Dir("www")))'

All the other examples work fine in web, Desktops and mobiles btw.

Caddy QUIC
https://ma.ttias.be/how-run-http-3-with-caddy-2/

Caddy AutoCert

protoc-gen-gopherjs: Consider migrating to https://github.com/lyft/protoc-gen-star

https://github.com/lyft/protoc-gen-star is an attempt at making it easier to write protoc plugins, building on protoc-gen-go, much like this repo does.

However, because the current protoc-gen-gopherjs is a fork of protoc-gen-go, there is some amount of work involved in maintaining it and merging fixes from protoc-gen-go. Migrating to https://github.com/lyft/protoc-gen-star might make this maintenance much easier as that work is left to the upstream repo.

Add js tags to protobuf-generated types

In my use case, I'd like to have a model that is isomorphic and handlers against this model mutate the model. In a normal world, I'd have the model once in JavaScript with mutators written in JavaScript, then I'd have the model again in Go with Go-based mutators.

In a Protobuf/GopherJS world, I'd like to define my model in a proto, generate the appropriate code for the model, then ship an initalState from Go code to the JS-client as a protobuf-on-the-wire which is deserialized and kept as a JS model (used in this case as a React model). I'm quite happy with JSX and various JavaScript UI-isms, so I don't want to use Go for React completely.

When a change to the model is needed, I want the server to create an event that mutates the model, then send that event to the browser. The handler for that event on the JS client would make the same mutation (because it would a GopherJS compiled version of the Go code).

It's my understanding that to turn the Go object to a JS object, I would need to include a *js.Object and the fields would need to be tagged:

   Name string `js:"name"`

..it seems the protoc in gopherjs mode doesn't do this; am I missing something? Am I expected to use pairs of Marshal methods in Go/JS to shovel the protobuf-serialized bytes between the languages? That feels like it'd be much slower, but I'm not really sure what heavy-lifting happens in GopherJS otherwise?

Suggestion: Allow enabeling WebSocket transport, even if rpc isn't BiDi or server stream

As lately discussed on twiiter, I haven a use case where a server stream gRPC call is used to deliver events after registration. This ultimately ends up in a never ending server stream, which floods memory in case the DefaultTransportFactory decides to communicate based on XHR as the "unfinished" HTTP response body accumulates in memory. On the other hand, for WebSocket based transport this issue seems to be none existend.

Here's an excerpt of the relevant part of my proto

syntax = "proto3";

package P4wnP1_grpc;

service P4WNP1 {
	 ... snip ...

	//Events
	rpc EventListen(EventRequest) returns (stream Event) {}
}

/* Events */
message EventRequest {
	int64 listenType = 1;
}

message EventValue {
	oneof val {
		string tstring = 1;
		bool tbool = 2;
		int64 tint64 = 3;
	}
}

message Event {
	int64 type = 1;
	repeated EventValue values = 2;
}
... snip ...

The relevant part of the go file generated with your compiler plugin for gopherjs

func (c *p4WNP1Client) EventListen(ctx context.Context, in *EventRequest, opts ...grpcweb.CallOption) (P4WNP1_EventListenClient, error) {
	srv, err := c.client.NewClientStream(ctx, false, true, "EventListen", opts...)
	if err != nil {
		return nil, err
	}

	err = srv.SendMsg(in.Marshal())
	if err != nil {
		return nil, err
	}

	return &p4WNP1EventListenClient{srv}, nil
}

As the isClientStream option in the generated source is set to false (which is correct in this context),
your implementation decides to disable WebsocketTransportFactory.

This is because the responsible method newProperties() from here https://github.com/johanbrandhorst/protobuf/blob/master/grpcweb/properties.go#L36 decides on WebSocket usage based on the fact if the gRPC call uses client-streaming, here:
https://github.com/johanbrandhorst/protobuf/blob/master/grpcweb/clientstream.go#L53

In order to overcome this behavior, I changed the generated gopherjs code to set isClientStreaming to true, even if the real method uses server streaming, only.

Manual change of generated code:

func (c *p4WNP1Client) EventListen(ctx context.Context, in *EventRequest, opts ...grpcweb.CallOption) (P4WNP1_EventListenClient, error) {
	srv, err := c.client.NewClientStream(ctx, true, true, "EventListen", opts...) //changed isClientStreaming to true to enable WebSocket usage
	if err != nil {
		return nil, err
	}

	err = srv.SendMsg(in.Marshal())
	if err != nil {
		return nil, err
	}

	return &p4WNP1EventListenClient{srv}, nil
}

After adding a filter criteria to the server, which redirects HTTP requests to the wrapped gRPC server (with WS support enabled), based on the presence of request header field "Sec-Websocket-Protocol: grpc-websockets like this ...

	//Wrap the server into a gRPC-web server
	grpc_web_srv := grpcweb.WrapServer(s, grpcweb.WithWebsockets(true)) //Wrap server to improbable grpc-web with websockets
	//define a handler for a HTTP web server using the gRPC-web proxy
	http_gRPC_web_handler := func(resp http.ResponseWriter, req *http.Request) {
		if strings.Contains(req.Header.Get("Content-Type"), "application/grpc") ||
			req.Method == "OPTIONS" ||
			strings.Contains(req.Header.Get("Sec-Websocket-Protocol"), "grpc-websockets") {
			fmt.Printf("gRPC-web req:\n %v\n", req)
			grpc_web_srv.ServeHTTP(resp, req) // if content type indicates grpc or REQUEST METHOD IS OPTIONS (pre-flight) serve gRPC-web
		} else {
			fmt.Printf("legacy web req:\n %v\n", req)
			http.FileServer(http.Dir((absWebRoot))).ServeHTTP(resp, req)
		}
	}

... the server-streaming gRPC call to EventListen is done via WebSocket without visible issues.

I haven't done much testing on this, especially not on memory consumption for an endless stream, compared to plain XHR or mozilla based XHR. Anyway, adding an option to the generated code, to allow gRPC calls via WebSockets seems to has its use cases, even if the RPC call isn't BiDi or client-streaming.

The ...grpcweb.CallOption parameters accepted by every client side RPC method, seems to be a feasible way to achieve this.

Maybe this could be called ....

EventListen(ctx context.Context, in *EventRequest, opts ...grpcweb.CallOption) (P4WNP1_EventListenClient, error)

... like this in the end:

... snip ...
evStream, err := Client.Client.EventListen(ctx, &pb.EventRequest{ListenType: common.EVT_LOG}, grpcweb.CallOptionUseWebsocket{true}) // <-- this would be great
... snip ...

Does not work with Node.js

Works in Chrome browser. Does not works with Node.js. Output:

rpc error: code = Unknown desc = JavaScript error: Cannot read property 'protocol' of undefined

After disabling the error handling, I see the error is in grpc.inc.js, when client.start() is called.

Generated proto file has wrong imports

import google_protobuf "google/protobuf"
import google_protobuf1 "google/protobuf"
import google_protobuf2 "google/protobuf"

instead of protoc:

import any "github.com/golang/protobuf/ptypes/any"
import empty "github.com/golang/protobuf/ptypes/empty"
import timestamp "github.com/golang/protobuf/ptypes/timestamp"

Unable to run simple gopherjs demo with protoc compiled grpc-web

After opening the page nothing happens except console error:

web.js:2535 Uncaught TypeError: Cannot read properties of undefined (reading 'length')
    at new Map (web.js:2535:46842)
    at $internalize (web.js:2049:36)
    at init (gopherjs__runtime.go:78:3)
    at Object.$init (web.js:2528:3)
    at web.js:3623:22
    at web.js:3627:4

The steps to reproduce:

git clone https://gitlab.com/softkot/gopher_grpc_web.git
cd gopher_grpc_web
make init
make proto
make serve

after seenig

alias go="go1.17.9"
export GOPHERJS_GOROOT=/home/kot/sdk/go1.17.9
gopherjs serve gitlab.com/softkot/gopher_grpc_web
serving at http://localhost:8080 and on port 8080 of any available addresses

open http://localhost:8080 and enable developer mode on browser. Navigate to console.

image

P.S.

gopherjs cersion v1.17.2
go version 1.17.9
protoc-gen-gopherjs virsion v0.7.1

Other combinations produce the same results

adapting the concept to interface with Flutter

I am using grpc with dart alot and flutter.
I also use this code and gropherjs.
But i am finding flutter a compelling solution where web just does not cut it.

so I am playing around with ideas for codegen from the protobufs all the way up to lots of client code.

I am not asking for this, but more raising the aspect that doing a similar thing with Flutter is a really good fit. For example. Flutter heavily uses the Reflux pattern to get stuff done.

Here is a really good boiler plate to show what i mean:
https://github.com/FranPitri/flutter_redux_boilerplate

Here is the codegen approach:
https://github.com/davidmarne/built_redux
BTW built IS the default way of naming stuff in the dart world that is used for codegen.

NONE of the above are hooked up with GRPC YET !
https://github.com/grpc/grpc-dart
Getting golang and dart / flutter talking over pure GRPC is really easy. Been doing that for a while.
Its just the normal protoc calls and your flying.

Update grpc-web-client version

The most significant change is that BrowserHeaders has been renamed Metadata. This would be a good time to rename the browserheaders package.

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.