blob: a85b317a10a6b659b6a213f224fd8e84056c9b3d [file] [log] [blame]
// Copyright (c) 2022, 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:io';
import 'package:cupertino_http/cupertino_http.dart';
import 'package:integration_test/integration_test.dart';
import 'package:objective_c/objective_c.dart';
import 'package:test/test.dart';
void testWebSocketTask() {
group('websocket', () {
late HttpServer server;
int? lastCloseCode;
String? lastCloseReason;
setUp(() async {
lastCloseCode = null;
lastCloseReason = null;
server = await HttpServer.bind('localhost', 0)
..listen((request) {
if (request.uri.path.endsWith('error')) {
request.response.statusCode = 500;
request.response.close();
} else {
WebSocketTransformer.upgrade(request)
.then((websocket) => websocket.listen((event) {
final code = request.uri.queryParameters['code'];
final reason = request.uri.queryParameters['reason'];
websocket.add(event);
if (!request.uri.queryParameters.containsKey('noclose')) {
websocket.close(
code == null ? null : int.parse(code), reason);
}
}, onDone: () {
lastCloseCode = websocket.closeCode;
lastCloseReason = websocket.closeReason;
}));
}
});
});
tearDown(() async {
await server.close();
});
test('background session', () {
final session = URLSession.sessionWithConfiguration(
URLSessionConfiguration.backgroundSession('background'));
expect(
() => session.webSocketTaskWithRequest(URLRequest.fromUrl(
Uri.parse('ws://localhost:${server.port}/?noclose'))),
throwsUnsupportedError);
session.finishTasksAndInvalidate();
});
test('client code and reason', () async {
final session = URLSession.sharedSession();
final task = session.webSocketTaskWithRequest(URLRequest.fromUrl(
Uri.parse('ws://localhost:${server.port}/?noclose')))
..resume();
await task
.sendMessage(URLSessionWebSocketMessage.fromString('Hello World!'));
await task.receiveMessage();
task.cancelWithCloseCode(4998, 'Bye'.codeUnits.toNSData());
// Allow the server to run and save the close code.
while (lastCloseCode == null) {
await Future<void>.delayed(const Duration(milliseconds: 10));
}
expect(lastCloseCode, 4998);
expect(lastCloseReason, 'Bye');
});
test('server code and reason', () async {
final session = URLSession.sharedSession();
final task = session.webSocketTaskWithRequest(URLRequest.fromUrl(
Uri.parse('ws://localhost:${server.port}/?code=4999&reason=fun')))
..resume();
await task
.sendMessage(URLSessionWebSocketMessage.fromString('Hello World!'));
await task.receiveMessage();
await expectLater(
task.receiveMessage(),
throwsA(
isA<NSError>().having((e) => e.code, 'code', 57 // NOT_CONNECTED
)));
expect(task.closeCode, 4999);
expect(task.closeReason!.toList(), 'fun'.codeUnits);
task.cancel();
session.finishTasksAndInvalidate();
});
test('data message', () async {
final session = URLSession.sharedSession();
final task = session.webSocketTaskWithRequest(
URLRequest.fromUrl(Uri.parse('ws://localhost:${server.port}')))
..resume();
await task.sendMessage(
URLSessionWebSocketMessage.fromData([1, 2, 3].toNSData()));
final receivedMessage = await task.receiveMessage();
expect(
receivedMessage.type,
NSURLSessionWebSocketMessageType
.NSURLSessionWebSocketMessageTypeData);
expect(receivedMessage.data!.toList(), [1, 2, 3]);
expect(receivedMessage.string, null);
task.cancel();
});
test('text message', () async {
final session = URLSession.sharedSession();
final task = session.webSocketTaskWithRequest(
URLRequest.fromUrl(Uri.parse('ws://localhost:${server.port}')))
..resume();
await task
.sendMessage(URLSessionWebSocketMessage.fromString('Hello World!'));
final receivedMessage = await task.receiveMessage();
expect(
receivedMessage.type,
NSURLSessionWebSocketMessageType
.NSURLSessionWebSocketMessageTypeString);
expect(receivedMessage.data, null);
expect(receivedMessage.string, 'Hello World!');
task.cancel();
});
test('send failure', () async {
final session = URLSession.sharedSession();
final task = session.webSocketTaskWithRequest(
URLRequest.fromUrl(Uri.parse('ws://localhost:${server.port}/error')))
..resume();
await expectLater(
task.sendMessage(
URLSessionWebSocketMessage.fromString('Hello World!')),
throwsA(isA<NSError>().having(
(e) => e.code, 'code', -1011 // NSURLErrorBadServerResponse
)));
task.cancel();
});
test('receive failure', () async {
final session = URLSession.sharedSession();
final task = session.webSocketTaskWithRequest(
URLRequest.fromUrl(Uri.parse('ws://localhost:${server.port}')))
..resume();
await task
.sendMessage(URLSessionWebSocketMessage.fromString('Hello World!'));
await task.receiveMessage();
await expectLater(
task.receiveMessage(),
throwsA(
isA<NSError>().having((e) => e.code, 'code', 57 // NOT_CONNECTED
)));
task.cancel();
});
});
}
void testURLSessionTaskCommon(
URLSessionTask Function(URLSession session, Uri url) f,
{bool suspendedAfterCancel = false}) {
group('task states', () {
late HttpServer server;
late URLSessionTask task;
setUp(() async {
server = (await HttpServer.bind('localhost', 0))
..listen((request) async {
await request.drain<void>();
request.response.headers.set('Content-Type', 'text/plain');
request.response.write('Hello World');
await request.response.close();
});
final session = URLSession.sharedSession();
task = f(session, Uri.parse('http://localhost:${server.port}'));
});
tearDown(() {
task.cancel();
server.close();
});
test('starts suspended', () {
expect(task.state, NSURLSessionTaskState.NSURLSessionTaskStateSuspended);
expect(task.response, null);
task.toString(); // Just verify that there is no crash.
});
test('resume to running', () {
task.resume();
expect(task.state, NSURLSessionTaskState.NSURLSessionTaskStateRunning);
expect(task.response, null);
task.toString(); // Just verify that there is no crash.
});
test('cancel', () {
task.cancel();
if (suspendedAfterCancel) {
expect(
task.state, NSURLSessionTaskState.NSURLSessionTaskStateSuspended);
} else {
expect(
task.state, NSURLSessionTaskState.NSURLSessionTaskStateCanceling);
}
expect(task.response, null);
task.toString(); // Just verify that there is no crash.
});
test('completed', () async {
task.resume();
while (
task.state != NSURLSessionTaskState.NSURLSessionTaskStateCompleted) {
// Let the event loop run.
await Future<void>(() {});
}
});
});
group('task completed', () {
late HttpServer server;
late URLSessionTask task;
setUp(() async {
server = (await HttpServer.bind('localhost', 0))
..listen((request) async {
await request.drain<void>();
request.response.headers.set('Content-Type', 'text/plain');
request.response.write('Hello World');
await request.response.close();
});
final session = URLSession.sharedSession();
task = session.dataTaskWithRequest(
MutableURLRequest.fromUrl(
Uri.parse('http://localhost:${server.port}/mypath'))
..httpMethod = 'POST'
..httpBody = [1, 2, 3].toNSData())
..prefersIncrementalDelivery = false
..priority = 0.2
..taskDescription = 'my task description'
..resume();
while (
task.state != NSURLSessionTaskState.NSURLSessionTaskStateCompleted) {
// Let the event loop run.
await Future<void>(() {});
}
});
tearDown(() {
task.cancel();
server.close();
});
test('priority', () async {
expect(task.priority, inInclusiveRange(0.19, 0.21));
});
test('current request', () async {
expect(task.currentRequest!.url!.path, '/mypath');
});
test('original request', () async {
expect(task.originalRequest!.url!.path, '/mypath');
});
test('has response', () async {
expect(task.response, isA<HTTPURLResponse>());
});
test('no error', () async {
expect(task.error, null);
});
test('countOfBytesExpectedToReceive - no content length set', () async {
expect(task.countOfBytesExpectedToReceive, -1);
});
test('countOfBytesReceived', () async {
expect(task.countOfBytesReceived, 11);
});
test('countOfBytesExpectedToSend', () async {
expect(task.countOfBytesExpectedToSend, 3);
});
test('countOfBytesSent', () async {
expect(task.countOfBytesSent, 3);
});
test('taskDescription', () {
expect(task.taskDescription, 'my task description');
});
test('taskIdentifier', () {
task.taskIdentifier; // Just verify that there is no crash.
});
test('prefersIncrementalDelivery', () {
expect(task.prefersIncrementalDelivery, false);
});
test('toString', () {
task.toString(); // Just verify that there is no crash.
});
});
group('task failed', () {
late URLSessionTask task;
setUp(() async {
final session = URLSession.sharedSession();
task = session.dataTaskWithRequest(
MutableURLRequest.fromUrl(Uri.parse('http://notarealserver')))
..resume();
while (
task.state != NSURLSessionTaskState.NSURLSessionTaskStateCompleted) {
// Let the event loop run.
await Future<void>(() {});
}
});
tearDown(() {
task.cancel();
});
test('no response', () async {
expect(task.response, null);
});
test('no error', () async {
expect(task.error!.code, -1003); // CannotFindHost
});
test('toString', () {
task.toString(); // Just verify that there is no crash.
});
});
group('task redirect', () {
late HttpServer server;
late URLSessionTask task;
setUp(() async {
// The task will request http://localhost:XXX/launch, which will be
// redirected to http://localhost:XXX/landed.
server = (await HttpServer.bind('localhost', 0))
..listen((request) async {
await request.drain<void>();
if (request.requestedUri.path != '/landed') {
await request.response.redirect(Uri(path: '/landed'));
return;
}
request.response.headers.set('Content-Type', 'text/plain');
request.response.write('Hello World');
await request.response.close();
});
final session = URLSession.sharedSession();
task = session.dataTaskWithRequest(MutableURLRequest.fromUrl(
Uri.parse('http://localhost:${server.port}/launch')))
..resume();
while (
task.state != NSURLSessionTaskState.NSURLSessionTaskStateCompleted) {
// Let the event loop run.
await Future<void>(() {});
}
});
tearDown(() {
task.cancel();
server.close();
});
test('current request', () async {
expect(task.currentRequest!.url!.path, '/landed');
});
test('original request', () async {
expect(task.originalRequest!.url!.path, '/launch');
});
test('toString', () {
task.toString(); // Just verify that there is no crash.
});
});
}
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('data task', () {
testURLSessionTaskCommon(
(session, uri) => session.dataTaskWithRequest(URLRequest.fromUrl(uri)));
});
group('download task', () {
testURLSessionTaskCommon((session, uri) =>
session.downloadTaskWithRequest(URLRequest.fromUrl(uri)));
});
group('websocket task', () {
testURLSessionTaskCommon(
(session, uri) =>
session.webSocketTaskWithRequest(URLRequest.fromUrl(uri)),
suspendedAfterCancel: true);
});
testWebSocketTask();
}