未验证 提交 55846392 编写于 作者: J Jon Shier 提交者: GitHub

Update cURL support for async request creation. (#2863)

* Connect events to cURL description.

* Clean up and add more tests.

* Add DataEncoding and KeyEncoding. (#2858)

* Appending response serializer now resumes request if finished to handle races (#2862)
上级 0bab25fa
......@@ -96,8 +96,9 @@ public protocol EventMonitor {
// MARK: - Request Events
/// Event called when a `URLRequest` is first created for a `Request`.
func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest)
/// Event called when a `URLRequest` is first created for a `Request`. If a `RequestAdapter` is active, the
/// `URLRequest` will be adapted before being issued.
func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest)
/// Event called when the attempt to create a `URLRequest` from a `Request`'s original `URLRequestConvertible` value fails.
func request(_ request: Request, didFailToCreateURLRequestWithError error: Error)
......@@ -108,6 +109,9 @@ public protocol EventMonitor {
/// Event called when a `RequestAdapter` fails to adapt the `Request`'s initial `URLRequest`.
func request(_ request: Request, didFailToAdaptURLRequest initialRequest: URLRequest, withError error: Error)
/// Event called when a final `URLRequest` is created for a `Request`.
func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest)
/// Event called when a `URLSessionTask` subclass instance is created for a `Request`.
func request(_ request: Request, didCreateTask task: URLSessionTask)
......@@ -236,7 +240,7 @@ extension EventMonitor {
public func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL) { }
public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { }
public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) { }
public func request(_ request: Request, didFailToCreateURLRequestWithError error: Error) { }
public func request(_ request: Request,
didAdaptInitialRequest initialRequest: URLRequest,
......@@ -244,6 +248,7 @@ extension EventMonitor {
public func request(_ request: Request,
didFailToAdaptURLRequest initialRequest: URLRequest,
withError error: Error) { }
public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) { }
public func request(_ request: Request, didCreateTask task: URLSessionTask) { }
public func request(_ request: Request, didGatherMetrics metrics: URLSessionTaskMetrics) { }
public func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: Error) { }
......@@ -392,8 +397,8 @@ public final class CompositeEventMonitor: EventMonitor {
performEvent { $0.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) }
}
public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
performEvent { $0.request(request, didCreateURLRequest: urlRequest) }
public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {
performEvent { $0.request(request, didCreateInitialURLRequest: urlRequest) }
}
public func request(_ request: Request, didFailToCreateURLRequestWithError error: Error) {
......@@ -408,6 +413,10 @@ public final class CompositeEventMonitor: EventMonitor {
performEvent { $0.request(request, didFailToAdaptURLRequest: initialRequest, withError: error) }
}
public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
performEvent { $0.request(request, didCreateURLRequest: urlRequest) }
}
public func request(_ request: Request, didCreateTask task: URLSessionTask) {
performEvent { $0.request(request, didCreateTask: task) }
}
......@@ -562,8 +571,8 @@ open class ClosureEventMonitor: EventMonitor {
// MARK: - Request Events
/// Closure called on the `request(_:didCreateURLRequest:)` event.
open var requestDidCreateURLRequest: ((Request, URLRequest) -> Void)?
/// Closure called on the `request(_:didCreateInitialURLRequest:)` event.
open var requestDidCreateInitialURLRequest: ((Request, URLRequest) -> Void)?
/// Closure called on the `request(_:didFailToCreateURLRequestWithError:)` event.
open var requestDidFailToCreateURLRequestWithError: ((Request, Error) -> Void)?
......@@ -574,6 +583,9 @@ open class ClosureEventMonitor: EventMonitor {
/// Closure called on the `request(_:didFailToAdaptURLRequest:withError:)` event.
open var requestDidFailToAdaptURLRequestWithError: ((Request, URLRequest, Error) -> Void)?
/// Closure called on the `request(_:didCreateURLRequest:)` event.
open var requestDidCreateURLRequest: ((Request, URLRequest) -> Void)?
/// Closure called on the `request(_:didCreateTask:)` event.
open var requestDidCreateTask: ((Request, URLSessionTask) -> Void)?
......@@ -714,8 +726,8 @@ open class ClosureEventMonitor: EventMonitor {
// MARK: Request Events
open func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
requestDidCreateURLRequest?(request, urlRequest)
open func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {
requestDidCreateInitialURLRequest?(request, urlRequest)
}
open func request(_ request: Request, didFailToCreateURLRequestWithError error: Error) {
......@@ -730,6 +742,10 @@ open class ClosureEventMonitor: EventMonitor {
requestDidFailToAdaptURLRequestWithError?(request, initialRequest, error)
}
open func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
requestDidCreateURLRequest?(request, urlRequest)
}
open func request(_ request: Request, didCreateTask task: URLSessionTask) {
requestDidCreateTask?(request, task)
}
......
......@@ -92,6 +92,8 @@ public class Request {
var redirectHandler: RedirectHandler?
/// `CachedResponseHandler` provided to handle response caching.
var cachedResponseHandler: CachedResponseHandler?
/// Closure called when the `Request` is able to create a cURL description of itself.
var cURLHandler: ((String) -> Void)?
/// Response serialization closures that handle response parsing.
var responseSerializers: [() -> Void] = []
/// Response serialization completion closures executed once all response serializers are complete.
......@@ -263,13 +265,14 @@ public class Request {
// MARK: - Internal Event API
// All API must be called from underlyingQueue.
/// Called when a `URLRequest` has been created on behalf of the instance.
/// Called when a initial `URLRequest` has been created on behalf of the instance. If a `RequestAdapter` is active,
/// the `URLRequest` will be adapted before being issued.
///
/// - Parameter request: `URLRequest` created.
func didCreateURLRequest(_ request: URLRequest) {
/// - Parameter request: The `URLRequest` created.
func didCreateInitialURLRequest(_ request: URLRequest) {
protectedMutableState.write { $0.requests.append(request) }
eventMonitor?.request(self, didCreateURLRequest: request)
eventMonitor?.request(self, didCreateInitialURLRequest: request)
}
/// Called when initial `URLRequest` creation has failed, typically through a `URLRequestConvertible`.
......@@ -282,6 +285,8 @@ public class Request {
eventMonitor?.request(self, didFailToCreateURLRequestWithError: error)
callCURLHandlerIfNecessary()
retryOrFinish(error: error)
}
......@@ -308,9 +313,30 @@ public class Request {
eventMonitor?.request(self, didFailToAdaptURLRequest: request, withError: error)
callCURLHandlerIfNecessary()
retryOrFinish(error: error)
}
/// Final `URLRequest` has been created for the instance.
///
/// - Parameter request: The `URLRequest` created.
func didCreateURLRequest(_ request: URLRequest) {
eventMonitor?.request(self, didCreateURLRequest: request)
callCURLHandlerIfNecessary()
}
/// Asynchronously calls any stored `cURLHandler` and then removes it from `mutableState`.
private func callCURLHandlerIfNecessary() {
protectedMutableState.write { mutableState in
guard let cURLHandler = mutableState.cURLHandler else { return }
self.underlyingQueue.async { cURLHandler(self.cURLDescription()) }
mutableState.cURLHandler = nil
}
}
/// Called when a `URLSessionTask` is created on behalf of the instance.
///
/// - Parameter task: The `URLSessionTask` created.
......@@ -693,7 +719,7 @@ public class Request {
/// Sets the redirect handler for the instance which will be used if a redirect response is encountered.
///
/// - Note: Overrides any `RedirectHandler` set on the `Session` which produced this `Request`.
/// - Note: Attempting to set the redirect handler more than once is a logic error and will crash.
///
/// - Parameter handler: The `RedirectHandler`.
///
......@@ -701,7 +727,7 @@ public class Request {
@discardableResult
public func redirect(using handler: RedirectHandler) -> Self {
protectedMutableState.write { mutableState in
precondition(mutableState.redirectHandler == nil, "Redirect handler has already been set")
precondition(mutableState.redirectHandler == nil, "Redirect handler has already been set.")
mutableState.redirectHandler = handler
}
......@@ -712,19 +738,41 @@ public class Request {
/// Sets the cached response handler for the `Request` which will be used when attempting to cache a response.
///
/// - Note: Attempting to set the cache handler more than once is a logic error and will crash.
///
/// - Parameter handler: The `CachedResponseHandler`.
///
/// - Returns: The `Request`.
/// - Returns: The instance.
@discardableResult
public func cacheResponse(using handler: CachedResponseHandler) -> Self {
protectedMutableState.write { mutableState in
precondition(mutableState.cachedResponseHandler == nil, "Cached response handler has already been set")
precondition(mutableState.cachedResponseHandler == nil, "Cached response handler has already been set.")
mutableState.cachedResponseHandler = handler
}
return self
}
/// Sets a handler to be called when the cURL description of the request is available.
///
/// - Note: When waiting for a `Request`'s `URLRequest` to be created, only the last `handler` will be called.
///
/// - Parameter handler: Closure to be called when the cURL description is available.
///
/// - Returns: The instance.
@discardableResult
public func cURLDescription(calling handler: @escaping (String) -> Void) -> Self {
protectedMutableState.write { mutableState in
if mutableState.requests.last != nil {
underlyingQueue.async { handler(self.cURLDescription()) }
} else {
mutableState.cURLHandler = handler
}
}
return self
}
// MARK: Cleanup
/// Final cleanup step executed when the instance finishes response serialization.
......@@ -761,16 +809,11 @@ extension Request: CustomStringConvertible {
}
}
extension Request: CustomDebugStringConvertible {
/// A textual representation of this instance in the form of a cURL command.
public var debugDescription: String {
return cURLRepresentation()
}
extension Request {
/// cURL representation of the instance.
///
/// - Returns: The cURL equivalent of the instance.
func cURLRepresentation() -> String {
public func cURLDescription() -> String {
guard
let request = lastRequest,
let url = request.url,
......
......@@ -798,7 +798,7 @@ open class Session {
func performSetupOperations(for request: Request, convertible: URLRequestConvertible) {
do {
let initialRequest = try convertible.asURLRequest()
rootQueue.async { request.didCreateURLRequest(initialRequest) }
rootQueue.async { request.didCreateInitialURLRequest(initialRequest) }
guard !request.isCancelled else { return }
......@@ -827,6 +827,8 @@ open class Session {
// MARK: - Task Handling
func didCreateURLRequest(_ urlRequest: URLRequest, for request: Request) {
request.didCreateURLRequest(urlRequest)
guard !request.isCancelled else { return }
let task = request.task(for: urlRequest, using: session)
......
......@@ -388,6 +388,7 @@ final class DownloadRequestEventsTestCase: BaseTestCase {
let session = Session(eventMonitors: [eventMonitor])
let taskDidFinishCollecting = expectation(description: "taskDidFinishCollecting should fire")
let didCreateInitialURLRequest = expectation(description: "didCreateInitialURLRequest should fire")
let didCreateURLRequest = expectation(description: "didCreateURLRequest should fire")
let didCreateTask = expectation(description: "didCreateTask should fire")
let didGatherMetrics = expectation(description: "didGatherMetrics should fire")
......@@ -405,6 +406,7 @@ final class DownloadRequestEventsTestCase: BaseTestCase {
var wroteData = false
eventMonitor.taskDidFinishCollectingMetrics = { (_, _, _) in taskDidFinishCollecting.fulfill() }
eventMonitor.requestDidCreateInitialURLRequest = { (_, _) in didCreateInitialURLRequest.fulfill() }
eventMonitor.requestDidCreateURLRequest = { (_, _) in didCreateURLRequest.fulfill() }
eventMonitor.requestDidCreateTask = { (_, _) in didCreateTask.fulfill() }
eventMonitor.requestDidGatherMetrics = { (_, _) in didGatherMetrics.fulfill() }
......@@ -440,6 +442,7 @@ final class DownloadRequestEventsTestCase: BaseTestCase {
let session = Session(startRequestsImmediately: false, eventMonitors: [eventMonitor])
let taskDidFinishCollecting = expectation(description: "taskDidFinishCollecting should fire")
let didCreateInitialURLRequest = expectation(description: "didCreateInitialURLRequest should fire")
let didCreateURLRequest = expectation(description: "didCreateURLRequest should fire")
let didCreateTask = expectation(description: "didCreateTask should fire")
let didGatherMetrics = expectation(description: "didGatherMetrics should fire")
......@@ -453,6 +456,7 @@ final class DownloadRequestEventsTestCase: BaseTestCase {
let responseHandler = expectation(description: "responseHandler should fire")
eventMonitor.taskDidFinishCollectingMetrics = { (_, _, _) in taskDidFinishCollecting.fulfill() }
eventMonitor.requestDidCreateInitialURLRequest = { (_, _) in didCreateInitialURLRequest.fulfill() }
eventMonitor.requestDidCreateURLRequest = { (_, _) in didCreateURLRequest.fulfill() }
eventMonitor.requestDidCreateTask = { (_, _) in didCreateTask.fulfill() }
eventMonitor.requestDidGatherMetrics = { (_, _) in didGatherMetrics.fulfill() }
......
......@@ -100,8 +100,8 @@ public final class NSLoggingEventMonitor: EventMonitor {
NSLog("URLSession: \(session), downloadTask: \(downloadTask), didFinishDownloadingTo: \(location)")
}
public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
NSLog("Request: \(request) didCreateURLRequest: \(urlRequest)")
public func request(_ request: Request, didCreateInitialURLRequest urlRequest: URLRequest) {
NSLog("Request: \(request) didCreateInitialURLRequest: \(urlRequest)")
}
public func request(_ request: Request, didFailToCreateURLRequestWithError error: Error) {
......@@ -116,6 +116,10 @@ public final class NSLoggingEventMonitor: EventMonitor {
NSLog("Request: \(request) didFailToAdaptURLRequest \(initialRequest) withError \(error)")
}
public func request(_ request: Request, didCreateURLRequest urlRequest: URLRequest) {
NSLog("Request: \(request) didCreateURLRequest: \(urlRequest)")
}
public func request(_ request: Request, didCreateTask task: URLSessionTask) {
NSLog("Request: \(request) didCreateTask \(task)")
}
......
......@@ -532,7 +532,7 @@ class RequestResponseTestCase: BaseTestCase {
let taskDidFinishCollecting = expectation(description: "taskDidFinishCollecting should fire")
let didReceiveData = expectation(description: "didReceiveData should fire")
let willCacheResponse = expectation(description: "willCacheResponse should fire")
let didCreateURLRequest = expectation(description: "didCreateURLRequest should fire")
let didCreateURLRequest = expectation(description: "didCreateInitialURLRequest should fire")
let didCreateTask = expectation(description: "didCreateTask should fire")
let didGatherMetrics = expectation(description: "didGatherMetrics should fire")
let didComplete = expectation(description: "didComplete should fire")
......@@ -553,7 +553,7 @@ class RequestResponseTestCase: BaseTestCase {
didReceiveData.fulfill()
}
eventMonitor.dataTaskWillCacheResponse = { (_, _, _) in willCacheResponse.fulfill() }
eventMonitor.requestDidCreateURLRequest = { (_, _) in didCreateURLRequest.fulfill() }
eventMonitor.requestDidCreateInitialURLRequest = { (_, _) in didCreateURLRequest.fulfill() }
eventMonitor.requestDidCreateTask = { (_, _) in didCreateTask.fulfill() }
eventMonitor.requestDidGatherMetrics = { (_, _) in didGatherMetrics.fulfill() }
eventMonitor.requestDidCompleteTaskWithError = { (_, _, _) in didComplete.fulfill() }
......@@ -579,7 +579,7 @@ class RequestResponseTestCase: BaseTestCase {
let session = Session(startRequestsImmediately: false, eventMonitors: [eventMonitor])
let taskDidFinishCollecting = expectation(description: "taskDidFinishCollecting should fire")
let didCreateURLRequest = expectation(description: "didCreateURLRequest should fire")
let didCreateURLRequest = expectation(description: "didCreateInitialURLRequest should fire")
let didCreateTask = expectation(description: "didCreateTask should fire")
let didGatherMetrics = expectation(description: "didGatherMetrics should fire")
let didComplete = expectation(description: "didComplete should fire")
......@@ -592,7 +592,7 @@ class RequestResponseTestCase: BaseTestCase {
let responseHandler = expectation(description: "responseHandler should fire")
eventMonitor.taskDidFinishCollectingMetrics = { (_, _, _) in taskDidFinishCollecting.fulfill() }
eventMonitor.requestDidCreateURLRequest = { (_, _) in didCreateURLRequest.fulfill() }
eventMonitor.requestDidCreateInitialURLRequest = { (_, _) in didCreateURLRequest.fulfill() }
eventMonitor.requestDidCreateTask = { (_, _) in didCreateTask.fulfill() }
eventMonitor.requestDidGatherMetrics = { (_, _) in didGatherMetrics.fulfill() }
eventMonitor.requestDidCompleteTaskWithError = { (_, _, _) in didComplete.fulfill() }
......@@ -747,7 +747,7 @@ class RequestDescriptionTestCase: BaseTestCase {
// MARK: -
class RequestDebugDescriptionTestCase: BaseTestCase {
final class RequestCURLDescriptionTestCase: BaseTestCase {
// MARK: Properties
let manager: Session = {
......@@ -798,86 +798,157 @@ class RequestDebugDescriptionTestCase: BaseTestCase {
// MARK: Tests
func testGETRequestDebugDescription() {
func testGETRequestCURLDescription() {
// Given
let urlString = "https://httpbin.org/get"
let expectation = self.expectation(description: "request should complete")
var components: [String]?
// When
let request = manager.request(urlString).response { _ in expectation.fulfill() }
manager.request(urlString).cURLDescription {
components = self.cURLCommandComponents(from: $0)
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
let components = cURLCommandComponents(for: request)
// Then
XCTAssertEqual(components?[0..<3], ["$", "curl", "-v"])
XCTAssertTrue(components?.contains("-X") == true)
XCTAssertEqual(components?.last, "\"\(urlString)\"")
}
func testGETRequestCURLDescriptionSynchronous() {
// Given
let urlString = "https://httpbin.org/get"
let expectation = self.expectation(description: "request should complete")
var components: [String]?
var syncComponents: [String]?
// When
let request = manager.request(urlString)
request.cURLDescription {
components = self.cURLCommandComponents(from: $0)
syncComponents = self.cURLCommandComponents(from:request.cURLDescription())
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
// Then
XCTAssertEqual(components[0..<3], ["$", "curl", "-v"])
XCTAssertTrue(components.contains("-X"))
XCTAssertEqual(components.last, "\"\(urlString)\"")
XCTAssertEqual(components?[0..<3], ["$", "curl", "-v"])
XCTAssertTrue(components?.contains("-X") == true)
XCTAssertEqual(components?.last, "\"\(urlString)\"")
XCTAssertEqual(components, syncComponents)
}
func testGETRequestWithJSONHeaderDebugDescription() {
func testGETRequestCURLDescriptionCanBeRequestedManyTimes() {
// Given
let urlString = "https://httpbin.org/get"
let expectation = self.expectation(description: "request should complete")
var components: [String]?
var secondComponents: [String]?
// When
let headers: HTTPHeaders = [ "X-Custom-Header": "{\"key\": \"value\"}" ]
let request = manager.request(urlString, headers: headers).response { _ in expectation.fulfill() }
let request = manager.request(urlString)
request.cURLDescription {
components = self.cURLCommandComponents(from: $0)
request.cURLDescription {
secondComponents = self.cURLCommandComponents(from: $0)
expectation.fulfill()
}
}
// Trigger the overwrite behavior.
request.cURLDescription {
components = self.cURLCommandComponents(from: $0)
request.cURLDescription {
secondComponents = self.cURLCommandComponents(from: $0)
expectation.fulfill()
}
}
waitForExpectations(timeout: timeout, handler: nil)
// Then
XCTAssertNotNil(request.debugDescription.range(of: "-H \"X-Custom-Header: {\\\"key\\\": \\\"value\\\"}\""))
XCTAssertEqual(components?[0..<3], ["$", "curl", "-v"])
XCTAssertTrue(components?.contains("-X") == true)
XCTAssertEqual(components?.last, "\"\(urlString)\"")
XCTAssertEqual(components, secondComponents)
}
func testGETRequestWithDuplicateHeadersDebugDescription() {
func testGETRequestWithCustomHeaderCURLDescription() {
// Given
let urlString = "https://httpbin.org/get"
let expectation = self.expectation(description: "request should complete")
var cURLDescription: String?
// When
let headers: HTTPHeaders = [ "Accept-Language": "en-GB" ]
let request = managerWithAcceptLanguageHeader.request(urlString, headers: headers).response { _ in expectation.fulfill() }
let headers: HTTPHeaders = ["X-Custom-Header": "{\"key\": \"value\"}"]
manager.request(urlString, headers: headers).cURLDescription {
cURLDescription = $0
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
let components = cURLCommandComponents(for: request)
// Then
XCTAssertNotNil(cURLDescription?.range(of: "-H \"X-Custom-Header: {\\\"key\\\": \\\"value\\\"}\""))
}
func testGETRequestWithDuplicateHeadersDebugDescription() {
// Given
let urlString = "https://httpbin.org/get"
let expectation = self.expectation(description: "request should complete")
var cURLDescription: String?
var components: [String]?
// When
let headers: HTTPHeaders = ["Accept-Language": "en-GB"]
managerWithAcceptLanguageHeader.request(urlString, headers: headers).cURLDescription {
components = self.cURLCommandComponents(from: $0)
cURLDescription = $0
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
// Then
XCTAssertEqual(components[0..<3], ["$", "curl", "-v"])
XCTAssertTrue(components.contains("-X"))
XCTAssertEqual(components.last, "\"\(urlString)\"")
XCTAssertEqual(components?[0..<3], ["$", "curl", "-v"])
XCTAssertTrue(components?.contains("-X") == true)
XCTAssertEqual(components?.last, "\"\(urlString)\"")
let tokens = request.debugDescription.components(separatedBy: "Accept-Language:")
XCTAssertTrue(tokens.count == 2, "command should contain a single Accept-Language header")
let acceptLanguageCount = components?.filter { $0.contains("Accept-Language") }.count
XCTAssertEqual(acceptLanguageCount, 1, "command should contain a single Accept-Language header")
XCTAssertNotNil(request.debugDescription.range(of: "-H \"Accept-Language: en-GB\""))
XCTAssertNotNil(cURLDescription?.range(of: "-H \"Accept-Language: en-GB\""))
}
func testPOSTRequestDebugDescription() {
func testPOSTRequestCURLDescription() {
// Given
let urlString = "https://httpbin.org/post"
let expectation = self.expectation(description: "request should complete")
var components: [String]?
// When
let request = manager.request(urlString, method: .post).response { _ in expectation.fulfill() }
manager.request(urlString, method: .post).cURLDescription {
components = self.cURLCommandComponents(from: $0)
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
let components = cURLCommandComponents(for: request)
// Then
XCTAssertEqual(components[0..<3], ["$", "curl", "-v"])
XCTAssertEqual(components[3..<5], ["-X", "POST"])
XCTAssertEqual(components.last, "\"\(urlString)\"")
XCTAssertEqual(components?[0..<3], ["$", "curl", "-v"])
XCTAssertEqual(components?[3..<5], ["-X", "POST"])
XCTAssertEqual(components?.last, "\"\(urlString)\"")
}
func testPOSTRequestWithJSONParametersDebugDescription() {
func testPOSTRequestWithJSONParametersCURLDescription() {
// Given
let urlString = "https://httpbin.org/post"
let expectation = self.expectation(description: "request should complete")
var cURLDescription: String?
var components: [String]?
let parameters = [
"foo": "bar",
......@@ -886,28 +957,28 @@ class RequestDebugDescriptionTestCase: BaseTestCase {
]
// When
let request = manager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default).response {
_ in expectation.fulfill()
manager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default).cURLDescription {
components = self.cURLCommandComponents(from: $0)
cURLDescription = $0
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
let components = cURLCommandComponents(for: request)
// Then
XCTAssertEqual(components[0..<3], ["$", "curl", "-v"])
XCTAssertEqual(components[3..<5], ["-X", "POST"])
XCTAssertEqual(components?[0..<3], ["$", "curl", "-v"])
XCTAssertEqual(components?[3..<5], ["-X", "POST"])
XCTAssertNotNil(request.debugDescription.range(of: "-H \"Content-Type: application/json\""))
XCTAssertNotNil(request.debugDescription.range(of: "-d \"{"))
XCTAssertNotNil(request.debugDescription.range(of: "\\\"f'oo\\\":\\\"ba'r\\\""))
XCTAssertNotNil(request.debugDescription.range(of: "\\\"fo\\\\\\\"o\\\":\\\"b\\\\\\\"ar\\\""))
XCTAssertNotNil(request.debugDescription.range(of: "\\\"foo\\\":\\\"bar\\"))
XCTAssertNotNil(cURLDescription?.range(of: "-H \"Content-Type: application/json\""))
XCTAssertNotNil(cURLDescription?.range(of: "-d \"{"))
XCTAssertNotNil(cURLDescription?.range(of: "\\\"f'oo\\\":\\\"ba'r\\\""))
XCTAssertNotNil(cURLDescription?.range(of: "\\\"fo\\\\\\\"o\\\":\\\"b\\\\\\\"ar\\\""))
XCTAssertNotNil(cURLDescription?.range(of: "\\\"foo\\\":\\\"bar\\"))
XCTAssertEqual(components.last, "\"\(urlString)\"")
XCTAssertEqual(components?.last, "\"\(urlString)\"")
}
func testPOSTRequestWithCookieDebugDescription() {
func testPOSTRequestWithCookieCURLDescription() {
// Given
let urlString = "https://httpbin.org/post"
......@@ -921,23 +992,24 @@ class RequestDebugDescriptionTestCase: BaseTestCase {
let cookie = HTTPCookie(properties: properties)!
let cookieManager = managerWithCookie(cookie)
let expectation = self.expectation(description: "request should complete")
var components: [String]?
// When
let request = cookieManager.request(urlString, method: .post).response { _ in expectation.fulfill() }
cookieManager.request(urlString, method: .post).cURLDescription {
components = self.cURLCommandComponents(from: $0)
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
let components = cURLCommandComponents(for: request)
// Then
XCTAssertEqual(components[0..<3], ["$", "curl", "-v"])
XCTAssertEqual(components[3..<5], ["-X", "POST"])
XCTAssertEqual(components.last, "\"\(urlString)\"")
XCTAssertEqual(components[5..<6], ["-b"])
XCTAssertEqual(components?[0..<3], ["$", "curl", "-v"])
XCTAssertEqual(components?[3..<5], ["-X", "POST"])
XCTAssertEqual(components?.last, "\"\(urlString)\"")
XCTAssertEqual(components?[5..<6], ["-b"])
}
func testPOSTRequestWithCookiesDisabledDebugDescription() {
func testPOSTRequestWithCookiesDisabledCURLDescriptionHasNoCookies() {
// Given
let urlString = "https://httpbin.org/post"
......@@ -950,67 +1022,74 @@ class RequestDebugDescriptionTestCase: BaseTestCase {
let cookie = HTTPCookie(properties: properties)!
managerDisallowingCookies.session.configuration.httpCookieStorage?.setCookie(cookie)
let expectation = self.expectation(description: "request should complete")
var components: [String]?
// When
let request = managerDisallowingCookies.request(urlString, method: .post)
let components = cURLCommandComponents(for: request)
managerDisallowingCookies.request(urlString, method: .post).cURLDescription {
components = self.cURLCommandComponents(from: $0)
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
// Then
let cookieComponents = components.filter { $0 == "-b" }
XCTAssertTrue(cookieComponents.isEmpty)
let cookieComponents = components?.filter { $0 == "-b" }
XCTAssertTrue(cookieComponents?.isEmpty == true)
}
func testMultipartFormDataRequestWithDuplicateHeadersDebugDescription() {
func testMultipartFormDataRequestWithDuplicateHeadersCURLDescriptionHasOneContentTypeHeader() {
// Given
let urlString = "https://httpbin.org/post"
let japaneseData = Data("日本語".utf8)
let expectation = self.expectation(description: "multipart form data encoding should succeed")
var cURLDescription: String?
var components: [String]?
// When
let request = managerWithContentTypeHeader.upload(multipartFormData: { (data) in
managerWithContentTypeHeader.upload(multipartFormData: { (data) in
data.append(japaneseData, withName: "japanese")
}, to: urlString)
.response { _ in
expectation.fulfill()
}
}, to: urlString).cURLDescription {
components = self.cURLCommandComponents(from: $0)
cURLDescription = $0
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
let components = cURLCommandComponents(for: request)
// Then
XCTAssertEqual(components[0..<3], ["$", "curl", "-v"])
XCTAssertTrue(components.contains("-X"))
XCTAssertEqual(components.last, "\"\(urlString)\"")
XCTAssertEqual(components?[0..<3], ["$", "curl", "-v"])
XCTAssertTrue(components?.contains("-X") == true)
XCTAssertEqual(components?.last, "\"\(urlString)\"")
let tokens = request.debugDescription.components(separatedBy: "Content-Type:")
XCTAssertTrue(tokens.count == 2, "command should contain a single Content-Type header")
let contentTypeCount = components?.filter { $0.contains("Content-Type") }.count
XCTAssertEqual(contentTypeCount, 1, "command should contain a single Content-Type header")
XCTAssertNotNil(request.debugDescription.range(of: "-H \"Content-Type: multipart/form-data;"))
XCTAssertNotNil(cURLDescription?.range(of: "-H \"Content-Type: multipart/form-data;"))
}
func testThatRequestWithInvalidURLDebugDescription() {
// Given
let urlString = "invalid_url"
let expectation = self.expectation(description: "request should complete")
var cURLDescription: String?
// When
let request = manager.request(urlString).response { _ in expectation.fulfill() }
manager.request(urlString).cURLDescription {
cURLDescription = $0
expectation.fulfill()
}
waitForExpectations(timeout: timeout, handler: nil)
let debugDescription = request.debugDescription
// Then
XCTAssertNotNil(debugDescription, "debugDescription should not crash")
XCTAssertNotNil(cURLDescription, "debugDescription should not crash")
}
// MARK: Test Helper Methods
private func cURLCommandComponents(for request: Request) -> [String] {
let whitespaceCharacterSet = CharacterSet.whitespacesAndNewlines
return request.debugDescription
.components(separatedBy: whitespaceCharacterSet)
.filter { $0 != "" && $0 != "\\" }
private func cURLCommandComponents(from cURLString: String) -> [String] {
return cURLString.components(separatedBy: .whitespacesAndNewlines)
.filter { $0 != "" && $0 != "\\" }
}
}
......@@ -656,6 +656,7 @@ final class UploadRequestEventsTestCase: BaseTestCase {
let session = Session(eventMonitors: [eventMonitor])
let taskDidFinishCollecting = expectation(description: "taskDidFinishCollecting should fire")
let didCreateInitialURLRequest = expectation(description: "didCreateInitialURLRequest should fire")
let didCreateURLRequest = expectation(description: "didCreateURLRequest should fire")
let didCreateTask = expectation(description: "didCreateTask should fire")
let didGatherMetrics = expectation(description: "didGatherMetrics should fire")
......@@ -668,6 +669,7 @@ final class UploadRequestEventsTestCase: BaseTestCase {
let responseHandler = expectation(description: "responseHandler should fire")
eventMonitor.taskDidFinishCollectingMetrics = { (_, _, _) in taskDidFinishCollecting.fulfill() }
eventMonitor.requestDidCreateInitialURLRequest = { (_, _) in didCreateInitialURLRequest.fulfill() }
eventMonitor.requestDidCreateURLRequest = { (_, _) in didCreateURLRequest.fulfill() }
eventMonitor.requestDidCreateTask = { (_, _) in didCreateTask.fulfill() }
eventMonitor.requestDidGatherMetrics = { (_, _) in didGatherMetrics.fulfill() }
......@@ -696,6 +698,7 @@ final class UploadRequestEventsTestCase: BaseTestCase {
let session = Session(startRequestsImmediately: false, eventMonitors: [eventMonitor])
let taskDidFinishCollecting = expectation(description: "taskDidFinishCollecting should fire")
let didCreateInitialURLRequest = expectation(description: "didCreateInitialURLRequest should fire")
let didCreateURLRequest = expectation(description: "didCreateURLRequest should fire")
let didCreateTask = expectation(description: "didCreateTask should fire")
let didGatherMetrics = expectation(description: "didGatherMetrics should fire")
......@@ -710,6 +713,7 @@ final class UploadRequestEventsTestCase: BaseTestCase {
let responseHandler = expectation(description: "responseHandler should fire")
eventMonitor.taskDidFinishCollectingMetrics = { (_, _, _) in taskDidFinishCollecting.fulfill() }
eventMonitor.requestDidCreateInitialURLRequest = { (_, _) in didCreateInitialURLRequest.fulfill() }
eventMonitor.requestDidCreateURLRequest = { (_, _) in didCreateURLRequest.fulfill() }
eventMonitor.requestDidCreateTask = { (_, _) in didCreateTask.fulfill() }
eventMonitor.requestDidGatherMetrics = { (_, _) in didGatherMetrics.fulfill() }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册