SessionTests.swift 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739
  1. //
  2. // SessionTests.swift
  3. //
  4. // Copyright (c) 2014-2018 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. final class SessionTestCase: BaseTestCase {
  28. // MARK: Helper Types
  29. private class HTTPMethodAdapter: RequestInterceptor {
  30. let method: HTTPMethod
  31. let throwsError: Bool
  32. var adaptedCount = 0
  33. init(method: HTTPMethod, throwsError: Bool = false) {
  34. self.method = method
  35. self.throwsError = throwsError
  36. }
  37. func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
  38. adaptedCount += 1
  39. let result: Result<URLRequest, Error> = Result {
  40. guard !throwsError else { throw AFError.invalidURL(url: "") }
  41. var urlRequest = urlRequest
  42. urlRequest.httpMethod = method.rawValue
  43. return urlRequest
  44. }
  45. completion(result)
  46. }
  47. }
  48. private class HeaderAdapter: RequestInterceptor {
  49. let headers: HTTPHeaders
  50. let throwsError: Bool
  51. var adaptedCount = 0
  52. init(headers: HTTPHeaders = ["field": "value"], throwsError: Bool = false) {
  53. self.headers = headers
  54. self.throwsError = throwsError
  55. }
  56. func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
  57. adaptedCount += 1
  58. let result: Result<URLRequest, Error> = Result {
  59. guard !throwsError else { throw AFError.invalidURL(url: "") }
  60. var urlRequest = urlRequest
  61. var finalHeaders = urlRequest.headers
  62. headers.forEach { finalHeaders.add($0) }
  63. urlRequest.headers = finalHeaders
  64. return urlRequest
  65. }
  66. completion(result)
  67. }
  68. }
  69. private class RequestHandler: RequestInterceptor {
  70. var adaptCalledCount = 0
  71. var adaptedCount = 0
  72. var retryCount = 0
  73. var retryCalledCount = 0
  74. var retryErrors: [Error] = []
  75. var shouldApplyAuthorizationHeader = false
  76. var throwsErrorOnFirstAdapt = false
  77. var throwsErrorOnSecondAdapt = false
  78. var throwsErrorOnRetry = false
  79. var shouldRetry = true
  80. var retryDelay: TimeInterval?
  81. func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
  82. adaptCalledCount += 1
  83. let result: Result<URLRequest, Error> = Result {
  84. if throwsErrorOnFirstAdapt {
  85. throwsErrorOnFirstAdapt = false
  86. throw AFError.invalidURL(url: "/adapt/error/1")
  87. }
  88. if throwsErrorOnSecondAdapt && adaptedCount == 1 {
  89. throwsErrorOnSecondAdapt = false
  90. throw AFError.invalidURL(url: "/adapt/error/2")
  91. }
  92. var urlRequest = urlRequest
  93. adaptedCount += 1
  94. if shouldApplyAuthorizationHeader && adaptedCount > 1 {
  95. urlRequest.headers.update(.authorization(username: "user", password: "password"))
  96. }
  97. return urlRequest
  98. }
  99. completion(result)
  100. }
  101. func retry(_ request: Request,
  102. for session: Session,
  103. dueTo error: Error,
  104. completion: @escaping (RetryResult) -> Void) {
  105. retryCalledCount += 1
  106. if throwsErrorOnRetry {
  107. let error = AFError.invalidURL(url: "/invalid/url/\(retryCalledCount)")
  108. completion(.doNotRetryWithError(error))
  109. return
  110. }
  111. guard shouldRetry else { completion(.doNotRetry); return }
  112. retryCount += 1
  113. retryErrors.append(error)
  114. if retryCount < 2 {
  115. if let retryDelay = retryDelay {
  116. completion(.retryWithDelay(retryDelay))
  117. } else {
  118. completion(.retry)
  119. }
  120. } else {
  121. completion(.doNotRetry)
  122. }
  123. }
  124. }
  125. private class UploadHandler: RequestInterceptor {
  126. var adaptCalledCount = 0
  127. var adaptedCount = 0
  128. var retryCalledCount = 0
  129. var retryCount = 0
  130. var retryErrors: [Error] = []
  131. func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
  132. adaptCalledCount += 1
  133. let result: Result<URLRequest, Error> = Result {
  134. adaptedCount += 1
  135. if adaptedCount == 1 { throw AFError.invalidURL(url: "") }
  136. return urlRequest
  137. }
  138. completion(result)
  139. }
  140. func retry(_ request: Request,
  141. for session: Session,
  142. dueTo error: Error,
  143. completion: @escaping (RetryResult) -> Void) {
  144. retryCalledCount += 1
  145. retryCount += 1
  146. retryErrors.append(error)
  147. completion(.retry)
  148. }
  149. }
  150. // MARK: Tests - Initialization
  151. func testInitializerWithDefaultArguments() {
  152. // Given, When
  153. let session = Session()
  154. // Then
  155. XCTAssertNotNil(session.session.delegate, "session delegate should not be nil")
  156. XCTAssertTrue(session.delegate === session.session.delegate, "manager delegate should equal session delegate")
  157. XCTAssertNil(session.serverTrustManager, "session server trust policy manager should be nil")
  158. }
  159. func testInitializerWithSpecifiedArguments() {
  160. // Given
  161. let configuration = URLSessionConfiguration.default
  162. let delegate = SessionDelegate()
  163. let serverTrustManager = ServerTrustManager(evaluators: [:])
  164. // When
  165. let session = Session(configuration: configuration,
  166. delegate: delegate,
  167. serverTrustManager: serverTrustManager)
  168. // Then
  169. XCTAssertNotNil(session.session.delegate, "session delegate should not be nil")
  170. XCTAssertTrue(session.delegate === session.session.delegate, "manager delegate should equal session delegate")
  171. XCTAssertNotNil(session.serverTrustManager, "session server trust policy manager should not be nil")
  172. }
  173. func testThatSessionInitializerSucceedsWithDefaultArguments() {
  174. // Given
  175. let delegate = SessionDelegate()
  176. let underlyingQueue = DispatchQueue(label: "underlyingQueue")
  177. let urlSession: URLSession = {
  178. let configuration = URLSessionConfiguration.default
  179. let queue = OperationQueue(underlyingQueue: underlyingQueue, name: "delegateQueue")
  180. return URLSession(configuration: configuration, delegate: delegate, delegateQueue: queue)
  181. }()
  182. // When
  183. let session = Session(session: urlSession, delegate: delegate, rootQueue: underlyingQueue)
  184. // Then
  185. XCTAssertTrue(session.delegate === session.session.delegate, "manager delegate should equal session delegate")
  186. XCTAssertNil(session.serverTrustManager, "session server trust policy manager should be nil")
  187. }
  188. func testThatSessionInitializerSucceedsWithSpecifiedArguments() {
  189. // Given
  190. let delegate = SessionDelegate()
  191. let underlyingQueue = DispatchQueue(label: "underlyingQueue")
  192. let urlSession: URLSession = {
  193. let configuration = URLSessionConfiguration.default
  194. let queue = OperationQueue(underlyingQueue: underlyingQueue, name: "delegateQueue")
  195. return URLSession(configuration: configuration, delegate: delegate, delegateQueue: queue)
  196. }()
  197. let serverTrustManager = ServerTrustManager(evaluators: [:])
  198. // When
  199. let session = Session(session: urlSession,
  200. delegate: delegate,
  201. rootQueue: underlyingQueue,
  202. serverTrustManager: serverTrustManager)
  203. // Then
  204. XCTAssertTrue(session.delegate === session.session.delegate, "manager delegate should equal session delegate")
  205. XCTAssertNotNil(session.serverTrustManager, "session server trust policy manager should not be nil")
  206. }
  207. // MARK: Tests - Default HTTP Headers
  208. func testDefaultUserAgentHeader() {
  209. // Given, When
  210. let userAgent = HTTPHeaders.default["User-Agent"]
  211. // Then
  212. let osNameVersion: String = {
  213. let version = ProcessInfo.processInfo.operatingSystemVersion
  214. let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
  215. let osName: String = {
  216. #if os(iOS)
  217. return "iOS"
  218. #elseif os(watchOS)
  219. return "watchOS"
  220. #elseif os(tvOS)
  221. return "tvOS"
  222. #elseif os(macOS)
  223. return "macOS"
  224. #elseif os(Linux)
  225. return "Linux"
  226. #else
  227. return "Unknown"
  228. #endif
  229. }()
  230. return "\(osName) \(versionString)"
  231. }()
  232. let alamofireVersion: String = {
  233. guard
  234. let afInfo = Bundle(for: Session.self).infoDictionary,
  235. let build = afInfo["CFBundleShortVersionString"]
  236. else { return "Unknown" }
  237. return "Alamofire/\(build)"
  238. }()
  239. XCTAssertTrue(userAgent?.contains(alamofireVersion) == true)
  240. XCTAssertTrue(userAgent?.contains(osNameVersion) == true)
  241. XCTAssertTrue(userAgent?.contains("Unknown/Unknown") == true)
  242. }
  243. // MARK: Tests - Supported Accept-Encodings
  244. func testDefaultAcceptEncodingSupportsAppropriateEncodingsOnAppropriateSystems() {
  245. // Given
  246. let brotliURL = URL(string: "https://httpbin.org/brotli")!
  247. let gzipURL = URL(string: "https://httpbin.org/gzip")!
  248. let deflateURL = URL(string: "https://httpbin.org/deflate")!
  249. let brotliExpectation = expectation(description: "brotli request should complete")
  250. let gzipExpectation = expectation(description: "gzip request should complete")
  251. let deflateExpectation = expectation(description: "deflate request should complete")
  252. var brotliResponse: DataResponse<Any, AFError>?
  253. var gzipResponse: DataResponse<Any, AFError>?
  254. var deflateResponse: DataResponse<Any, AFError>?
  255. // When
  256. AF.request(brotliURL).responseJSON { response in
  257. brotliResponse = response
  258. brotliExpectation.fulfill()
  259. }
  260. AF.request(gzipURL).responseJSON { response in
  261. gzipResponse = response
  262. gzipExpectation.fulfill()
  263. }
  264. AF.request(deflateURL).responseJSON { response in
  265. deflateResponse = response
  266. deflateExpectation.fulfill()
  267. }
  268. waitForExpectations(timeout: timeout, handler: nil)
  269. // Then
  270. if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) {
  271. XCTAssertTrue(brotliResponse?.result.isSuccess == true)
  272. } else {
  273. XCTAssertTrue(brotliResponse?.result.isFailure == true)
  274. }
  275. XCTAssertTrue(gzipResponse?.result.isSuccess == true)
  276. XCTAssertTrue(deflateResponse?.result.isSuccess == true)
  277. }
  278. // MARK: Tests - Start Requests Immediately
  279. func testSetStartRequestsImmediatelyToFalseAndResumeRequest() {
  280. // Given
  281. let session = Session(startRequestsImmediately: false)
  282. let url = URL(string: "https://httpbin.org/get")!
  283. let urlRequest = URLRequest(url: url)
  284. let expectation = self.expectation(description: "\(url)")
  285. var response: HTTPURLResponse?
  286. // When
  287. session.request(urlRequest)
  288. .response { resp in
  289. response = resp.response
  290. expectation.fulfill()
  291. }
  292. .resume()
  293. waitForExpectations(timeout: timeout, handler: nil)
  294. // Then
  295. XCTAssertNotNil(response, "response should not be nil")
  296. XCTAssertTrue(response?.statusCode == 200, "response status code should be 200")
  297. }
  298. func testSetStartRequestsImmediatelyToFalseAndCancelledCallsResponseHandlers() {
  299. // Given
  300. let session = Session(startRequestsImmediately: false)
  301. let url = URL(string: "https://httpbin.org/get")!
  302. let urlRequest = URLRequest(url: url)
  303. let expectation = self.expectation(description: "\(url)")
  304. var response: DataResponse<Data?, AFError>?
  305. // When
  306. let request = session.request(urlRequest)
  307. .cancel()
  308. .response { resp in
  309. response = resp
  310. expectation.fulfill()
  311. }
  312. waitForExpectations(timeout: timeout, handler: nil)
  313. // Then
  314. XCTAssertNotNil(response, "response should not be nil")
  315. XCTAssertTrue(request.isCancelled)
  316. XCTAssertTrue((request.task == nil) || (request.task?.state == .canceling || request.task?.state == .completed))
  317. XCTAssertEqual(request.error?.isExplicitlyCancelledError, true)
  318. }
  319. func testSetStartRequestsImmediatelyToFalseAndResumeThenCancelRequestHasCorrectOutput() {
  320. // Given
  321. let session = Session(startRequestsImmediately: false)
  322. let url = URL(string: "https://httpbin.org/get")!
  323. let urlRequest = URLRequest(url: url)
  324. let expectation = self.expectation(description: "\(url)")
  325. var response: DataResponse<Data?, AFError>?
  326. // When
  327. let request = session.request(urlRequest)
  328. .resume()
  329. .cancel()
  330. .response { resp in
  331. response = resp
  332. expectation.fulfill()
  333. }
  334. waitForExpectations(timeout: timeout, handler: nil)
  335. // Then
  336. XCTAssertNotNil(response, "response should not be nil")
  337. XCTAssertTrue(request.isCancelled)
  338. XCTAssertTrue((request.task == nil) || (request.task?.state == .canceling || request.task?.state == .completed))
  339. XCTAssertEqual(request.error?.isExplicitlyCancelledError, true)
  340. }
  341. func testSetStartRequestsImmediatelyToFalseAndCancelThenResumeRequestDoesntCreateTaskAndStaysCancelled() {
  342. // Given
  343. let session = Session(startRequestsImmediately: false)
  344. let url = URL(string: "https://httpbin.org/get")!
  345. let urlRequest = URLRequest(url: url)
  346. let expectation = self.expectation(description: "\(url)")
  347. var response: DataResponse<Data?, AFError>?
  348. // When
  349. let request = session.request(urlRequest)
  350. .cancel()
  351. .resume()
  352. .response { resp in
  353. response = resp
  354. expectation.fulfill()
  355. }
  356. waitForExpectations(timeout: timeout, handler: nil)
  357. // Then
  358. XCTAssertNotNil(response, "response should not be nil")
  359. XCTAssertTrue(request.isCancelled)
  360. XCTAssertTrue((request.task == nil) || (request.task?.state == .canceling || request.task?.state == .completed))
  361. XCTAssertEqual(request.error?.isExplicitlyCancelledError, true)
  362. }
  363. // MARK: Tests - Deinitialization
  364. func testReleasingManagerWithPendingRequestDeinitializesSuccessfully() {
  365. // Given
  366. let monitor = ClosureEventMonitor()
  367. let expectation = self.expectation(description: "Request created")
  368. monitor.requestDidCreateTask = { _, _ in expectation.fulfill() }
  369. var session: Session? = Session(startRequestsImmediately: false, eventMonitors: [monitor])
  370. let url = URL(string: "https://httpbin.org/get")!
  371. let urlRequest = URLRequest(url: url)
  372. // When
  373. let request = session?.request(urlRequest)
  374. session = nil
  375. waitForExpectations(timeout: timeout, handler: nil)
  376. // Then
  377. XCTAssertEqual(request?.task?.state, .suspended)
  378. XCTAssertNil(session, "manager should be nil")
  379. }
  380. func testReleasingManagerWithPendingCanceledRequestDeinitializesSuccessfully() {
  381. // Given
  382. var session: Session? = Session(startRequestsImmediately: false)
  383. let url = URL(string: "https://httpbin.org/get")!
  384. let urlRequest = URLRequest(url: url)
  385. // When
  386. let request = session?.request(urlRequest)
  387. request?.cancel()
  388. session = nil
  389. let state = request?.state
  390. // Then
  391. XCTAssertTrue(state == .cancelled, "state should be .cancelled")
  392. XCTAssertNil(session, "manager should be nil")
  393. }
  394. // MARK: Tests - Bad Requests
  395. func testThatDataRequestWithInvalidURLStringThrowsResponseHandlerError() {
  396. // Given
  397. let session = Session()
  398. let expectation = self.expectation(description: "Request should fail with error")
  399. var response: DataResponse<Data?, AFError>?
  400. // When
  401. session.request("https://httpbin.org/get/äëïöü").response { resp in
  402. response = resp
  403. expectation.fulfill()
  404. }
  405. waitForExpectations(timeout: timeout, handler: nil)
  406. // Then
  407. XCTAssertNil(response?.request)
  408. XCTAssertNil(response?.response)
  409. XCTAssertNil(response?.data)
  410. XCTAssertNotNil(response?.error)
  411. XCTAssertEqual(response?.error?.isInvalidURLError, true)
  412. XCTAssertEqual(response?.error?.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  413. }
  414. func testThatDownloadRequestWithInvalidURLStringThrowsResponseHandlerError() {
  415. // Given
  416. let session = Session()
  417. let expectation = self.expectation(description: "Download should fail with error")
  418. var response: DownloadResponse<URL?, AFError>?
  419. // When
  420. session.download("https://httpbin.org/get/äëïöü").response { resp in
  421. response = resp
  422. expectation.fulfill()
  423. }
  424. waitForExpectations(timeout: timeout, handler: nil)
  425. // Then
  426. XCTAssertNil(response?.request)
  427. XCTAssertNil(response?.response)
  428. XCTAssertNil(response?.fileURL)
  429. XCTAssertNil(response?.resumeData)
  430. XCTAssertNotNil(response?.error)
  431. XCTAssertEqual(response?.error?.isInvalidURLError, true)
  432. XCTAssertEqual(response?.error?.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  433. }
  434. func testThatUploadDataRequestWithInvalidURLStringThrowsResponseHandlerError() {
  435. // Given
  436. let session = Session()
  437. let expectation = self.expectation(description: "Upload should fail with error")
  438. var response: DataResponse<Data?, AFError>?
  439. // When
  440. session.upload(Data(), to: "https://httpbin.org/get/äëïöü").response { resp in
  441. response = resp
  442. expectation.fulfill()
  443. }
  444. waitForExpectations(timeout: timeout, handler: nil)
  445. // Then
  446. XCTAssertNil(response?.request)
  447. XCTAssertNil(response?.response)
  448. XCTAssertNil(response?.data)
  449. XCTAssertNotNil(response?.error)
  450. XCTAssertEqual(response?.error?.isInvalidURLError, true)
  451. XCTAssertEqual(response?.error?.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  452. }
  453. func testThatUploadFileRequestWithInvalidURLStringThrowsResponseHandlerError() {
  454. // Given
  455. let session = Session()
  456. let expectation = self.expectation(description: "Upload should fail with error")
  457. var response: DataResponse<Data?, AFError>?
  458. // When
  459. session.upload(URL(fileURLWithPath: "/invalid"), to: "https://httpbin.org/get/äëïöü").response { resp in
  460. response = resp
  461. expectation.fulfill()
  462. }
  463. waitForExpectations(timeout: timeout, handler: nil)
  464. // Then
  465. XCTAssertNil(response?.request)
  466. XCTAssertNil(response?.response)
  467. XCTAssertNil(response?.data)
  468. XCTAssertNotNil(response?.error)
  469. XCTAssertEqual(response?.error?.isInvalidURLError, true)
  470. XCTAssertEqual(response?.error?.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  471. }
  472. func testThatUploadStreamRequestWithInvalidURLStringThrowsResponseHandlerError() {
  473. // Given
  474. let session = Session()
  475. let expectation = self.expectation(description: "Upload should fail with error")
  476. var response: DataResponse<Data?, AFError>?
  477. // When
  478. session.upload(InputStream(data: Data()), to: "https://httpbin.org/get/äëïöü").response { resp in
  479. response = resp
  480. expectation.fulfill()
  481. }
  482. waitForExpectations(timeout: timeout, handler: nil)
  483. // Then
  484. XCTAssertNil(response?.request)
  485. XCTAssertNil(response?.response)
  486. XCTAssertNil(response?.data)
  487. XCTAssertNotNil(response?.error)
  488. XCTAssertEqual(response?.error?.isInvalidURLError, true)
  489. XCTAssertEqual(response?.error?.urlConvertible as? String, "https://httpbin.org/get/äëïöü")
  490. }
  491. // MARK: Tests - Request Adapter
  492. func testThatSessionCallsRequestAdaptersWhenCreatingDataRequest() {
  493. // Given
  494. let urlString = "https://httpbin.org/get"
  495. let methodAdapter = HTTPMethodAdapter(method: .post)
  496. let headerAdapter = HeaderAdapter()
  497. let monitor = ClosureEventMonitor()
  498. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  499. // When
  500. let expectation1 = expectation(description: "Request 1 created")
  501. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  502. let request1 = session.request(urlString)
  503. waitForExpectations(timeout: timeout, handler: nil)
  504. let expectation2 = expectation(description: "Request 2 created")
  505. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  506. let request2 = session.request(urlString, interceptor: headerAdapter)
  507. waitForExpectations(timeout: timeout, handler: nil)
  508. // Then
  509. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  510. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  511. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  512. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  513. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  514. }
  515. func testThatSessionCallsRequestAdaptersWhenCreatingDownloadRequest() {
  516. // Given
  517. let urlString = "https://httpbin.org/get"
  518. let methodAdapter = HTTPMethodAdapter(method: .post)
  519. let headerAdapter = HeaderAdapter()
  520. let monitor = ClosureEventMonitor()
  521. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  522. // When
  523. let expectation1 = expectation(description: "Request 1 created")
  524. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  525. let request1 = session.download(urlString)
  526. waitForExpectations(timeout: timeout, handler: nil)
  527. let expectation2 = expectation(description: "Request 2 created")
  528. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  529. let request2 = session.download(urlString, interceptor: headerAdapter)
  530. waitForExpectations(timeout: timeout, handler: nil)
  531. // Then
  532. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  533. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  534. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  535. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  536. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  537. }
  538. func testThatSessionCallsRequestAdaptersWhenCreatingUploadRequestWithData() {
  539. // Given
  540. let data = Data("data".utf8)
  541. let urlString = "https://httpbin.org/post"
  542. let methodAdapter = HTTPMethodAdapter(method: .get)
  543. let headerAdapter = HeaderAdapter()
  544. let monitor = ClosureEventMonitor()
  545. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  546. // When
  547. let expectation1 = expectation(description: "Request 1 created")
  548. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  549. let request1 = session.upload(data, to: urlString)
  550. waitForExpectations(timeout: timeout, handler: nil)
  551. let expectation2 = expectation(description: "Request 2 created")
  552. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  553. let request2 = session.upload(data, to: urlString, interceptor: headerAdapter)
  554. waitForExpectations(timeout: timeout, handler: nil)
  555. // Then
  556. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  557. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  558. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  559. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  560. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  561. }
  562. func testThatSessionCallsRequestAdaptersWhenCreatingUploadRequestWithFile() {
  563. // Given
  564. let fileURL = URL(fileURLWithPath: "/path/to/some/file.txt")
  565. let urlString = "https://httpbin.org/post"
  566. let methodAdapter = HTTPMethodAdapter(method: .get)
  567. let headerAdapter = HeaderAdapter()
  568. let monitor = ClosureEventMonitor()
  569. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  570. // When
  571. let expectation1 = expectation(description: "Request 1 created")
  572. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  573. let request1 = session.upload(fileURL, to: urlString)
  574. waitForExpectations(timeout: timeout, handler: nil)
  575. let expectation2 = expectation(description: "Request 2 created")
  576. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  577. let request2 = session.upload(fileURL, to: urlString, interceptor: headerAdapter)
  578. waitForExpectations(timeout: timeout, handler: nil)
  579. // Then
  580. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  581. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  582. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  583. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  584. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  585. }
  586. func testThatSessionCallsRequestAdaptersWhenCreatingUploadRequestWithInputStream() {
  587. // Given
  588. let inputStream = InputStream(data: Data("data".utf8))
  589. let urlString = "https://httpbin.org/post"
  590. let methodAdapter = HTTPMethodAdapter(method: .get)
  591. let headerAdapter = HeaderAdapter()
  592. let monitor = ClosureEventMonitor()
  593. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  594. // When
  595. let expectation1 = expectation(description: "Request 1 created")
  596. monitor.requestDidCreateTask = { _, _ in expectation1.fulfill() }
  597. let request1 = session.upload(inputStream, to: urlString)
  598. waitForExpectations(timeout: timeout, handler: nil)
  599. let expectation2 = expectation(description: "Request 2 created")
  600. monitor.requestDidCreateTask = { _, _ in expectation2.fulfill() }
  601. let request2 = session.upload(inputStream, to: urlString, interceptor: headerAdapter)
  602. waitForExpectations(timeout: timeout, handler: nil)
  603. // Then
  604. XCTAssertEqual(request1.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  605. XCTAssertEqual(request2.task?.originalRequest?.httpMethod, methodAdapter.method.rawValue)
  606. XCTAssertEqual(request2.task?.originalRequest?.allHTTPHeaderFields?.count, 1)
  607. XCTAssertEqual(methodAdapter.adaptedCount, 2)
  608. XCTAssertEqual(headerAdapter.adaptedCount, 1)
  609. }
  610. func testThatSessionReturnsRequestAdaptationErrorWhenRequestAdapterThrowsError() {
  611. // Given
  612. let urlString = "https://httpbin.org/get"
  613. let methodAdapter = HTTPMethodAdapter(method: .post, throwsError: true)
  614. let headerAdapter = HeaderAdapter(throwsError: true)
  615. let monitor = ClosureEventMonitor()
  616. let session = Session(startRequestsImmediately: false, interceptor: methodAdapter, eventMonitors: [monitor])
  617. // When
  618. let expectation1 = expectation(description: "Request 1 created")
  619. monitor.requestDidFailToAdaptURLRequestWithError = { _, _, _ in expectation1.fulfill() }
  620. let request1 = session.request(urlString)
  621. waitForExpectations(timeout: timeout, handler: nil)
  622. let expectation2 = expectation(description: "Request 2 created")
  623. monitor.requestDidFailToAdaptURLRequestWithError = { _, _, _ in expectation2.fulfill() }
  624. let request2 = session.request(urlString, interceptor: headerAdapter)
  625. waitForExpectations(timeout: timeout, handler: nil)
  626. let requests = [request1, request2]
  627. // Then
  628. for request in requests {
  629. XCTAssertEqual(request.error?.isRequestAdaptationError, true)
  630. XCTAssertEqual(request.error?.underlyingError?.asAFError?.urlConvertible as? String, "")
  631. }
  632. }
  633. // MARK: Tests - Request Retrier
  634. func testThatSessionCallsRequestRetrierWhenRequestEncountersError() {
  635. // Given
  636. let handler = RequestHandler()
  637. let session = Session()
  638. let expectation = self.expectation(description: "request should eventually fail")
  639. var response: DataResponse<Any, AFError>?
  640. // When
  641. let request = session.request("https://httpbin.org/basic-auth/user/password", interceptor: handler)
  642. .validate()
  643. .responseJSON { jsonResponse in
  644. response = jsonResponse
  645. expectation.fulfill()
  646. }
  647. waitForExpectations(timeout: timeout, handler: nil)
  648. // Then
  649. XCTAssertEqual(handler.adaptCalledCount, 2)
  650. XCTAssertEqual(handler.adaptedCount, 2)
  651. XCTAssertEqual(handler.retryCalledCount, 3)
  652. XCTAssertEqual(handler.retryCount, 3)
  653. XCTAssertEqual(request.retryCount, 1)
  654. XCTAssertEqual(response?.result.isSuccess, false)
  655. assert(on: session.rootQueue) {
  656. XCTAssertTrue(session.requestTaskMap.isEmpty)
  657. XCTAssertTrue(session.activeRequests.isEmpty)
  658. }
  659. }
  660. func testThatSessionCallsRequestRetrierThenSessionRetrierWhenRequestEncountersError() {
  661. // Given
  662. let sessionHandler = RequestHandler()
  663. let requestHandler = RequestHandler()
  664. let session = Session(interceptor: sessionHandler)
  665. let expectation = self.expectation(description: "request should eventually fail")
  666. var response: DataResponse<Any, AFError>?
  667. // When
  668. let request = session.request("https://httpbin.org/basic-auth/user/password", interceptor: requestHandler)
  669. .validate()
  670. .responseJSON { jsonResponse in
  671. response = jsonResponse
  672. expectation.fulfill()
  673. }
  674. waitForExpectations(timeout: timeout, handler: nil)
  675. // Then
  676. XCTAssertEqual(sessionHandler.adaptCalledCount, 3)
  677. XCTAssertEqual(sessionHandler.adaptedCount, 3)
  678. XCTAssertEqual(sessionHandler.retryCalledCount, 3)
  679. XCTAssertEqual(sessionHandler.retryCount, 3)
  680. XCTAssertEqual(requestHandler.adaptCalledCount, 3)
  681. XCTAssertEqual(requestHandler.adaptedCount, 3)
  682. XCTAssertEqual(requestHandler.retryCalledCount, 4)
  683. XCTAssertEqual(requestHandler.retryCount, 4)
  684. XCTAssertEqual(request.retryCount, 2)
  685. XCTAssertEqual(response?.result.isSuccess, false)
  686. assert(on: session.rootQueue) {
  687. XCTAssertTrue(session.requestTaskMap.isEmpty)
  688. XCTAssertTrue(session.activeRequests.isEmpty)
  689. }
  690. }
  691. func testThatSessionCallsRequestRetrierWhenRequestInitiallyEncountersAdaptError() {
  692. // Given
  693. let handler = RequestHandler()
  694. handler.adaptedCount = 1
  695. handler.throwsErrorOnSecondAdapt = true
  696. handler.shouldApplyAuthorizationHeader = true
  697. let session = Session()
  698. let expectation = self.expectation(description: "request should eventually fail")
  699. var response: DataResponse<Any, AFError>?
  700. // When
  701. session.request("https://httpbin.org/basic-auth/user/password", interceptor: handler)
  702. .validate()
  703. .responseJSON { jsonResponse in
  704. response = jsonResponse
  705. expectation.fulfill()
  706. }
  707. waitForExpectations(timeout: timeout, handler: nil)
  708. // Then
  709. XCTAssertEqual(handler.adaptCalledCount, 2)
  710. XCTAssertEqual(handler.adaptedCount, 2)
  711. XCTAssertEqual(handler.retryCalledCount, 1)
  712. XCTAssertEqual(handler.retryCount, 1)
  713. XCTAssertEqual(response?.result.isSuccess, true)
  714. assert(on: session.rootQueue) {
  715. XCTAssertTrue(session.requestTaskMap.isEmpty)
  716. XCTAssertTrue(session.activeRequests.isEmpty)
  717. }
  718. }
  719. func testThatSessionCallsRequestRetrierWhenDownloadInitiallyEncountersAdaptError() {
  720. // Given
  721. let handler = RequestHandler()
  722. handler.adaptedCount = 1
  723. handler.throwsErrorOnSecondAdapt = true
  724. handler.shouldApplyAuthorizationHeader = true
  725. let session = Session()
  726. let expectation = self.expectation(description: "request should eventually fail")
  727. var response: DownloadResponse<Any, AFError>?
  728. let destination: DownloadRequest.Destination = { _, _ in
  729. let fileURL = self.testDirectoryURL.appendingPathComponent("test-output.json")
  730. return (fileURL, [.removePreviousFile])
  731. }
  732. // When
  733. session.download("https://httpbin.org/basic-auth/user/password", interceptor: handler, to: destination)
  734. .validate()
  735. .responseJSON { jsonResponse in
  736. response = jsonResponse
  737. expectation.fulfill()
  738. }
  739. waitForExpectations(timeout: timeout, handler: nil)
  740. // Then
  741. XCTAssertEqual(handler.adaptCalledCount, 2)
  742. XCTAssertEqual(handler.adaptedCount, 2)
  743. XCTAssertEqual(handler.retryCalledCount, 1)
  744. XCTAssertEqual(handler.retryCount, 1)
  745. XCTAssertEqual(response?.result.isSuccess, true)
  746. assert(on: session.rootQueue) {
  747. XCTAssertTrue(session.requestTaskMap.isEmpty)
  748. XCTAssertTrue(session.activeRequests.isEmpty)
  749. }
  750. }
  751. func testThatSessionCallsRequestRetrierWhenUploadInitiallyEncountersAdaptError() {
  752. // Given
  753. let handler = UploadHandler()
  754. let session = Session(interceptor: handler)
  755. let expectation = self.expectation(description: "request should eventually fail")
  756. var response: DataResponse<Any, AFError>?
  757. let uploadData = Data("upload data".utf8)
  758. // When
  759. session.upload(uploadData, to: "https://httpbin.org/post")
  760. .validate()
  761. .responseJSON { jsonResponse in
  762. response = jsonResponse
  763. expectation.fulfill()
  764. }
  765. waitForExpectations(timeout: timeout, handler: nil)
  766. // Then
  767. XCTAssertEqual(handler.adaptCalledCount, 2)
  768. XCTAssertEqual(handler.adaptedCount, 2)
  769. XCTAssertEqual(handler.retryCalledCount, 1)
  770. XCTAssertEqual(handler.retryCount, 1)
  771. XCTAssertEqual(response?.result.isSuccess, true)
  772. assert(on: session.rootQueue) {
  773. XCTAssertTrue(session.requestTaskMap.isEmpty)
  774. XCTAssertTrue(session.activeRequests.isEmpty)
  775. }
  776. }
  777. func testThatSessionCallsAdapterWhenRequestIsRetried() {
  778. // Given
  779. let handler = RequestHandler()
  780. handler.shouldApplyAuthorizationHeader = true
  781. let session = Session(interceptor: handler)
  782. let expectation = self.expectation(description: "request should eventually succeed")
  783. var response: DataResponse<Any, AFError>?
  784. // When
  785. let request = session.request("https://httpbin.org/basic-auth/user/password")
  786. .validate()
  787. .responseJSON { jsonResponse in
  788. response = jsonResponse
  789. expectation.fulfill()
  790. }
  791. waitForExpectations(timeout: timeout, handler: nil)
  792. // Then
  793. XCTAssertEqual(handler.adaptCalledCount, 2)
  794. XCTAssertEqual(handler.adaptedCount, 2)
  795. XCTAssertEqual(handler.retryCalledCount, 1)
  796. XCTAssertEqual(handler.retryCount, 1)
  797. XCTAssertEqual(request.retryCount, 1)
  798. XCTAssertEqual(response?.result.isSuccess, true)
  799. assert(on: session.rootQueue) {
  800. XCTAssertTrue(session.requestTaskMap.isEmpty)
  801. XCTAssertTrue(session.activeRequests.isEmpty)
  802. }
  803. }
  804. func testThatSessionReturnsRequestAdaptationErrorWhenRequestIsRetried() {
  805. // Given
  806. let handler = RequestHandler()
  807. handler.throwsErrorOnSecondAdapt = true
  808. let session = Session(interceptor: handler)
  809. let expectation = self.expectation(description: "request should eventually fail")
  810. var response: DataResponse<Any, AFError>?
  811. // When
  812. let request = session.request("https://httpbin.org/basic-auth/user/password")
  813. .validate()
  814. .responseJSON { jsonResponse in
  815. response = jsonResponse
  816. expectation.fulfill()
  817. }
  818. waitForExpectations(timeout: timeout, handler: nil)
  819. // Then
  820. XCTAssertEqual(handler.adaptCalledCount, 2)
  821. XCTAssertEqual(handler.adaptedCount, 1)
  822. XCTAssertEqual(handler.retryCalledCount, 3)
  823. XCTAssertEqual(handler.retryCount, 3)
  824. XCTAssertEqual(request.retryCount, 1)
  825. XCTAssertEqual(response?.result.isSuccess, false)
  826. XCTAssertEqual(request.error?.isRequestAdaptationError, true)
  827. XCTAssertEqual(request.error?.underlyingError?.asAFError?.urlConvertible as? String, "/adapt/error/2")
  828. assert(on: session.rootQueue) {
  829. XCTAssertTrue(session.requestTaskMap.isEmpty)
  830. XCTAssertTrue(session.activeRequests.isEmpty)
  831. }
  832. }
  833. func testThatSessionRetriesRequestWithDelayWhenRetryResultContainsDelay() {
  834. // Given
  835. let handler = RequestHandler()
  836. handler.retryDelay = 0.01
  837. handler.throwsErrorOnSecondAdapt = true
  838. let session = Session(interceptor: handler)
  839. let expectation = self.expectation(description: "request should eventually fail")
  840. var response: DataResponse<Any, AFError>?
  841. // When
  842. let request = session.request("https://httpbin.org/basic-auth/user/password")
  843. .validate()
  844. .responseJSON { jsonResponse in
  845. response = jsonResponse
  846. expectation.fulfill()
  847. }
  848. waitForExpectations(timeout: timeout, handler: nil)
  849. // Then
  850. XCTAssertEqual(handler.adaptCalledCount, 2)
  851. XCTAssertEqual(handler.adaptedCount, 1)
  852. XCTAssertEqual(handler.retryCalledCount, 3)
  853. XCTAssertEqual(handler.retryCount, 3)
  854. XCTAssertEqual(request.retryCount, 1)
  855. XCTAssertEqual(response?.result.isSuccess, false)
  856. XCTAssertEqual(request.error?.isRequestAdaptationError, true)
  857. XCTAssertEqual(request.error?.underlyingError?.asAFError?.urlConvertible as? String, "/adapt/error/2")
  858. assert(on: session.rootQueue) {
  859. XCTAssertTrue(session.requestTaskMap.isEmpty)
  860. XCTAssertTrue(session.activeRequests.isEmpty)
  861. }
  862. }
  863. func testThatSessionReturnsRequestRetryErrorWhenRequestRetrierThrowsError() {
  864. // Given
  865. let handler = RequestHandler()
  866. handler.throwsErrorOnRetry = true
  867. let session = Session(interceptor: handler)
  868. let expectation = self.expectation(description: "request should eventually fail")
  869. var response: DataResponse<Any, AFError>?
  870. // When
  871. let request = session.request("https://httpbin.org/basic-auth/user/password")
  872. .validate()
  873. .responseJSON { jsonResponse in
  874. response = jsonResponse
  875. expectation.fulfill()
  876. }
  877. waitForExpectations(timeout: timeout, handler: nil)
  878. // Then
  879. XCTAssertEqual(handler.adaptCalledCount, 1)
  880. XCTAssertEqual(handler.adaptedCount, 1)
  881. XCTAssertEqual(handler.retryCalledCount, 2)
  882. XCTAssertEqual(handler.retryCount, 0)
  883. XCTAssertEqual(request.retryCount, 0)
  884. XCTAssertEqual(response?.result.isSuccess, false)
  885. assert(on: session.rootQueue) {
  886. XCTAssertTrue(session.requestTaskMap.isEmpty)
  887. XCTAssertTrue(session.activeRequests.isEmpty)
  888. }
  889. if let error = response?.result.failure {
  890. XCTAssertTrue(error.isRequestRetryError)
  891. XCTAssertEqual(error.underlyingError?.asAFError?.urlConvertible as? String, "/invalid/url/2")
  892. } else {
  893. XCTFail("error should not be nil")
  894. }
  895. }
  896. // MARK: Tests - Response Serializer Retry
  897. func testThatSessionCallsRequestRetrierWhenResponseSerializerThrowsError() {
  898. // Given
  899. let handler = RequestHandler()
  900. handler.shouldRetry = false
  901. let session = Session()
  902. let expectation = self.expectation(description: "request should eventually fail")
  903. var response: DataResponse<Any, AFError>?
  904. // When
  905. let request = session.request("https://httpbin.org/image/jpeg", interceptor: handler)
  906. .validate()
  907. .responseJSON { jsonResponse in
  908. response = jsonResponse
  909. expectation.fulfill()
  910. }
  911. waitForExpectations(timeout: timeout, handler: nil)
  912. // Then
  913. XCTAssertEqual(handler.adaptCalledCount, 1)
  914. XCTAssertEqual(handler.adaptedCount, 1)
  915. XCTAssertEqual(handler.retryCalledCount, 1)
  916. XCTAssertEqual(handler.retryCount, 0)
  917. XCTAssertEqual(request.retryCount, 0)
  918. XCTAssertEqual(response?.result.isSuccess, false)
  919. XCTAssertEqual(response?.error?.isResponseSerializationError, true)
  920. XCTAssertEqual((response?.error?.underlyingError as? CocoaError)?.code, .propertyListReadCorrupt)
  921. assert(on: session.rootQueue) {
  922. XCTAssertTrue(session.requestTaskMap.isEmpty)
  923. XCTAssertTrue(session.activeRequests.isEmpty)
  924. }
  925. }
  926. func testThatSessionCallsRequestRetrierForAllResponseSerializersThatThrowError() throws {
  927. // Given
  928. let handler = RequestHandler()
  929. handler.throwsErrorOnRetry = true
  930. let session = Session()
  931. let json1Expectation = expectation(description: "request should eventually fail")
  932. var json1Response: DataResponse<Any, AFError>?
  933. let json2Expectation = expectation(description: "request should eventually fail")
  934. var json2Response: DataResponse<Any, AFError>?
  935. // When
  936. let request = session.request("https://httpbin.org/image/jpeg", interceptor: handler)
  937. .validate()
  938. .responseJSON { response in
  939. json1Response = response
  940. json1Expectation.fulfill()
  941. }
  942. .responseJSON { response in
  943. json2Response = response
  944. json2Expectation.fulfill()
  945. }
  946. waitForExpectations(timeout: timeout, handler: nil)
  947. // Then
  948. XCTAssertEqual(handler.adaptCalledCount, 1)
  949. XCTAssertEqual(handler.adaptedCount, 1)
  950. XCTAssertEqual(handler.retryCalledCount, 2)
  951. XCTAssertEqual(handler.retryCount, 0)
  952. XCTAssertEqual(request.retryCount, 0)
  953. XCTAssertEqual(json1Response?.result.isSuccess, false)
  954. XCTAssertEqual(json2Response?.result.isSuccess, false)
  955. assert(on: session.rootQueue) {
  956. XCTAssertTrue(session.requestTaskMap.isEmpty)
  957. XCTAssertTrue(session.activeRequests.isEmpty)
  958. }
  959. let errors = [json1Response, json2Response].compactMap { $0?.error }
  960. XCTAssertEqual(errors.count, 2)
  961. for (index, error) in errors.enumerated() {
  962. XCTAssertTrue(error.isRequestRetryError)
  963. if case let .requestRetryFailed(retryError, originalError) = error {
  964. XCTAssertEqual(retryError.asAFError?.urlConvertible as? String, "/invalid/url/\(index + 1)")
  965. XCTAssertEqual((originalError.asAFError?.underlyingError as? CocoaError)?.code, .propertyListReadCorrupt)
  966. } else {
  967. XCTFail("Error failure reason should be response serialization failure")
  968. }
  969. }
  970. }
  971. func testThatSessionRetriesRequestImmediatelyWhenResponseSerializerRequestsRetry() throws {
  972. // Given
  973. let handler = RequestHandler()
  974. let session = Session()
  975. let json1Expectation = expectation(description: "request should eventually fail")
  976. var json1Response: DataResponse<Any, AFError>?
  977. let json2Expectation = expectation(description: "request should eventually fail")
  978. var json2Response: DataResponse<Any, AFError>?
  979. // When
  980. let request = session.request("https://httpbin.org/image/jpeg", interceptor: handler)
  981. .validate()
  982. .responseJSON { response in
  983. json1Response = response
  984. json1Expectation.fulfill()
  985. }
  986. .responseJSON { response in
  987. json2Response = response
  988. json2Expectation.fulfill()
  989. }
  990. waitForExpectations(timeout: 10, handler: nil)
  991. // Then
  992. XCTAssertEqual(handler.adaptCalledCount, 2)
  993. XCTAssertEqual(handler.adaptedCount, 2)
  994. XCTAssertEqual(handler.retryCalledCount, 3)
  995. XCTAssertEqual(handler.retryCount, 3)
  996. XCTAssertEqual(request.retryCount, 1)
  997. XCTAssertEqual(json1Response?.result.isSuccess, false)
  998. XCTAssertEqual(json2Response?.result.isSuccess, false)
  999. assert(on: session.rootQueue) {
  1000. XCTAssertTrue(session.requestTaskMap.isEmpty)
  1001. XCTAssertTrue(session.activeRequests.isEmpty)
  1002. }
  1003. let errors = [json1Response, json2Response].compactMap { $0?.error }
  1004. XCTAssertEqual(errors.count, 2)
  1005. for error in errors {
  1006. XCTAssertTrue(error.isResponseSerializationError)
  1007. XCTAssertEqual((error.underlyingError as? CocoaError)?.code, .propertyListReadCorrupt)
  1008. }
  1009. }
  1010. func testThatSessionCallsResponseSerializerCompletionsWhenAdapterThrowsErrorDuringRetry() {
  1011. // Four retries should occur given this scenario:
  1012. // 1) Retrier is called from first response serializer failure (trips retry)
  1013. // 2) Retrier is called by Session for adapt error thrown
  1014. // 3) Retrier is called again from first response serializer failure
  1015. // 4) Retrier is called from second response serializer failure
  1016. // Given
  1017. let handler = RequestHandler()
  1018. handler.throwsErrorOnSecondAdapt = true
  1019. let session = Session()
  1020. let json1Expectation = expectation(description: "request should eventually fail")
  1021. var json1Response: DataResponse<Any, AFError>?
  1022. let json2Expectation = expectation(description: "request should eventually fail")
  1023. var json2Response: DataResponse<Any, AFError>?
  1024. // When
  1025. let request = session.request("https://httpbin.org/image/jpeg", interceptor: handler)
  1026. .validate()
  1027. .responseJSON { response in
  1028. json1Response = response
  1029. json1Expectation.fulfill()
  1030. }
  1031. .responseJSON { response in
  1032. json2Response = response
  1033. json2Expectation.fulfill()
  1034. }
  1035. waitForExpectations(timeout: 10, handler: nil)
  1036. // Then
  1037. XCTAssertEqual(handler.adaptCalledCount, 2)
  1038. XCTAssertEqual(handler.adaptedCount, 1)
  1039. XCTAssertEqual(handler.retryCalledCount, 4)
  1040. XCTAssertEqual(handler.retryCount, 4)
  1041. XCTAssertEqual(request.retryCount, 1)
  1042. XCTAssertEqual(json1Response?.result.isSuccess, false)
  1043. XCTAssertEqual(json2Response?.result.isSuccess, false)
  1044. assert(on: session.rootQueue) {
  1045. XCTAssertTrue(session.requestTaskMap.isEmpty)
  1046. XCTAssertTrue(session.activeRequests.isEmpty)
  1047. }
  1048. let errors = [json1Response, json2Response].compactMap { $0?.error }
  1049. XCTAssertEqual(errors.count, 2)
  1050. for error in errors {
  1051. XCTAssertTrue(error.isRequestAdaptationError)
  1052. XCTAssertEqual(error.underlyingError?.asAFError?.urlConvertible as? String, "/adapt/error/2")
  1053. }
  1054. }
  1055. func testThatSessionCallsResponseSerializerCompletionsWhenAdapterThrowsErrorDuringRetryForDownloads() {
  1056. // Four retries should occur given this scenario:
  1057. // 1) Retrier is called from first response serializer failure (trips retry)
  1058. // 2) Retrier is called by Session for adapt error thrown
  1059. // 3) Retrier is called again from first response serializer failure
  1060. // 4) Retrier is called from second response serializer failure
  1061. // Given
  1062. let handler = RequestHandler()
  1063. handler.throwsErrorOnSecondAdapt = true
  1064. let session = Session()
  1065. let json1Expectation = expectation(description: "request should eventually fail")
  1066. var json1Response: DownloadResponse<Any, AFError>?
  1067. let json2Expectation = expectation(description: "request should eventually fail")
  1068. var json2Response: DownloadResponse<Any, AFError>?
  1069. // When
  1070. let request = session.download("https://httpbin.org/image/jpeg", interceptor: handler)
  1071. .validate()
  1072. .responseJSON { response in
  1073. json1Response = response
  1074. json1Expectation.fulfill()
  1075. }
  1076. .responseJSON { response in
  1077. json2Response = response
  1078. json2Expectation.fulfill()
  1079. }
  1080. waitForExpectations(timeout: 10, handler: nil)
  1081. // Then
  1082. XCTAssertEqual(handler.adaptCalledCount, 2)
  1083. XCTAssertEqual(handler.adaptedCount, 1)
  1084. XCTAssertEqual(handler.retryCalledCount, 4)
  1085. XCTAssertEqual(handler.retryCount, 4)
  1086. XCTAssertEqual(request.retryCount, 1)
  1087. XCTAssertEqual(json1Response?.result.isSuccess, false)
  1088. XCTAssertEqual(json2Response?.result.isSuccess, false)
  1089. assert(on: session.rootQueue) {
  1090. XCTAssertTrue(session.requestTaskMap.isEmpty)
  1091. XCTAssertTrue(session.activeRequests.isEmpty)
  1092. }
  1093. let errors = [json1Response, json2Response].compactMap { $0?.error }
  1094. XCTAssertEqual(errors.count, 2)
  1095. for error in errors {
  1096. XCTAssertTrue(error.isRequestAdaptationError)
  1097. XCTAssertEqual(error.underlyingError?.asAFError?.urlConvertible as? String, "/adapt/error/2")
  1098. }
  1099. }
  1100. // MARK: Tests - Session Invalidation
  1101. func testThatSessionIsInvalidatedAndAllRequestsCompleteWhenSessionIsDeinitialized() {
  1102. // Given
  1103. let invalidationExpectation = expectation(description: "sessionDidBecomeInvalidWithError should be called")
  1104. let events = ClosureEventMonitor()
  1105. events.sessionDidBecomeInvalidWithError = { _, _ in
  1106. invalidationExpectation.fulfill()
  1107. }
  1108. var session: Session? = Session(startRequestsImmediately: false, eventMonitors: [events])
  1109. var error: AFError?
  1110. let requestExpectation = expectation(description: "request should complete")
  1111. // When
  1112. session?.request(URLRequest.makeHTTPBinRequest()).response { response in
  1113. error = response.error
  1114. requestExpectation.fulfill()
  1115. }
  1116. session = nil
  1117. waitForExpectations(timeout: timeout, handler: nil)
  1118. // Then
  1119. XCTAssertEqual(error?.isSessionDeinitializedError, true)
  1120. }
  1121. // MARK: Tests - Request Cancellation
  1122. func testThatSessionOnlyCallsResponseSerializerCompletionWhenCancellingInsideCompletion() {
  1123. // Given
  1124. let handler = RequestHandler()
  1125. let session = Session()
  1126. let expectation = self.expectation(description: "request should complete")
  1127. var response: DataResponse<Any, AFError>?
  1128. var completionCallCount = 0
  1129. // When
  1130. let request = session.request("https://httpbin.org/get", interceptor: handler)
  1131. request.validate()
  1132. request.responseJSON { resp in
  1133. request.cancel()
  1134. response = resp
  1135. completionCallCount += 1
  1136. DispatchQueue.main.after(0.01) { expectation.fulfill() }
  1137. }
  1138. waitForExpectations(timeout: timeout, handler: nil)
  1139. // Then
  1140. XCTAssertEqual(handler.adaptCalledCount, 1)
  1141. XCTAssertEqual(handler.adaptedCount, 1)
  1142. XCTAssertEqual(handler.retryCalledCount, 0)
  1143. XCTAssertEqual(handler.retryCount, 0)
  1144. XCTAssertEqual(request.retryCount, 0)
  1145. XCTAssertEqual(response?.result.isSuccess, true)
  1146. XCTAssertEqual(completionCallCount, 1)
  1147. assert(on: session.rootQueue) {
  1148. XCTAssertTrue(session.requestTaskMap.isEmpty)
  1149. XCTAssertTrue(session.activeRequests.isEmpty)
  1150. }
  1151. }
  1152. // MARK: Tests - Request State
  1153. func testThatSessionSetsRequestStateWhenStartRequestsImmediatelyIsTrue() {
  1154. // Given
  1155. let session = Session()
  1156. let expectation = self.expectation(description: "request should complete")
  1157. var response: DataResponse<Any, AFError>?
  1158. // When
  1159. let request = session.request("https://httpbin.org/get").responseJSON { resp in
  1160. response = resp
  1161. expectation.fulfill()
  1162. }
  1163. waitForExpectations(timeout: timeout, handler: nil)
  1164. // Then
  1165. XCTAssertEqual(request.state, .finished)
  1166. XCTAssertEqual(response?.result.isSuccess, true)
  1167. }
  1168. }
  1169. // MARK: -
  1170. final class SessionCancellationTestCase: BaseTestCase {
  1171. func testThatAutomaticallyResumedRequestsCanBeMassCancelled() {
  1172. // Given
  1173. let count = 100
  1174. let completion = expectation(description: "all requests should finish")
  1175. completion.expectedFulfillmentCount = count
  1176. let createdTasks = expectation(description: "all tasks created")
  1177. createdTasks.expectedFulfillmentCount = count
  1178. let gatheredMetrics = expectation(description: "metrics gathered for all tasks")
  1179. gatheredMetrics.expectedFulfillmentCount = count
  1180. let cancellation = expectation(description: "cancel all requests should be called")
  1181. let monitor = ClosureEventMonitor()
  1182. monitor.requestDidCreateTask = { _, _ in createdTasks.fulfill() }
  1183. monitor.requestDidGatherMetrics = { _, _ in gatheredMetrics.fulfill() }
  1184. let session = Session(eventMonitors: [monitor])
  1185. let request = URLRequest.makeHTTPBinRequest(path: "delay/1")
  1186. var requests: [DataRequest] = []
  1187. var responses: [DataResponse<Data?, AFError>] = []
  1188. // When
  1189. requests = (0..<count).map { _ in session.request(request) }
  1190. wait(for: [createdTasks], timeout: timeout)
  1191. requests.forEach { request in
  1192. request.response { response in
  1193. responses.append(response)
  1194. completion.fulfill()
  1195. }
  1196. }
  1197. session.cancelAllRequests {
  1198. cancellation.fulfill()
  1199. }
  1200. wait(for: [gatheredMetrics, cancellation, completion], timeout: timeout)
  1201. // Then
  1202. XCTAssertTrue(responses.allSatisfy { $0.error?.isExplicitlyCancelledError == true })
  1203. assert(on: session.rootQueue) {
  1204. XCTAssertTrue(session.requestTaskMap.isEmpty, "requestTaskMap should be empty but has \(session.requestTaskMap.count) items")
  1205. XCTAssertTrue(session.activeRequests.isEmpty, "activeRequests should be empty but has \(session.activeRequests.count) items")
  1206. }
  1207. }
  1208. func testThatManuallyResumedRequestsCanBeMassCancelled() {
  1209. // Given
  1210. let count = 100
  1211. let completion = expectation(description: "all requests should finish")
  1212. completion.expectedFulfillmentCount = count
  1213. let createdTasks = expectation(description: "all tasks created")
  1214. createdTasks.expectedFulfillmentCount = count
  1215. let gatheredMetrics = expectation(description: "metrics gathered for all tasks")
  1216. gatheredMetrics.expectedFulfillmentCount = count
  1217. let cancellation = expectation(description: "cancel all requests should be called")
  1218. let monitor = ClosureEventMonitor()
  1219. monitor.requestDidCreateTask = { _, _ in createdTasks.fulfill() }
  1220. monitor.requestDidGatherMetrics = { _, _ in gatheredMetrics.fulfill() }
  1221. let session = Session(startRequestsImmediately: false, eventMonitors: [monitor])
  1222. let request = URLRequest.makeHTTPBinRequest(path: "delay/1")
  1223. var responses: [DataResponse<Data?, AFError>] = []
  1224. // When
  1225. for _ in 0..<count {
  1226. session.request(request).response { response in
  1227. responses.append(response)
  1228. completion.fulfill()
  1229. }
  1230. }
  1231. wait(for: [createdTasks], timeout: timeout)
  1232. session.cancelAllRequests {
  1233. cancellation.fulfill()
  1234. }
  1235. wait(for: [gatheredMetrics, cancellation, completion], timeout: timeout)
  1236. // Then
  1237. XCTAssertTrue(responses.allSatisfy { $0.error?.isExplicitlyCancelledError == true })
  1238. assert(on: session.rootQueue) {
  1239. XCTAssertTrue(session.requestTaskMap.isEmpty, "requestTaskMap should be empty but has \(session.requestTaskMap.count) items")
  1240. XCTAssertTrue(session.activeRequests.isEmpty, "activeRequests should be empty but has \(session.activeRequests.count) items")
  1241. }
  1242. }
  1243. func testThatRetriedRequestsCanBeMassCancelled() {
  1244. // Given
  1245. final class OnceRetrier: RequestInterceptor {
  1246. private var hasRetried = false
  1247. func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
  1248. completion(hasRetried ? .doNotRetry : .retry)
  1249. hasRetried = true
  1250. }
  1251. }
  1252. let monitor = ClosureEventMonitor()
  1253. let session = Session(interceptor: OnceRetrier(), eventMonitors: [monitor])
  1254. let request = URLRequest.makeHTTPBinRequest(path: "status/401")
  1255. let completion = expectation(description: "all requests should finish")
  1256. let cancellation = expectation(description: "cancel all requests should be called")
  1257. let createTask = expectation(description: "should create task twice")
  1258. createTask.expectedFulfillmentCount = 2
  1259. var tasksCreated = 0
  1260. monitor.requestDidCreateTask = { _, _ in
  1261. tasksCreated += 1
  1262. createTask.fulfill()
  1263. // Cancel after the second task is created to ensure proper lifetime events.
  1264. if tasksCreated == 2 {
  1265. session.cancelAllRequests {
  1266. cancellation.fulfill()
  1267. }
  1268. }
  1269. }
  1270. var received: DataResponse<Data?, AFError>?
  1271. // When
  1272. session.request(request).validate().response { response in
  1273. received = response
  1274. completion.fulfill()
  1275. }
  1276. waitForExpectations(timeout: timeout)
  1277. // Then
  1278. XCTAssertEqual(received?.error?.isExplicitlyCancelledError, true)
  1279. assert(on: session.rootQueue) {
  1280. XCTAssertTrue(session.requestTaskMap.isEmpty, "requestTaskMap should be empty but has \(session.requestTaskMap.count) items")
  1281. XCTAssertTrue(session.activeRequests.isEmpty, "activeRequests should be empty but has \(session.activeRequests.count) items")
  1282. }
  1283. }
  1284. func testThatGETRequestsWithBodyDataAreConsideredInvalid() {
  1285. // Given
  1286. let session = Session()
  1287. var request = URLRequest.makeHTTPBinRequest()
  1288. request.httpBody = Data("invalid".utf8)
  1289. let expect = expectation(description: "request should complete")
  1290. var response: DataResponse<HTTPBinResponse, AFError>?
  1291. // When
  1292. session.request(request).responseDecodable(of: HTTPBinResponse.self) { resp in
  1293. response = resp
  1294. expect.fulfill()
  1295. }
  1296. waitForExpectations(timeout: timeout)
  1297. // Then
  1298. XCTAssertEqual(response?.result.isFailure, true)
  1299. XCTAssertEqual(response?.error?.isBodyDataInGETRequest, true)
  1300. }
  1301. func testThatAdaptedGETRequestsWithBodyDataAreConsideredInvalid() {
  1302. // Given
  1303. struct InvalidAdapter: RequestInterceptor {
  1304. func adapt(_ urlRequest: URLRequest,
  1305. for session: Session,
  1306. completion: @escaping (Result<URLRequest, Error>) -> Void) {
  1307. var request = urlRequest
  1308. request.httpBody = Data("invalid".utf8)
  1309. completion(.success(request))
  1310. }
  1311. }
  1312. let session = Session(interceptor: InvalidAdapter())
  1313. let request = URLRequest.makeHTTPBinRequest()
  1314. let expect = expectation(description: "request should complete")
  1315. var response: DataResponse<HTTPBinResponse, AFError>?
  1316. // When
  1317. session.request(request).responseDecodable(of: HTTPBinResponse.self) { resp in
  1318. response = resp
  1319. expect.fulfill()
  1320. }
  1321. waitForExpectations(timeout: timeout)
  1322. // Then
  1323. XCTAssertEqual(response?.result.isFailure, true)
  1324. XCTAssertEqual(response?.error?.isRequestAdaptationError, true)
  1325. XCTAssertEqual(response?.error?.underlyingError?.asAFError?.isBodyDataInGETRequest, true)
  1326. }
  1327. }
  1328. // MARK: -
  1329. final class SessionConfigurationHeadersTestCase: BaseTestCase {
  1330. enum ConfigurationType {
  1331. case `default`, ephemeral, background
  1332. }
  1333. func testThatDefaultConfigurationHeadersAreSentWithRequest() {
  1334. // Given, When, Then
  1335. executeAuthorizationHeaderTest(for: .default)
  1336. }
  1337. func testThatEphemeralConfigurationHeadersAreSentWithRequest() {
  1338. // Given, When, Then
  1339. executeAuthorizationHeaderTest(for: .ephemeral)
  1340. }
  1341. #if os(macOS)
  1342. func disabled_testThatBackgroundConfigurationHeadersAreSentWithRequest() {
  1343. // Given, When, Then
  1344. executeAuthorizationHeaderTest(for: .background)
  1345. }
  1346. #endif
  1347. private func executeAuthorizationHeaderTest(for type: ConfigurationType) {
  1348. // Given
  1349. let session: Session = {
  1350. let configuration: URLSessionConfiguration = {
  1351. let configuration: URLSessionConfiguration
  1352. switch type {
  1353. case .default:
  1354. configuration = .default
  1355. case .ephemeral:
  1356. configuration = .ephemeral
  1357. case .background:
  1358. let identifier = "org.alamofire.test.manager-configuration-tests"
  1359. configuration = .background(withIdentifier: identifier)
  1360. }
  1361. var headers = HTTPHeaders.default
  1362. headers["Authorization"] = "Bearer 123456"
  1363. configuration.headers = headers
  1364. return configuration
  1365. }()
  1366. return Session(configuration: configuration)
  1367. }()
  1368. let expectation = self.expectation(description: "request should complete successfully")
  1369. var response: DataResponse<HTTPBinResponse, AFError>?
  1370. // When
  1371. session.request("https://httpbin.org/get")
  1372. .responseDecodable(of: HTTPBinResponse.self) { closureResponse in
  1373. response = closureResponse
  1374. expectation.fulfill()
  1375. }
  1376. waitForExpectations(timeout: timeout, handler: nil)
  1377. // Then
  1378. XCTAssertNotNil(response?.request, "request should not be nil")
  1379. XCTAssertNotNil(response?.response, "response should not be nil")
  1380. XCTAssertNotNil(response?.data, "data should not be nil")
  1381. XCTAssertEqual(response?.result.isSuccess, true)
  1382. XCTAssertEqual(response?.value?.headers["Authorization"], "Bearer 123456", "Authorization header should match")
  1383. }
  1384. }