Change default authority for UDS connections. (#577)

We were using path to the UDS socket itself, which is incorrect `:authority` value. 

This was tripping checks in some HTTP2 protocol implementations.

Instead default `:authority` to `localhost`, which in line with other gRPC implementations.

Fixes #576
This commit is contained in:
Vyacheslav Egorov 2022-09-08 14:41:34 +02:00 committed by GitHub
parent b8f872a3dc
commit 27a235976a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 16 deletions

View File

@ -3,6 +3,10 @@
* Expose a stream for connection state changes on ClientChannel to address * Expose a stream for connection state changes on ClientChannel to address
[#428](https://github.com/grpc/grpc-dart/issues/428). [#428](https://github.com/grpc/grpc-dart/issues/428).
This allows users to react to state changes in the connection. This allows users to react to state changes in the connection.
* Fix [#576](https://github.com/grpc/grpc-dart/issues/576): set default
`:authority` value for UDS connections to `localhost` instead of using
UDS path. Using path triggers checks in HTTP2 servers which
attempt to validate `:authority` value.
## 3.0.2 ## 3.0.2

View File

@ -355,10 +355,23 @@ class _SocketTransportConnector implements ClientTransportConnector {
@override @override
String get authority { String get authority {
final host = return _options.credentials.authority ?? _makeAuthority();
_host is String ? _host as String : (_host as InternetAddress).host; }
return _options.credentials.authority ??
(_port == 443 ? host : '$host:$_port'); String _makeAuthority() {
final host = _host;
final portSuffix = _port == 443 ? '' : ':$_port';
final hostName;
if (host is String) {
hostName = '$host';
} else {
host as InternetAddress;
if (host.type == InternetAddressType.unix) {
return 'localhost'; // UDS don't have a meaningful authority.
}
hostName = host.host;
}
return '$hostName$portSuffix';
} }
@override @override

View File

@ -16,6 +16,21 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:test/test.dart'; import 'package:test/test.dart';
/// Test functionality for Unix domain socket.
void testUds(String name, FutureOr<void> Function(InternetAddress) testCase) {
if (Platform.isWindows) {
return;
}
test(name, () async {
final tempDir = await Directory.systemTemp.createTemp();
final address = InternetAddress(tempDir.path + '/socket',
type: InternetAddressType.unix);
addTearDown(() => tempDir.delete(recursive: true));
await testCase(address);
});
}
/// Test functionality for both TCP and Unix domain sockets. /// Test functionality for both TCP and Unix domain sockets.
void testTcpAndUds( void testTcpAndUds(
String name, FutureOr<void> Function(InternetAddress) testCase, String name, FutureOr<void> Function(InternetAddress) testCase,
@ -25,15 +40,5 @@ void testTcpAndUds(
await testCase(address.first); await testCase(address.first);
}); });
if (Platform.isWindows) { testUds('$name (over uds)', testCase);
return;
}
test('$name (over uds)', () async {
final tempDir = await Directory.systemTemp.createTemp();
final address = InternetAddress(tempDir.path + '/socket',
type: InternetAddressType.unix);
addTearDown(() => tempDir.delete(recursive: true));
await testCase(address);
});
} }

View File

@ -23,10 +23,12 @@ class TestClient extends Client {
} }
class TestService extends Service { class TestService extends Service {
final String? expectedAuthority;
@override @override
String get $name => 'test.TestService'; String get $name => 'test.TestService';
TestService() { TestService({this.expectedAuthority}) {
$addMethod(ServiceMethod<int, int>('stream', stream, false, true, $addMethod(ServiceMethod<int, int>('stream', stream, false, true,
(List<int> value) => value[0], (int value) => [value])); (List<int> value) => value[0], (int value) => [value]));
} }
@ -35,12 +37,20 @@ class TestService extends Service {
static const requestInfiniteStream = 2; static const requestInfiniteStream = 2;
Stream<int> stream(ServiceCall call, Future request) async* { Stream<int> stream(ServiceCall call, Future request) async* {
checkMetadata(call.clientMetadata);
final isInfinite = 2 == await request; final isInfinite = 2 == await request;
for (var i = 1; i <= 3 || isInfinite; i++) { for (var i = 1; i <= 3 || isInfinite; i++) {
yield i; yield i;
await Future.delayed(Duration(milliseconds: 100)); await Future.delayed(Duration(milliseconds: 100));
} }
} }
void checkMetadata(Map<String, String>? metadata) {
if (expectedAuthority != null) {
expect(metadata![':authority'], equals(expectedAuthority));
}
}
} }
class TestServiceWithOnMetadataException extends TestService { class TestServiceWithOnMetadataException extends TestService {
@ -93,6 +103,22 @@ Future<void> main() async {
server.shutdown(); server.shutdown();
}); });
testUds('UDS provides valid default authority', (address) async {
// round trip test of insecure connection.
final server = Server([TestService(expectedAuthority: 'localhost')]);
await server.serve(address: address, port: 0);
final channel = FixedConnectionClientChannel(Http2ClientConnection(
address,
server.port!,
ChannelOptions(credentials: ChannelCredentials.insecure()),
));
final testClient = TestClient(channel);
expect(await testClient.stream(TestService.requestFiniteStream).toList(),
[1, 2, 3]);
server.shutdown();
});
testTcpAndUds('round trip with outgoing and incoming compression', testTcpAndUds('round trip with outgoing and incoming compression',
(address) async { (address) async {
final server = Server( final server = Server(