1414// limitations under the License.
1515
1616import 'dart:async' ;
17- import 'dart:html ' ;
17+ import 'dart:js_interop ' ;
1818import 'dart:typed_data' ;
1919
2020import 'package:meta/meta.dart' ;
21+ import 'package:web/web.dart' ;
2122
2223import '../../client/call.dart' ;
2324import '../../shared/message.dart' ;
@@ -30,7 +31,7 @@ import 'web_streams.dart';
3031const _contentTypeKey = 'Content-Type' ;
3132
3233class XhrTransportStream implements GrpcTransportStream {
33- final HttpRequest _request;
34+ final XMLHttpRequest _request;
3435 final ErrorHandler _onError;
3536 final Function (XhrTransportStream stream) _onDone;
3637 bool _headersReceived = false ;
@@ -45,23 +46,22 @@ class XhrTransportStream implements GrpcTransportStream {
4546 @override
4647 StreamSink <List <int >> get outgoingMessages => _outgoingMessages.sink;
4748
48- XhrTransportStream (this ._request,
49- {required ErrorHandler onError, required onDone})
49+ XhrTransportStream (this ._request, {required ErrorHandler onError, required onDone})
5050 : _onError = onError,
5151 _onDone = onDone {
5252 _outgoingMessages.stream
5353 .map (frame)
54- .listen ((data) => _request.send (data), cancelOnError: true );
54+ .listen ((data) => _request.send (Int8List . fromList ( data).toJS) , cancelOnError: true , onError : _onError );
5555
56- _request.onReadyStateChange.listen ((data ) {
56+ _request.onReadyStateChange.listen ((_ ) {
5757 if (_incomingProcessor.isClosed) {
5858 return ;
5959 }
6060 switch (_request.readyState) {
61- case HttpRequest . HEADERS_RECEIVED :
61+ case 2 :
6262 _onHeadersReceived ();
6363 break ;
64- case HttpRequest . DONE :
64+ case 4 :
6565 _onRequestDone ();
6666 _close ();
6767 break ;
@@ -72,36 +72,29 @@ class XhrTransportStream implements GrpcTransportStream {
7272 if (_incomingProcessor.isClosed) {
7373 return ;
7474 }
75- _onError (GrpcError .unavailable ('XhrConnection connection-error' ),
76- StackTrace .current);
75+ _onError (GrpcError .unavailable ('XhrConnection connection-error' ), StackTrace .current);
7776 terminate ();
7877 });
7978
8079 _request.onProgress.listen ((_) {
8180 if (_incomingProcessor.isClosed) {
8281 return ;
8382 }
84- // Use response over responseText as most browsers don't support
85- // using responseText during an onProgress event.
86- final responseString = _request.response as String ;
87- final bytes = Uint8List .fromList (
88- responseString.substring (_requestBytesRead).codeUnits)
89- .buffer;
90- _requestBytesRead = responseString.length;
83+ final responseText = _request.responseText;
84+ final bytes = Uint8List .fromList (responseText.substring (_requestBytesRead).codeUnits).buffer;
85+ _requestBytesRead = responseText.length;
9186 _incomingProcessor.add (bytes);
9287 });
9388
9489 _incomingProcessor.stream
9590 .transform (GrpcWebDecoder ())
9691 .transform (grpcDecompressor ())
97- .listen (_incomingMessages.add,
98- onError: _onError, onDone: _incomingMessages.close);
92+ .listen (_incomingMessages.add, onError: _onError, onDone: _incomingMessages.close);
9993 }
10094
10195 bool _validateResponseState () {
10296 try {
103- validateHttpStatusAndContentType (
104- _request.status, _request.responseHeaders,
97+ validateHttpStatusAndContentType (_request.status, _parseHeaders (_request.getAllResponseHeaders ()),
10598 rawResponse: _request.responseText);
10699 return true ;
107100 } catch (e, st) {
@@ -115,17 +108,15 @@ class XhrTransportStream implements GrpcTransportStream {
115108 if (! _validateResponseState ()) {
116109 return ;
117110 }
118- _incomingMessages.add (GrpcMetadata (_request.responseHeaders ));
111+ _incomingMessages.add (GrpcMetadata (_parseHeaders ( _request.getAllResponseHeaders ()) ));
119112 }
120113
121114 void _onRequestDone () {
122115 if (! _headersReceived && ! _validateResponseState ()) {
123116 return ;
124117 }
125- if (_request.response == null ) {
126- _onError (
127- GrpcError .unavailable ('XhrConnection request null response' , null ,
128- _request.responseText),
118+ if (_request.status != 200 ) {
119+ _onError (GrpcError .unavailable ('Request failed with status: ${_request .status }' , null , _request.responseText),
129120 StackTrace .current);
130121 return ;
131122 }
@@ -137,6 +128,20 @@ class XhrTransportStream implements GrpcTransportStream {
137128 _onDone (this );
138129 }
139130
131+ Map <String , String > _parseHeaders (String rawHeaders) {
132+ final headers = < String , String > {};
133+ final lines = rawHeaders.split ('\r\n ' );
134+ for (var line in lines) {
135+ final index = line.indexOf (': ' );
136+ if (index != - 1 ) {
137+ final key = line.substring (0 , index);
138+ final value = line.substring (index + 2 );
139+ headers[key] = value;
140+ }
141+ }
142+ return headers;
143+ }
144+
140145 @override
141146 Future <void > terminate () async {
142147 _close ();
@@ -153,24 +158,24 @@ class XhrClientConnection implements ClientConnection {
153158
154159 @override
155160 String get authority => uri.authority;
161+
156162 @override
157163 String get scheme => uri.scheme;
158164
159- void _initializeRequest (HttpRequest request, Map <String , String > metadata) {
160- for ( final header in metadata.keys ) {
161- request.setRequestHeader (header, metadata[header] ! );
162- }
165+ void _initializeRequest (XMLHttpRequest request, Map <String , String > metadata) {
166+ metadata.forEach ((key, value ) {
167+ request.setRequestHeader (key, value );
168+ });
163169 // Overriding the mimetype allows us to stream and parse the data
164170 request.overrideMimeType ('text/plain; charset=x-user-defined' );
165171 request.responseType = 'text' ;
166172 }
167173
168174 @visibleForTesting
169- HttpRequest createHttpRequest () => HttpRequest ();
175+ XMLHttpRequest createHttpRequest () => XMLHttpRequest ();
170176
171177 @override
172- GrpcTransportStream makeRequest (String path, Duration ? timeout,
173- Map <String , String > metadata, ErrorHandler onError,
178+ GrpcTransportStream makeRequest (String path, Duration ? timeout, Map <String , String > metadata, ErrorHandler onError,
174179 {CallOptions ? callOptions}) {
175180 // gRPC-web headers.
176181 if (_getContentTypeHeader (metadata) == null ) {
@@ -180,8 +185,7 @@ class XhrClientConnection implements ClientConnection {
180185 }
181186
182187 var requestUri = uri.resolve (path);
183- if (callOptions is WebCallOptions &&
184- callOptions.bypassCorsPreflight == true ) {
188+ if (callOptions is WebCallOptions && callOptions.bypassCorsPreflight == true ) {
185189 requestUri = cors.moveHttpHeadersToQueryParam (metadata, requestUri);
186190 }
187191
@@ -193,8 +197,7 @@ class XhrClientConnection implements ClientConnection {
193197 // Must set headers after calling open().
194198 _initializeRequest (request, metadata);
195199
196- final transportStream =
197- XhrTransportStream (request, onError: onError, onDone: _removeStream);
200+ final transportStream = XhrTransportStream (request, onError: onError, onDone: _removeStream);
198201 _requests.add (transportStream);
199202 return transportStream;
200203 }
0 commit comments