diff --git a/pkgs/cupertino_http/CHANGELOG.md b/pkgs/cupertino_http/CHANGELOG.md index e21c2fd886..b5b1c56c8d 100644 --- a/pkgs/cupertino_http/CHANGELOG.md +++ b/pkgs/cupertino_http/CHANGELOG.md @@ -1,6 +1,8 @@ -## 2.1.2-wip +## 2.2.0 * Cancel requests when the response stream is cancelled. +* Add a new exception type `NSErrorClientException` that contains the + `NSError` associated with the failure. ## 2.1.1 diff --git a/pkgs/cupertino_http/example/integration_test/client_profile_test.dart b/pkgs/cupertino_http/example/integration_test/client_profile_test.dart index aa74415881..eba570d800 100644 --- a/pkgs/cupertino_http/example/integration_test/client_profile_test.dart +++ b/pkgs/cupertino_http/example/integration_test/client_profile_test.dart @@ -164,7 +164,8 @@ void main() { 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.error, startsWith('NSErrorClientException:')); expect( profile.requestData.headers, containsPair('Content-Length', ['2'])); expect(profile.requestData.headers, @@ -247,7 +248,8 @@ void main() { expect(profile.responseData.compressionState, isNull); expect(profile.responseData.contentLength, 11); expect(profile.responseData.endTime, isNotNull); - expect(profile.responseData.error, startsWith('ClientException:')); + expect( + profile.responseData.error, startsWith('NSErrorClientException:')); expect(profile.responseData.headers, containsPair('content-type', ['text/plain'])); expect(profile.responseData.headers, diff --git a/pkgs/cupertino_http/example/integration_test/ns_error_client_exception.dart b/pkgs/cupertino_http/example/integration_test/ns_error_client_exception.dart new file mode 100644 index 0000000000..46b34befeb --- /dev/null +++ b/pkgs/cupertino_http/example/integration_test/ns_error_client_exception.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2025, 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 'package:cupertino_http/cupertino_http.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:objective_c/objective_c.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('NSErrorClientException', () { + late CupertinoClient client; + + setUpAll(() => client = CupertinoClient.defaultSessionConfiguration()); + tearDownAll(() => client.close()); + + test('thrown', () async { + expect( + () => client.get(Uri.http('doesnotexist', '/')), + throwsA(isA() + .having((e) => e.error.domain.toDartString(), 'error.domain', + 'NSURLErrorDomain') + .having((e) => e.error.code, 'error.code', -1003) + .having( + (e) => e.toString(), + 'toString()', + 'NSErrorClientException: A server with the specified ' + 'hostname could not be found. ' + '[domain=NSURLErrorDomain, code=-1003], ' + 'uri=http://doesnotexist/'))); + }); + }); +} diff --git a/pkgs/cupertino_http/lib/cupertino_http.dart b/pkgs/cupertino_http/lib/cupertino_http.dart index b85a868b4e..a24e2a8fc3 100644 --- a/pkgs/cupertino_http/lib/cupertino_http.dart +++ b/pkgs/cupertino_http/lib/cupertino_http.dart @@ -92,5 +92,5 @@ import 'package:http/http.dart'; import 'src/cupertino_client.dart'; export 'src/cupertino_api.dart'; -export 'src/cupertino_client.dart' show CupertinoClient; +export 'src/cupertino_client.dart' show CupertinoClient, NSErrorClientException; export 'src/cupertino_web_socket.dart'; diff --git a/pkgs/cupertino_http/lib/src/cupertino_client.dart b/pkgs/cupertino_http/lib/src/cupertino_client.dart index 72db68525e..7e592eb3e6 100644 --- a/pkgs/cupertino_http/lib/src/cupertino_client.dart +++ b/pkgs/cupertino_http/lib/src/cupertino_client.dart @@ -17,6 +17,26 @@ final _digitRegex = RegExp(r'^\d+$'); const _nsurlErrorCancelled = -999; +/// A [ClientException] generated from an [NSError]. +class NSErrorClientException extends ClientException { + final NSError error; + + NSErrorClientException(this.error, [Uri? uri]) + : super(error.localizedDescription.toDartString(), uri); + + @override + String toString() { + final b = StringBuffer( + 'NSErrorClientException: ${error.localizedDescription.toDartString()} ' + '[domain=${error.domain.toDartString()}, code=${error.code}]'); + + if (uri != null) { + b.write(', uri=$uri'); + } + return b.toString(); + } +} + /// This class can be removed when `package:http` v2 is released. class _StreamedResponseWithUrl extends StreamedResponse implements BaseResponseWithUrl { @@ -176,8 +196,7 @@ class CupertinoClient extends BaseClient { if (error != null && !(error.domain.toDartString() == 'NSURLErrorDomain' && error.code == _nsurlErrorCancelled)) { - final exception = ClientException( - error.localizedDescription.toDartString(), taskTracker.request.url); + final exception = NSErrorClientException(error, taskTracker.request.url); if (taskTracker.profile != null && taskTracker.profile!.requestData.endTime == null) { // Error occurred during the request. diff --git a/pkgs/cupertino_http/pubspec.yaml b/pkgs/cupertino_http/pubspec.yaml index 585b5ea7ac..fd5a228ff6 100644 --- a/pkgs/cupertino_http/pubspec.yaml +++ b/pkgs/cupertino_http/pubspec.yaml @@ -1,5 +1,5 @@ name: cupertino_http -version: 2.1.2-wip +version: 2.2.0-wip description: >- A macOS/iOS Flutter plugin that provides access to the Foundation URL Loading System.