Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support running grpc-web in React Native #141

Open
begoat opened this issue Feb 10, 2018 · 36 comments
Open

Support running grpc-web in React Native #141

begoat opened this issue Feb 10, 2018 · 36 comments
Labels
enhancement help-wanted We'd like your help! needs-pr waiting-on-submitter Waiting on original poster action

Comments

@begoat
Copy link

begoat commented Feb 10, 2018

I tried grpc-web today. But unfortunately errors occurred when send request.
The error log is cannot resolve http module from node_module/grpc-web-client/dist/transports/nodeHttp.js. The version of grpc-web-client is 0.5.0 which is uploaded 3 days ago.

I try to install http npm package, but another error shows that the node_modules/http/package.json was successfully found but this package itself specifies a 'main' module field that couldn't be found.

I also try to install some browserified package instead for example stream-http and change the nodeHttp.js file's requirement, but seems that I will do many serious tweak.

Could someone give me some more suggestions? :)

@easyCZ
Copy link
Contributor

easyCZ commented Feb 10, 2018

Hi @begoat,

Thanks for raising. Will try to look into it today. Is there anything specific about your react-native setup or is it similar to the standard react-native setup?

@begoat
Copy link
Author

begoat commented Feb 10, 2018

I setup typescript workflow following this tutorial
Thanks in advance. :)

@easyCZ
Copy link
Contributor

easyCZ commented Feb 11, 2018

Hi @begoat,

I can reproduce the problem but I do not have a fix yet. It appears that react-native does not allow the http or https modules from node to be imported. Because they are declared as top level imports rather than imported lazily, react-native errors as not being able to find them. I'll continue digging into.

@begoat
Copy link
Author

begoat commented Feb 11, 2018

:)

@RobIsHere
Copy link

Hi! I'm also looking into using this lib with react native.

React native is more browser-like than node-like. It should support fetch and XMLHttpRequest, see https://facebook.github.io/react-native/docs/network.html

Node transport is the last one tried by the factory. So it should select fetch which is first and as a fallback possibly XMLHttpRequest, but not node http.

The quesition is: is there a problem with the detection code itself, or is the detection code failing because of some missing sub-feature of fetch/XMLHttpRequest in RN?

Did you two find out more during debugging? Any hints, where the problem is?

@rogchap
Copy link

rogchap commented Feb 27, 2018

The issue is with RN. It's fetch API implementation does not support streaming: facebook/react-native#9629

@RobIsHere
Copy link

Thank you for the hint. I guess, they don't support overrideMimeType, too.

So the remaining options are websockets like in #137 with polling fallback. Or binding the native platform clients of grpc in RN.

@easyCZ
Copy link
Contributor

easyCZ commented Feb 27, 2018

The issue is two fold.

  1. RN appears to traverse the import tree and flags up any missing imports. Because the imports are traversed we reach a statement attempting to import http which is not available on RN. An error is displayed.

  2. The fetch transport does not fully support the features used by grpc-web as mentioned above. Websockets should help alleviate this problem.

@easyCZ easyCZ changed the title problems come across when run it in react-native Cannot resolve module 'http' in react-native Feb 27, 2018
@rogchap
Copy link

rogchap commented Feb 27, 2018

I would not recommend Websockets for Mobile; it drains your battery really fast. One benifits of gRPC is its efficiency I.e better on your battery. The best option for RN is to bind to the native gRPC implementations.

@begoat
Copy link
Author

begoat commented Feb 28, 2018

Yeah, currently I use both grpc-java and grpc-objectiveC in my react-native project instead. It won't take too much time to implement them and it works well.

@linjson
Copy link

linjson commented Apr 8, 2018

@easyCZ, Did you mean grpc-web wouldn't used on react-native?

@MikeSilvis
Copy link

@easyCZ can we look at migrating away from this http client and into something that react-native will support?

@RobIsHere
Copy link

When I did look into this there has been absolutely no way to support grpc in RN. Every option I checked did not work because details have not been implemented in RN.
Although they are writing that they support the necessary things in their docs, there are issues open that say: detail xyz is implemented different from the spec/not implemented.
So you would probably have to send PRs zo RN first.
I just want to throw this hint into the discussion so you can check up front and not when you‘ll have spent a lot of time on this 😉

@MikeSilvis
Copy link

Thanks Rob!

@MikeSilvis
Copy link

@RobIsHere
Copy link

No. But the readme says adapted from stream-http which say „It tries to match Node's API and behavior as closely as possible, but some features aren't available, since browsers don't give nearly as much control over requests.“
That’s the problem. RN stuff works like browsers.
Grpc-web would be the only way to do it. But as I understood that‘s not yet available.

@jonbretman
Copy link

@begoat @easyCZ After many frustrating hours I got this library working in React Native yesterday using a mix of https://github.com/tradle/rn-nodeify and the following custom transport (based on the xhr one).

As already noted React Native has support for the Fetch API but does not support response.body.getReader() or response.arrayBuffer() so there is no way to get the response as raw bytes. I thought I'd be able to get it working with response.blob() and then FileReader.readAsDataURL but I couldn't seem to get the base64 string back into a Uint8Array without it being corrupted.

