Skip to content

Commit

Permalink
Grpc-web Handle empty trailers (grpc#247)
Browse files Browse the repository at this point in the history
* Grpc-web Handle empty trailers
  • Loading branch information
sigurdm authored Nov 7, 2019
1 parent fd92060 commit 6061512
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 2.1.3

* Fix bug in grpc-web when receiving an empty trailer.
* Fix a state bug in the server.

## 2.1.2
Expand Down
3 changes: 2 additions & 1 deletion lib/src/client/transport/web_streams.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ class _GrpcWebConversionSink extends ChunkedConversionSink<ByteBuffer> {
}

Map<String, String> _parseHttp1Headers(String stringData) {
final chunks = stringData.trim().split('\r\n');
final trimmed = stringData.trim();
final chunks = trimmed == '' ? [] : trimmed.split('\r\n');
final headers = <String, String>{};
for (final chunk in chunks) {
final pos = chunk.indexOf(':');
Expand Down
62 changes: 62 additions & 0 deletions test/client_tests/client_xhr_transport_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,68 @@ void main() {
});
});

test('Stream handles trailers properly', () async {
final trailers = <String, String>{
'trailer_1': 'value_1',
'trailer_2': 'value_2'
};

final connection = MockXhrClientConnection();

final stream = connection.makeRequest('test_path', Duration(seconds: 10),
{}, (error) => fail(error.toString()));

final encodedTrailers = frame(trailers.entries
.map((e) => '${e.key}:${e.value}')
.join('\r\n')
.codeUnits);
encodedTrailers[0] = 0x80; // Mark this frame as trailers.
final encodedString = String.fromCharCodes(encodedTrailers);

stream.incomingMessages.listen((message) {
expect(message, TypeMatcher<GrpcMetadata>());
if (message is GrpcMetadata) {
message.metadata.forEach((key, value) {
expect(value, trailers[key]);
});
}
});
when(connection.latestRequest.getResponseHeader('Content-Type'))
.thenReturn('application/grpc+proto');
when(connection.latestRequest.responseHeaders).thenReturn({});
when(connection.latestRequest.readyState)
.thenReturn(HttpRequest.HEADERS_RECEIVED);
when(connection.latestRequest.response).thenReturn(encodedString);
connection.latestRequest.readyStateChangeController.add(null);
connection.latestRequest.progressController.add(null);
});

test('Stream handles empty trailers properly', () async {
final connection = MockXhrClientConnection();

final stream = connection.makeRequest('test_path', Duration(seconds: 10),
{}, (error) => fail(error.toString()));

final encoded = frame(''.codeUnits);
encoded[0] = 0x80; // Mark this frame as trailers.
final encodedString = String.fromCharCodes(encoded);

stream.incomingMessages.listen((message) {
expect(message, TypeMatcher<GrpcMetadata>());
if (message is GrpcMetadata) {
message.metadata.isEmpty;
}
});
when(connection.latestRequest.getResponseHeader('Content-Type'))
.thenReturn('application/grpc+proto');
when(connection.latestRequest.responseHeaders).thenReturn({});
when(connection.latestRequest.readyState)
.thenReturn(HttpRequest.HEADERS_RECEIVED);
when(connection.latestRequest.response).thenReturn(encodedString);
connection.latestRequest.readyStateChangeController.add(null);
connection.latestRequest.progressController.add(null);
});

test('Stream deserializes data properly', () async {
final metadata = <String, String>{
'parameter_1': 'value_1',
Expand Down

0 comments on commit 6061512

Please sign in to comment.