blob: d549f6345a475b388edfbda93e858dd5cf6eceb1 [file] [log] [blame]
// Copyright (c) 2017, the gRPC project authors. Please see the AUTHORS file
// for details. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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:async';
import 'dart:io';
import 'package:http2/transport.dart';
import 'package:meta/meta.dart';
import '../shared/security.dart';
import 'handler.dart';
import 'interceptor.dart';
import 'service.dart';
class ServerTlsCredentials {
final List<int> certificate;
final String certificatePassword;
final List<int> privateKey;
final String privateKeyPassword;
/// TLS credentials for a [Server].
///
/// If the [certificate] or [privateKey] is encrypted, the password must also
/// be provided.
ServerTlsCredentials(
{this.certificate,
this.certificatePassword,
this.privateKey,
this.privateKeyPassword});
SecurityContext get securityContext {
final context = createSecurityContext(true);
if (privateKey != null) {
context.usePrivateKeyBytes(privateKey, password: privateKeyPassword);
}
if (certificate != null) {
context.useCertificateChainBytes(certificate,
password: certificatePassword);
}
return context;
}
}
/// A gRPC server.
///
/// Listens for incoming RPCs, dispatching them to the right [Service] handler.
class Server {
final Map<String, Service> _services = {};
final List<Interceptor> _interceptors;
ServerSocket _insecureServer;
SecureServerSocket _secureServer;
final _connections = <ServerTransportConnection>[];
/// Create a server for the given [services].
Server(List<Service> services,
[List<Interceptor> interceptors = const <Interceptor>[]])
: _interceptors = interceptors {
for (final service in services) {
_services[service.$name] = service;
}
}
/// The port that the server is listening on, or `null` if the server is not
/// active.
int get port {
if (_secureServer != null) return _secureServer.port;
if (_insecureServer != null) return _insecureServer.port;
return null;
}
Service lookupService(String service) => _services[service];
Future<void> serve(
{dynamic address, int port, ServerTlsCredentials security}) async {
// TODO(dart-lang/grpc-dart#9): Handle HTTP/1.1 upgrade to h2c, if allowed.
Stream<Socket> server;
if (security != null) {
_secureServer = await SecureServerSocket.bind(
address ?? InternetAddress.anyIPv4,
port ?? 443,
security.securityContext);
server = _secureServer;
} else {
_insecureServer = await ServerSocket.bind(
address ?? InternetAddress.anyIPv4, port ?? 80);
server = _insecureServer;
}
server.listen((socket) {
final connection = new ServerTransportConnection.viaSocket(socket);
_connections.add(connection);
ServerHandler_ handler;
// TODO(jakobr): Set active state handlers, close connection after idle
// timeout.
connection.incomingStreams.listen((stream) {
handler = serveStream_(stream);
}, onError: (error) {
print('Connection error: $error');
}, onDone: () {
// TODO(sigurdm): This is not correct behavior in the presence of
// half-closed tcp streams.
// Half-closed streams seems to not be fully supported by package:http2.
// https://github.com/dart-lang/http2/issues/42
handler?.cancel();
_connections.remove(connection);
});
}, onError: (error) {
print('Socket error: $error');
});
}
@visibleForTesting
ServerHandler_ serveStream_(ServerTransportStream stream) {
return new ServerHandler_(lookupService, stream, _interceptors)..handle();
}
@Deprecated(
'This is internal functionality, and will be removed in next major version.')
void serveStream(ServerTransportStream stream) {
serveStream_(stream);
}
Future<void> shutdown() async {
final done = _connections.map((connection) => connection.finish()).toList();
if (_insecureServer != null) {
done.add(_insecureServer.close());
}
if (_secureServer != null) {
done.add(_secureServer.close());
}
await Future.wait(done);
_insecureServer = null;
_secureServer = null;
}
}