The transport below is based on the XHR transport with the following changes:

  • removed overrideMimeType as this is not supported in React Native
  • responseType is set to 'arraybuffer'
  • not listening for the 'progress' event - this never seems to fire
  • due to the previous point the response body is read in the 'loadend' handler
import { Metadata } from 'grpc-web-client/dist/metadata';
import {
    Transport,
    TransportOptions,
} from 'grpc-web-client/dist/transports/Transport';
import detach from 'grpc-web-client/dist/detach';

declare const XMLHttpRequest: any;

class XHR implements Transport {
    options: TransportOptions;
    xhr: any = null;
    metadata: Metadata | null = null;

    constructor(transportOptions: TransportOptions) {
        this.options = transportOptions;
    }

    onLoadEvent() {
        const result = new Uint8Array(this.xhr.response);
        detach(() => {
            this.options.onChunk(result);
        });
        detach(() => {
            this.options.onEnd();
        });
    }

    onStateChange() {
        if (this.xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
            detach(() => {
                this.options.onHeaders(
                    new Metadata(this.xhr.getAllResponseHeaders()),
                    this.xhr.status
                );
            });
        }
    }

    sendMessage(msgBytes: Uint8Array) {
        this.xhr.send(msgBytes);
    }

    finishSend() {}

    start(metadata: Metadata) {
        this.metadata = metadata;
        const xhr = new XMLHttpRequest();
        this.xhr = xhr;
        xhr.open('POST', this.options.url);
        (xhr as any).responseType = 'arraybuffer';
        this.metadata.forEach((key, values) => {
            xhr.setRequestHeader(key, values.join(', '));
        });
        xhr.addEventListener('readystatechange', this.onStateChange.bind(this));
        xhr.addEventListener('loadend', this.onLoadEvent.bind(this));
        xhr.addEventListener('error', (err: any) => {
            detach(() => {
                this.options.onEnd(err.error);
            });
        });
    }

    cancel() {
        this.xhr.abort();
    }
}

export default function xhrTransport(options: TransportOptions): Transport {
    return new XHR(options);
}

Hope this helps someone 😄

@jonnyreeves
Copy link
Contributor

jonnyreeves commented Aug 21, 2018 via email

@jonbretman
Copy link

@jonnyreeves Yeh sure, probably won't have time for another week or so though. In the meantime I will continue to test out my solution on the app I'm working on. Haven't tried it on Android yet... 😟

@johanbrandhorst johanbrandhorst changed the title Cannot resolve module 'http' in react-native Support running grpc-web in React Native Sep 9, 2018
@johanbrandhorst johanbrandhorst added help-wanted We'd like your help! enhancement labels Sep 9, 2018
@johanbrandhorst
Copy link
Contributor

Lets recap; with the transport provided by @jonbretman, we could add support for React Native? Is that correct? Does anything else need to be done?

@johanbrandhorst johanbrandhorst added waiting-on-submitter Waiting on original poster action needs-pr labels Sep 9, 2018
@linjson
Copy link

linjson commented Sep 10, 2018

@johanbrandhorst, that's correct for support in React Native

@jonbretman
Copy link

jonbretman commented Sep 12, 2018 via email

@johanbrandhorst
Copy link
Contributor

@jonbretman that is very exciting to hear. Would you be interested in contributing this transport to the project?

@jonbretman
Copy link

jonbretman commented Sep 12, 2018 via email

@john-osullivan
Copy link

Hey all, you seem to be some of the only ones running into my issue. Currently trying to use web3-providers-http in a React-Native app, but the build fails because the http module can't be imported. When I install it directly, the corresponding folder is added to node_modules, but the main index.js file is not installed, so it fails.

Are there really no work-arounds to let us use that module? Having to rewrite this provider to use a different request solution would be a real drag.

@jonnyreeves
Copy link
Contributor

@john-osullivan #265 provides a solution by extracting the Node HTTP Transport out from the core project and into a separate module.

jonny-improbable pushed a commit that referenced this issue Oct 27, 2018
Following on from the conversation in #261 this PR allows the user to configure the behavior of a given Transport, separate from the pre-existing TransportOptions interface which deals with passing request state (ie: host, url, callbacks, etc).

To facilitate this change, the transport option passed to the unary, invoke and client functions is now expected to meet the new grpc.TransportFactory interface, ie: a func which can accept zero or more args and will return a grpc.Transport instance.

```
grpc.unary(BookService.GetBook, {
    request: getBookRequest,
    host: host,
    transport: grpc.HttpTransport({ credentials: 'include' }),
    onEnd: ( /*...*/ )
});
```

Note this results in a breaking change for anyone currently using the Websocket Transport; instead of specifying:

```
transport: gprc.WebsocketTransportFactory
```

They would instead now call:

```
transport: grpc.WebsocketTransport()
```

