From b96174d219e90ccf6e516e6d7f39a6f4ee9f4416 Mon Sep 17 00:00:00 2001 From: minoic Date: Fri, 16 Aug 2024 07:24:45 +0000 Subject: [PATCH] update: Migrate off legacy JS/HTML apis --- lib/grpc_or_grpcweb.dart | 4 +- lib/src/client/transport/xhr_transport.dart | 77 +++++++++++---------- pubspec.yaml | 1 + 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/lib/grpc_or_grpcweb.dart b/lib/grpc_or_grpcweb.dart index b23bed5d..dffbde2e 100644 --- a/lib/grpc_or_grpcweb.dart +++ b/lib/grpc_or_grpcweb.dart @@ -13,8 +13,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'src/client/grpc_or_grpcweb_channel_grpc.dart' - if (dart.library.html) 'src/client/grpc_or_grpcweb_channel_web.dart'; +import 'src/client/grpc_or_grpcweb_channel_web.dart' + if (dart.library.io) 'src/client/grpc_or_grpcweb_channel_grpc.dart'; import 'src/client/http2_channel.dart'; import 'src/client/options.dart'; diff --git a/lib/src/client/transport/xhr_transport.dart b/lib/src/client/transport/xhr_transport.dart index 16b0dca5..d3293e8e 100644 --- a/lib/src/client/transport/xhr_transport.dart +++ b/lib/src/client/transport/xhr_transport.dart @@ -14,10 +14,11 @@ // limitations under the License. import 'dart:async'; -import 'dart:html'; +import 'dart:js_interop'; import 'dart:typed_data'; import 'package:meta/meta.dart'; +import 'package:web/web.dart'; import '../../client/call.dart'; import '../../shared/message.dart'; @@ -30,7 +31,7 @@ import 'web_streams.dart'; const _contentTypeKey = 'Content-Type'; class XhrTransportStream implements GrpcTransportStream { - final HttpRequest _request; + final XMLHttpRequest _request; final ErrorHandler _onError; final Function(XhrTransportStream stream) _onDone; bool _headersReceived = false; @@ -45,23 +46,22 @@ class XhrTransportStream implements GrpcTransportStream { @override StreamSink> get outgoingMessages => _outgoingMessages.sink; - XhrTransportStream(this._request, - {required ErrorHandler onError, required onDone}) + XhrTransportStream(this._request, {required ErrorHandler onError, required onDone}) : _onError = onError, _onDone = onDone { _outgoingMessages.stream .map(frame) - .listen((data) => _request.send(data), cancelOnError: true); + .listen((data) => _request.send(Int8List.fromList(data).toJS), cancelOnError: true, onError: _onError); - _request.onReadyStateChange.listen((data) { + _request.onReadyStateChange.listen((_) { if (_incomingProcessor.isClosed) { return; } switch (_request.readyState) { - case HttpRequest.HEADERS_RECEIVED: + case 2: _onHeadersReceived(); break; - case HttpRequest.DONE: + case 4: _onRequestDone(); _close(); break; @@ -72,8 +72,7 @@ class XhrTransportStream implements GrpcTransportStream { if (_incomingProcessor.isClosed) { return; } - _onError(GrpcError.unavailable('XhrConnection connection-error'), - StackTrace.current); + _onError(GrpcError.unavailable('XhrConnection connection-error'), StackTrace.current); terminate(); }); @@ -81,27 +80,21 @@ class XhrTransportStream implements GrpcTransportStream { if (_incomingProcessor.isClosed) { return; } - // Use response over responseText as most browsers don't support - // using responseText during an onProgress event. - final responseString = _request.response as String; - final bytes = Uint8List.fromList( - responseString.substring(_requestBytesRead).codeUnits) - .buffer; - _requestBytesRead = responseString.length; + final responseText = _request.responseText; + final bytes = Uint8List.fromList(responseText.substring(_requestBytesRead).codeUnits).buffer; + _requestBytesRead = responseText.length; _incomingProcessor.add(bytes); }); _incomingProcessor.stream .transform(GrpcWebDecoder()) .transform(grpcDecompressor()) - .listen(_incomingMessages.add, - onError: _onError, onDone: _incomingMessages.close); + .listen(_incomingMessages.add, onError: _onError, onDone: _incomingMessages.close); } bool _validateResponseState() { try { - validateHttpStatusAndContentType( - _request.status, _request.responseHeaders, + validateHttpStatusAndContentType(_request.status, _parseHeaders(_request.getAllResponseHeaders()), rawResponse: _request.responseText); return true; } catch (e, st) { @@ -115,17 +108,15 @@ class XhrTransportStream implements GrpcTransportStream { if (!_validateResponseState()) { return; } - _incomingMessages.add(GrpcMetadata(_request.responseHeaders)); + _incomingMessages.add(GrpcMetadata(_parseHeaders(_request.getAllResponseHeaders()))); } void _onRequestDone() { if (!_headersReceived && !_validateResponseState()) { return; } - if (_request.response == null) { - _onError( - GrpcError.unavailable('XhrConnection request null response', null, - _request.responseText), + if (_request.status != 200) { + _onError(GrpcError.unavailable('Request failed with status: ${_request.status}', null, _request.responseText), StackTrace.current); return; } @@ -137,6 +128,20 @@ class XhrTransportStream implements GrpcTransportStream { _onDone(this); } + Map _parseHeaders(String rawHeaders) { + final headers = {}; + final lines = rawHeaders.split('\r\n'); + for (var line in lines) { + final index = line.indexOf(': '); + if (index != -1) { + final key = line.substring(0, index); + final value = line.substring(index + 2); + headers[key] = value; + } + } + return headers; + } + @override Future terminate() async { _close(); @@ -153,24 +158,24 @@ class XhrClientConnection implements ClientConnection { @override String get authority => uri.authority; + @override String get scheme => uri.scheme; - void _initializeRequest(HttpRequest request, Map metadata) { - for (final header in metadata.keys) { - request.setRequestHeader(header, metadata[header]!); - } + void _initializeRequest(XMLHttpRequest request, Map metadata) { + metadata.forEach((key, value) { + request.setRequestHeader(key, value); + }); // Overriding the mimetype allows us to stream and parse the data request.overrideMimeType('text/plain; charset=x-user-defined'); request.responseType = 'text'; } @visibleForTesting - HttpRequest createHttpRequest() => HttpRequest(); + XMLHttpRequest createHttpRequest() => XMLHttpRequest(); @override - GrpcTransportStream makeRequest(String path, Duration? timeout, - Map metadata, ErrorHandler onError, + GrpcTransportStream makeRequest(String path, Duration? timeout, Map metadata, ErrorHandler onError, {CallOptions? callOptions}) { // gRPC-web headers. if (_getContentTypeHeader(metadata) == null) { @@ -180,8 +185,7 @@ class XhrClientConnection implements ClientConnection { } var requestUri = uri.resolve(path); - if (callOptions is WebCallOptions && - callOptions.bypassCorsPreflight == true) { + if (callOptions is WebCallOptions && callOptions.bypassCorsPreflight == true) { requestUri = cors.moveHttpHeadersToQueryParam(metadata, requestUri); } @@ -193,8 +197,7 @@ class XhrClientConnection implements ClientConnection { // Must set headers after calling open(). _initializeRequest(request, metadata); - final transportStream = - XhrTransportStream(request, onError: onError, onDone: _removeStream); + final transportStream = XhrTransportStream(request, onError: onError, onDone: _removeStream); _requests.add(transportStream); return transportStream; } diff --git a/pubspec.yaml b/pubspec.yaml index de16e0fa..f455f33e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: http2: ^2.2.0 protobuf: '>=2.0.0 <4.0.0' clock: ^1.1.1 + web: ^1.0.0 dev_dependencies: build_runner: ^2.0.0