From 6248e8d9fa6b568ca2e844295fb90ef4b3b3a41b Mon Sep 17 00:00:00 2001 From: Jon Shier Date: Sat, 24 Nov 2018 17:40:07 -0500 Subject: [PATCH] Squashed commit of the following: commit a3dc191bec2b79bc189c2fbc05d2cf7fcccbfd02 Author: Jon Shier Date: Sat Nov 24 17:35:17 2018 -0500 Add additional tests. commit ae8fde4f8957f84cf3dcb8ac6b99374e198e1a18 Merge: 34f3668 ea48749 Author: Jon Shier Date: Sat Nov 24 16:43:12 2018 -0500 Merge remote-tracking branch 'origin/feature/httpheaders' into feature/encodable-requests # Conflicts: # Source/AFError.swift commit ea48749660944da7837b4a329fdbcd86c87b9323 Author: Jon Shier Date: Sat Nov 24 16:25:48 2018 -0500 Call add instead of update. commit 2d735627ac2ed9278dbc892be13f848728f6ae7d Author: Jon Shier Date: Sat Nov 24 16:24:48 2018 -0500 Add add methods to HTTPHeaders, whitespace cleanup. commit f2bf3952e23cf2ac03dddb26541fe545365a6aca Merge: 410a2e3 7a73af6 Author: Jon Shier Date: Wed Nov 21 19:45:38 2018 -0500 Merge remote-tracking branch 'origin/alamofire5' into feature/httpheaders # Conflicts: # Tests/SessionManagerTests.swift commit 410a2e3d9edb8ab26d4b7ed396d686725c17ba59 Author: Jon Shier Date: Wed Nov 21 19:44:15 2018 -0500 Squashed commit of the following: commit 7a73af699037fb447c33412f92cfe032cfd84019 Author: Jon Shier Date: Wed Nov 21 19:39:20 2018 -0500 Async RequestAdapter (#2628) * Work towards server trust enhancements. * Refactor the rewrite! (#2585) * Refactor request storage out of SessionDelegate. * Continue development. * Rename SessionManager -> Session, update environment. * Rename global Alamofire enum to AF, to avoid collision. * Sort project. * Whitespace cleanup. * Reimplement module changes from bad rebase. * Finalize errors, refactor testing. * Standardize self-signed support, add error descriptions. * Remove per-target setting. * Make RequestAdapter async. commit ccfb96acd46c4477d24ea3f6a01c395cd273c5bd Author: Jon Shier Date: Wed Nov 21 19:32:04 2018 -0500 Alamofire 5: Server Trust Errors (#2608) * Work towards server trust enhancements. * Refactor the rewrite! (#2585) * Refactor request storage out of SessionDelegate. * Continue development. * Rename SessionManager -> Session, update environment. * Rename global Alamofire enum to AF, to avoid collision. * Sort project. * Whitespace cleanup. * Reimplement module changes from bad rebase. * Finalize errors, refactor testing. * Standardize self-signed support, add error descriptions. * Remove per-target setting. * Refactor evaluation API, DRY up a little bit. * Update convienience property. * Add comment for public `Error` API. commit 7a73af699037fb447c33412f92cfe032cfd84019 Author: Jon Shier Date: Wed Nov 21 19:39:20 2018 -0500 Async RequestAdapter (#2628) * Work towards server trust enhancements. * Refactor the rewrite! (#2585) * Refactor request storage out of SessionDelegate. * Continue development. * Rename SessionManager -> Session, update environment. * Rename global Alamofire enum to AF, to avoid collision. * Sort project. * Whitespace cleanup. * Reimplement module changes from bad rebase. * Finalize errors, refactor testing. * Standardize self-signed support, add error descriptions. * Remove per-target setting. * Make RequestAdapter async. commit ccfb96acd46c4477d24ea3f6a01c395cd273c5bd Author: Jon Shier Date: Wed Nov 21 19:32:04 2018 -0500 Alamofire 5: Server Trust Errors (#2608) * Work towards server trust enhancements. * Refactor the rewrite! (#2585) * Refactor request storage out of SessionDelegate. * Continue development. * Rename SessionManager -> Session, update environment. * Rename global Alamofire enum to AF, to avoid collision. * Sort project. * Whitespace cleanup. * Reimplement module changes from bad rebase. * Finalize errors, refactor testing. * Standardize self-signed support, add error descriptions. * Remove per-target setting. * Refactor evaluation API, DRY up a little bit. * Update convienience property. * Add comment for public `Error` API. commit 34f36687944e891a196709a52da3df0b3b066d9c Author: Jon Shier Date: Wed Nov 21 19:26:32 2018 -0500 Add additional tests. commit ba2e15cba641af7626a38abda2d78a3ceda2612b Author: Jon Shier Date: Wed Nov 21 18:56:14 2018 -0500 Update comments, DRY up single value container. commit 38d764bf693f4312b62c9819c9d16644170f277f Author: Jon Shier Date: Wed Nov 21 18:36:01 2018 -0500 Whitespace cleanup. commit b0cf0c529c603b5603b7944032a52798e1d8d70b Merge: 22ccd91 4adae8c Author: Jon Shier Date: Wed Nov 21 18:35:16 2018 -0500 Merge remote-tracking branch 'origin/feature/httpheaders' into feature/encodable-requests commit 4adae8c7b250486aeac4f55c1f06501ac44fc05b Author: Jon Shier Date: Wed Nov 21 18:30:59 2018 -0500 Whitespace cleanup. commit aaa3e6a10d2b14ad0dbc049dc5aa3b1b9bb8fa2e Author: Jon Shier Date: Wed Nov 21 18:29:30 2018 -0500 Updates for review. commit 22ccd9125263c538c7ee21c7d1f16bbb9be1f217 Author: Christian Noon Date: Wed Nov 21 14:20:08 2018 -0800 Enabled code coverage for iOS, macOS, and tvOS framework schemes (#2645) --- Alamofire.xcodeproj/project.pbxproj | 16 +- .../xcschemes/Alamofire iOS.xcscheme | 11 + .../xcschemes/Alamofire macOS.xcscheme | 10 + .../xcschemes/Alamofire tvOS.xcscheme | 11 + Source/AFError.swift | 7 +- Source/Alamofire.swift | 2 +- Source/HTTPHeaders.swift | 42 ++- Source/MultipartFormData.swift | 2 +- Source/ParameterEncoder.swift | 135 +++----- Source/Response.swift | 12 +- Source/ServerTrustEvaluation.swift | 115 +++---- Source/SessionStateProvider.swift | 6 +- Tests/AFError+AlamofireTests.swift | 5 - Tests/HTTPBin.swift | 4 +- Tests/HTTPHeadersTests.swift | 3 +- Tests/ParameterEncoderTests.swift | 288 ++++++++++++++++-- Tests/RequestTests.swift | 8 +- Tests/ServerTrustEvaluatorTests.swift | 16 +- ...nManagerTests.swift => SessionTests.swift} | 4 +- 19 files changed, 462 insertions(+), 235 deletions(-) rename Tests/{SessionManagerTests.swift => SessionTests.swift} (99%) diff --git a/Alamofire.xcodeproj/project.pbxproj b/Alamofire.xcodeproj/project.pbxproj index 1f28a30d5..edbbea043 100644 --- a/Alamofire.xcodeproj/project.pbxproj +++ b/Alamofire.xcodeproj/project.pbxproj @@ -33,9 +33,9 @@ 3107EA3F20A1267C00445260 /* SessionDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9DCE771CB1BCE2003E6463 /* SessionDelegateTests.swift */; }; 3107EA4020A1267C00445260 /* SessionDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9DCE771CB1BCE2003E6463 /* SessionDelegateTests.swift */; }; 3107EA4120A1267D00445260 /* SessionDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9DCE771CB1BCE2003E6463 /* SessionDelegateTests.swift */; }; - 3111CE8420A7636E008315E2 /* SessionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8D1C6F419D52968002E74FE /* SessionManagerTests.swift */; }; - 3111CE8520A7636F008315E2 /* SessionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8D1C6F419D52968002E74FE /* SessionManagerTests.swift */; }; - 3111CE8620A76370008315E2 /* SessionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8D1C6F419D52968002E74FE /* SessionManagerTests.swift */; }; + 3111CE8420A7636E008315E2 /* SessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8D1C6F419D52968002E74FE /* SessionTests.swift */; }; + 3111CE8520A7636F008315E2 /* SessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8D1C6F419D52968002E74FE /* SessionTests.swift */; }; + 3111CE8620A76370008315E2 /* SessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8D1C6F419D52968002E74FE /* SessionTests.swift */; }; 3111CE8820A77843008315E2 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3111CE8720A77843008315E2 /* EventMonitor.swift */; }; 3111CE8920A77944008315E2 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3111CE8720A77843008315E2 /* EventMonitor.swift */; }; 3111CE8A20A77945008315E2 /* EventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3111CE8720A77843008315E2 /* EventMonitor.swift */; }; @@ -445,7 +445,7 @@ F86AEFE51AE6A282007D9C76 /* TLSEvaluationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TLSEvaluationTests.swift; sourceTree = ""; }; F897FF4019AA800700AB5182 /* Alamofire.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Alamofire.swift; sourceTree = ""; }; F8AE910119D28DCC0078C7B2 /* ValidationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationTests.swift; sourceTree = ""; }; - F8D1C6F419D52968002E74FE /* SessionManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionManagerTests.swift; sourceTree = ""; }; + F8D1C6F419D52968002E74FE /* SessionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionTests.swift; sourceTree = ""; }; F8E6024419CB46A800A3E7F1 /* AuthenticationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -516,7 +516,7 @@ F8111E5E19A9674D0040E7D1 /* ResponseTests.swift */, 4CA028C41B7466C500C84163 /* ResultTests.swift */, 4C9DCE771CB1BCE2003E6463 /* SessionDelegateTests.swift */, - F8D1C6F419D52968002E74FE /* SessionManagerTests.swift */, + F8D1C6F419D52968002E74FE /* SessionTests.swift */, F8111E5F19A9674D0040E7D1 /* UploadTests.swift */, ); name = Core; @@ -1312,7 +1312,7 @@ 4CF627141BA7CC240011A099 /* BaseTestCase.swift in Sources */, 31727424218BB9A50039FFCC /* HTTPBin.swift in Sources */, 31EBD9C320D1D89D00D1FF34 /* ValidationTests.swift in Sources */, - 3111CE8620A76370008315E2 /* SessionManagerTests.swift in Sources */, + 3111CE8620A76370008315E2 /* SessionTests.swift in Sources */, 31C2B0F220B271380089BA7C /* TLSEvaluationTests.swift in Sources */, 3111CE9D20A7EC58008315E2 /* URLProtocolTests.swift in Sources */, 317A6A7820B2208000A9FEC5 /* DownloadTests.swift in Sources */, @@ -1445,7 +1445,7 @@ 4C256A531B096C770065714F /* BaseTestCase.swift in Sources */, 31727422218BB9A50039FFCC /* HTTPBin.swift in Sources */, 31EBD9C120D1D89C00D1FF34 /* ValidationTests.swift in Sources */, - 3111CE8420A7636E008315E2 /* SessionManagerTests.swift in Sources */, + 3111CE8420A7636E008315E2 /* SessionTests.swift in Sources */, 31C2B0F020B271370089BA7C /* TLSEvaluationTests.swift in Sources */, 3111CE9B20A7EC57008315E2 /* URLProtocolTests.swift in Sources */, 317A6A7620B2207F00A9FEC5 /* DownloadTests.swift in Sources */, @@ -1476,7 +1476,7 @@ F829C6BF1A7A950600A2CD59 /* RequestTests.swift in Sources */, 31727423218BB9A50039FFCC /* HTTPBin.swift in Sources */, 31EBD9C220D1D89C00D1FF34 /* ValidationTests.swift in Sources */, - 3111CE8520A7636F008315E2 /* SessionManagerTests.swift in Sources */, + 3111CE8520A7636F008315E2 /* SessionTests.swift in Sources */, 31C2B0F120B271370089BA7C /* TLSEvaluationTests.swift in Sources */, 3111CE9C20A7EC58008315E2 /* URLProtocolTests.swift in Sources */, 317A6A7720B2208000A9FEC5 /* DownloadTests.swift in Sources */, diff --git a/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire iOS.xcscheme b/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire iOS.xcscheme index 2977742fa..464578939 100644 --- a/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire iOS.xcscheme +++ b/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire iOS.xcscheme @@ -41,7 +41,18 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" enableThreadSanitizer = "YES" + codeCoverageEnabled = "YES" + onlyGenerateCoverageForSpecifiedTargets = "YES" shouldUseLaunchSchemeArgsEnv = "NO"> + + + + diff --git a/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire macOS.xcscheme b/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire macOS.xcscheme index 3e8a60cc4..1630f02b9 100644 --- a/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire macOS.xcscheme +++ b/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire macOS.xcscheme @@ -42,7 +42,17 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" enableThreadSanitizer = "YES" codeCoverageEnabled = "YES" + onlyGenerateCoverageForSpecifiedTargets = "YES" shouldUseLaunchSchemeArgsEnv = "NO"> + + + + diff --git a/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire tvOS.xcscheme b/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire tvOS.xcscheme index 5f7189056..89faa570a 100644 --- a/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire tvOS.xcscheme +++ b/Alamofire.xcodeproj/xcshareddata/xcschemes/Alamofire tvOS.xcscheme @@ -41,7 +41,18 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" enableThreadSanitizer = "YES" + codeCoverageEnabled = "YES" + onlyGenerateCoverageForSpecifiedTargets = "YES" shouldUseLaunchSchemeArgsEnv = "NO"> + + + + diff --git a/Source/AFError.swift b/Source/AFError.swift index 381cde048..e65a1a9e4 100644 --- a/Source/AFError.swift +++ b/Source/AFError.swift @@ -160,10 +160,6 @@ public enum AFError: Error { self.result = result } } - /// Trust evaluation was failed for an unknown reason. Usually this means the evaluator failed but didn't throw - /// an error. - case unknown(host: String) - /// `allHostsMustBeEvaluated` was `true` but no evaluator was found for the associated host. case noRequiredEvaluator(host: String) /// No certificates were found with which to perform the trust evaluation. case noCertificatesFound @@ -198,6 +194,7 @@ public enum AFError: Error { } extension Error { + /// Returns the instance cast as an `AFError`. public var asAFError: AFError? { return self as? AFError } @@ -581,8 +578,6 @@ extension AFError.ResponseValidationFailureReason { extension AFError.ServerTrustFailureReason { var localizedDescription: String { switch self { - case .unknown: - return "Server trust evaluation failed but no specific error was thrown." case let .noRequiredEvaluator(host): return "A ServerTrustEvaluating value is required for host \(host) but none was found." case .noCertificatesFound: diff --git a/Source/Alamofire.swift b/Source/Alamofire.swift index a7ac5fd92..9aeead92b 100644 --- a/Source/Alamofire.swift +++ b/Source/Alamofire.swift @@ -24,7 +24,7 @@ import Foundation -/// Global namespace containing API for the `default` `SessionManager` instance. +/// Global namespace containing API for the `default` `Session` instance. public enum AF { // MARK: - Data Request diff --git a/Source/HTTPHeaders.swift b/Source/HTTPHeaders.swift index 5652d64f2..921182348 100644 --- a/Source/HTTPHeaders.swift +++ b/Source/HTTPHeaders.swift @@ -48,6 +48,22 @@ public struct HTTPHeaders { dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) } } + /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. + /// + /// - Parameters: + /// - name: The `HTTPHeader` name. + /// - value: The `HTTPHeader value. + public mutating func add(name: String, value: String) { + update(HTTPHeader(name: name, value: value)) + } + + /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. + /// + /// - Parameter header: The `HTTPHeader` to update or append. + public mutating func add(_ header: HTTPHeader) { + update(header) + } + /// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`. /// /// - Parameters: @@ -57,7 +73,7 @@ public struct HTTPHeaders { update(HTTPHeader(name: name, value: value)) } - /// Case-insensitively updates or apprends the provided `HTTPHeader` into the instance. + /// Case-insensitively updates or appends the provided `HTTPHeader` into the instance. /// /// - Parameter header: The `HTTPHeader` to update or append. public mutating func update(_ header: HTTPHeader) { @@ -80,7 +96,7 @@ public struct HTTPHeaders { /// Sort the current instance by header name. mutating public func sort() { - return headers.sort { $0.name < $1.name } + headers.sort { $0.name < $1.name } } /// Returns an instance sorted by header name. @@ -100,7 +116,6 @@ public struct HTTPHeaders { return headers[index].value } - /// Case-insensitively access the header with the given name. /// /// - Parameter name: The name of the header. @@ -198,6 +213,14 @@ extension HTTPHeader: CustomStringConvertible { } extension HTTPHeader { + /// Returns an `Accept-Charset` header. + /// + /// - Parameter value: The `Accept-Charset` value. + /// - Returns: The header. + public static func acceptCharset(_ value: String) -> HTTPHeader { + return HTTPHeader(name: "Accept-Charset", value: value) + } + /// Returns an `Accept-Language` header. /// /// Alamofire offers a default Accept-Language header that accumulates and encodes the system's preferred languages. @@ -232,10 +255,19 @@ extension HTTPHeader { return authorization("Basic \(credential)") } + /// Returns a `Bearer` `Authorization` header using the `bearerToken` provided + /// + /// - Parameter bearerToken: The bearer token. + /// - Returns: The header. + public static func authorization(bearerToken: String) -> HTTPHeader { + return authorization("Bearer \(bearerToken)") + } + /// Returns an `Authorization` header. /// - /// Alamofire provides a built-in method to produce Basic Authorization headers. Use - /// `HTTPHeader.authorization(username: password:)`. + /// Alamofire provides built-in methods to produce `Authorization` headers. For a Basic `Authorization` header use + /// `HTTPHeader.authorization(username: password:)`. For a Bearer `Authorization` header, use + /// `HTTPHeader.authorization(bearerToken:)`. /// /// - Parameter value: The `Authorization` value. /// - Returns: The header. diff --git a/Source/MultipartFormData.swift b/Source/MultipartFormData.swift index ff1838f28..9b258f756 100644 --- a/Source/MultipartFormData.swift +++ b/Source/MultipartFormData.swift @@ -551,7 +551,7 @@ open class MultipartFormData { if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" } var headers: HTTPHeaders = [.contentDisposition(disposition)] - if let mimeType = mimeType { headers.update(.contentType(mimeType)) } + if let mimeType = mimeType { headers.add(.contentType(mimeType)) } return headers } diff --git a/Source/ParameterEncoder.swift b/Source/ParameterEncoder.swift index 7d723a47a..d3c698d23 100644 --- a/Source/ParameterEncoder.swift +++ b/Source/ParameterEncoder.swift @@ -187,6 +187,19 @@ open class URLEncodedFormParameterEncoder: ParameterEncoder { } /// An object that encodes instances into URL-encoded query strings. +/// +/// There is no published specification for how to encode collection types. By default, the convention of appending +/// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for +/// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the +/// square brackets appended to array keys. +/// +/// `BoolEncoding` can be used to configure how `Bool` values are encoded. The default behavior is to encode +/// `true` as 1 and `false` as 0. +/// +/// `SpaceEncoding` can be used to configure how spaces are encoded. Modern encodings use percent replacement (%20), +/// while older encoding may expect spaces to be replaced with +. +/// +/// This type is largely based on Vapor's [`url-encoded-form`](https://github.com/vapor/url-encoded-form) project. public final class URLEncodedFormEncoder { /// Configures how `Bool` parameters are encoded. public enum BoolEncoding { @@ -221,14 +234,14 @@ public final class URLEncodedFormEncoder { } } } - + /// Configures how spaces are encoded. public enum SpaceEncoding { /// Encodes spaces according to normal percent escaping rules (%20). case percentEscaped /// Encodes spaces as `+`, case plusReplaced - + /// Encodes the string according to the encoding. /// /// - Parameter string: The `String` to encode. @@ -241,16 +254,18 @@ public final class URLEncodedFormEncoder { } } - /// Internal `URLEncodedFormEncoder` error. + /// `URLEncodedFormEncoder` error. public enum Error: Swift.Error { /// An invalid root object was created by the encoder. Only keyed values are valid. - case invalidRootObject + case invalidRootObject(String) var localizedDescription: String { - return "Root `Encodable` values must be keyed." + switch self { + case let .invalidRootObject(object): return "URLEncodedFormEncoder requires keyed root object. Received \(object) instead." + } } } - + /// The `ArrayEncoding` to use. public let arrayEncoding: ArrayEncoding /// The `BoolEncoding` to use. @@ -292,16 +307,16 @@ public final class URLEncodedFormEncoder { /// - Throws: An `Error` or `EncodingError` instance if encoding fails. public func encode(_ value: Encodable) throws -> String { let component: URLEncodedFormComponent = try encode(value) - + guard case let .object(object) = component else { - throw Error.invalidRootObject + throw Error.invalidRootObject("\(component)") } - + let serializer = URLEncodedFormSerializer(arrayEncoding: arrayEncoding, spaceEncoding: spaceEncoding, allowedCharacters: allowedCharacters) let query = serializer.serialize(object) - + return query } @@ -320,7 +335,7 @@ public final class URLEncodedFormEncoder { final class _URLEncodedFormEncoder { var codingPath: [CodingKey] - // Return empty dictionary, as this encoder supports no userInfo. + // Returns an empty dictionary, as this encoder doesn't support userInfo. var userInfo: [CodingUserInfoKey : Any] { return [:] } let context: URLEncodedFormContext @@ -405,29 +420,6 @@ enum URLEncodedFormComponent { set(&self, to: value, at: path) } - /// Sets self to the supplied value at a given path. - /// - /// data.get(at: ["path", "to", "value"]) - /// - /// - parameters: - /// - path: `CodingKey` path to fetch the supplied value at. - /// - returns: An instance of `Self` if a value exists at the path, otherwise `nil`. - public func get(at path: [CodingKey]) -> URLEncodedFormComponent? { - var child = self - - for seg in path { - if let object = child.object, let c = object[seg.stringValue] { - child = c - } else if let array = child.array, let index = seg.intValue { - child = array[index] - } else { - return nil - } - } - - return child - } - /// Recursive backing method to `set(to:at:)`. private func set(_ context: inout URLEncodedFormComponent, to value: URLEncodedFormComponent, at path: [CodingKey]) { guard path.count >= 1 else { @@ -603,101 +595,66 @@ extension _URLEncodedFormEncoder.SingleValueContainer: SingleValueEncodingContai } func encode(_ value: Bool) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(boolEncoding.encode(value)), at: codingPath) + try encode(value, as: String(boolEncoding.encode(value))) } func encode(_ value: String) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(value), at: codingPath) + try encode(value, as: value) } func encode(_ value: Double) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: Float) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: Int) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: Int8) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: Int16) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: Int32) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: Int64) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: UInt) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: UInt8) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: UInt16) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: UInt32) throws { - try checkCanEncode(value: value) - defer { canEncodeNewValue = false } - - context.component.set(to: .string(String(value)), at: codingPath) + try encode(value, as: String(value)) } func encode(_ value: UInt64) throws { + try encode(value, as: String(value)) + } + + private func encode(_ value: T, as string: String) throws where T : Encodable { try checkCanEncode(value: value) defer { canEncodeNewValue = false } - context.component.set(to: .string(String(value)), at: codingPath) + context.component.set(to: .string(string), at: codingPath) } func encode(_ value: T) throws where T : Encodable { @@ -825,13 +782,13 @@ final class URLEncodedFormSerializer { return segments.joinedWithAmpersands() } - + func escape(_ query: String) -> String { var allowedCharactersWithSpace = allowedCharacters allowedCharactersWithSpace.insert(charactersIn: " ") let escapedQuery = query.addingPercentEncoding(withAllowedCharacters: allowedCharactersWithSpace) ?? query let spaceEncodedQuery = spaceEncoding.encode(escapedQuery) - + return spaceEncodedQuery } } diff --git a/Source/Response.swift b/Source/Response.swift index fd8b1a3a6..142a178a1 100644 --- a/Source/Response.swift +++ b/Source/Response.swift @@ -92,8 +92,8 @@ extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible { let sortedHeaders = response.httpHeaders.sorted() return """ - Status Code: \(response.statusCode) - Headers: + [Status Code]: \(response.statusCode) + [Headers]: \(sortedHeaders) """ } ?? "nil" @@ -101,7 +101,7 @@ extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible { return """ [Request]: \(requestDescription) - [Response]: \(responseDescription) + [Response]: \n\(responseDescription) [Data]: \(data?.description ?? "None") [Network Duration]: \(metricsDescription) [Serialization Duration]: \(serializationDuration)s @@ -276,8 +276,8 @@ extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertibl let sortedHeaders = response.httpHeaders.sorted() return """ - Status Code: \(response.statusCode) - Headers: + [Status Code]: \(response.statusCode) + [Headers]: \(sortedHeaders) """ } ?? "nil" @@ -286,7 +286,7 @@ extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertibl return """ [Request]: \(requestDescription) - [Response]: \(responseDescription) + [Response]: \n\(responseDescription) [File URL]: \(fileURL?.path ?? "nil") [ResumeData]: \(resumeDataDescription) [Network Duration]: \(metricsDescription) diff --git a/Source/ServerTrustEvaluation.swift b/Source/ServerTrustEvaluation.swift index 1b16347a6..17fede880 100644 --- a/Source/ServerTrustEvaluation.swift +++ b/Source/ServerTrustEvaluation.swift @@ -81,8 +81,7 @@ public protocol ServerTrustEvaluating { /// - trust: The `SecTrust` value to evaluate. /// - host: The host for which to evaluate the `SecTrust` value. /// - Returns: A `Bool` indicating whether the evaluator considers the `SecTrust` value valid for `host`. - /// - Throws: An `AFError.serverTrustEvaluationFailed` with an associated `ServerTrustFailureReason`. - func evaluate(_ trust: SecTrust, forHost host: String) throws -> Bool + func evaluate(_ trust: SecTrust, forHost host: String) throws #endif } @@ -96,12 +95,10 @@ extension Array where Element == ServerTrustEvaluating { /// - trust: The `SecTrust` value to evaluate. /// - host: The host for which to evaluate the `SecTrust` value. /// - Returns: Whether or not the evaluator considers the `SecTrust` value valid for `host`. - func evaluate(_ trust: SecTrust, forHost host: String) throws -> Bool { + func evaluate(_ trust: SecTrust, forHost host: String) throws { for evaluator in self { - guard try evaluator.evaluate(trust, forHost: host) else { return false } + try evaluator.evaluate(trust, forHost: host) } - - return true } #endif } @@ -121,18 +118,12 @@ public final class DefaultTrustEvaluator: ServerTrustEvaluating { self.validateHost = validateHost } - public func evaluate(_ trust: SecTrust, forHost host: String) throws -> Bool { - try trust.validate(policy: .default) { (status, result) in - AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, trust, status, result))) - } - + public func evaluate(_ trust: SecTrust, forHost host: String) throws { if validateHost { - try trust.validate(policy: .hostname(host)) { (status, result) in - AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, trust, status, result))) - } + try trust.validateHost(host) } - return true + try trust.performDefaultEvaluation(forHost: host) } } @@ -193,24 +184,18 @@ public final class RevocationTrustEvaluator: ServerTrustEvaluating { self.options = options } - public func evaluate(_ trust: SecTrust, forHost host: String) throws -> Bool { + public func evaluate(_ trust: SecTrust, forHost host: String) throws { if performDefaultValidation { - try trust.validate(policy: .default) { (status, result) in - AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, trust, status, result))) - } + try trust.performDefaultEvaluation(forHost: host) } if validateHost { - try trust.validate(policy: .hostname(host)) { (status, result) in - AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, trust, status, result))) - } + try trust.validateHost(host) } try trust.validate(policy: .revocation(options: options)) { (status, result) in AFError.serverTrustEvaluationFailed(reason: .revocationCheckFailed(output: .init(host, trust, status, result), options: options)) } - - return true } } @@ -248,7 +233,7 @@ public final class PinnedCertificatesTrustEvaluator: ServerTrustEvaluating { self.validateHost = validateHost } - public func evaluate(_ trust: SecTrust, forHost host: String) throws -> Bool { + public func evaluate(_ trust: SecTrust, forHost host: String) throws { guard !certificates.isEmpty else { throw AFError.serverTrustEvaluationFailed(reason: .noCertificatesFound) } @@ -258,28 +243,22 @@ public final class PinnedCertificatesTrustEvaluator: ServerTrustEvaluating { } if performDefaultValidation { - try trust.validate(policy: .default) { (status, result) in - AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, trust, status, result))) - } + try trust.performDefaultEvaluation(forHost: host) } if validateHost { - try trust.validate(policy: .hostname(host)) { (status, result) in - AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, trust, status, result))) - } + try trust.validateHost(host) } let serverCertificatesData = Set(trust.certificateData) let pinnedCertificatesData = Set(certificates.data) - let certificatesPinned = !serverCertificatesData.isDisjoint(with: pinnedCertificatesData) - if !certificatesPinned { + let pinnedCertificatesInServerData = !serverCertificatesData.isDisjoint(with: pinnedCertificatesData) + if !pinnedCertificatesInServerData { throw AFError.serverTrustEvaluationFailed(reason: .certificatePinningFailed(host: host, trust: trust, pinnedCertificates: certificates, serverCertificates: trust.certificates)) } - - return certificatesPinned } } @@ -314,24 +293,20 @@ public final class PublicKeysTrustEvaluator: ServerTrustEvaluating { self.validateHost = validateHost } - public func evaluate(_ trust: SecTrust, forHost host: String) throws -> Bool { + public func evaluate(_ trust: SecTrust, forHost host: String) throws { guard !keys.isEmpty else { throw AFError.serverTrustEvaluationFailed(reason: .noPublicKeysFound) } if performDefaultValidation { - try trust.validate(policy: .default) { (status, result) in - AFError.serverTrustEvaluationFailed(reason: .defaultEvaluationFailed(output: .init(host, trust, status, result))) - } + try trust.performDefaultEvaluation(forHost: host) } if validateHost { - try trust.validate(policy: .hostname(host)) { (status, result) in - AFError.serverTrustEvaluationFailed(reason: .hostValidationFailed(output: .init(host, trust, status, result))) - } + try trust.validateHost(host) } - let keysPinned: Bool = { + let pinnedKeysInServerKeys: Bool = { for serverPublicKey in trust.publicKeys as [AnyHashable] { for pinnedPublicKey in keys as [AnyHashable] { if serverPublicKey == pinnedPublicKey { @@ -342,14 +317,12 @@ public final class PublicKeysTrustEvaluator: ServerTrustEvaluating { return false }() - if !keysPinned { + if !pinnedKeysInServerKeys { throw AFError.serverTrustEvaluationFailed(reason: .publicKeyPinningFailed(host: host, trust: trust, pinnedKeys: keys, serverKeys: trust.publicKeys)) } - - return keysPinned } } @@ -365,8 +338,8 @@ public final class CompositeTrustEvaluator: ServerTrustEvaluating { self.evaluators = evaluators } - public func evaluate(_ trust: SecTrust, forHost host: String) throws -> Bool { - return try evaluators.evaluate(trust, forHost: host) + public func evaluate(_ trust: SecTrust, forHost host: String) throws { + try evaluators.evaluate(trust, forHost: host) } } @@ -376,9 +349,7 @@ public final class CompositeTrustEvaluator: ServerTrustEvaluating { public final class DisabledEvaluator: ServerTrustEvaluating { public init() { } - public func evaluate(_ trust: SecTrust, forHost host: String) throws -> Bool { - return true - } + public func evaluate(_ trust: SecTrust, forHost host: String) throws { } } extension Bundle { @@ -408,29 +379,17 @@ extension Bundle { } public extension SecTrust { - /// Evaluates `self` and returns `true` if the evaluation succeeds with a value of `.unspecified` or `.proceed`. - var isValid: Bool { - var result = SecTrustResultType.invalid - let status = SecTrustEvaluate(self, &result) - - return (status.isSuccess) ? (result == .unspecified || result == .proceed) : false - } - - @discardableResult - func validate(policy: SecPolicy, errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws -> Bool { - return try apply(policy: policy).isValid(errorProducer: errorProducer) + func validate(policy: SecPolicy, errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws { + try apply(policy: policy).validate(errorProducer: errorProducer) } - @discardableResult - func isValid(errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws -> Bool { + func validate(errorProducer: (_ status: OSStatus, _ result: SecTrustResultType) -> Error) throws { var result = SecTrustResultType.invalid let status = SecTrustEvaluate(self, &result) - guard status.isSuccess && (result == .unspecified || result == .proceed) else { + guard status.isSuccess && result.isSuccess else { throw errorProducer(status, result) } - - return true } func apply(policy: SecPolicy) throws -> SecTrust { @@ -463,9 +422,7 @@ public extension SecTrust { /// The public keys contained in `self`. var publicKeys: [SecKey] { - return (0.. { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1.0") + } + + func testEncoderCanEncodeFloat() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: Float] = ["a": 1.0] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1.0") + } + + func testEncoderCanEncodeInt8() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: Int8] = ["a": 1] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1") + } + + func testEncoderCanEncodeInt16() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: Int16] = ["a": 1] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1") + } + + func testEncoderCanEncodeInt32() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: Int32] = ["a": 1] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1") + } + + func testEncoderCanEncodeInt64() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: Int64] = ["a": 1] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1") + } + + func testEncoderCanEncodeUInt() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: UInt] = ["a": 1] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1") + } + + func testEncoderCanEncodeUInt8() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: UInt8] = ["a": 1] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1") + } + + func testEncoderCanEncodeUInt16() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: UInt16] = ["a": 1] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1") + } + + func testEncoderCanEncodeUInt32() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: UInt32] = ["a": 1] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1") + } + + func testEncoderCanEncodeUInt64() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters: [String: UInt64] = ["a": 1] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertEqual(result.value, "a=1") + } + func testThatNestedDictionariesHaveBracketedKeys() { // Given let encoder = URLEncodedFormEncoder() @@ -163,7 +295,20 @@ final class URLEncodedFormEncoderTests: BaseTestCase { let result = Result { try encoder.encode(parameters) } // Then - let expected = "four%5B%5D=1&four%5B%5D=2&four%5B%5D=3&two=2&five%5Ba%5D=a&five%5Bb%5D=b&three=1&one=one" + let expected = "four%5B%5D=1&four%5B%5D=2&four%5B%5D=3&three=1&one=one&two=2&five%5Ba%5D=a&six%5Ba%5D%5Bb%5D=b&seven%5Ba%5D=a" + XCTAssertEqual(result.value, expected) + } + + func testThatManuallyEncodableStructCanBeEncoded() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters = ManuallyEncodableStruct() + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + let expected = "root%5B%5D%5B%5D=1&root%5B%5D%5B%5D=2&root%5B%5D%5B%5D=3&root%5B%5D%5Ba%5D%5Bstring%5D=string&root%5B%5D%5B%5D%5B%5D=1&root%5B%5D%5B%5D%5B%5D=2&root%5B%5D%5B%5D%5B%5D=3" XCTAssertEqual(result.value, expected) } @@ -192,6 +337,19 @@ final class URLEncodedFormEncoderTests: BaseTestCase { XCTAssertEqual(result.value, expected) } + func testThatManuallyEncodableSubclassCanBeEncoded() { + // Given + let encoder = URLEncodedFormEncoder() + let parameters = ManuallyEncodableSubclass() + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + let expected = "five%5Ba%5D=a&five%5Bb%5D=b&four%5Bfour%5D=one&four%5Bfive%5D=2" + XCTAssertEqual(result.value, expected) + } + func testThatARootArrayCannotBeEncoded() { // Given let encoder = URLEncodedFormEncoder() @@ -216,6 +374,18 @@ final class URLEncodedFormEncoderTests: BaseTestCase { XCTAssertFalse(result.isSuccess) } + func testThatOptionalValuesCannotBeEncoded() { + // Givenp + let encoder = URLEncodedFormEncoder() + let parameters: [String: String?] = ["string": nil] + + // When + let result = Result { try encoder.encode(parameters) } + + // Then + XCTAssertFalse(result.isSuccess) + } + func testThatBoolsCanBeLiteralEncoded() { // Given let encoder = URLEncodedFormEncoder(boolEncoding: .literal) @@ -239,29 +409,29 @@ final class URLEncodedFormEncoderTests: BaseTestCase { // Then XCTAssertEqual(result.value, "array=1&array=2") } - + func testThatSpacesCanBeEncodedAsPluses() { // Given let encoder = URLEncodedFormEncoder(spaceEncoding: .plusReplaced) let parameters = ["spaces": "replace with spaces"] - + // When let result = Result { try encoder.encode(parameters) } - + // Then XCTAssertEqual(result.value, "spaces=replace+with+spaces") } - + func testThatEscapedCharactersCanBeCustomized() { // Given var allowed = CharacterSet.afURLQueryAllowed allowed.remove(charactersIn: "?/") let encoder = URLEncodedFormEncoder(allowedCharacters: allowed) let parameters = ["allowed": "?/"] - + // When let result = Result { try encoder.encode(parameters) } - + // Then XCTAssertEqual(result.value, "allowed=%3F%2F") } @@ -424,7 +594,13 @@ private struct EncodableStruct: Encodable { let two = 2 let three = true let four = [1, 2, 3] - let five = ["a": "a", "b": "b"] + let five = ["a": "a"] + let six = ["a": ["b": "b"]] + let seven = NestedEncodableStruct() +} + +private struct NestedEncodableStruct: Encodable { + let a = "a" } private class EncodableSuperclass: Encodable { @@ -449,3 +625,61 @@ private final class EncodableSubclass: EncodableSuperclass { try container.encode(five, forKey: .five) } } + +private final class ManuallyEncodableSubclass: EncodableSuperclass { + let four = [1, 2, 3] + let five = ["a": "a", "b": "b"] + + private enum CodingKeys: String, CodingKey { + case four, five + } + + override func encode(to encoder: Encoder) throws { + var keyedContainer = encoder.container(keyedBy: CodingKeys.self) + + try keyedContainer.encode(four, forKey: .four) + try keyedContainer.encode(five, forKey: .five) + + let superEncoder = keyedContainer.superEncoder() + var superContainer = superEncoder.container(keyedBy: CodingKeys.self) + try superContainer.encode(one, forKey: .four) + + let keyedSuperEncoder = keyedContainer.superEncoder(forKey: .four) + var superKeyedContainer = keyedSuperEncoder.container(keyedBy: CodingKeys.self) + try superKeyedContainer.encode(two, forKey: .five) + + var unkeyedContainer = keyedContainer.nestedUnkeyedContainer(forKey: .four) + let unkeyedSuperEncoder = unkeyedContainer.superEncoder() + var keyedUnkeyedSuperContainer = unkeyedSuperEncoder.container(keyedBy: CodingKeys.self) + try keyedUnkeyedSuperContainer.encode(one, forKey: .four) + } +} + +private struct ManuallyEncodableStruct: Encodable { + let a = ["string": "string"] + let b = [1, 2, 3] + + private enum RootKey: String, CodingKey { + case root + } + + private enum TypeKeys: String, CodingKey { + case a, b + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: RootKey.self) + + var nestedKeyedContainer = container.nestedContainer(keyedBy: TypeKeys.self, forKey: .root) + try nestedKeyedContainer.encode(a, forKey: .a) + + var nestedUnkeyedContainer = container.nestedUnkeyedContainer(forKey: .root) + try nestedUnkeyedContainer.encode(b) + + var nestedUnkeyedKeyedContainer = nestedUnkeyedContainer.nestedContainer(keyedBy: TypeKeys.self) + try nestedUnkeyedKeyedContainer.encode(a, forKey: .a) + + var nestedUnkeyedUnkeyedContainer = nestedUnkeyedContainer.nestedUnkeyedContainer() + try nestedUnkeyedUnkeyedContainer.encode(b) + } +} diff --git a/Tests/RequestTests.swift b/Tests/RequestTests.swift index 832726ce7..9bbd86b10 100644 --- a/Tests/RequestTests.swift +++ b/Tests/RequestTests.swift @@ -243,22 +243,22 @@ class RequestResponseTestCase: BaseTestCase { // Then XCTAssertEqual(receivedResponse?.result.value?.args, ["property": "one"]) } - + func testThatRequestsCanPassEncodableParametersAsURLEncodedBodyData() { // Given let parameters = HTTPBinParameters(property: "one") let expect = expectation(description: "request should complete") var receivedResponse: DataResponse? - + // When AF.request("https://httpbin.org/post", method: .post, parameters: parameters) .responseJSONDecodable { (response: DataResponse) in receivedResponse = response expect.fulfill() } - + waitForExpectations(timeout: timeout, handler: nil) - + // Then XCTAssertEqual(receivedResponse?.result.value?.form, ["property": "one"]) } diff --git a/Tests/ServerTrustEvaluatorTests.swift b/Tests/ServerTrustEvaluatorTests.swift index 8c76081a2..3854f313b 100644 --- a/Tests/ServerTrustEvaluatorTests.swift +++ b/Tests/ServerTrustEvaluatorTests.swift @@ -169,6 +169,18 @@ class ServerTrustPolicyTestCase: BaseTestCase { } } +// MARK: - SecTrust Extension + +extension SecTrust { + /// Evaluates `self` and returns `true` if the evaluation succeeds with a value of `.unspecified` or `.proceed`. + var isValid: Bool { + var result = SecTrustResultType.invalid + let status = SecTrustEvaluate(self, &result) + + return (status == errSecSuccess) ? (result == .unspecified || result == .proceed) : false + } +} + // MARK: - class ServerTrustPolicyExplorationBasicX509PolicyValidationTestCase: ServerTrustPolicyTestCase { @@ -1394,10 +1406,10 @@ class ServerTrustPolicyCompositeTestCase: ServerTrustPolicyTestCase { // When setRootCertificateAsLoneAnchorCertificateForTrust(serverTrust) - let serverTrustIsValid = try compositePolicy.evaluate(serverTrust, forHost: host) + let result = Result { try compositePolicy.evaluate(serverTrust, forHost: host) } // Then - XCTAssertTrue(serverTrustIsValid, "server trust should pass evaluation") + XCTAssertTrue(result.isSuccess, "server trust should pass evaluation") } func testThatNonAnchoredRootCertificateChainFailsEvaluationWithoutHostValidation() throws { diff --git a/Tests/SessionManagerTests.swift b/Tests/SessionTests.swift similarity index 99% rename from Tests/SessionManagerTests.swift rename to Tests/SessionTests.swift index 613f3b98b..5ebb6c072 100644 --- a/Tests/SessionManagerTests.swift +++ b/Tests/SessionTests.swift @@ -1,5 +1,5 @@ // -// SessionManagerTests.swift +// SessionTests.swift // // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/) // @@ -26,7 +26,7 @@ import Foundation import XCTest -class SessionManagerTestCase: BaseTestCase { +class SessionTestCase: BaseTestCase { // MARK: Helper Types