SessionDelegateTests.swift 18 KB

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