This change also spurred me to to fix #191 and #141 by extracting the 'Node HTTP' transport out from the gprc-web-client package and into a new module: grpc-web-node-http-client, which will be published to npm separately.

As one thing leads to another, extracting node-grpc-web-node-http-transport led to implement grpc.setDefaultTransport() which allows the user to specify which TransportFactory` should be .invoked if none is supplied in the options.

## Breaking Changes
transport option passed to unary, invoke and client is now expected to by an instance of Transport, not a reference to a function which will return a Transport when invoked.
grpc-web-client no longer auto-selects a suitable transport in a NodeJS environment; users of grpc-web-client in a NodeJS environment will need to depend upon grpc-web-node-http-transport and make use of grpc.setDefaultTranpsport().
@bourquep
Copy link

bourquep commented Feb 2, 2019

FYI, I'm able to use @improbable-eng/grpc-web in React Native by using the @improbable-eng/grpc-web-node-http-transport transport.

In order for that transport to work in RN, I had to:

  • Add the following packages:
    • buffer
    • stream-http
    • https-browserify
  • Add this to rn-cli-config.js:
  extraNodeModules: {
    buffer: require.resolve('buffer/'),
    http: require.resolve('stream-http'),
    https: require.resolve('https-browserify')
  }
  • Add this to my top-level index.js file:
global.Buffer = require('buffer').Buffer;

My main source for getting to this result was: https://github.com/parshap/node-libs-react-native

@easyCZ
Copy link
Contributor

easyCZ commented Feb 4, 2019

Awesome, thanks for posting this. It would be useful to create a react native example in the repo. Would you be interested in contributing? I'm happy to walk you through the contribution if needed.

@bourquep
Copy link

bourquep commented Feb 5, 2019

Unfortunately I don't have time for this now. Sorry.

@pbsf
Copy link
Contributor

pbsf commented Jun 1, 2019

With #458 I was able to get gRPC-WEB working with RN. Looks pretty similar to what @jonbretman did.

@mikebm
Copy link

mikebm commented Jul 13, 2019

Any update to this? I haven't been able to get any of these solutions to work. The 'grpc-web-client' no longer has any of those files in list and bourquep's change results in errors. Would be nice to have built in support without hacking around the issue.

@pojntfx
Copy link

pojntfx commented Jan 29, 2020

@mikebm You could take a look at https://github.com/improbable-eng/grpc-web/tree/master/client/grpc-web-react-native-transport

@andrewkryshtal
Copy link

andrewkryshtal commented Mar 8, 2020

hello guys! Thank you for such hard work around gRPC support in RN!
I have issue with implementation, connection creates - but event onMessage doesn't triggers at all. Nothing shows in console at all in RN, but wireshark shows, that data flows correctly, but i can't check what kind of data because it's encoded with SSL.

I've tried with ReactNativeTransport and NodeHttpTransport, according to @bourquep instructions.

I have separate file with NodeHttpTransport implementation which works correctly in pure JS without any frameworks, so, i pretty sure, that our backend works well.

Any clues?

This is my app info:

"@improbable-eng/grpc-web": "^0.12.0",
"@improbable-eng/grpc-web-node-http-transport": "^0.12.0",
"@improbable-eng/grpc-web-react-native-transport": "^0.12.0",
"expo": "~36.0.0",

@christianholman
Copy link

hello guys! Thank you for such hard work around gRPC support in RN!
I have issue with implementation, connection creates - but event onMessage doesn't triggers at all. Nothing shows in console at all in RN, but wireshark shows, that data flows correctly, but i can't check what kind of data because it's encoded with SSL.

I've tried with ReactNativeTransport and NodeHttpTransport, according to @bourquep instructions.

I have separate file with NodeHttpTransport implementation which works correctly in pure JS without any frameworks, so, i pretty sure, that our backend works well.

Any clues?

This is my app info:

"@improbable-eng/grpc-web": "^0.12.0",
"@improbable-eng/grpc-web-node-http-transport": "^0.12.0",
"@improbable-eng/grpc-web-react-native-transport": "^0.12.0",
"expo": "~36.0.0",

Did you ever figure this one out?

@Yasaswini134
Copy link

Yasaswini134 commented Feb 2, 2021

Yeah, currently I use both grpc-java and grpc-objectiveC in my react-native project instead. It won't take too much time to implement them and it works well.

Hi, can you please suggest me a proper document on gRPC on react native

@begoat
Copy link
Author

begoat commented Feb 2, 2021

Yeah, currently I use both grpc-java and grpc-objectiveC in my react-native project instead. It won't take too much time to implement them and it works well.

Hi, can you please suggest me a proper document on gRPC on react native

https://reactnative.dev/docs/native-modules-ios
https://reactnative.dev/docs/native-modules-android
https://grpc.io/docs/languages/java/quickstart
https://grpc.io/docs/languages/objective-c/basics

Hope this can help.
And some comments above also mentioned some promising workaround that deserve a try.

Good luck.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement help-wanted We'd like your help! needs-pr waiting-on-submitter Waiting on original poster action
Projects
None yet
Development

No branches or pull requests