mirror of https://github.com/grpc/grpc-dart.git
update: Migrate off legacy JS/HTML apis
This commit is contained in:
parent
6676c20df2
commit
74897b2cc9
|
@ -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';
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue