SessionDelegateTests.swift 21 KB

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