diff --git a/pkgs/cupertino_http/CHANGELOG.md b/pkgs/cupertino_http/CHANGELOG.md index 48d990e185..47e25cb590 100644 --- a/pkgs/cupertino_http/CHANGELOG.md +++ b/pkgs/cupertino_http/CHANGELOG.md @@ -1,4 +1,7 @@ -## 1.1.1-wip +## 1.2.0-wip + +* Add support for setting additional http headers in + `URLSessionConfiguration`. ## 1.1.0 diff --git a/pkgs/cupertino_http/example/integration_test/url_session_configuration_test.dart b/pkgs/cupertino_http/example/integration_test/url_session_configuration_test.dart index 85386bcd90..ab117c37d0 100644 --- a/pkgs/cupertino_http/example/integration_test/url_session_configuration_test.dart +++ b/pkgs/cupertino_http/example/integration_test/url_session_configuration_test.dart @@ -2,10 +2,38 @@ // 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:test/test.dart'; +/// Make a HTTP request using the given configuration and return the headers +/// received by the server. +Future>> sentHeaders( + URLSessionConfiguration config) async { + final session = URLSession.sessionWithConfiguration(config); + final headers = >{}; + final server = (await HttpServer.bind('localhost', 0)) + ..listen((request) async { + request.headers.forEach((k, v) => headers[k] = v); + await request.drain(); + request.response.headers.set('Content-Type', 'text/plain'); + request.response.write('Hello World'); + await request.response.close(); + }); + + final task = session.dataTaskWithRequest(URLRequest.fromUrl( + Uri(scheme: 'http', host: 'localhost', port: server.port))) + ..resume(); + while (task.state != URLSessionTaskState.urlSessionTaskStateCompleted) { + await pumpEventQueue(); + } + + await server.close(); + return headers; +} + void testProperties(URLSessionConfiguration config) { group('properties', () { test('allowsCellularAccess', () { @@ -32,6 +60,22 @@ void testProperties(URLSessionConfiguration config) { config.discretionary = false; expect(config.discretionary, false); }); + test('httpAdditionalHeaders', () async { + expect(config.httpAdditionalHeaders, isNull); + + config.httpAdditionalHeaders = { + 'User-Agent': 'My Client', + 'MyHeader': 'myvalue' + }; + expect(config.httpAdditionalHeaders, + {'User-Agent': 'My Client', 'MyHeader': 'myvalue'}); + final headers = await sentHeaders(config); + expect(headers, containsPair('user-agent', ['My Client'])); + expect(headers, containsPair('myheader', ['myvalue'])); + + config.httpAdditionalHeaders = null; + expect(config.httpAdditionalHeaders, isNull); + }); test('httpCookieAcceptPolicy', () { config.httpCookieAcceptPolicy = HTTPCookieAcceptPolicy.httpCookieAcceptPolicyAlways; diff --git a/pkgs/cupertino_http/example/lib/main.dart b/pkgs/cupertino_http/example/lib/main.dart index edfceac92e..32c2b08980 100644 --- a/pkgs/cupertino_http/example/lib/main.dart +++ b/pkgs/cupertino_http/example/lib/main.dart @@ -15,7 +15,10 @@ import 'book.dart'; void main() { var clientFactory = Client.new; // The default Client. if (Platform.isIOS || Platform.isMacOS) { - clientFactory = CupertinoClient.defaultSessionConfiguration.call; + final config = URLSessionConfiguration.ephemeralSessionConfiguration() + ..cache = URLCache.withCapacity(memoryCapacity: 2 * 1024 * 1024) + ..httpAdditionalHeaders = {'User-Agent': 'Book Agent'}; + clientFactory = () => CupertinoClient.fromSessionConfiguration(config); } runWithClient(() => runApp(const BookSearchApp()), clientFactory); } diff --git a/pkgs/cupertino_http/lib/src/cupertino_api.dart b/pkgs/cupertino_http/lib/src/cupertino_api.dart index 01c4853b1c..5cebd7ed42 100644 --- a/pkgs/cupertino_http/lib/src/cupertino_api.dart +++ b/pkgs/cupertino_http/lib/src/cupertino_api.dart @@ -344,6 +344,32 @@ class URLSessionConfiguration bool get discretionary => _nsObject.discretionary; set discretionary(bool value) => _nsObject.discretionary = value; + /// Additional headers to send with each request. + /// + /// Note that the getter for this field returns a **copy** of the headers. + /// + /// See [NSURLSessionConfiguration.HTTPAdditionalHeaders](https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1411532-httpadditionalheaders) + Map? get httpAdditionalHeaders { + if (_nsObject.HTTPAdditionalHeaders case var additionalHeaders?) { + final headers = ncb.NSDictionary.castFrom(additionalHeaders); + return stringDictToMap(headers); + } + return null; + } + + set httpAdditionalHeaders(Map? headers) { + if (headers == null) { + _nsObject.HTTPAdditionalHeaders = null; + return; + } + final d = ncb.NSMutableDictionary.alloc(linkedLibs).init(); + headers.forEach((key, value) { + d.setObject_forKey_( + value.toNSString(linkedLibs), key.toNSString(linkedLibs)); + }); + _nsObject.HTTPAdditionalHeaders = d; + } + /// What policy to use when deciding whether to accept cookies. /// /// See [NSURLSessionConfiguration.HTTPCookieAcceptPolicy](https://developer.apple.com/documentation/foundation/nsurlsessionconfiguration/1408933-httpcookieacceptpolicy). @@ -441,6 +467,7 @@ class URLSessionConfiguration 'allowsConstrainedNetworkAccess=$allowsConstrainedNetworkAccess ' 'allowsExpensiveNetworkAccess=$allowsExpensiveNetworkAccess ' 'discretionary=$discretionary ' + 'httpAdditionalHeaders=$httpAdditionalHeaders ' 'httpCookieAcceptPolicy=$httpCookieAcceptPolicy ' 'httpShouldSetCookies=$httpShouldSetCookies ' 'httpMaximumConnectionsPerHost=$httpMaximumConnectionsPerHost ' diff --git a/pkgs/cupertino_http/pubspec.yaml b/pkgs/cupertino_http/pubspec.yaml index 824a581b8a..81905aa87e 100644 --- a/pkgs/cupertino_http/pubspec.yaml +++ b/pkgs/cupertino_http/pubspec.yaml @@ -1,5 +1,5 @@ name: cupertino_http -version: 1.1.1-wip +version: 1.2.0-wip description: >- A macOS/iOS Flutter plugin that provides access to the Foundation URL Loading System.