blob: 10b6a5b2fac650d10e41338d9cc6b9022930deb4 [file] [log] [blame]
// Copyright (c) 2013, 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.
// @dart = 2.9
// VMOptions=--enable-isolate-groups
// VMOptions=--no-enable-isolate-groups
//
// VMOptions=
// VMOptions=--short_socket_read
// VMOptions=--short_socket_write
// VMOptions=--short_socket_read --short_socket_write
import "package:expect/expect.dart";
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
class IsolatedHttpServer {
IsolatedHttpServer()
: _statusPort = new ReceivePort(),
_serverPort = null;
void setServerStartedHandler(void startedCallback(int port)) {
_startedCallback = startedCallback;
}
void start() {
ReceivePort receivePort = new ReceivePort();
var remote = Isolate.spawn(startIsolatedHttpServer, receivePort.sendPort);
receivePort.first.then((port) {
_serverPort = port;
// Send server start message to the server.
var command = new IsolatedHttpServerCommand.start();
port.send([command, _statusPort.sendPort]);
});
// Handle status messages from the server.
_statusPort.listen((var status) {
if (status.isStarted) {
_startedCallback(status.port);
}
});
}
void shutdown() {
// Send server stop message to the server.
_serverPort
.send([new IsolatedHttpServerCommand.stop(), _statusPort.sendPort]);
_statusPort.close();
}
void chunkedEncoding() {
// Send chunked encoding message to the server.
_serverPort.send([
new IsolatedHttpServerCommand.chunkedEncoding(),
_statusPort.sendPort
]);
}
ReceivePort _statusPort; // Port for receiving messages from the server.
SendPort _serverPort; // Port for sending messages to the server.
var _startedCallback;
}
class IsolatedHttpServerCommand {
static const START = 0;
static const STOP = 1;
static const CHUNKED_ENCODING = 2;
IsolatedHttpServerCommand.start() : _command = START;
IsolatedHttpServerCommand.stop() : _command = STOP;
IsolatedHttpServerCommand.chunkedEncoding() : _command = CHUNKED_ENCODING;
bool get isStart => _command == START;
bool get isStop => _command == STOP;
bool get isChunkedEncoding => _command == CHUNKED_ENCODING;
int _command;
}
class IsolatedHttpServerStatus {
static const STARTED = 0;
static const STOPPED = 1;
static const ERROR = 2;
IsolatedHttpServerStatus.started(this._port) : _state = STARTED;
IsolatedHttpServerStatus.stopped() : _state = STOPPED;
IsolatedHttpServerStatus.error() : _state = ERROR;
bool get isStarted => _state == STARTED;
bool get isStopped => _state == STOPPED;
bool get isError => _state == ERROR;
int get port => _port;
int _state;
int _port;
}
void startIsolatedHttpServer(Object replyToObj) {
SendPort replyTo = replyToObj;
var server = new TestServer();
server.init();
replyTo.send(server.dispatchSendPort);
}
class TestServer {
// Return a 404.
void _notFoundHandler(HttpRequest request) {
var response = request.response;
response.statusCode = HttpStatus.notFound;
response.headers.set("Content-Type", "text/html; charset=UTF-8");
response.write("Page not found");
response.close();
}
// Check the "Host" header.
void _hostHandler(HttpRequest request) {
var response = request.response;
Expect.equals(1, request.headers["Host"].length);
Expect.equals("www.dartlang.org:1234", request.headers["Host"][0]);
Expect.equals("www.dartlang.org", request.headers.host);
Expect.equals(1234, request.headers.port);
response.statusCode = HttpStatus.ok;
response.close();
}
// Set the "Expires" header using the expires property.
void _expires1Handler(HttpRequest request) {
var response = request.response;
DateTime date = new DateTime.utc(1999, DateTime.june, 11, 18, 46, 53, 0);
response.headers.expires = date;
Expect.equals(date, response.headers.expires);
response.close();
}
// Set the "Expires" header.
void _expires2Handler(HttpRequest request) {
var response = request.response;
response.headers.set("Expires", "Fri, 11 Jun 1999 18:46:53 GMT");
DateTime date = new DateTime.utc(1999, DateTime.june, 11, 18, 46, 53, 0);
Expect.equals(date, response.headers.expires);
response.close();
}
void _contentType1Handler(HttpRequest request) {
var response = request.response;
Expect.equals("text/html", request.headers.contentType.value);
Expect.equals("text", request.headers.contentType.primaryType);
Expect.equals("html", request.headers.contentType.subType);
Expect.equals("utf-8", request.headers.contentType.parameters["charset"]);
ContentType contentType = new ContentType("text", "html", charset: "utf-8");
response.headers.contentType = contentType;
response.close();
}
void _contentType2Handler(HttpRequest request) {
var response = request.response;
Expect.equals("text/html", request.headers.contentType.value);
Expect.equals("text", request.headers.contentType.primaryType);
Expect.equals("html", request.headers.contentType.subType);
Expect.equals("utf-8", request.headers.contentType.parameters["charset"]);
response.headers
.set(HttpHeaders.contentTypeHeader, "text/html; charset = utf-8");
response.close();
}
void _cookie1Handler(HttpRequest request) {
var response = request.response;
// No cookies passed with this request.
Expect.equals(0, request.cookies.length);
Cookie cookie1 = new Cookie("name1", "value1");
DateTime date = new DateTime.utc(2014, DateTime.january, 5, 23, 59, 59, 0);
cookie1.expires = date;
cookie1.domain = "www.example.com";
cookie1.httpOnly = true;
response.cookies.add(cookie1);
Cookie cookie2 = new Cookie("name2", "value2");
cookie2.maxAge = 100;
cookie2.domain = ".example.com";
cookie2.path = "/shop";
response.cookies.add(cookie2);
response.close();
}
void _cookie2Handler(HttpRequest request) {
var response = request.response;
// Two cookies passed with this request.
Expect.equals(2, request.cookies.length);
response.close();
}
void init() {
// Setup request handlers.
_requestHandlers = new Map();
_requestHandlers["/host"] = _hostHandler;
_requestHandlers["/expires1"] = _expires1Handler;
_requestHandlers["/expires2"] = _expires2Handler;
_requestHandlers["/contenttype1"] = _contentType1Handler;
_requestHandlers["/contenttype2"] = _contentType2Handler;
_requestHandlers["/cookie1"] = _cookie1Handler;
_requestHandlers["/cookie2"] = _cookie2Handler;
_dispatchPort = new ReceivePort();
_dispatchPort.listen(dispatch);
}
SendPort get dispatchSendPort => _dispatchPort.sendPort;
void dispatch(message) {
IsolatedHttpServerCommand command = message[0];
SendPort replyTo = message[1];
if (command.isStart) {
try {
HttpServer.bind("127.0.0.1", 0).then((server) {
_server = server;
_server.listen(_requestReceivedHandler);
replyTo.send(new IsolatedHttpServerStatus.started(_server.port));
});
} catch (e) {
replyTo.send(new IsolatedHttpServerStatus.error());
}
} else if (command.isStop) {
_server.close();
_dispatchPort.close();
replyTo.send(new IsolatedHttpServerStatus.stopped());
} else if (command.isChunkedEncoding) {
_chunkedEncoding = true;
}
}
void _requestReceivedHandler(HttpRequest request) {
var requestHandler = _requestHandlers[request.uri.path];
if (requestHandler != null) {
requestHandler(request);
} else {
_notFoundHandler(request);
}
}
HttpServer _server; // HTTP server instance.
ReceivePort _dispatchPort;
Map _requestHandlers;
bool _chunkedEncoding = false;
}
Future testHost() {
Completer completer = new Completer();
IsolatedHttpServer server = new IsolatedHttpServer();
server.setServerStartedHandler((int port) {
HttpClient httpClient = new HttpClient();
httpClient.get("127.0.0.1", port, "/host").then((request) {
Expect.equals("127.0.0.1:$port", request.headers["host"][0]);
request.headers.host = "www.dartlang.com";
Expect.equals("www.dartlang.com:$port", request.headers["host"][0]);
Expect.equals("www.dartlang.com", request.headers.host);
Expect.equals(port, request.headers.port);
request.headers.port = 1234;
Expect.equals("www.dartlang.com:1234", request.headers["host"][0]);
Expect.equals(1234, request.headers.port);
request.headers.port = HttpClient.defaultHttpPort;
Expect.equals(HttpClient.defaultHttpPort, request.headers.port);
Expect.equals("www.dartlang.com", request.headers["host"][0]);
request.headers.set("Host", "www.dartlang.org");
Expect.equals("www.dartlang.org", request.headers.host);
Expect.equals(HttpClient.defaultHttpPort, request.headers.port);
request.headers.set("Host", "www.dartlang.org:");
Expect.equals("www.dartlang.org", request.headers.host);
Expect.equals(HttpClient.defaultHttpPort, request.headers.port);
request.headers.set("Host", "www.dartlang.org:1234");
Expect.equals("www.dartlang.org", request.headers.host);
Expect.equals(1234, request.headers.port);
return request.close();
}).then((response) {
Expect.equals(HttpStatus.ok, response.statusCode);
response.listen((_) {}, onDone: () {
httpClient.close();
server.shutdown();
completer.complete(true);
});
});
});
server.start();
return completer.future;
}
Future testExpires() {
Completer completer = new Completer();
IsolatedHttpServer server = new IsolatedHttpServer();
server.setServerStartedHandler((int port) {
int responses = 0;
HttpClient httpClient = new HttpClient();
void processResponse(HttpClientResponse response) {
Expect.equals(HttpStatus.ok, response.statusCode);
Expect.equals(
"Fri, 11 Jun 1999 18:46:53 GMT", response.headers["expires"][0]);
Expect.equals(new DateTime.utc(1999, DateTime.june, 11, 18, 46, 53, 0),
response.headers.expires);
response.listen((_) {}, onDone: () {
responses++;
if (responses == 2) {
httpClient.close();
server.shutdown();
completer.complete(true);
}
});
}
httpClient
.get("127.0.0.1", port, "/expires1")
.then((request) => request.close())
.then(processResponse);
httpClient
.get("127.0.0.1", port, "/expires2")
.then((request) => request.close())
.then(processResponse);
});
server.start();
return completer.future;
}
Future testContentType() {
Completer completer = new Completer();
IsolatedHttpServer server = new IsolatedHttpServer();
server.setServerStartedHandler((int port) {
int responses = 0;
HttpClient httpClient = new HttpClient();
void processResponse(HttpClientResponse response) {
Expect.equals(HttpStatus.ok, response.statusCode);
Expect.equals(
"text/html; charset=utf-8", response.headers.contentType.toString());
Expect.equals("text/html", response.headers.contentType.value);
Expect.equals("text", response.headers.contentType.primaryType);
Expect.equals("html", response.headers.contentType.subType);
Expect.equals(
"utf-8", response.headers.contentType.parameters["charset"]);
response.listen((_) {}, onDone: () {
responses++;
if (responses == 2) {
httpClient.close();
server.shutdown();
completer.complete(true);
}
});
}
httpClient.get("127.0.0.1", port, "/contenttype1").then((request) {
request.headers.contentType =
new ContentType("text", "html", charset: "utf-8");
return request.close();
}).then(processResponse);
httpClient.get("127.0.0.1", port, "/contenttype2").then((request) {
request.headers
.set(HttpHeaders.contentTypeHeader, "text/html; charset = utf-8");
return request.close();
}).then(processResponse);
});
server.start();
return completer.future;
}
Future testCookies() {
Completer completer = new Completer();
IsolatedHttpServer server = new IsolatedHttpServer();
server.setServerStartedHandler((int port) {
int responses = 0;
HttpClient httpClient = new HttpClient();
httpClient
.get("127.0.0.1", port, "/cookie1")
.then((request) => request.close())
.then((response) {
Expect.equals(2, response.cookies.length);
response.cookies.forEach((cookie) {
if (cookie.name == "name1") {
Expect.equals("value1", cookie.value);
DateTime date =
new DateTime.utc(2014, DateTime.january, 5, 23, 59, 59, 0);
Expect.equals(date, cookie.expires);
Expect.equals("www.example.com", cookie.domain);
Expect.isTrue(cookie.httpOnly);
} else if (cookie.name == "name2") {
Expect.equals("value2", cookie.value);
Expect.equals(100, cookie.maxAge);
Expect.equals(".example.com", cookie.domain);
Expect.equals("/shop", cookie.path);
} else {
Expect.fail("Unexpected cookie");
}
});
response.listen((_) {}, onDone: () {
httpClient.get("127.0.0.1", port, "/cookie2").then((request) {
request.cookies.add(response.cookies[0]);
request.cookies.add(response.cookies[1]);
return request.close();
}).then((response) {
response.listen((_) {}, onDone: () {
httpClient.close();
server.shutdown();
completer.complete(true);
});
});
});
});
});
server.start();
return completer.future;
}
void main() {
testHost().then((_) {
return testExpires().then((_) {
return testContentType().then((_) {
return testCookies();
});
});
});
}