blob: 6637cc28f4be7b8b17a933da7734a5c040389adb [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library http2.experimental.server;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:http2/src/testing/debug.dart' hide print;
import 'package:http2/transport.dart';
const bool DEBUGGING = false;
const String HOSTNAME = 'localhost';
const int PORT = 7777;
main() async {
String localFile(String path) => Platform.script.resolve(path).toFilePath();
var context = new SecurityContext()
..usePrivateKey(localFile('server_key.pem'), password: 'dartdart')
..useCertificateChain(localFile('server_chain.pem'))
..setAlpnProtocols(['h2'], true);
var server = await SecureServerSocket.bind(HOSTNAME, PORT, context);
print('HTTP/2 server listening on https://$HOSTNAME:$PORT');
runZoned(() {
server.listen(handleClient);
}, onError: (e, s) {
print("Unexpected error: $e");
print("Unexpected error - stack: $s");
});
}
handleClient(SecureSocket socket) {
dumpInfo('main', 'Got new https client');
var connection;
if (DEBUGGING) {
connection = debugPrintingConnection(socket);
} else {
connection = new ServerTransportConnection.viaSocket(socket);
}
connection.incomingStreams.listen((ServerTransportStream stream) async {
dumpInfo('main', 'Got new HTTP/2 stream with id: ${stream.id}');
String path;
stream.incomingMessages.listen((StreamMessage msg) async {
dumpInfo('${stream.id}', 'Got new incoming message');
if (msg is HeadersStreamMessage) {
dumpHeaders('${stream.id}', msg.headers);
if (path == null) {
path = pathFromHeaders(msg.headers);
if (path == null) throw new Exception('no path given');
if (path == '/') {
sendHtml(stream);
} else if (['/iframe', '/iframe2'].contains(path)) {
sendIFrameHtml(stream, path);
} else {
send404(stream, path);
}
}
} else if (msg is DataStreamMessage) {
dumpData('${stream.id}', msg.bytes);
}
});
});
}
void dumpHeaders(String prefix, List<Header> headers) {
for (int i = 0; i < headers.length; i++) {
String key = ascii.decode(headers[i].name);
String value = ascii.decode(headers[i].value);
print('[$prefix] $key: $value');
}
}
String pathFromHeaders(List<Header> headers) {
for (int i = 0; i < headers.length; i++) {
if (ascii.decode(headers[i].name) == ':path') {
return ascii.decode(headers[i].value);
}
}
throw new Exception('Expected a :path header, but did not find one.');
}
void dumpData(String prefix, List<int> data) {
print('[$prefix] Got ${data.length} bytes.');
}
void dumpInfo(String prefix, String msg) {
print('[$prefix] $msg');
}
Future sendHtml(ServerTransportStream stream) async {
push(stream, '/iframe', sendIFrameHtml);
push(stream, '/iframe2', sendIFrameHtml);
push(stream, '/favicon.ico', send404);
stream.sendHeaders([
new Header.ascii(':status', '200'),
new Header.ascii('content-type', 'text/html; charset=utf-8'),
]);
stream.sendData(ascii.encode('''
<html>
<head><title>hello</title></head>
<body>
<h1> head </h1>
first <br />
<iframe src='/iframe' with="100" height="100"></iframe> <br />
second <br />
<iframe src='/iframe2' with="100" height="100"></iframe> <br />
</body>
</html>
'''));
return stream.outgoingMessages.close();
}
Future push(ServerTransportStream stream, String path,
Future sendResponse(TransportStream stream, String path)) async {
var requestHeaders = [
new Header.ascii(':authority', '$HOSTNAME:$PORT'),
new Header.ascii(':method', 'GET'),
new Header.ascii(':path', path),
new Header.ascii(':scheme', 'https'),
];
var pushStream = stream.push(requestHeaders);
await sendResponse(pushStream, path);
}
Future sendIFrameHtml(TransportStream stream, String path) async {
stream.sendHeaders([
new Header.ascii(':status', '200'),
new Header.ascii('content-type', 'text/html; charset=utf-8'),
]);
stream.sendData(ascii.encode('''
<html>
<head><title>Content for '$path' inside an IFrame.</title></head>
<body>
<h2>Content for '$path' inside an IFrame.</h2>
</body>
</html>
'''));
await stream.outgoingMessages.close();
}
Future send404(TransportStream stream, String path) async {
stream.sendHeaders([
new Header.ascii(':status', '404'),
new Header.ascii('content-type', 'text/html; charset=utf-8'),
]);
stream.sendData(ascii.encode('''
<html>
<head><title>Path '$path' was not found on this server.</title></head>
<body>
<h1>Path '$path' was not found on this server.</h1>
</body>
</html>
'''));
return stream.outgoingMessages.close();
}