SessionDelegateTests.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. //
  2. // SessionDelegateTests.swift
  3. //
  4. // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. @testable import Alamofire
  25. import Foundation
  26. import XCTest
  27. class SessionDelegateTestCase: BaseTestCase {
  28. var manager: SessionManager!
  29. // MARK: - Setup and Teardown
  30. override func setUp() {
  31. super.setUp()
  32. manager = SessionManager(configuration: .ephemeral)
  33. }
  34. // MARK: - Tests - Session Invalidation
  35. func testThatSessionDidBecomeInvalidWithErrorClosureIsCalledWhenSet() {
  36. // Given
  37. let expectation = self.expectation(description: "Override closure should be called")
  38. var overrideClosureCalled = false
  39. var invalidationError: Error?
  40. manager.delegate.sessionDidBecomeInvalidWithError = { _, error in
  41. overrideClosureCalled = true
  42. invalidationError = error
  43. expectation.fulfill()
  44. }
  45. // When
  46. manager.session.invalidateAndCancel()
  47. waitForExpectations(timeout: timeout, handler: nil)
  48. // Then
  49. XCTAssertTrue(overrideClosureCalled)
  50. XCTAssertNil(invalidationError)
  51. }
  52. // MARK: - Tests - Session Challenges
  53. func testThatSessionDidReceiveChallengeClosureIsCalledWhenSet() {
  54. // Given
  55. let expectation = self.expectation(description: "Override closure should be called")
  56. var overrideClosureCalled = false
  57. var response: HTTPURLResponse?
  58. manager.delegate.sessionDidReceiveChallenge = { session, challenge in
  59. overrideClosureCalled = true
  60. return (.performDefaultHandling, nil)
  61. }
  62. // When
  63. manager.request("https://httpbin.org/get", withMethod: .get).responseJSON { closureResponse in
  64. response = closureResponse.response
  65. expectation.fulfill()
  66. }
  67. waitForExpectations(timeout: timeout, handler: nil)
  68. // Then
  69. XCTAssertTrue(overrideClosureCalled)
  70. XCTAssertEqual(response?.statusCode, 200)
  71. }
  72. func testThatSessionDidReceiveChallengeWithCompletionClosureIsCalledWhenSet() {
  73. // Given
  74. let expectation = self.expectation(description: "Override closure should be called")
  75. var overrideClosureCalled = false
  76. var response: HTTPURLResponse?
  77. manager.delegate.sessionDidReceiveChallengeWithCompletion = { session, challenge, completion in
  78. overrideClosureCalled = true
  79. completion(.performDefaultHandling, nil)
  80. }
  81. // When
  82. manager.request("https://httpbin.org/get", withMethod: .get).responseJSON { closureResponse in
  83. response = closureResponse.response
  84. expectation.fulfill()
  85. }
  86. waitForExpectations(timeout: timeout, handler: nil)
  87. // Then
  88. XCTAssertTrue(overrideClosureCalled)
  89. XCTAssertEqual(response?.statusCode, 200)
  90. }
  91. // MARK: - Tests - Redirects
  92. func testThatRequestWillPerformHTTPRedirectionByDefault() {
  93. // Given
  94. let redirectURLString = "https://www.apple.com/"
  95. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  96. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  97. var request: URLRequest?
  98. var response: HTTPURLResponse?
  99. var data: Data?
  100. var error: Error?
  101. // When
  102. manager.request(urlString, withMethod: .get)
  103. .response { responseRequest, responseResponse, responseData, responseError in
  104. request = responseRequest
  105. response = responseResponse
  106. data = responseData
  107. error = responseError
  108. expectation.fulfill()
  109. }
  110. waitForExpectations(timeout: timeout, handler: nil)
  111. // Then
  112. XCTAssertNotNil(request, "request should not be nil")
  113. XCTAssertNotNil(response, "response should not be nil")
  114. XCTAssertNotNil(data, "data should not be nil")
  115. XCTAssertNil(error, "error should be nil")
  116. XCTAssertEqual(response?.url?.urlString ?? "", redirectURLString, "response URL should match the redirect URL")
  117. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  118. }
  119. func testThatRequestWillPerformRedirectionMultipleTimesByDefault() {
  120. // Given
  121. let redirectURLString = "https://httpbin.org/get"
  122. let urlString = "https://httpbin.org/redirect/5"
  123. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  124. var request: URLRequest?
  125. var response: HTTPURLResponse?
  126. var data: Data?
  127. var error: Error?
  128. // When
  129. manager.request(urlString, withMethod: .get)
  130. .response { responseRequest, responseResponse, responseData, responseError in
  131. request = responseRequest
  132. response = responseResponse
  133. data = responseData
  134. error = responseError
  135. expectation.fulfill()
  136. }
  137. waitForExpectations(timeout: timeout, handler: nil)
  138. // Then
  139. XCTAssertNotNil(request, "request should not be nil")
  140. XCTAssertNotNil(response, "response should not be nil")
  141. XCTAssertNotNil(data, "data should not be nil")
  142. XCTAssertNil(error, "error should be nil")
  143. XCTAssertEqual(response?.url?.urlString ?? "", redirectURLString, "response URL should match the redirect URL")
  144. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  145. }
  146. func testThatTaskOverrideClosureCanPerformHTTPRedirection() {
  147. // Given
  148. let redirectURLString = "https://www.apple.com/"
  149. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  150. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  151. let callbackExpectation = self.expectation(description: "Redirect callback should be made")
  152. let delegate: SessionDelegate = manager.delegate
  153. delegate.taskWillPerformHTTPRedirection = { _, _, _, request in
  154. callbackExpectation.fulfill()
  155. return request
  156. }
  157. var request: URLRequest?
  158. var response: HTTPURLResponse?
  159. var data: Data?
  160. var error: Error?
  161. // When
  162. manager.request(urlString, withMethod: .get)
  163. .response { responseRequest, responseResponse, responseData, responseError in
  164. request = responseRequest
  165. response = responseResponse
  166. data = responseData
  167. error = responseError
  168. expectation.fulfill()
  169. }
  170. waitForExpectations(timeout: timeout, handler: nil)
  171. // Then
  172. XCTAssertNotNil(request, "request should not be nil")
  173. XCTAssertNotNil(response, "response should not be nil")
  174. XCTAssertNotNil(data, "data should not be nil")
  175. XCTAssertNil(error, "error should be nil")
  176. XCTAssertEqual(response?.url?.urlString ?? "", redirectURLString, "response URL should match the redirect URL")
  177. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  178. }
  179. func testThatTaskOverrideClosureWithCompletionCanPerformHTTPRedirection() {
  180. // Given
  181. let redirectURLString = "https://www.apple.com/"
  182. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  183. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  184. let callbackExpectation = self.expectation(description: "Redirect callback should be made")
  185. let delegate: SessionDelegate = manager.delegate
  186. delegate.taskWillPerformHTTPRedirectionWithCompletion = { _, _, _, request, completion in
  187. completion(request)
  188. callbackExpectation.fulfill()
  189. }
  190. var request: URLRequest?
  191. var response: HTTPURLResponse?
  192. var data: Data?
  193. var error: Error?
  194. // When
  195. manager.request(urlString, withMethod: .get)
  196. .response { responseRequest, responseResponse, responseData, responseError in
  197. request = responseRequest
  198. response = responseResponse
  199. data = responseData
  200. error = responseError
  201. expectation.fulfill()
  202. }
  203. waitForExpectations(timeout: timeout, handler: nil)
  204. // Then
  205. XCTAssertNotNil(request, "request should not be nil")
  206. XCTAssertNotNil(response, "response should not be nil")
  207. XCTAssertNotNil(data, "data should not be nil")
  208. XCTAssertNil(error, "error should be nil")
  209. XCTAssertEqual(response?.url?.urlString ?? "", redirectURLString, "response URL should match the redirect URL")
  210. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  211. }
  212. func testThatTaskOverrideClosureCanCancelHTTPRedirection() {
  213. // Given
  214. let redirectURLString = "https://www.apple.com"
  215. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  216. let expectation = self.expectation(description: "Request should not redirect to \(redirectURLString)")
  217. let callbackExpectation = self.expectation(description: "Redirect callback should be made")
  218. let delegate: SessionDelegate = manager.delegate
  219. delegate.taskWillPerformHTTPRedirectionWithCompletion = { _, _, _, _, completion in
  220. callbackExpectation.fulfill()
  221. completion(nil)
  222. }
  223. var request: URLRequest?
  224. var response: HTTPURLResponse?
  225. var data: Data?
  226. var error: Error?
  227. // When
  228. manager.request(urlString, withMethod: .get)
  229. .response { responseRequest, responseResponse, responseData, responseError in
  230. request = responseRequest
  231. response = responseResponse
  232. data = responseData
  233. error = responseError
  234. expectation.fulfill()
  235. }
  236. waitForExpectations(timeout: timeout, handler: nil)
  237. // Then
  238. XCTAssertNotNil(request, "request should not be nil")
  239. XCTAssertNotNil(response, "response should not be nil")
  240. XCTAssertNotNil(data, "data should not be nil")
  241. XCTAssertNil(error, "error should be nil")
  242. XCTAssertEqual(response?.url?.urlString ?? "", urlString, "response URL should match the origin URL")
  243. XCTAssertEqual(response?.statusCode ?? -1, 302, "response should have a 302 status code")
  244. }
  245. func testThatTaskOverrideClosureWithCompletionCanCancelHTTPRedirection() {
  246. // Given
  247. let redirectURLString = "https://www.apple.com"
  248. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  249. let expectation = self.expectation(description: "Request should not redirect to \(redirectURLString)")
  250. let callbackExpectation = self.expectation(description: "Redirect callback should be made")
  251. let delegate: SessionDelegate = manager.delegate
  252. delegate.taskWillPerformHTTPRedirection = { _, _, _, _ in
  253. callbackExpectation.fulfill()
  254. return nil
  255. }
  256. var request: URLRequest?
  257. var response: HTTPURLResponse?
  258. var data: Data?
  259. var error: Error?
  260. // When
  261. manager.request(urlString, withMethod: .get)
  262. .response { responseRequest, responseResponse, responseData, responseError in
  263. request = responseRequest
  264. response = responseResponse
  265. data = responseData
  266. error = responseError
  267. expectation.fulfill()
  268. }
  269. waitForExpectations(timeout: timeout, handler: nil)
  270. // Then
  271. XCTAssertNotNil(request, "request should not be nil")
  272. XCTAssertNotNil(response, "response should not be nil")
  273. XCTAssertNotNil(data, "data should not be nil")
  274. XCTAssertNil(error, "error should be nil")
  275. XCTAssertEqual(response?.url?.urlString ?? "", urlString, "response URL should match the origin URL")
  276. XCTAssertEqual(response?.statusCode ?? -1, 302, "response should have a 302 status code")
  277. }
  278. func testThatTaskOverrideClosureIsCalledMultipleTimesForMultipleHTTPRedirects() {
  279. // Given
  280. let redirectCount = 5
  281. let redirectURLString = "https://httpbin.org/get"
  282. let urlString = "https://httpbin.org/redirect/\(redirectCount)"
  283. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  284. let delegate: SessionDelegate = manager.delegate
  285. var redirectExpectations = [XCTestExpectation]()
  286. for index in 0..<redirectCount {
  287. redirectExpectations.insert(self.expectation(description: "Redirect #\(index) callback was received"), at: 0)
  288. }
  289. delegate.taskWillPerformHTTPRedirection = { _, _, _, request in
  290. if let redirectExpectation = redirectExpectations.popLast() {
  291. redirectExpectation.fulfill()
  292. } else {
  293. XCTFail("Too many redirect callbacks were received")
  294. }
  295. return request
  296. }
  297. var request: URLRequest?
  298. var response: HTTPURLResponse?
  299. var data: Data?
  300. var error: Error?
  301. // When
  302. manager.request(urlString, withMethod: .get)
  303. .response { responseRequest, responseResponse, responseData, responseError in
  304. request = responseRequest
  305. response = responseResponse
  306. data = responseData
  307. error = responseError
  308. expectation.fulfill()
  309. }
  310. waitForExpectations(timeout: timeout, handler: nil)
  311. // Then
  312. XCTAssertNotNil(request, "request should not be nil")
  313. XCTAssertNotNil(response, "response should not be nil")
  314. XCTAssertNotNil(data, "data should not be nil")
  315. XCTAssertNil(error, "error should be nil")
  316. XCTAssertEqual(response?.url?.urlString ?? "", redirectURLString, "response URL should match the redirect URL")
  317. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  318. }
  319. func testThatTaskOverrideClosureWithCompletionIsCalledMultipleTimesForMultipleHTTPRedirects() {
  320. // Given
  321. let redirectCount = 5
  322. let redirectURLString = "https://httpbin.org/get"
  323. let urlString = "https://httpbin.org/redirect/\(redirectCount)"
  324. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  325. let delegate: SessionDelegate = manager.delegate
  326. var redirectExpectations = [XCTestExpectation]()
  327. for index in 0..<redirectCount {
  328. redirectExpectations.insert(self.expectation(description: "Redirect #\(index) callback was received"), at: 0)
  329. }
  330. delegate.taskWillPerformHTTPRedirectionWithCompletion = { _, _, _, request, completion in
  331. if let redirectExpectation = redirectExpectations.popLast() {
  332. redirectExpectation.fulfill()
  333. } else {
  334. XCTFail("Too many redirect callbacks were received")
  335. }
  336. completion(request)
  337. }
  338. var request: URLRequest?
  339. var response: HTTPURLResponse?
  340. var data: Data?
  341. var error: Error?
  342. // When
  343. manager.request(urlString, withMethod: .get)
  344. .response { responseRequest, responseResponse, responseData, responseError in
  345. request = responseRequest
  346. response = responseResponse
  347. data = responseData
  348. error = responseError
  349. expectation.fulfill()
  350. }
  351. waitForExpectations(timeout: timeout, handler: nil)
  352. // Then
  353. XCTAssertNotNil(request, "request should not be nil")
  354. XCTAssertNotNil(response, "response should not be nil")
  355. XCTAssertNotNil(data, "data should not be nil")
  356. XCTAssertNil(error, "error should be nil")
  357. XCTAssertEqual(response?.url?.urlString ?? "", redirectURLString, "response URL should match the redirect URL")
  358. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  359. }
  360. func testThatRedirectedRequestContainsAllHeadersFromOriginalRequest() {
  361. // Given
  362. let redirectURLString = "https://httpbin.org/get"
  363. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  364. let headers = [
  365. "Authorization": "1234",
  366. "Custom-Header": "foobar",
  367. ]
  368. // NOTE: It appears that most headers are maintained during a redirect with the exception of the `Authorization`
  369. // header. It appears that Apple's strips the `Authorization` header from the redirected URL request. If you
  370. // need to maintain the `Authorization` header, you need to manually append it to the redirected request.
  371. manager.delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
  372. var redirectedRequest = request
  373. if
  374. let originalRequest = task.originalRequest,
  375. let headers = originalRequest.allHTTPHeaderFields,
  376. let authorizationHeaderValue = headers["Authorization"]
  377. {
  378. var mutableRequest = request
  379. mutableRequest.setValue(authorizationHeaderValue, forHTTPHeaderField: "Authorization")
  380. redirectedRequest = mutableRequest
  381. }
  382. return redirectedRequest
  383. }
  384. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  385. var response: Response<Any>?
  386. // When
  387. manager.request(urlString, withMethod: .get, headers: headers)
  388. .responseJSON { closureResponse in
  389. response = closureResponse
  390. expectation.fulfill()
  391. }
  392. waitForExpectations(timeout: timeout, handler: nil)
  393. // Then
  394. XCTAssertNotNil(response?.request, "request should not be nil")
  395. XCTAssertNotNil(response?.response, "response should not be nil")
  396. XCTAssertNotNil(response?.data, "data should not be nil")
  397. XCTAssertTrue(response?.result.isSuccess ?? false, "response result should be a success")
  398. if let json = response?.result.value as? [String: Any], let headers = json["headers"] as? [String: String] {
  399. XCTAssertEqual(headers["Custom-Header"], "foobar", "Custom-Header should be equal to foobar")
  400. XCTAssertEqual(headers["Authorization"], "1234", "Authorization header should be equal to 1234")
  401. }
  402. }
  403. // MARK: - Tests - Data Task Responses
  404. func testThatDataTaskDidReceiveResponseClosureIsCalledWhenSet() {
  405. // Given
  406. let expectation = self.expectation(description: "Override closure should be called")
  407. var overrideClosureCalled = false
  408. var response: HTTPURLResponse?
  409. manager.delegate.dataTaskDidReceiveResponse = { session, task, response in
  410. overrideClosureCalled = true
  411. return .allow
  412. }
  413. // When
  414. manager.request("https://httpbin.org/get", withMethod: .get).responseJSON { closureResponse in
  415. response = closureResponse.response
  416. expectation.fulfill()
  417. }
  418. waitForExpectations(timeout: timeout, handler: nil)
  419. // Then
  420. XCTAssertTrue(overrideClosureCalled)
  421. XCTAssertEqual(response?.statusCode, 200)
  422. }
  423. func testThatDataTaskDidReceiveResponseWithCompletionClosureIsCalledWhenSet() {
  424. // Given
  425. let expectation = self.expectation(description: "Override closure should be called")
  426. var overrideClosureCalled = false
  427. var response: HTTPURLResponse?
  428. manager.delegate.dataTaskDidReceiveResponseWithCompletion = { session, task, response, completion in
  429. overrideClosureCalled = true
  430. completion(.allow)
  431. }
  432. // When
  433. manager.request("https://httpbin.org/get", withMethod: .get).responseJSON { closureResponse in
  434. response = closureResponse.response
  435. expectation.fulfill()
  436. }
  437. waitForExpectations(timeout: timeout, handler: nil)
  438. // Then
  439. XCTAssertTrue(overrideClosureCalled)
  440. XCTAssertEqual(response?.statusCode, 200)
  441. }
  442. }