SessionDelegateTests.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  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: Alamofire.Manager!
  29. // MARK: - Setup and Teardown
  30. override func setUp() {
  31. super.setUp()
  32. manager = Alamofire.Manager(configuration: NSURLSessionConfiguration.ephemeralSessionConfiguration())
  33. }
  34. // MARK: - Tests - Session Invalidation
  35. func testThatSessionDidBecomeInvalidWithErrorClosureIsCalledWhenSet() {
  36. // Given
  37. let expectation = expectationWithDescription("Override closure should be called")
  38. var overrideClosureCalled = false
  39. var invalidationError: NSError?
  40. manager.delegate.sessionDidBecomeInvalidWithError = { _, error in
  41. overrideClosureCalled = true
  42. invalidationError = error
  43. expectation.fulfill()
  44. }
  45. // When
  46. manager.session.invalidateAndCancel()
  47. waitForExpectationsWithTimeout(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 = expectationWithDescription("Override closure should be called")
  57. var overrideClosureCalled = false
  58. var response: NSHTTPURLResponse?
  59. manager.delegate.sessionDidReceiveChallenge = { session, challenge in
  60. overrideClosureCalled = true
  61. return (.PerformDefaultHandling, nil)
  62. }
  63. // When
  64. manager.request(.GET, "https://httpbin.org/get").responseJSON { closureResponse in
  65. response = closureResponse.response
  66. expectation.fulfill()
  67. }
  68. waitForExpectationsWithTimeout(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 = expectationWithDescription("Override closure should be called")
  83. var overrideClosureCalled = false
  84. var response: NSHTTPURLResponse?
  85. manager.delegate.sessionDidReceiveChallengeWithCompletion = { session, challenge, completion in
  86. overrideClosureCalled = true
  87. completion(.PerformDefaultHandling, nil)
  88. }
  89. // When
  90. manager.request(.GET, "https://httpbin.org/get").responseJSON { closureResponse in
  91. response = closureResponse.response
  92. expectation.fulfill()
  93. }
  94. waitForExpectationsWithTimeout(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 = expectationWithDescription("Request should redirect to \(redirectURLString)")
  111. var request: NSURLRequest?
  112. var response: NSHTTPURLResponse?
  113. var data: NSData?
  114. var error: NSError?
  115. // When
  116. manager.request(.GET, URLString)
  117. .response { responseRequest, responseResponse, responseData, responseError in
  118. request = responseRequest
  119. response = responseResponse
  120. data = responseData
  121. error = responseError
  122. expectation.fulfill()
  123. }
  124. waitForExpectationsWithTimeout(timeout, handler: nil)
  125. // Then
  126. XCTAssertNotNil(request, "request should not be nil")
  127. XCTAssertNotNil(response, "response should not be nil")
  128. XCTAssertNotNil(data, "data should not be nil")
  129. XCTAssertNil(error, "error should be nil")
  130. XCTAssertEqual(response?.URL?.URLString ?? "", redirectURLString, "response URL should match the redirect URL")
  131. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  132. }
  133. func testThatRequestWillPerformRedirectionMultipleTimesByDefault() {
  134. // Given
  135. let redirectURLString = "https://httpbin.org/get"
  136. let URLString = "https://httpbin.org/redirect/5"
  137. let expectation = expectationWithDescription("Request should redirect to \(redirectURLString)")
  138. var request: NSURLRequest?
  139. var response: NSHTTPURLResponse?
  140. var data: NSData?
  141. var error: NSError?
  142. // When
  143. manager.request(.GET, URLString)
  144. .response { responseRequest, responseResponse, responseData, responseError in
  145. request = responseRequest
  146. response = responseResponse
  147. data = responseData
  148. error = responseError
  149. expectation.fulfill()
  150. }
  151. waitForExpectationsWithTimeout(timeout, handler: nil)
  152. // Then
  153. XCTAssertNotNil(request, "request should not be nil")
  154. XCTAssertNotNil(response, "response should not be nil")
  155. XCTAssertNotNil(data, "data should not be nil")
  156. XCTAssertNil(error, "error should be nil")
  157. XCTAssertEqual(response?.URL?.URLString ?? "", redirectURLString, "response URL should match the redirect URL")
  158. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  159. }
  160. func testThatTaskOverrideClosureCanPerformHTTPRedirection() {
  161. // Given
  162. let redirectURLString = "https://www.apple.com"
  163. let URLString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  164. let expectation = expectationWithDescription("Request should redirect to \(redirectURLString)")
  165. let callbackExpectation = expectationWithDescription("Redirect callback should be made")
  166. let delegate: Alamofire.Manager.SessionDelegate = manager.delegate
  167. delegate.taskWillPerformHTTPRedirection = { _, _, _, request in
  168. callbackExpectation.fulfill()
  169. return request
  170. }
  171. var request: NSURLRequest?
  172. var response: NSHTTPURLResponse?
  173. var data: NSData?
  174. var error: NSError?
  175. // When
  176. manager.request(.GET, URLString)
  177. .response { responseRequest, responseResponse, responseData, responseError in
  178. request = responseRequest
  179. response = responseResponse
  180. data = responseData
  181. error = responseError
  182. expectation.fulfill()
  183. }
  184. waitForExpectationsWithTimeout(timeout, handler: nil)
  185. // Then
  186. XCTAssertNotNil(request, "request should not be nil")
  187. XCTAssertNotNil(response, "response should not be nil")
  188. XCTAssertNotNil(data, "data should not be nil")
  189. XCTAssertNil(error, "error should be nil")
  190. XCTAssertEqual(response?.URL?.URLString ?? "", redirectURLString, "response URL should match the redirect URL")
  191. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  192. }
  193. func testThatTaskOverrideClosureWithCompletionCanPerformHTTPRedirection() {
  194. // Given
  195. let redirectURLString = "https://www.apple.com"
  196. let URLString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  197. let expectation = expectationWithDescription("Request should redirect to \(redirectURLString)")
  198. let callbackExpectation = expectationWithDescription("Redirect callback should be made")
  199. let delegate: Alamofire.Manager.SessionDelegate = manager.delegate
  200. delegate.taskWillPerformHTTPRedirectionWithCompletion = { _, _, _, request, completion in
  201. completion(request)
  202. callbackExpectation.fulfill()
  203. }
  204. var request: NSURLRequest?
  205. var response: NSHTTPURLResponse?
  206. var data: NSData?
  207. var error: NSError?
  208. // When
  209. manager.request(.GET, URLString)
  210. .response { responseRequest, responseResponse, responseData, responseError in
  211. request = responseRequest
  212. response = responseResponse
  213. data = responseData
  214. error = responseError
  215. expectation.fulfill()
  216. }
  217. waitForExpectationsWithTimeout(timeout, handler: nil)
  218. // Then
  219. XCTAssertNotNil(request, "request should not be nil")
  220. XCTAssertNotNil(response, "response should not be nil")
  221. XCTAssertNotNil(data, "data should not be nil")
  222. XCTAssertNil(error, "error should be nil")
  223. XCTAssertEqual(response?.URL?.URLString ?? "", redirectURLString, "response URL should match the redirect URL")
  224. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  225. }
  226. func testThatTaskOverrideClosureCanCancelHTTPRedirection() {
  227. // Given
  228. let redirectURLString = "https://www.apple.com"
  229. let URLString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  230. let expectation = expectationWithDescription("Request should not redirect to \(redirectURLString)")
  231. let callbackExpectation = expectationWithDescription("Redirect callback should be made")
  232. let delegate: Alamofire.Manager.SessionDelegate = manager.delegate
  233. delegate.taskWillPerformHTTPRedirectionWithCompletion = { _, _, _, _, completion in
  234. callbackExpectation.fulfill()
  235. completion(nil)
  236. }
  237. var request: NSURLRequest?
  238. var response: NSHTTPURLResponse?
  239. var data: NSData?
  240. var error: NSError?
  241. // When
  242. manager.request(.GET, URLString)
  243. .response { responseRequest, responseResponse, responseData, responseError in
  244. request = responseRequest
  245. response = responseResponse
  246. data = responseData
  247. error = responseError
  248. expectation.fulfill()
  249. }
  250. waitForExpectationsWithTimeout(timeout, handler: nil)
  251. // Then
  252. XCTAssertNotNil(request, "request should not be nil")
  253. XCTAssertNotNil(response, "response should not be nil")
  254. XCTAssertNotNil(data, "data should not be nil")
  255. XCTAssertNil(error, "error should be nil")
  256. XCTAssertEqual(response?.URL?.URLString ?? "", URLString, "response URL should match the origin URL")
  257. XCTAssertEqual(response?.statusCode ?? -1, 302, "response should have a 302 status code")
  258. }
  259. func testThatTaskOverrideClosureWithCompletionCanCancelHTTPRedirection() {
  260. // Given
  261. let redirectURLString = "https://www.apple.com"
  262. let URLString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  263. let expectation = expectationWithDescription("Request should not redirect to \(redirectURLString)")
  264. let callbackExpectation = expectationWithDescription("Redirect callback should be made")
  265. let delegate: Alamofire.Manager.SessionDelegate = manager.delegate
  266. delegate.taskWillPerformHTTPRedirection = { _, _, _, _ in
  267. callbackExpectation.fulfill()
  268. return nil
  269. }
  270. var request: NSURLRequest?
  271. var response: NSHTTPURLResponse?
  272. var data: NSData?
  273. var error: NSError?
  274. // When
  275. manager.request(.GET, URLString)
  276. .response { responseRequest, responseResponse, responseData, responseError in
  277. request = responseRequest
  278. response = responseResponse
  279. data = responseData
  280. error = responseError
  281. expectation.fulfill()
  282. }
  283. waitForExpectationsWithTimeout(timeout, handler: nil)
  284. // Then
  285. XCTAssertNotNil(request, "request should not be nil")
  286. XCTAssertNotNil(response, "response should not be nil")
  287. XCTAssertNotNil(data, "data should not be nil")
  288. XCTAssertNil(error, "error should be nil")
  289. XCTAssertEqual(response?.URL?.URLString ?? "", URLString, "response URL should match the origin URL")
  290. XCTAssertEqual(response?.statusCode ?? -1, 302, "response should have a 302 status code")
  291. }
  292. func testThatTaskOverrideClosureIsCalledMultipleTimesForMultipleHTTPRedirects() {
  293. // Given
  294. let redirectCount = 5
  295. let redirectURLString = "https://httpbin.org/get"
  296. let URLString = "https://httpbin.org/redirect/\(redirectCount)"
  297. let expectation = expectationWithDescription("Request should redirect to \(redirectURLString)")
  298. let delegate: Alamofire.Manager.SessionDelegate = manager.delegate
  299. var redirectExpectations = [XCTestExpectation]()
  300. for index in 0..<redirectCount {
  301. redirectExpectations.insert(expectationWithDescription("Redirect #\(index) callback was received"), atIndex: 0)
  302. }
  303. delegate.taskWillPerformHTTPRedirection = { _, _, _, request in
  304. if let redirectExpectation = redirectExpectations.popLast() {
  305. redirectExpectation.fulfill()
  306. } else {
  307. XCTFail("Too many redirect callbacks were received")
  308. }
  309. return request
  310. }
  311. var request: NSURLRequest?
  312. var response: NSHTTPURLResponse?
  313. var data: NSData?
  314. var error: NSError?
  315. // When
  316. manager.request(.GET, URLString)
  317. .response { responseRequest, responseResponse, responseData, responseError in
  318. request = responseRequest
  319. response = responseResponse
  320. data = responseData
  321. error = responseError
  322. expectation.fulfill()
  323. }
  324. waitForExpectationsWithTimeout(timeout, handler: nil)
  325. // Then
  326. XCTAssertNotNil(request, "request should not be nil")
  327. XCTAssertNotNil(response, "response should not be nil")
  328. XCTAssertNotNil(data, "data should not be nil")
  329. XCTAssertNil(error, "error should be nil")
  330. XCTAssertEqual(response?.URL?.URLString ?? "", redirectURLString, "response URL should match the redirect URL")
  331. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  332. }
  333. func testThatTaskOverrideClosureWithCompletionIsCalledMultipleTimesForMultipleHTTPRedirects() {
  334. // Given
  335. let redirectCount = 5
  336. let redirectURLString = "https://httpbin.org/get"
  337. let URLString = "https://httpbin.org/redirect/\(redirectCount)"
  338. let expectation = expectationWithDescription("Request should redirect to \(redirectURLString)")
  339. let delegate: Alamofire.Manager.SessionDelegate = manager.delegate
  340. var redirectExpectations = [XCTestExpectation]()
  341. for index in 0..<redirectCount {
  342. redirectExpectations.insert(expectationWithDescription("Redirect #\(index) callback was received"), atIndex: 0)
  343. }
  344. delegate.taskWillPerformHTTPRedirectionWithCompletion = { _, _, _, request, completion in
  345. if let redirectExpectation = redirectExpectations.popLast() {
  346. redirectExpectation.fulfill()
  347. } else {
  348. XCTFail("Too many redirect callbacks were received")
  349. }
  350. completion(request)
  351. }
  352. var request: NSURLRequest?
  353. var response: NSHTTPURLResponse?
  354. var data: NSData?
  355. var error: NSError?
  356. // When
  357. manager.request(.GET, URLString)
  358. .response { responseRequest, responseResponse, responseData, responseError in
  359. request = responseRequest
  360. response = responseResponse
  361. data = responseData
  362. error = responseError
  363. expectation.fulfill()
  364. }
  365. waitForExpectationsWithTimeout(timeout, handler: nil)
  366. // Then
  367. XCTAssertNotNil(request, "request should not be nil")
  368. XCTAssertNotNil(response, "response should not be nil")
  369. XCTAssertNotNil(data, "data should not be nil")
  370. XCTAssertNil(error, "error should be nil")
  371. XCTAssertEqual(response?.URL?.URLString ?? "", redirectURLString, "response URL should match the redirect URL")
  372. XCTAssertEqual(response?.statusCode ?? -1, 200, "response should have a 200 status code")
  373. }
  374. func testThatRedirectedRequestContainsAllHeadersFromOriginalRequest() {
  375. // Given
  376. let redirectURLString = "https://httpbin.org/get"
  377. let URLString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
  378. let headers = [
  379. "Authorization": "1234",
  380. "Custom-Header": "foobar",
  381. ]
  382. // NOTE: It appears that most headers are maintained during a redirect with the exception of the `Authorization`
  383. // header. It appears that Apple's strips the `Authorization` header from the redirected URL request. If you
  384. // need to maintain the `Authorization` header, you need to manually append it to the redirected request.
  385. manager.delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
  386. var redirectedRequest = request
  387. if let
  388. originalRequest = task.originalRequest,
  389. headers = originalRequest.allHTTPHeaderFields,
  390. authorizationHeaderValue = headers["Authorization"]
  391. {
  392. let mutableRequest = request.mutableCopy() as! NSMutableURLRequest
  393. mutableRequest.setValue(authorizationHeaderValue, forHTTPHeaderField: "Authorization")
  394. redirectedRequest = mutableRequest
  395. }
  396. return redirectedRequest
  397. }
  398. let expectation = expectationWithDescription("Request should redirect to \(redirectURLString)")
  399. var response: Response<AnyObject, NSError>?
  400. // When
  401. manager.request(.GET, URLString, headers: headers)
  402. .responseJSON { closureResponse in
  403. response = closureResponse
  404. expectation.fulfill()
  405. }
  406. waitForExpectationsWithTimeout(timeout, handler: nil)
  407. // Then
  408. XCTAssertNotNil(response?.request, "request should not be nil")
  409. XCTAssertNotNil(response?.response, "response should not be nil")
  410. XCTAssertNotNil(response?.data, "data should not be nil")
  411. XCTAssertTrue(response?.result.isSuccess ?? false, "response result should be a success")
  412. if let
  413. JSON = response?.result.value as? [String: AnyObject],
  414. headers = JSON["headers"] as? [String: String]
  415. {
  416. XCTAssertEqual(headers["Custom-Header"], "foobar", "Custom-Header should be equal to foobar")
  417. XCTAssertEqual(headers["Authorization"], "1234", "Authorization header should be equal to 1234")
  418. }
  419. }
  420. // MARK: - Tests - Data Task Responses
  421. func testThatDataTaskDidReceiveResponseClosureIsCalledWhenSet() {
  422. // Given
  423. let expectation = expectationWithDescription("Override closure should be called")
  424. var overrideClosureCalled = false
  425. var response: NSHTTPURLResponse?
  426. manager.delegate.dataTaskDidReceiveResponse = { session, task, response in
  427. overrideClosureCalled = true
  428. return .Allow
  429. }
  430. // When
  431. manager.request(.GET, "https://httpbin.org/get").responseJSON { closureResponse in
  432. response = closureResponse.response
  433. expectation.fulfill()
  434. }
  435. waitForExpectationsWithTimeout(timeout, handler: nil)
  436. // Then
  437. XCTAssertTrue(overrideClosureCalled)
  438. XCTAssertEqual(response?.statusCode, 200)
  439. }
  440. func testThatDataTaskDidReceiveResponseWithCompletionClosureIsCalledWhenSet() {
  441. // Given
  442. let expectation = expectationWithDescription("Override closure should be called")
  443. var overrideClosureCalled = false
  444. var response: NSHTTPURLResponse?
  445. manager.delegate.dataTaskDidReceiveResponseWithCompletion = { session, task, response, completion in
  446. overrideClosureCalled = true
  447. completion(.Allow)
  448. }
  449. // When
  450. manager.request(.GET, "https://httpbin.org/get").responseJSON { closureResponse in
  451. response = closureResponse.response
  452. expectation.fulfill()
  453. }
  454. waitForExpectationsWithTimeout(timeout, handler: nil)
  455. // Then
  456. XCTAssertTrue(overrideClosureCalled)
  457. XCTAssertEqual(response?.statusCode, 200)
  458. }
  459. }