|  | // 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. | 
|  |  | 
|  | // VMOptions= | 
|  | // VMOptions=--short_socket_read | 
|  | // VMOptions=--short_socket_write | 
|  | // VMOptions=--short_socket_read --short_socket_write | 
|  |  | 
|  | import "dart:isolate"; | 
|  | import "dart:io"; | 
|  | import "package:expect/expect.dart"; | 
|  |  | 
|  | class IsolatedHttpServer { | 
|  | IsolatedHttpServer(); | 
|  |  | 
|  | void setServerStartedHandler(void startedCallback(int port)) { | 
|  | _startedCallback = startedCallback; | 
|  | } | 
|  |  | 
|  | void start([bool chunkedEncoding = false]) { | 
|  | ReceivePort receivePort = new ReceivePort(); | 
|  | var remote = Isolate.spawn(startIsolatedHttpServer, receivePort.sendPort); | 
|  | receivePort.first.then((port) { | 
|  | _serverPort = port; | 
|  |  | 
|  | if (chunkedEncoding) { | 
|  | // Send chunked encoding message to the server. | 
|  | port.send([ | 
|  | new IsolatedHttpServerCommand.chunkedEncoding(), | 
|  | _statusPort.sendPort | 
|  | ]); | 
|  | } | 
|  |  | 
|  | // 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(); | 
|  | } | 
|  |  | 
|  | final _statusPort = | 
|  | new ReceivePort(); // Port for receiving messages from the server. | 
|  | late 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 = 0; | 
|  | } | 
|  |  | 
|  | void startIsolatedHttpServer(Object replyToObj) { | 
|  | final replyTo = replyToObj as SendPort; | 
|  | var server = new TestServer(); | 
|  | server.init(); | 
|  | replyTo.send(server.dispatchSendPort); | 
|  | } | 
|  |  | 
|  | class TestServer { | 
|  | // Echo the request content back to the response. | 
|  | void _echoHandler(HttpRequest request) { | 
|  | var response = request.response; | 
|  | Expect.equals("POST", request.method); | 
|  | response.contentLength = request.contentLength; | 
|  | request.cast<List<int>>().pipe(response); | 
|  | } | 
|  |  | 
|  | // 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(); | 
|  | } | 
|  |  | 
|  | void init() { | 
|  | // Setup request handlers. | 
|  | _requestHandlers["/echo"] = _echoHandler; | 
|  | _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); | 
|  | } | 
|  | } | 
|  |  | 
|  | late HttpServer _server; // HTTP server instance. | 
|  | final _dispatchPort = new ReceivePort(); | 
|  | final _requestHandlers = {}; | 
|  | bool _chunkedEncoding = false; | 
|  | } | 
|  |  | 
|  | void testRead(bool chunkedEncoding) { | 
|  | String data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | 
|  | final int kMessageCount = 10; | 
|  |  | 
|  | IsolatedHttpServer server = new IsolatedHttpServer(); | 
|  |  | 
|  | void runTest(int port) { | 
|  | int count = 0; | 
|  | HttpClient httpClient = new HttpClient(); | 
|  | void sendRequest() { | 
|  | httpClient.post("127.0.0.1", port, "/echo").then((request) { | 
|  | if (chunkedEncoding) { | 
|  | request.write(data.substring(0, 10)); | 
|  | request.write(data.substring(10, data.length)); | 
|  | } else { | 
|  | request.contentLength = data.length; | 
|  | request.add(data.codeUnits); | 
|  | } | 
|  | return request.close(); | 
|  | }).then((response) { | 
|  | Expect.equals(HttpStatus.ok, response.statusCode); | 
|  | List<int> body = <int>[]; | 
|  | response.listen(body.addAll, onDone: () { | 
|  | Expect.equals(data, new String.fromCharCodes(body)); | 
|  | count++; | 
|  | if (count < kMessageCount) { | 
|  | sendRequest(); | 
|  | } else { | 
|  | httpClient.close(); | 
|  | server.shutdown(); | 
|  | } | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | sendRequest(); | 
|  | } | 
|  |  | 
|  | server.setServerStartedHandler(runTest); | 
|  | server.start(chunkedEncoding); | 
|  | } | 
|  |  | 
|  | void main() { | 
|  | testRead(true); | 
|  | testRead(false); | 
|  | } |