Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Updates the grpc-web example to avoid dart:html #748

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
the connection, as defined in the gRPC spec.
* Upgrade to `package:lints` version 5.0.0 and Dart SDK version 3.5.0.
* Upgrade `example/grpc-web` code.
* Update xhr transport to migrate off legacy JS/HTML apis.

## 4.0.1

Expand Down
27 changes: 18 additions & 9 deletions example/grpc-web/lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
// limitations under the License.

import 'dart:async';
import 'dart:html';

import 'package:web/web.dart';
import 'src/generated/echo.pbgrpc.dart';

class EchoApp {
Expand Down Expand Up @@ -56,13 +55,23 @@ class EchoApp {
}

void _addMessage(String message, String cssClass) {
final classes = cssClass.split(' ');
querySelector('#first')!.after(DivElement()
..classes.add('row')
..append(Element.tag('h2')
..append(SpanElement()
..classes.add('label')
..classes.addAll(classes)
document.querySelector('#first')!.after(HTMLDivElement()
..classList.add('row')
..append(HTMLHeadingElement.h2()
..append(HTMLSpanElement()
..classList.add('label')
..classList.addAll(cssClass)
..text = message)));
}
}

// The documentation of DOMTokenList.add implies it can handle multiple classes,
// but in Chrome at least it does not.
extension AddAll on DOMTokenList {
void addAll(String cssClass) {
final classes = cssClass.split(' ');
for (final c in classes) {
add(c);
}
}
}
1 change: 1 addition & 0 deletions example/grpc-web/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies:
grpc:
path: ../../
protobuf: ^3.0.0
web: ^1.1.0

dev_dependencies:
build_runner: ^2.4.13
Expand Down
9 changes: 4 additions & 5 deletions example/grpc-web/web/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,20 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import 'dart:html';

import 'package:grpc/grpc_web.dart';
import 'package:grpc_web/app.dart';
import 'package:grpc_web/src/generated/echo.pbgrpc.dart';
import 'package:web/web.dart';

void main() {
final channel = GrpcWebClientChannel.xhr(Uri.parse('http://localhost:8080'));
final service = EchoServiceClient(channel);
final app = EchoApp(service);

final button = querySelector('#send') as ButtonElement;
final button = document.querySelector('#send') as HTMLButtonElement;
button.onClick.listen((e) async {
final msg = querySelector('#msg') as TextInputElement;
final value = msg.value!.trim();
final msg = document.querySelector('#msg') as HTMLInputElement;
final value = msg.value.trim();
msg.value = '';

if (value.isEmpty) return;
Expand Down
2 changes: 1 addition & 1 deletion lib/grpc_or_grpcweb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// 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';
if (dart.library.js_interop) 'src/client/grpc_or_grpcweb_channel_web.dart';
import 'src/client/http2_channel.dart';
import 'src/client/options.dart';

Expand Down
42 changes: 22 additions & 20 deletions lib/src/client/transport/xhr_transport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand All @@ -49,19 +50,20 @@ class XhrTransportStream implements GrpcTransportStream {
{required ErrorHandler onError, required onDone})
: _onError = onError,
_onDone = onDone {
_outgoingMessages.stream
.map(frame)
.listen((data) => _request.send(data), cancelOnError: true);
_outgoingMessages.stream.map(frame).listen(
(data) => _request.send(Uint8List.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 XMLHttpRequest.HEADERS_RECEIVED:
_onHeadersReceived();
break;
case HttpRequest.DONE:
case XMLHttpRequest.DONE:
_onRequestDone();
_close();
break;
Expand All @@ -81,13 +83,11 @@ 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 responseText = _request.responseText;
final bytes = Uint8List.fromList(
responseString.substring(_requestBytesRead).codeUnits)
responseText.substring(_requestBytesRead).codeUnits)
.buffer;
_requestBytesRead = responseString.length;
_requestBytesRead = responseText.length;
_incomingProcessor.add(bytes);
});

Expand Down Expand Up @@ -122,9 +122,11 @@ class XhrTransportStream implements GrpcTransportStream {
if (!_headersReceived && !_validateResponseState()) {
return;
}
if (_request.response == null) {
if (_request.status != 200) {
_onError(
GrpcError.unavailable('XhrConnection request null response', null,
GrpcError.unavailable(
'Request failed with status: ${_request.status}',
null,
_request.responseText),
StackTrace.current);
return;
Expand Down Expand Up @@ -153,20 +155,20 @@ class XhrClientConnection implements ClientConnection {

@override
String get authority => uri.authority;

@override
String get scheme => uri.scheme;

void _initializeRequest(HttpRequest request, Map<String, String> metadata) {
for (final header in metadata.keys) {
request.setRequestHeader(header, metadata[header]!);
}
void _initializeRequest(
XMLHttpRequest request, Map<String, String> metadata) {
metadata.forEach(request.setRequestHeader);
// 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,
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ dependencies:
http2: ^2.2.0
protobuf: '>=2.0.0 <4.0.0'
clock: ^1.1.1
web: ^1.1.0

dev_dependencies:
build_runner: ^2.0.0
Expand Down
Loading