SessionDelegateTests.swift 22 KB

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