blob: 34d6468dbfdc80a8cd2d2d89ccae75eea8d91d61 [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'package:sync_http/sync_http.dart';
import 'package:test/test.dart';
typedef ServerCallback = void Function(int? port);
class TestServerMain {
TestServerMain() : _statusPort = ReceivePort();
final ReceivePort _statusPort; // Port for receiving messages from the server.
late SendPort _serverPort; // Port for sending messages to the server.
late ServerCallback _startedCallback;
void setServerStartedHandler(ServerCallback startedCallback) {
_startedCallback = startedCallback;
}
void start() {
final receivePort = ReceivePort();
Isolate.spawn(startTestServer, receivePort.sendPort);
receivePort.first.then((port) {
_serverPort = port as SendPort;
// Send server start message to the server.
final command = TestServerCommand.start();
port.send([command, _statusPort.sendPort]);
});
// Handle status messages from the server.
_statusPort.listen((var value) {
final status = value as TestServerStatus;
if (status.isStarted) {
_startedCallback(status.port);
}
});
}
void close() {
// Send server stop message to the server.
_serverPort.send([TestServerCommand.stop(), _statusPort.sendPort]);
_statusPort.close();
}
}
enum TestServerCommandState {
start,
stop,
}
class TestServerCommand {
TestServerCommand.start() : _command = TestServerCommandState.start;
TestServerCommand.stop() : _command = TestServerCommandState.stop;
bool get isStart => _command == TestServerCommandState.start;
bool get isStop => _command == TestServerCommandState.stop;
final TestServerCommandState _command;
}
enum TestServerStatusState {
started,
stopped,
error,
}
class TestServerStatus {
TestServerStatus.started(this._port) : _state = TestServerStatusState.started;
TestServerStatus.stopped() : _state = TestServerStatusState.stopped;
TestServerStatus.error() : _state = TestServerStatusState.error;
bool get isStarted => _state == TestServerStatusState.started;
int? get port => _port;
final TestServerStatusState _state;
int? _port;
}
void startTestServer(SendPort replyTo) {
final server = TestServer();
server.init();
replyTo.send(server.dispatchSendPort);
}
class TestServer {
// Echo the request content back to the response.
void _echoHandler(HttpRequest request) {
final response = request.response;
if (request.method != 'POST') {
response.close();
return;
}
response.contentLength = request.contentLength;
request.listen((List<int> data) {
final string = String.fromCharCodes(data);
response.write(string);
response.close();
});
}
// Echo the request content back to the response.
void _zeroToTenHandler(HttpRequest request) {
final response = request.response;
const msg = '01234567890';
if (request.method != 'GET') {
response.close();
return;
}
response.contentLength = msg.length;
response.write(msg);
response.close();
}
// Return a 404.
void _notFoundHandler(HttpRequest request) {
final response = request.response;
response.statusCode = HttpStatus.notFound;
const msg = 'Page not found';
response.contentLength = msg.length;
response.headers.set('Content-Type', 'text/html; charset=UTF-8');
response.write(msg);
response.close();
}
// Return a 301 with a custom reason phrase.
void _reasonForMovingHandler(HttpRequest request) {
final response = request.response;
response.statusCode = HttpStatus.movedPermanently;
response.reasonPhrase = "Don't come looking here any more";
response.close();
}
// Check the "Host" header.
void _hostHandler(HttpRequest request) {
final response = request.response;
expect(1, equals(request.headers['Host']!.length));
expect('dart.dev:1234', equals(request.headers['Host']![0]));
expect('dart.dev', equals(request.headers.host));
expect(1234, equals(request.headers.port));
response.statusCode = HttpStatus.ok;
response.close();
}
void _hugeHandler(HttpRequest request) {
final response = request.response;
final expected = List<int>.generate(1 << 20, (i) => (i + 1) % 256);
final msg = expected.toString();
response.contentLength = msg.length;
response.statusCode = HttpStatus.ok;
response.write(msg);
response.close();
}
void init() {
// Setup request handlers.
_requestHandlers = {};
_requestHandlers['/echo'] = _echoHandler;
_requestHandlers['/0123456789'] = _zeroToTenHandler;
_requestHandlers['/reasonformoving'] = _reasonForMovingHandler;
_requestHandlers['/host'] = _hostHandler;
_requestHandlers['/huge'] = _hugeHandler;
_dispatchPort = ReceivePort();
_dispatchPort.listen(dispatch);
}
SendPort get dispatchSendPort => _dispatchPort.sendPort;
Future<void> dispatch(Object? message) async {
final command = (message as List)[0] as TestServerCommand;
final replyTo = message[1] as SendPort;
if (command.isStart) {
try {
final addr = (await InternetAddress.lookup('localhost'))[0];
await HttpServer.bind(addr, 0).then((server) {
_server = server;
_server.listen(_requestReceivedHandler);
replyTo.send(TestServerStatus.started(_server.port));
});
} catch (e) {
replyTo.send(TestServerStatus.error());
}
} else if (command.isStop) {
await _server.close();
_dispatchPort.close();
replyTo.send(TestServerStatus.stopped());
}
}
void _requestReceivedHandler(HttpRequest request) {
final requestHandler = _requestHandlers[request.uri.path];
if (requestHandler != null) {
requestHandler(request);
} else {
_notFoundHandler(request);
}
}
late HttpServer _server; // HTTP server instance.
late ReceivePort _dispatchPort;
late Map<String, void Function(HttpRequest)> _requestHandlers;
}
Future<void> testStartStop() async {
final completer = Completer<void>();
final testServerMain = TestServerMain();
testServerMain.setServerStartedHandler((int? port) {
testServerMain.close();
completer.complete();
});
testServerMain.start();
return completer.future;
}
Future<void> testGET() async {
final completer = Completer<void>();
final testServerMain = TestServerMain();
testServerMain.setServerStartedHandler((int? port) {
final request =
SyncHttpClient.getUrl(Uri.http('localhost:$port', '/0123456789'));
final response = request.close();
expect(HttpStatus.ok, equals(response.statusCode));
expect(11, equals(response.contentLength));
expect('01234567890', equals(response.body));
testServerMain.close();
completer.complete();
});
testServerMain.start();
return completer.future;
}
Future<void> testPOST() async {
final completer = Completer<void>();
const data = 'ABCDEFGHIJKLMONPQRSTUVWXYZ';
const kMessageCount = 10;
final testServerMain = TestServerMain();
void runTest(int? port) {
var count = 0;
void sendRequest() {
final request =
SyncHttpClient.postUrl(Uri.http('localhost:$port', '/echo'));
request.write(data);
final response = request.close();
expect(HttpStatus.ok, equals(response.statusCode));
expect(data, equals(response.body));
count++;
if (count < kMessageCount) {
sendRequest();
} else {
testServerMain.close();
completer.complete();
}
}
sendRequest();
}
testServerMain.setServerStartedHandler(runTest);
testServerMain.start();
return completer.future;
}
Future<void> test404() async {
final completer = Completer<void>();
final testServerMain = TestServerMain();
testServerMain.setServerStartedHandler((int? port) {
final request =
SyncHttpClient.getUrl(Uri.http('localhost:$port', '/thisisnotfound'));
final response = request.close();
expect(HttpStatus.notFound, equals(response.statusCode));
expect('Page not found', equals(response.body));
testServerMain.close();
completer.complete();
});
testServerMain.start();
return completer.future;
}
Future<void> testReasonPhrase() async {
final completer = Completer<void>();
final testServerMain = TestServerMain();
testServerMain.setServerStartedHandler((int? port) {
final request =
SyncHttpClient.getUrl(Uri.http('localhost:$port', '/reasonformoving'));
final response = request.close();
expect(HttpStatus.movedPermanently, equals(response.statusCode));
expect(
"Don't come looking here any more\r\n", equals(response.reasonPhrase));
testServerMain.close();
completer.complete();
});
testServerMain.start();
return completer.future;
}
Future<void> testHuge() async {
final completer = Completer<void>();
final testServerMain = TestServerMain();
testServerMain.setServerStartedHandler((int? port) {
final request = SyncHttpClient.getUrl(Uri.http('localhost:$port', '/huge'));
final response = request.close();
final expected =
List<int>.generate(1 << 20, (i) => (i + 1) % 256).toString();
expect(HttpStatus.ok, equals(response.statusCode));
expect(expected.length, equals(response.contentLength));
expect(expected.toString(), equals(response.body));
testServerMain.close();
completer.complete();
});
testServerMain.start();
return completer.future;
}
void main() {
test('Simple server test', () async {
await testStartStop();
});
test('Sync HTTP GET test', () async {
await testGET();
});
test('Sync HTTP POST test', () async {
await testPOST();
});
test('Sync HTTP 404 test', () async {
await test404();
});
test('Sync HTTP moved test', () async {
await testReasonPhrase();
});
test('Sync HTTP huge test', () async {
await testHuge();
});
}