| // 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 |
| |
| // @dart = 2.9 |
| |
| import 'dart:async'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import "package:async_helper/async_helper.dart"; |
| import "package:expect/expect.dart"; |
| |
| void testGetEmptyRequest() { |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| server.listen((request) { |
| request.cast<List<int>>().pipe(request.response); |
| }); |
| |
| var client = new HttpClient(); |
| client |
| .get("127.0.0.1", server.port, "/") |
| .then((request) => request.close()) |
| .then((response) { |
| response.listen((data) {}, onDone: server.close); |
| }); |
| }); |
| } |
| |
| void testGetDataRequest() { |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| var data = "lalala".codeUnits; |
| server.listen((request) { |
| request.response.add(data); |
| request.cast<List<int>>().pipe(request.response); |
| }); |
| |
| var client = new HttpClient(); |
| client |
| .get("127.0.0.1", server.port, "/") |
| .then((request) => request.close()) |
| .then((response) { |
| int count = 0; |
| response.listen((data) => count += data.length, onDone: () { |
| server.close(); |
| Expect.equals(data.length, count); |
| }); |
| }); |
| }); |
| } |
| |
| void testGetInvalidHost() { |
| asyncStart(); |
| var client = new HttpClient(); |
| client.get("__SOMETHING_INVALID__", 8888, "/").catchError((error) { |
| client.close(); |
| asyncEnd(); |
| }); |
| } |
| |
| void testGetServerClose() { |
| asyncStart(); |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| server.listen((request) { |
| server.close(); |
| new Timer(const Duration(milliseconds: 100), () { |
| request.response.close(); |
| }); |
| }); |
| |
| var client = new HttpClient(); |
| client |
| .get("127.0.0.1", server.port, "/") |
| .then((request) => request.close()) |
| .then((response) => response.drain()) |
| .then((_) => asyncEnd()); |
| }); |
| } |
| |
| void testGetServerCloseNoKeepAlive() { |
| asyncStart(); |
| var client = new HttpClient(); |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| int port = server.port; |
| server.first.then((request) => request.response.close()); |
| |
| client |
| .get("127.0.0.1", port, "/") |
| .then((request) => request.close()) |
| .then((response) => response.drain()) |
| .then((_) => client.get("127.0.0.1", port, "/")) |
| .then((request) => request.close()) |
| .then((_) => Expect.fail('should not succeed'), onError: (_) {}) |
| .then((_) => asyncEnd()); |
| }); |
| } |
| |
| void testGetServerForceClose() { |
| asyncStart(); |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| server.listen((request) { |
| server.close(force: true); |
| }); |
| |
| var client = new HttpClient(); |
| client |
| .get("127.0.0.1", server.port, "/") |
| .then((request) => request.close()) |
| .then((response) { |
| Expect.fail("Request not expected"); |
| }).catchError((error) => asyncEnd(), |
| test: (error) => error is HttpException); |
| }); |
| } |
| |
| void testGetDataServerForceClose() { |
| asyncStart(); |
| var completer = new Completer(); |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| server.listen((request) { |
| request.response.bufferOutput = false; |
| request.response.contentLength = 100; |
| request.response.write("data"); |
| request.response.write("more data"); |
| completer.future.then((_) => server.close(force: true)); |
| }); |
| |
| var client = new HttpClient(); |
| client |
| .get("127.0.0.1", server.port, "/") |
| .then((request) => request.close()) |
| .then((response) { |
| // Close the (incomplete) response, now that we have seen |
| // the response object. |
| completer.complete(null); |
| int errors = 0; |
| response.listen((data) {}, |
| onError: (error) => errors++, |
| onDone: () { |
| Expect.equals(1, errors); |
| asyncEnd(); |
| }); |
| }); |
| }); |
| } |
| |
| typedef Future<HttpClientRequest> Callback1(String a1, int a2, String a3); |
| void testOpenEmptyRequest() { |
| var client = new HttpClient(); |
| var methods = [ |
| [client.get, 'GET'], |
| [client.post, 'POST'], |
| [client.put, 'PUT'], |
| [client.delete, 'DELETE'], |
| [client.patch, 'PATCH'], |
| [client.head, 'HEAD'] |
| ]; |
| |
| for (var method in methods) { |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| server.listen((request) { |
| Expect.equals(method[1], request.method); |
| request.cast<List<int>>().pipe(request.response); |
| }); |
| |
| Callback1 cb = method[0] as Callback1; |
| cb("127.0.0.1", server.port, "/") |
| .then((request) => request.close()) |
| .then((response) { |
| response.listen((data) {}, onDone: server.close); |
| }); |
| }); |
| } |
| } |
| |
| typedef Future<HttpClientRequest> Callback2(Uri a1); |
| void testOpenUrlEmptyRequest() { |
| var client = new HttpClient(); |
| var methods = [ |
| [client.getUrl, 'GET'], |
| [client.postUrl, 'POST'], |
| [client.putUrl, 'PUT'], |
| [client.deleteUrl, 'DELETE'], |
| [client.patchUrl, 'PATCH'], |
| [client.headUrl, 'HEAD'] |
| ]; |
| |
| for (var method in methods) { |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| server.listen((request) { |
| Expect.equals(method[1], request.method); |
| request.cast<List<int>>().pipe(request.response); |
| }); |
| |
| Callback2 cb = method[0] as Callback2; |
| cb(Uri.parse("http://127.0.0.1:${server.port}/")) |
| .then((request) => request.close()) |
| .then((response) { |
| response.listen((data) {}, onDone: server.close); |
| }); |
| }); |
| } |
| } |
| |
| void testNoBuffer() { |
| asyncStart(); |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| var response; |
| server.listen((request) { |
| response = request.response; |
| response.bufferOutput = false; |
| response.writeln('init'); |
| }); |
| |
| var client = new HttpClient(); |
| client |
| .get("127.0.0.1", server.port, "/") |
| .then((request) => request.close()) |
| .then((clientResponse) { |
| var iterator = new StreamIterator(clientResponse |
| .cast<List<int>>() |
| .transform(utf8.decoder) |
| .transform(new LineSplitter())); |
| iterator.moveNext().then((hasValue) { |
| Expect.isTrue(hasValue); |
| Expect.equals('init', iterator.current); |
| int count = 0; |
| void run() { |
| if (count == 10) { |
| response.close(); |
| iterator.moveNext().then((hasValue) { |
| Expect.isFalse(hasValue); |
| server.close(); |
| asyncEnd(); |
| }); |
| } else { |
| response.writeln('output$count'); |
| iterator.moveNext().then((hasValue) { |
| Expect.isTrue(hasValue); |
| Expect.equals('output$count', iterator.current); |
| count++; |
| run(); |
| }); |
| } |
| } |
| |
| run(); |
| }); |
| }); |
| }); |
| } |
| |
| void testMaxConnectionsPerHost(int connectionCap, int connections) { |
| asyncStart(); |
| HttpServer.bind("127.0.0.1", 0).then((server) { |
| int handled = 0; |
| server.listen((request) { |
| Expect.isTrue(server.connectionsInfo().total <= connectionCap); |
| request.response.close(); |
| handled++; |
| if (handled == connections) { |
| asyncEnd(); |
| server.close(); |
| } |
| }); |
| |
| var client = new HttpClient(); |
| client.maxConnectionsPerHost = connectionCap; |
| for (int i = 0; i < connections; i++) { |
| asyncStart(); |
| client |
| .get("127.0.0.1", server.port, "/") |
| .then((request) => request.close()) |
| .then((response) { |
| response.listen(null, onDone: () { |
| asyncEnd(); |
| }); |
| }); |
| } |
| }); |
| } |
| |
| Future<void> testMaxConnectionsWithFailure() async { |
| // When DNS lookup failed, counter for connecting doesn't decrement which |
| // prevents the following connections. |
| final client = HttpClient(); |
| client.maxConnectionsPerHost = 1; |
| try { |
| await client.getUrl(Uri.parse('http://domain.invalid')); |
| } catch (e) { |
| if (e is! SocketException) { |
| Expect.fail("Unexpected exception $e is thrown"); |
| } |
| } |
| try { |
| await client.getUrl(Uri.parse('http://domain.invalid')); |
| Expect.fail("Calls exceed client's maxConnectionsPerHost should throw " |
| "exceptions as well"); |
| } catch (e) { |
| if (e is! SocketException) { |
| Expect.fail("Unexpected exception $e is thrown"); |
| } |
| } |
| } |
| |
| Future<void> testHttpAbort() async { |
| // Test that abort() is called after request is sent. |
| asyncStart(); |
| final completer = Completer<void>(); |
| final server = await HttpServer.bind("127.0.0.1", 0); |
| server.listen((request) { |
| completer.complete(); |
| request.response.close(); |
| }); |
| |
| final request = await HttpClient().get("127.0.0.1", server.port, "/"); |
| request.headers.add(HttpHeaders.contentLengthHeader, "8"); |
| request.write('somedata'); |
| completer.future.then((_) { |
| request.abort(); |
| asyncStart(); |
| Future.delayed(Duration(milliseconds: 500), () { |
| server.close(); |
| asyncEnd(); |
| }); |
| }); |
| request.close().then((response) { |
| Expect.fail('abort() prevents a response being returned'); |
| }, onError: (e) { |
| Expect.type<HttpException>(e); |
| Expect.isTrue(e.toString().contains('abort')); |
| asyncEnd(); |
| }); |
| } |
| |
| Future<void> testHttpAbortBeforeWrite() async { |
| // Test that abort() is called before write(). No message should be sent from |
| // HttpClientRequest. |
| asyncStart(); |
| final completer = Completer<Socket>(); |
| final server = await ServerSocket.bind("127.0.0.1", 0); |
| server.listen((s) async { |
| s.listen((data) { |
| Expect.fail('No message should be received'); |
| }); |
| await Future.delayed(Duration(milliseconds: 500)); |
| completer.complete(s); |
| }); |
| |
| final request = await HttpClient().get("127.0.0.1", server.port, "/"); |
| request.headers.add(HttpHeaders.contentLengthHeader, "8"); |
| // This HttpException will go to onError callback. |
| request.abort(HttpException('Error')); |
| asyncStart(); |
| request.write('somedata'); |
| completer.future.then((socket) { |
| socket.destroy(); |
| server.close(); |
| asyncEnd(); |
| }); |
| request.close().then((response) { |
| Expect.fail('abort() prevents a response being returned'); |
| }, onError: (e) { |
| Expect.type<HttpException>(e); |
| asyncEnd(); |
| }); |
| } |
| |
| Future<void> testHttpAbortBeforeClose() async { |
| // Test that abort() is called after write(). Some messages added prior to |
| // abort() are sent. |
| final completer = new Completer<void>(); |
| asyncStart(); |
| final server = await ServerSocket.bind("127.0.0.1", 0); |
| server.listen((s) { |
| StringBuffer buffer = StringBuffer(); |
| s.listen((data) { |
| buffer.write(utf8.decode(data)); |
| if (buffer.toString().contains("content-length: 8")) { |
| completer.complete(); |
| s.destroy(); |
| server.close(); |
| } |
| }); |
| }); |
| |
| final request = await HttpClient().get("127.0.0.1", server.port, "/"); |
| // Add an additional header field for server to verify. |
| request.headers.add(HttpHeaders.contentLengthHeader, "8"); |
| request.write('somedata'); |
| await completer.future; |
| final string = 'abort message'; |
| request.abort(string); |
| request.close().then((response) { |
| Expect.fail('abort() prevents a response being returned'); |
| }, onError: (e) { |
| Expect.type<String>(e); |
| Expect.equals(string, e); |
| asyncEnd(); |
| }); |
| } |
| |
| Future<void> testHttpAbortAfterClose() async { |
| // Test that abort() is called after response is received. It should not |
| // affect HttpClientResponse. |
| asyncStart(); |
| final value = 'someRandomData'; |
| final server = await HttpServer.bind("127.0.0.1", 0); |
| server.listen((request) { |
| request.response.write(value); |
| request.response.close(); |
| }); |
| |
| final request = await HttpClient().get("127.0.0.1", server.port, "/"); |
| request.close().then((response) { |
| request.abort(); |
| response.listen((data) { |
| Expect.equals(utf8.decode(data), value); |
| }, onDone: () { |
| asyncEnd(); |
| server.close(); |
| }); |
| }); |
| } |
| |
| void main() async { |
| testGetEmptyRequest(); |
| testGetDataRequest(); |
| testGetInvalidHost(); |
| testGetServerClose(); |
| testGetServerCloseNoKeepAlive(); |
| testGetServerForceClose(); |
| testGetDataServerForceClose(); |
| testOpenEmptyRequest(); |
| testOpenUrlEmptyRequest(); |
| testNoBuffer(); |
| testMaxConnectionsPerHost(1, 1); |
| testMaxConnectionsPerHost(1, 10); |
| testMaxConnectionsPerHost(5, 10); |
| testMaxConnectionsPerHost(10, 50); |
| testMaxConnectionsWithFailure(); |
| await testHttpAbort(); |
| await testHttpAbortBeforeWrite(); |
| await testHttpAbortBeforeClose(); |
| await testHttpAbortAfterClose(); |
| } |