update: Migrate off legacy JS/HTML apis

This commit is contained in:
minoic 2024-12-16 09:15:03 -08:00 committed by Aran Donohue
parent 6676c20df2
commit 74897b2cc9
No known key found for this signature in database
GPG Key ID: EA98761C2C20A998
3 changed files with 43 additions and 39 deletions

View File

@ -13,8 +13,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import 'src/client/grpc_or_grpcweb_channel_grpc.dart' import 'src/client/grpc_or_grpcweb_channel_web.dart'
if (dart.library.html) '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/http2_channel.dart';
import 'src/client/options.dart'; import 'src/client/options.dart';

View File

@ -14,10 +14,11 @@
// limitations under the License. // limitations under the License.
import 'dart:async'; import 'dart:async';
import 'dart:html'; import 'dart:js_interop';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:web/web.dart';
import '../../client/call.dart'; import '../../client/call.dart';
import '../../shared/message.dart'; import '../../shared/message.dart';
@ -30,7 +31,7 @@ import 'web_streams.dart';
const _contentTypeKey = 'Content-Type'; const _contentTypeKey = 'Content-Type';
class XhrTransportStream implements GrpcTransportStream { class XhrTransportStream implements GrpcTransportStream {
final HttpRequest _request; final XMLHttpRequest _request;
final ErrorHandler _onError; final ErrorHandler _onError;
final Function(XhrTransportStream stream) _onDone; final Function(XhrTransportStream stream) _onDone;
bool _headersReceived = false; bool _headersReceived = false;
@ -45,23 +46,22 @@ class XhrTransportStream implements GrpcTransportStream {
@override @override
StreamSink<List<int>> get outgoingMessages => _outgoingMessages.sink; StreamSink<List<int>> get outgoingMessages => _outgoingMessages.sink;
XhrTransportStream(this._request, XhrTransportStream(this._request, {required ErrorHandler onError, required onDone})
{required ErrorHandler onError, required onDone})
: _onError = onError, : _onError = onError,
_onDone = onDone { _onDone = onDone {
_outgoingMessages.stream _outgoingMessages.stream
.map(frame) .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) { if (_incomingProcessor.isClosed) {
return; return;
} }
switch (_request.readyState) { switch (_request.readyState) {
case HttpRequest.HEADERS_RECEIVED: case 2:
_onHeadersReceived(); _onHeadersReceived();
break; break;
case HttpRequest.DONE: case 4:
_onRequestDone(); _onRequestDone();
_close(); _close();
break; break;
@ -72,8 +72,7 @@ class XhrTransportStream implements GrpcTransportStream {
if (_incomingProcessor.isClosed) { if (_incomingProcessor.isClosed) {
return; return;
} }
_onError(GrpcError.unavailable('XhrConnection connection-error'), _onError(GrpcError.unavailable('XhrConnection connection-error'), StackTrace.current);
StackTrace.current);
terminate(); terminate();
}); });
@ -81,27 +80,21 @@ class XhrTransportStream implements GrpcTransportStream {
if (_incomingProcessor.isClosed) { if (_incomingProcessor.isClosed) {
return; return;
} }
// Use response over responseText as most browsers don't support final responseText = _request.responseText;
// using responseText during an onProgress event. final bytes = Uint8List.fromList(responseText.substring(_requestBytesRead).codeUnits).buffer;
final responseString = _request.response as String; _requestBytesRead = responseText.length;
final bytes = Uint8List.fromList(
responseString.substring(_requestBytesRead).codeUnits)
.buffer;
_requestBytesRead = responseString.length;
_incomingProcessor.add(bytes); _incomingProcessor.add(bytes);
}); });
_incomingProcessor.stream _incomingProcessor.stream
.transform(GrpcWebDecoder()) .transform(GrpcWebDecoder())
.transform(grpcDecompressor()) .transform(grpcDecompressor())
.listen(_incomingMessages.add, .listen(_incomingMessages.add, onError: _onError, onDone: _incomingMessages.close);
onError: _onError, onDone: _incomingMessages.close);
} }
bool _validateResponseState() { bool _validateResponseState() {
try { try {
validateHttpStatusAndContentType( validateHttpStatusAndContentType(_request.status, _parseHeaders(_request.getAllResponseHeaders()),
_request.status, _request.responseHeaders,
rawResponse: _request.responseText); rawResponse: _request.responseText);
return true; return true;
} catch (e, st) { } catch (e, st) {
@ -115,17 +108,15 @@ class XhrTransportStream implements GrpcTransportStream {
if (!_validateResponseState()) { if (!_validateResponseState()) {
return; return;
} }
_incomingMessages.add(GrpcMetadata(_request.responseHeaders)); _incomingMessages.add(GrpcMetadata(_parseHeaders(_request.getAllResponseHeaders())));
} }
void _onRequestDone() { void _onRequestDone() {
if (!_headersReceived && !_validateResponseState()) { if (!_headersReceived && !_validateResponseState()) {
return; return;
} }
if (_request.response == null) { if (_request.status != 200) {
_onError( _onError(GrpcError.unavailable('Request failed with status: ${_request.status}', null, _request.responseText),
GrpcError.unavailable('XhrConnection request null response', null,
_request.responseText),
StackTrace.current); StackTrace.current);
return; return;
} }
@ -137,6 +128,20 @@ class XhrTransportStream implements GrpcTransportStream {
_onDone(this); _onDone(this);
} }
Map<String, String> _parseHeaders(String rawHeaders) {
final headers = <String, String>{};
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 @override
Future<void> terminate() async { Future<void> terminate() async {
_close(); _close();
@ -153,24 +158,24 @@ class XhrClientConnection implements ClientConnection {
@override @override
String get authority => uri.authority; String get authority => uri.authority;
@override @override
String get scheme => uri.scheme; String get scheme => uri.scheme;
void _initializeRequest(HttpRequest request, Map<String, String> metadata) { void _initializeRequest(XMLHttpRequest request, Map<String, String> metadata) {
for (final header in metadata.keys) { metadata.forEach((key, value) {
request.setRequestHeader(header, metadata[header]!); request.setRequestHeader(key, value);
} });
// Overriding the mimetype allows us to stream and parse the data // Overriding the mimetype allows us to stream and parse the data
request.overrideMimeType('text/plain; charset=x-user-defined'); request.overrideMimeType('text/plain; charset=x-user-defined');
request.responseType = 'text'; request.responseType = 'text';
} }
@visibleForTesting @visibleForTesting
HttpRequest createHttpRequest() => HttpRequest(); XMLHttpRequest createHttpRequest() => XMLHttpRequest();
@override @override
GrpcTransportStream makeRequest(String path, Duration? timeout, GrpcTransportStream makeRequest(String path, Duration? timeout, Map<String, String> metadata, ErrorHandler onError,
Map<String, String> metadata, ErrorHandler onError,
{CallOptions? callOptions}) { {CallOptions? callOptions}) {
// gRPC-web headers. // gRPC-web headers.
if (_getContentTypeHeader(metadata) == null) { if (_getContentTypeHeader(metadata) == null) {
@ -180,8 +185,7 @@ class XhrClientConnection implements ClientConnection {
} }
var requestUri = uri.resolve(path); var requestUri = uri.resolve(path);
if (callOptions is WebCallOptions && if (callOptions is WebCallOptions && callOptions.bypassCorsPreflight == true) {
callOptions.bypassCorsPreflight == true) {
requestUri = cors.moveHttpHeadersToQueryParam(metadata, requestUri); requestUri = cors.moveHttpHeadersToQueryParam(metadata, requestUri);
} }
@ -193,8 +197,7 @@ class XhrClientConnection implements ClientConnection {
// Must set headers after calling open(). // Must set headers after calling open().
_initializeRequest(request, metadata); _initializeRequest(request, metadata);
final transportStream = final transportStream = XhrTransportStream(request, onError: onError, onDone: _removeStream);
XhrTransportStream(request, onError: onError, onDone: _removeStream);
_requests.add(transportStream); _requests.add(transportStream);
return transportStream; return transportStream;
} }

View File

@ -17,6 +17,7 @@ dependencies:
http2: ^2.2.0 http2: ^2.2.0
protobuf: '>=2.0.0 <4.0.0' protobuf: '>=2.0.0 <4.0.0'
clock: ^1.1.1 clock: ^1.1.1
web: ^1.0.0
dev_dependencies: dev_dependencies:
build_runner: ^2.0.0 build_runner: ^2.0.0