Skip to content

Commit

Permalink
Integrate cronet_http with package:http_profile (#1167)
Browse files Browse the repository at this point in the history
  • Loading branch information
brianquinlan authored May 23, 2024
1 parent 46d49e3 commit 4726f43
Show file tree
Hide file tree
Showing 6 changed files with 415 additions and 16 deletions.
5 changes: 5 additions & 0 deletions pkgs/cronet_http/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.3.0

* Add integration to the
[DevTools Network View](https://docs.flutter.dev/tools/devtools/network).

## 1.2.1

* Upgrade `package:jni` to 0.9.2 to fix the build error in the latest versions
Expand Down
288 changes: 288 additions & 0 deletions pkgs/cronet_http/example/integration_test/client_profile_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
// 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:cronet_http/src/cronet_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('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 = CronetClientWithProfile.defaultCronetEngine();
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:cronet_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('failed POST request', () {
late HttpClientRequestProfile profile;

setUpAll(() async {
final client = CronetClientWithProfile.defaultCronetEngine();
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:cronet_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('ClientException:'));
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 = CronetClientWithProfile.defaultCronetEngine();

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:cronet_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('ClientException:'));
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('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 = CronetClientWithProfile.defaultCronetEngine();
await client.get(successServerUri);
profile = client.profile!;

expect(profile.responseData.redirects, isEmpty);
});

test('follow redirects', () async {
final client = CronetClientWithProfile.defaultCronetEngine();
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 = CronetClientWithProfile.defaultCronetEngine();
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);
});
});
});
}
39 changes: 31 additions & 8 deletions pkgs/cronet_http/example/integration_test/client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,41 @@

import 'package:cronet_http/cronet_http.dart';
import 'package:http_client_conformance_tests/http_client_conformance_tests.dart';
import 'package:http_profile/http_profile.dart';
import 'package:integration_test/integration_test.dart';
import 'package:test/test.dart';

Future<void> testConformance() async {
group(
'default cronet engine',
() => testAll(
CronetClient.defaultCronetEngine,
canStreamRequestBody: false,
canReceiveSetCookieHeaders: true,
canSendCookieHeaders: true,
));
group('default cronet engine', () {
group('profile enabled', () {
final profile = HttpClientRequestProfile.profilingEnabled;
HttpClientRequestProfile.profilingEnabled = true;
try {
testAll(
CronetClient.defaultCronetEngine,
canStreamRequestBody: false,
canReceiveSetCookieHeaders: true,
canSendCookieHeaders: true,
);
} finally {
HttpClientRequestProfile.profilingEnabled = profile;
}
});
group('profile disabled', () {
final profile = HttpClientRequestProfile.profilingEnabled;
HttpClientRequestProfile.profilingEnabled = false;
try {
testAll(
CronetClient.defaultCronetEngine,
canStreamRequestBody: false,
canReceiveSetCookieHeaders: true,
canSendCookieHeaders: true,
);
} finally {
HttpClientRequestProfile.profilingEnabled = profile;
}
});
});

group('from cronet engine', () {
testAll(
Expand Down
3 changes: 2 additions & 1 deletion pkgs/cronet_http/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Demonstrates how to use the cronet_http plugin.
publish_to: 'none'

environment:
sdk: ^3.2.0
sdk: ^3.4.0

dependencies:
cronet_http:
Expand All @@ -22,6 +22,7 @@ dev_dependencies:
sdk: flutter
http_client_conformance_tests:
path: ../../http_client_conformance_tests/
http_profile: ^0.1.0
integration_test:
sdk: flutter
test: ^1.23.1
Expand Down
Loading

0 comments on commit 4726f43

Please sign in to comment.