grpc / grpc-dart

The Dart language implementation of gRPC.
https://pub.dev/packages/grpc
Apache License 2.0
834 stars 256 forks source link

Streaming issue on web only #717

Open divan opened 2 weeks ago

divan commented 2 weeks ago

Hi, I'm having an issue with streaming only when compiled to the web platform. It's the simple file upload chunking code that works fine on native platforms:

upload.dart

import 'package:<....>/pb/upload.pb.dart' as grpc;

upload(...) {
    final metadata =
        grpcu.UploadRequest_Metadata(size: bytes.length, fileName: fileName);
    var stream = uploadChunks(bytes, metadata: metadata);
    return client.uploadPhoto(stream).then((r) => r.url);
}

Stream<grpc.UploadRequest> uploadChunks(List<int> bytes,
    {required grpc.UploadRequest_Metadata metadata}) async* {
    yield grpc.UploadRequest(metadata: metadata);

    <...chunking logic...>
    for chunk in chunks {
        yield grpc.UploadRequest(chunk: chunk);
    }
  }
}

proto file:

message UploadRequest{
  message Metadata {
      int32 size = 1;
      string file_name = 2; 
      optional string ext = 3;
      optional int32 width = 4;
      optional int32 height = 5;
  }
  oneof data {
    bytes chunk = 1;
    Metadata metadata = 2;
  }
}

message UploadResponse{
  string url = 1;
}

But on web it returns this for every chunk:

DOMException: Failed to execute 'send' on 'XMLHttpRequest': The object's state must be OPENED.
packages/grpc/src/client/transport/xhr_transport.dart 54:36         <fn>
dart-sdk/lib/async/zone.dart 1594:9                                 runUnaryGuarded
dart-sdk/lib/async/stream_impl.dart 365:5                           [_sendData]
dart-sdk/lib/async/stream_impl.dart 297:7                           [_add]
dart-sdk/lib/async/stream_pipe.dart 123:11                          [_add]
dart-sdk/lib/async/stream_pipe.dart 218:9                           [_handleData]
dart-sdk/lib/async/stream_pipe.dart 153:5                           [_handleData]
dart-sdk/lib/async/zone.dart 1594:9                                 runUnaryGuarded
dart-sdk/lib/async/stream_impl.dart 365:5                           [_sendData]
dart-sdk/lib/async/stream_impl.dart 541:13                          perform
dart-sdk/lib/async/stream_impl.dart 646:10                          handleNext
dart-sdk/lib/async/stream_impl.dart 617:7                           callback
dart-sdk/lib/async/schedule_microtask.dart 40:11                    _microtaskLoop
dart-sdk/lib/async/schedule_microtask.dart 49:5                     _startMicrotaskLoop
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 181:7  <fn>

Seems like after the initial metadata, the sending connection is closed/cancelled, and subsequent chunks sends are resulting in this error. I can't, however, see any errors neither on server nor on client (using logging with interceptors).

Meanwhile, as I understood, grpc-dart is using grpc-web under the hood, and on the grpc-web README page, there is a paragraph:

Server-side Streaming RPCs (example) (NOTE: Only when grpcwebtext mode is used.) Client-side and Bi-directional streaming is not currently supported (see streaming roadmap).

It leads to this document: https://github.com/grpc/grpc-web/blob/master/doc/streaming-roadmap.md, which is basically saying, "forget about streaming support from the client".

So, I have two questions:

  1. Is it correct that grpc-dart client-side streaming is not working for apps built for web platform?
  2. If so, any solutions/workarounds?
mosuem commented 2 weeks ago

It's the simple file upload chunking code that works fine on native platforms

There are two different protocols, grpc and grpc-web, which are only related by name.

  1. Is it correct that grpc-dart client-side streaming is not working for apps built for web platform?

Yes, grpc-web does not support client-side streaming at the moment.

  1. If so, any solutions/workarounds?

I am no expert, but I would probably try using multiple unary requests.