blob: 0f940e1370cdfb56476053ac19fd2377739a2244 [file] [log] [blame]
// Copyright (c) 2024, 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 'package:cupertino_http/src/cupertino_client.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart';
import 'package:http_profile/http_profile.dart';
import 'package:integration_test/integration_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('profile', () {
final profilingEnabled = HttpClientRequestProfile.profilingEnabled;
setUpAll(() {
HttpClientRequestProfile.profilingEnabled = true;
});
tearDownAll(() {
HttpClientRequestProfile.profilingEnabled = profilingEnabled;
});
group('non-streamed POST', () {
late HttpServer successServer;
late Uri successServerUri;
late HttpClientRequestProfile profile;
setUpAll(() async {
successServer = (await HttpServer.bind('localhost', 0))
..listen((request) async {
await request.drain<void>();
request.response.headers.set('Content-Type', 'text/plain');
request.response.headers.set('Content-Length', '11');
request.response.write('Hello World');
await request.response.close();
});
successServerUri = Uri.http('localhost:${successServer.port}');
final client = CupertinoClientWithProfile.defaultSessionConfiguration();
await client.post(
successServerUri,
headers: {'Content-Type': 'text/plain'},
body: 'Hi',
);
profile = client.profile!;
});
tearDownAll(() {
successServer.close();
});
test('profile attributes', () {
expect(profile.events, isEmpty);
expect(profile.requestMethod, 'POST');
expect(profile.requestUri, successServerUri.toString());
expect(
profile.connectionInfo,
containsPair('package', 'package:cupertino_http'),
);
});
test('request attributes', () {
expect(profile.requestData.bodyBytes, 'Hi'.codeUnits);
expect(profile.requestData.contentLength, 2);
expect(profile.requestData.endTime, isNotNull);
expect(profile.requestData.error, isNull);
expect(
profile.requestData.headers,
containsPair('Content-Length', ['2']),
);
expect(
profile.requestData.headers,
containsPair('Content-Type', ['text/plain; charset=utf-8']),
);
expect(profile.requestData.persistentConnection, isNull);
expect(profile.requestData.proxyDetails, isNull);
expect(profile.requestData.startTime, isNotNull);
});
test('response attributes', () {
expect(profile.responseData.bodyBytes, 'Hello World'.codeUnits);
expect(profile.responseData.compressionState, isNull);
expect(profile.responseData.contentLength, 11);
expect(profile.responseData.endTime, isNotNull);
expect(profile.responseData.error, isNull);
expect(
profile.responseData.headers,
containsPair('content-type', ['text/plain']),
);
expect(
profile.responseData.headers,
containsPair('content-length', ['11']),
);
expect(profile.responseData.isRedirect, false);
expect(profile.responseData.persistentConnection, isNull);
expect(profile.responseData.reasonPhrase, 'OK');
expect(profile.responseData.redirects, isEmpty);
expect(profile.responseData.startTime, isNotNull);
expect(profile.responseData.statusCode, 200);
});
});
group('streaming POST request', () {
late HttpServer successServer;
late Uri successServerUri;
late HttpClientRequestProfile profile;
setUpAll(() async {
successServer = (await HttpServer.bind('localhost', 0))
..listen((request) async {
await request.drain<void>();
request.response.headers.set('Content-Type', 'text/plain');
request.response.headers.set('Content-Length', '11');
request.response.write('Hello World');
await request.response.close();
});
successServerUri = Uri.http('localhost:${successServer.port}');
final client = CupertinoClientWithProfile.defaultSessionConfiguration();
final request = StreamedRequest('POST', successServerUri);
final stream = () async* {
for (var i = 0; i < 1000; ++i) {
await Future<void>.delayed(const Duration());
// The request has started but not finished.
expect(client.profile!.requestData.startTime, isNotNull);
expect(client.profile!.requestData.endTime, isNull);
expect(client.profile!.responseData.startTime, isNull);
expect(client.profile!.responseData.endTime, isNull);
yield 'Hello'.codeUnits;
}
}();
unawaited(
request.sink.addStream(stream).then((_) => request.sink.close()),
);
await client.send(request);
profile = client.profile!;
});
tearDownAll(() {
successServer.close();
});
test('request attributes', () async {
expect(profile.requestData.bodyBytes, ('Hello' * 1000).codeUnits);
expect(profile.requestData.contentLength, isNull);
expect(profile.requestData.endTime, isNotNull);
expect(profile.requestData.startTime, isNotNull);
expect(profile.requestData.headers, isNot(contains('Content-Length')));
});
});
group('failed POST request', () {
late HttpClientRequestProfile profile;
setUpAll(() async {
final client = CupertinoClientWithProfile.defaultSessionConfiguration();
try {
await client.post(
Uri.http('thisisnotahost'),
headers: {'Content-Type': 'text/plain'},
body: 'Hi',
);
fail('expected exception');
} on ClientException {
// Expected exception.
}
profile = client.profile!;
});
test('profile attributes', () {
expect(profile.events, isEmpty);
expect(profile.requestMethod, 'POST');
expect(profile.requestUri, 'http://thisisnotahost');
expect(
profile.connectionInfo,
containsPair('package', 'package:cupertino_http'),
);
});
test('request attributes', () {
expect(profile.requestData.bodyBytes, 'Hi'.codeUnits);
expect(profile.requestData.contentLength, 2);
expect(profile.requestData.endTime, isNotNull);
expect(
profile.requestData.error,
startsWith('NSErrorClientException:'),
);
expect(
profile.requestData.headers,
containsPair('Content-Length', ['2']),
);
expect(
profile.requestData.headers,
containsPair('Content-Type', ['text/plain; charset=utf-8']),
);
expect(profile.requestData.persistentConnection, isNull);
expect(profile.requestData.proxyDetails, isNull);
expect(profile.requestData.startTime, isNotNull);
});
test('response attributes', () {
expect(profile.responseData.bodyBytes, isEmpty);
expect(profile.responseData.compressionState, isNull);
expect(profile.responseData.contentLength, isNull);
expect(profile.responseData.endTime, isNull);
expect(profile.responseData.error, isNull);
expect(profile.responseData.headers, isNull);
expect(profile.responseData.isRedirect, isNull);
expect(profile.responseData.persistentConnection, isNull);
expect(profile.responseData.reasonPhrase, isNull);
expect(profile.responseData.redirects, isEmpty);
expect(profile.responseData.startTime, isNull);
expect(profile.responseData.statusCode, isNull);
});
});
group('failed POST response', () {
late HttpServer successServer;
late Uri successServerUri;
late HttpClientRequestProfile profile;
setUpAll(() async {
successServer = (await HttpServer.bind('localhost', 0))
..listen((request) async {
await request.drain<void>();
request.response.headers.set('Content-Type', 'text/plain');
request.response.headers.set('Content-Length', '11');
final socket = await request.response.detachSocket();
await socket.close();
});
successServerUri = Uri.http('localhost:${successServer.port}');
final client = CupertinoClientWithProfile.defaultSessionConfiguration();
try {
await client.post(
successServerUri,
headers: {'Content-Type': 'text/plain'},
body: 'Hi',
);
fail('expected exception');
} on ClientException {
// Expected exception.
}
profile = client.profile!;
});
tearDownAll(() {
successServer.close();
});
test('profile attributes', () {
expect(profile.events, isEmpty);
expect(profile.requestMethod, 'POST');
expect(profile.requestUri, successServerUri.toString());
expect(
profile.connectionInfo,
containsPair('package', 'package:cupertino_http'),
);
});
test('request attributes', () {
expect(profile.requestData.bodyBytes, 'Hi'.codeUnits);
expect(profile.requestData.contentLength, 2);
expect(profile.requestData.endTime, isNotNull);
expect(profile.requestData.error, isNull);
expect(
profile.requestData.headers,
containsPair('Content-Length', ['2']),
);
expect(
profile.requestData.headers,
containsPair('Content-Type', ['text/plain; charset=utf-8']),
);
expect(profile.requestData.persistentConnection, isNull);
expect(profile.requestData.proxyDetails, isNull);
expect(profile.requestData.startTime, isNotNull);
});
test('response attributes', () {
expect(profile.responseData.bodyBytes, isEmpty);
expect(profile.responseData.compressionState, isNull);
expect(profile.responseData.contentLength, 11);
expect(profile.responseData.endTime, isNotNull);
expect(
profile.responseData.error,
startsWith('NSErrorClientException:'),
);
expect(
profile.responseData.headers,
containsPair('content-type', ['text/plain']),
);
expect(
profile.responseData.headers,
containsPair('content-length', ['11']),
);
expect(profile.responseData.isRedirect, false);
expect(profile.responseData.persistentConnection, isNull);
expect(profile.responseData.reasonPhrase, 'OK');
expect(profile.responseData.redirects, isEmpty);
expect(profile.responseData.startTime, isNotNull);
expect(profile.responseData.statusCode, 200);
});
});
group('cancel streaming GET response', () {
late HttpServer successServer;
late Uri successServerUri;
late HttpClientRequestProfile profile;
late List<int> receivedData;
setUpAll(() async {
successServer = (await HttpServer.bind('localhost', 0))
..listen((request) async {
await request.drain<void>();
request.response.headers.set('Content-Type', 'text/plain');
while (true) {
request.response.write('Hello World');
await request.response.flush();
await Future<void>.delayed(const Duration(seconds: 0));
}
});
final cancelCompleter = Completer<void>();
successServerUri = Uri.http('localhost:${successServer.port}');
final client = CupertinoClientWithProfile.defaultSessionConfiguration();
final request = StreamedRequest('GET', successServerUri);
unawaited(request.sink.close());
final response = await client.send(request);
var i = 0;
late final StreamSubscription<List<int>> s;
receivedData = [];
s = response.stream.listen((d) {
receivedData += d;
if (++i == 1000) {
s.cancel();
cancelCompleter.complete();
}
});
await cancelCompleter.future;
profile = client.profile!;
});
tearDownAll(() {
successServer.close();
});
test('request attributes', () async {
expect(profile.requestData.contentLength, isNull);
expect(profile.requestData.startTime, isNotNull);
expect(profile.requestData.endTime, isNotNull);
expect(profile.responseData.bodyBytes, receivedData);
});
});
group('redirects', () {
late HttpServer successServer;
late Uri successServerUri;
late HttpClientRequestProfile profile;
setUpAll(() async {
successServer = (await HttpServer.bind('localhost', 0))
..listen((request) async {
if (request.requestedUri.pathSegments.isEmpty) {
unawaited(request.response.close());
} else {
final n = int.parse(request.requestedUri.pathSegments.last);
final nextPath = n - 1 == 0 ? '' : '${n - 1}';
unawaited(
request.response.redirect(
successServerUri.replace(path: '/$nextPath'),
),
);
}
});
successServerUri = Uri.http('localhost:${successServer.port}');
});
tearDownAll(() {
successServer.close();
});
test('no redirects', () async {
final client = CupertinoClientWithProfile.defaultSessionConfiguration();
await client.get(successServerUri);
profile = client.profile!;
expect(profile.responseData.redirects, isEmpty);
});
test('follow redirects', () async {
final client = CupertinoClientWithProfile.defaultSessionConfiguration();
await client.send(
Request('GET', successServerUri.replace(path: '/3'))
..followRedirects = true
..maxRedirects = 4,
);
profile = client.profile!;
expect(profile.requestData.followRedirects, true);
expect(profile.requestData.maxRedirects, 4);
expect(profile.responseData.isRedirect, false);
expect(profile.responseData.redirects, [
HttpProfileRedirectData(
statusCode: 302,
method: 'GET',
location: successServerUri.replace(path: '/2').toString(),
),
HttpProfileRedirectData(
statusCode: 302,
method: 'GET',
location: successServerUri.replace(path: '/1').toString(),
),
HttpProfileRedirectData(
statusCode: 302,
method: 'GET',
location: successServerUri.replace(path: '/').toString(),
),
]);
});
test('no follow redirects', () async {
final client = CupertinoClientWithProfile.defaultSessionConfiguration();
await client.send(
Request('GET', successServerUri.replace(path: '/3'))
..followRedirects = false,
);
profile = client.profile!;
expect(profile.requestData.followRedirects, false);
expect(profile.responseData.isRedirect, true);
expect(profile.responseData.redirects, isEmpty);
});
});
});
}