SessionDelegateTests.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  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. if #available(iOS 9.0, *) {
  55. // Given
  56. let expectation = self.expectation(description: "Override closure should be called")
  57. var overrideClosureCalled = false
  58. var response: HTTPURLResponse?
  59. manager.delegate.sessionDidReceiveChallenge = { session, challenge in
  60. overrideClosureCalled = true
  61. return (.performDefaultHandling, nil)
  62. }
  63. // When
  64. manager.request("https://httpbin.org/get").responseJSON { closureResponse in
  65. response = closureResponse.response
  66. expectation.fulfill()
  67. }
  68. waitForExpectations(timeout: timeout, handler: nil)
  69. // Then
  70. XCTAssertTrue(overrideClosureCalled)
  71. XCTAssertEqual(response?.statusCode, 200)
  72. } else {
  73. // This test MUST be disabled on iOS 8.x because `respondsToSelector` is not being called for the
  74. // `URLSession:didReceiveChallenge:completionHandler:` selector when more than one test here is run
  75. // at a time. Whether we flush the URL session of wipe all the shared credentials, the behavior is
  76. // still the same. Until we find a better solution, we'll need to disable this test on iOS 8.x.
  77. }
  78. }
  79. func testThatSessionDidReceiveChallengeWithCompletionClosureIsCalledWhenSet() {
  80. if #available(iOS 9.0, *) {
  81. // Given
  82. let expectation = self.expectation(description: "Override closure should be called")
  83. var overrideClosureCalled = false
  84. var response: HTTPURLResponse?
  85. manager.delegate.sessionDidReceiveChallengeWithCompletion = { session, challenge, completion in
  86. overrideClosureCalled = true
  87. completion(.performDefaultHandling, nil)
  88. }
  89. // When
  90. manager.request("https://httpbin.org/get").responseJSON { closureResponse in
  91. response = closureResponse.response
  92. expectation.fulfill()
  93. }
  94. waitForExpectations(timeout: timeout, handler: nil)
  95. // Then
  96. XCTAssertTrue(overrideClosureCalled)
  97. XCTAssertEqual(response?.statusCode, 200)
  98. } else {
  99. // This test MUST be disabled on iOS 8.x because `respondsToSelector` is not being called for the
  100. // `URLSession:didReceiveChallenge:completionHandler:` selector when more than one test here is run
  101. // at a time. Whether we flush the URL session of wipe all the shared credentials, the behavior is
  102. // still the same. Until we find a better solution, we'll need to disable this test on iOS 8.x.
  103. }
  104. }
  105. // MARK: - Tests - Redirects
  106. func testThatRequestWillPerformHTTPRedirectionByDefault() {
  107. // Given
  108. let redirectURLString = "https://www.apple.com/"
  109. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  110. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  111. var response: DefaultDataResponse?
  112. // When
  113. manager.request(urlString)
  114. .response { resp in
  115. response = resp
  116. expectation.fulfill()
  117. }
  118. waitForExpectations(timeout: timeout, handler: nil)
  119. // Then
  120. XCTAssertNotNil(response?.request)
  121. XCTAssertNotNil(response?.response)
  122. XCTAssertNotNil(response?.data)
  123. XCTAssertNil(response?.error)
  124. XCTAssertEqual(response?.response?.url?.absoluteString, redirectURLString)
  125. XCTAssertEqual(response?.response?.statusCode, 200)
  126. }
  127. func testThatRequestWillPerformRedirectionMultipleTimesByDefault() {
  128. // Given
  129. let redirectURLString = "https://httpbin.org/get"
  130. let urlString = "https://httpbin.org/redirect/5"
  131. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  132. var response: DefaultDataResponse?
  133. // When
  134. manager.request(urlString)
  135. .response { resp in
  136. response = resp
  137. expectation.fulfill()
  138. }
  139. waitForExpectations(timeout: timeout, handler: nil)
  140. // Then
  141. XCTAssertNotNil(response?.request)
  142. XCTAssertNotNil(response?.response)
  143. XCTAssertNotNil(response?.data)
  144. XCTAssertNil(response?.error)
  145. XCTAssertEqual(response?.response?.url?.absoluteString, redirectURLString)
  146. XCTAssertEqual(response?.response?.statusCode, 200)
  147. }
  148. func testThatTaskOverrideClosureCanPerformHTTPRedirection() {
  149. // Given
  150. let redirectURLString = "https://www.apple.com/"
  151. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  152. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  153. let callbackExpectation = self.expectation(description: "Redirect callback should be made")
  154. let delegate: SessionDelegate = manager.delegate
  155. delegate.taskWillPerformHTTPRedirection = { _, _, _, request in
  156. callbackExpectation.fulfill()
  157. return request
  158. }
  159. var response: DefaultDataResponse?
  160. // When
  161. manager.request(urlString)
  162. .response { resp in
  163. response = resp
  164. expectation.fulfill()
  165. }
  166. waitForExpectations(timeout: timeout, handler: nil)
  167. // Then
  168. XCTAssertNotNil(response?.request)
  169. XCTAssertNotNil(response?.response)
  170. XCTAssertNotNil(response?.data)
  171. XCTAssertNil(response?.error)
  172. XCTAssertEqual(response?.response?.url?.absoluteString, redirectURLString)
  173. XCTAssertEqual(response?.response?.statusCode, 200)
  174. }
  175. func testThatTaskOverrideClosureWithCompletionCanPerformHTTPRedirection() {
  176. // Given
  177. let redirectURLString = "https://www.apple.com/"
  178. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  179. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  180. let callbackExpectation = self.expectation(description: "Redirect callback should be made")
  181. let delegate: SessionDelegate = manager.delegate
  182. delegate.taskWillPerformHTTPRedirectionWithCompletion = { _, _, _, request, completion in
  183. completion(request)
  184. callbackExpectation.fulfill()
  185. }
  186. var response: DefaultDataResponse?
  187. // When
  188. manager.request(urlString)
  189. .response { resp in
  190. response = resp
  191. expectation.fulfill()
  192. }
  193. waitForExpectations(timeout: timeout, handler: nil)
  194. // Then
  195. XCTAssertNotNil(response?.request)
  196. XCTAssertNotNil(response?.response)
  197. XCTAssertNotNil(response?.data)
  198. XCTAssertNil(response?.error)
  199. XCTAssertEqual(response?.response?.url?.absoluteString, redirectURLString)
  200. XCTAssertEqual(response?.response?.statusCode, 200)
  201. }
  202. func testThatTaskOverrideClosureCanCancelHTTPRedirection() {
  203. // Given
  204. let redirectURLString = "https://www.apple.com"
  205. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  206. let expectation = self.expectation(description: "Request should not redirect to \(redirectURLString)")
  207. let callbackExpectation = self.expectation(description: "Redirect callback should be made")
  208. let delegate: SessionDelegate = manager.delegate
  209. delegate.taskWillPerformHTTPRedirectionWithCompletion = { _, _, _, _, completion in
  210. callbackExpectation.fulfill()
  211. completion(nil)
  212. }
  213. var response: DefaultDataResponse?
  214. // When
  215. manager.request(urlString)
  216. .response { resp in
  217. response = resp
  218. expectation.fulfill()
  219. }
  220. waitForExpectations(timeout: timeout, handler: nil)
  221. // Then
  222. XCTAssertNotNil(response?.request)
  223. XCTAssertNotNil(response?.response)
  224. XCTAssertNotNil(response?.data)
  225. XCTAssertNil(response?.error)
  226. XCTAssertEqual(response?.response?.url?.absoluteString, urlString)
  227. XCTAssertEqual(response?.response?.statusCode, 302)
  228. }
  229. func testThatTaskOverrideClosureWithCompletionCanCancelHTTPRedirection() {
  230. // Given
  231. let redirectURLString = "https://www.apple.com"
  232. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  233. let expectation = self.expectation(description: "Request should not redirect to \(redirectURLString)")
  234. let callbackExpectation = self.expectation(description: "Redirect callback should be made")
  235. let delegate: SessionDelegate = manager.delegate
  236. delegate.taskWillPerformHTTPRedirection = { _, _, _, _ in
  237. callbackExpectation.fulfill()
  238. return nil
  239. }
  240. var response: DefaultDataResponse?
  241. // When
  242. manager.request(urlString)
  243. .response { resp in
  244. response = resp
  245. expectation.fulfill()
  246. }
  247. waitForExpectations(timeout: timeout, handler: nil)
  248. // Then
  249. XCTAssertNotNil(response?.request)
  250. XCTAssertNotNil(response?.response)
  251. XCTAssertNotNil(response?.data)
  252. XCTAssertNil(response?.error)
  253. XCTAssertEqual(response?.response?.url?.absoluteString, urlString)
  254. XCTAssertEqual(response?.response?.statusCode, 302)
  255. }
  256. func testThatTaskOverrideClosureIsCalledMultipleTimesForMultipleHTTPRedirects() {
  257. // Given
  258. let redirectCount = 5
  259. let redirectURLString = "https://httpbin.org/get"
  260. let urlString = "https://httpbin.org/redirect/\(redirectCount)"
  261. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  262. let delegate: SessionDelegate = manager.delegate
  263. var redirectExpectations = [XCTestExpectation]()
  264. for index in 0..<redirectCount {
  265. redirectExpectations.insert(self.expectation(description: "Redirect #\(index) callback was received"), at: 0)
  266. }
  267. delegate.taskWillPerformHTTPRedirection = { _, _, _, request in
  268. if let redirectExpectation = redirectExpectations.popLast() {
  269. redirectExpectation.fulfill()
  270. } else {
  271. XCTFail("Too many redirect callbacks were received")
  272. }
  273. return request
  274. }
  275. var response: DefaultDataResponse?
  276. // When
  277. manager.request(urlString)
  278. .response { resp in
  279. response = resp
  280. expectation.fulfill()
  281. }
  282. waitForExpectations(timeout: timeout, handler: nil)
  283. // Then
  284. XCTAssertNotNil(response?.request)
  285. XCTAssertNotNil(response?.response)
  286. XCTAssertNotNil(response?.data)
  287. XCTAssertNil(response?.error)
  288. XCTAssertEqual(response?.response?.url?.absoluteString, redirectURLString)
  289. XCTAssertEqual(response?.response?.statusCode, 200)
  290. }
  291. func testThatTaskOverrideClosureWithCompletionIsCalledMultipleTimesForMultipleHTTPRedirects() {
  292. // Given
  293. let redirectCount = 5
  294. let redirectURLString = "https://httpbin.org/get"
  295. let urlString = "https://httpbin.org/redirect/\(redirectCount)"
  296. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  297. let delegate: SessionDelegate = manager.delegate
  298. var redirectExpectations = [XCTestExpectation]()
  299. for index in 0..<redirectCount {
  300. redirectExpectations.insert(self.expectation(description: "Redirect #\(index) callback was received"), at: 0)
  301. }
  302. delegate.taskWillPerformHTTPRedirectionWithCompletion = { _, _, _, request, completion in
  303. if let redirectExpectation = redirectExpectations.popLast() {
  304. redirectExpectation.fulfill()
  305. } else {
  306. XCTFail("Too many redirect callbacks were received")
  307. }
  308. completion(request)
  309. }
  310. var response: DefaultDataResponse?
  311. // When
  312. manager.request(urlString)
  313. .response { resp in
  314. response = resp
  315. expectation.fulfill()
  316. }
  317. waitForExpectations(timeout: timeout, handler: nil)
  318. // Then
  319. XCTAssertNotNil(response?.request)
  320. XCTAssertNotNil(response?.response)
  321. XCTAssertNotNil(response?.data)
  322. XCTAssertNil(response?.error)
  323. XCTAssertEqual(response?.response?.url?.absoluteString, redirectURLString)
  324. XCTAssertEqual(response?.response?.statusCode, 200)
  325. }
  326. func testThatRedirectedRequestContainsAllHeadersFromOriginalRequest() {
  327. // Given
  328. let redirectURLString = "https://httpbin.org/get"
  329. let urlString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  330. let headers = [
  331. "Authorization": "1234",
  332. "Custom-Header": "foobar",
  333. ]
  334. // NOTE: It appears that most headers are maintained during a redirect with the exception of the `Authorization`
  335. // header. It appears that Apple's strips the `Authorization` header from the redirected URL request. If you
  336. // need to maintain the `Authorization` header, you need to manually append it to the redirected request.
  337. manager.delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
  338. var redirectedRequest = request
  339. if
  340. let originalRequest = task.originalRequest,
  341. let headers = originalRequest.allHTTPHeaderFields,
  342. let authorizationHeaderValue = headers["Authorization"]
  343. {
  344. var mutableRequest = request
  345. mutableRequest.setValue(authorizationHeaderValue, forHTTPHeaderField: "Authorization")
  346. redirectedRequest = mutableRequest
  347. }
  348. return redirectedRequest
  349. }
  350. let expectation = self.expectation(description: "Request should redirect to \(redirectURLString)")
  351. var response: DataResponse<Any>?
  352. // When
  353. manager.request(urlString, headers: headers)
  354. .responseJSON { closureResponse in
  355. response = closureResponse
  356. expectation.fulfill()
  357. }
  358. waitForExpectations(timeout: timeout, handler: nil)
  359. // Then
  360. XCTAssertNotNil(response?.request)
  361. XCTAssertNotNil(response?.response)
  362. XCTAssertNotNil(response?.data)
  363. XCTAssertEqual(response?.result.isSuccess, true)
  364. if let json = response?.result.value as? [String: Any], let headers = json["headers"] as? [String: String] {
  365. XCTAssertEqual(headers["Authorization"], "1234")
  366. XCTAssertEqual(headers["Custom-Header"], "foobar")
  367. }
  368. }
  369. // MARK: - Tests - Data Task Responses
  370. func testThatDataTaskDidReceiveResponseClosureIsCalledWhenSet() {
  371. // Given
  372. let expectation = self.expectation(description: "Override closure should be called")
  373. var overrideClosureCalled = false
  374. var response: HTTPURLResponse?
  375. manager.delegate.dataTaskDidReceiveResponse = { session, task, response in
  376. overrideClosureCalled = true
  377. return .allow
  378. }
  379. // When
  380. manager.request("https://httpbin.org/get").responseJSON { closureResponse in
  381. response = closureResponse.response
  382. expectation.fulfill()
  383. }
  384. waitForExpectations(timeout: timeout, handler: nil)
  385. // Then
  386. XCTAssertTrue(overrideClosureCalled)
  387. XCTAssertEqual(response?.statusCode, 200)
  388. }
  389. func testThatDataTaskDidReceiveResponseWithCompletionClosureIsCalledWhenSet() {
  390. // Given
  391. let expectation = self.expectation(description: "Override closure should be called")
  392. var overrideClosureCalled = false
  393. var response: HTTPURLResponse?
  394. manager.delegate.dataTaskDidReceiveResponseWithCompletion = { session, task, response, completion in
  395. overrideClosureCalled = true
  396. completion(.allow)
  397. }
  398. // When
  399. manager.request("https://httpbin.org/get").responseJSON { closureResponse in
  400. response = closureResponse.response
  401. expectation.fulfill()
  402. }
  403. waitForExpectations(timeout: timeout, handler: nil)
  404. // Then
  405. XCTAssertTrue(overrideClosureCalled)
  406. XCTAssertEqual(response?.statusCode, 200)
  407. }
  408. }