2
0

Alamofire.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. //
  2. // Alamofire.swift
  3. //
  4. // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. //
  24. import Foundation
  25. /// Types adopting the `URLStringConvertible` protocol can be used to construct URL strings, which are then used to
  26. /// construct URL requests.
  27. public protocol URLStringConvertible {
  28. /// A URL that conforms to RFC 2396.
  29. ///
  30. /// Methods accepting a `URLStringConvertible` type parameter parse it according to RFCs 1738 and 1808.
  31. ///
  32. /// See https://tools.ietf.org/html/rfc2396
  33. /// See https://tools.ietf.org/html/rfc1738
  34. /// See https://tools.ietf.org/html/rfc1808
  35. var urlString: String { get }
  36. }
  37. extension String: URLStringConvertible {
  38. /// The URL string.
  39. public var urlString: String { return self }
  40. }
  41. extension URL: URLStringConvertible {
  42. /// The URL string.
  43. public var urlString: String { return absoluteString }
  44. }
  45. extension URLComponents: URLStringConvertible {
  46. /// The URL string.
  47. public var urlString: String { return url!.urlString }
  48. }
  49. extension URLRequest: URLStringConvertible {
  50. /// The URL string.
  51. public var urlString: String { return url!.urlString }
  52. }
  53. // MARK: -
  54. /// Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests.
  55. public protocol URLRequestConvertible {
  56. /// The URL request.
  57. var urlRequest: URLRequest { get }
  58. }
  59. extension URLRequest: URLRequestConvertible {
  60. /// The URL request.
  61. public var urlRequest: URLRequest { return self }
  62. }
  63. // MARK: -
  64. extension URLRequest {
  65. /// Creates an instance with the specified `method`, `urlString` and `headers`.
  66. ///
  67. /// - parameter urlString: The URL string.
  68. /// - parameter method: The HTTP method.
  69. /// - parameter headers: The HTTP headers. `nil` by default.
  70. ///
  71. /// - returns: The new `URLRequest` instance.
  72. public init(urlString: URLStringConvertible, method: HTTPMethod, headers: [String: String]? = nil) {
  73. self.init(url: URL(string: urlString.urlString)!)
  74. if let request = urlString as? URLRequest { self = request }
  75. httpMethod = method.rawValue
  76. if let headers = headers {
  77. for (headerField, headerValue) in headers {
  78. setValue(headerValue, forHTTPHeaderField: headerField)
  79. }
  80. }
  81. }
  82. func adapt(using adapter: RequestAdapter?) -> URLRequest {
  83. guard let adapter = adapter else { return self }
  84. return adapter.adapt(self)
  85. }
  86. }
  87. // MARK: - Data Request
  88. /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
  89. /// specified `urlString`, `method`, `parameters`, `encoding` and `headers`.
  90. ///
  91. /// - parameter urlString: The URL string.
  92. /// - parameter method: The HTTP method. `.get` by default.
  93. /// - parameter parameters: The parameters. `nil` by default.
  94. /// - parameter encoding: The parameter encoding. `.url` by default.
  95. /// - parameter headers: The HTTP headers. `nil` by default.
  96. ///
  97. /// - returns: The created `DataRequest`.
  98. @discardableResult
  99. public func request(
  100. _ urlString: URLStringConvertible,
  101. method: HTTPMethod = .get,
  102. parameters: [String: Any]? = nil,
  103. encoding: ParameterEncoding = .url,
  104. headers: [String: String]? = nil)
  105. -> DataRequest
  106. {
  107. return SessionManager.default.request(
  108. urlString,
  109. method: method,
  110. parameters: parameters,
  111. encoding: encoding,
  112. headers: headers
  113. )
  114. }
  115. /// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
  116. /// specified `urlRequest`.
  117. ///
  118. /// - parameter urlRequest: The URL request
  119. ///
  120. /// - returns: The created `DataRequest`.
  121. @discardableResult
  122. public func request(resource urlRequest: URLRequestConvertible) -> DataRequest {
  123. return SessionManager.default.request(resource: urlRequest)
  124. }
  125. // MARK: - Download Request
  126. // MARK: URL Request
  127. /// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
  128. /// specified `urlString`, `method`, `parameters`, `encoding`, `headers` and save them to the `destination`.
  129. ///
  130. /// If `destination` is not specified, the contents will remain in the temporary location determined by the
  131. /// underlying URL session.
  132. ///
  133. /// - parameter urlString: The URL string.
  134. /// - parameter method: The HTTP method. `.get` by default.
  135. /// - parameter parameters: The parameters. `nil` by default.
  136. /// - parameter encoding: The parameter encoding. `.url` by default.
  137. /// - parameter headers: The HTTP headers. `nil` by default.
  138. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
  139. ///
  140. /// - returns: The created `DownloadRequest`.
  141. @discardableResult
  142. public func download(
  143. _ urlString: URLStringConvertible,
  144. method: HTTPMethod = .get,
  145. parameters: [String: Any]? = nil,
  146. encoding: ParameterEncoding = .url,
  147. headers: [String: String]? = nil,
  148. to destination: DownloadRequest.DownloadFileDestination? = nil)
  149. -> DownloadRequest
  150. {
  151. return SessionManager.default.download(
  152. urlString,
  153. method: method,
  154. parameters: parameters,
  155. encoding: encoding,
  156. headers: headers,
  157. to: destination
  158. )
  159. }
  160. /// Creates a `DownloadRequest` using the default `SessionManager` to retrieve the contents of a URL based on the
  161. /// specified `urlRequest` and save them to the `destination`.
  162. ///
  163. /// If `destination` is not specified, the contents will remain in the temporary location determined by the
  164. /// underlying URL session.
  165. ///
  166. /// - parameter urlRequest: The URL request.
  167. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
  168. ///
  169. /// - returns: The created `DownloadRequest`.
  170. @discardableResult
  171. public func download(
  172. resource urlRequest: URLRequestConvertible,
  173. to destination: DownloadRequest.DownloadFileDestination? = nil)
  174. -> DownloadRequest
  175. {
  176. return SessionManager.default.download(resource: urlRequest, to: destination)
  177. }
  178. // MARK: Resume Data
  179. /// Creates a `DownloadRequest` using the default `SessionManager` from the `resumeData` produced from a
  180. /// previous request cancellation to retrieve the contents of the original request and save them to the `destination`.
  181. ///
  182. /// If `destination` is not specified, the contents will remain in the temporary location determined by the
  183. /// underlying URL session.
  184. ///
  185. /// - parameter resumeData: The resume data. This is an opaque data blob produced by `URLSessionDownloadTask`
  186. /// when a task is cancelled. See `URLSession -downloadTask(withResumeData:)` for additional
  187. /// information.
  188. /// - parameter destination: The closure used to determine the destination of the downloaded file. `nil` by default.
  189. ///
  190. /// - returns: The created `DownloadRequest`.
  191. @discardableResult
  192. public func download(
  193. resourceWithin resumeData: Data,
  194. to destination: DownloadRequest.DownloadFileDestination? = nil)
  195. -> DownloadRequest
  196. {
  197. return SessionManager.default.download(resourceWithin: resumeData, to: destination)
  198. }
  199. // MARK: - Upload Request
  200. // MARK: File
  201. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `method`, `urlString`
  202. /// and `headers` for uploading the `file`.
  203. ///
  204. /// - parameter file: The file to upload.
  205. /// - parameter urlString: The URL string.
  206. /// - parameter method: The HTTP method. `.post` by default.
  207. /// - parameter headers: The HTTP headers. `nil` by default.
  208. ///
  209. /// - returns: The created `UploadRequest`.
  210. @discardableResult
  211. public func upload(
  212. _ fileURL: URL,
  213. to urlString: URLStringConvertible,
  214. method: HTTPMethod = .post,
  215. headers: [String: String]? = nil)
  216. -> UploadRequest
  217. {
  218. return SessionManager.default.upload(fileURL, to: urlString, method: method, headers: headers)
  219. }
  220. /// Creates a `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
  221. /// uploading the `file`.
  222. ///
  223. /// - parameter file: The file to upload.
  224. /// - parameter urlRequest: The URL request.
  225. ///
  226. /// - returns: The created `UploadRequest`.
  227. @discardableResult
  228. public func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest {
  229. return SessionManager.default.upload(fileURL, with: urlRequest)
  230. }
  231. // MARK: Data
  232. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `method`, `urlString`
  233. /// and `headers` for uploading the `data`.
  234. ///
  235. /// - parameter data: The data to upload.
  236. /// - parameter urlString: The URL string.
  237. /// - parameter method: The HTTP method. `.post` by default.
  238. /// - parameter headers: The HTTP headers. `nil` by default.
  239. ///
  240. /// - returns: The created `UploadRequest`.
  241. @discardableResult
  242. public func upload(
  243. _ data: Data,
  244. to urlString: URLStringConvertible,
  245. method: HTTPMethod = .post,
  246. headers: [String: String]? = nil)
  247. -> UploadRequest
  248. {
  249. return SessionManager.default.upload(data, to: urlString, method: method, headers: headers)
  250. }
  251. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
  252. /// uploading the `data`.
  253. ///
  254. /// - parameter data: The data to upload.
  255. /// - parameter urlRequest: The URL request.
  256. ///
  257. /// - returns: The created `UploadRequest`.
  258. @discardableResult
  259. public func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
  260. return SessionManager.default.upload(data, with: urlRequest)
  261. }
  262. // MARK: InputStream
  263. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `method`, `urlString`
  264. /// and `headers` for uploading the `stream`.
  265. ///
  266. /// - parameter stream: The stream to upload.
  267. /// - parameter urlString: The URL string.
  268. /// - parameter method: The HTTP method. `.post` by default.
  269. /// - parameter headers: The HTTP headers. `nil` by default.
  270. ///
  271. /// - returns: The created `UploadRequest`.
  272. @discardableResult
  273. public func upload(
  274. _ stream: InputStream,
  275. to urlString: URLStringConvertible,
  276. method: HTTPMethod = .post,
  277. headers: [String: String]? = nil)
  278. -> UploadRequest
  279. {
  280. return SessionManager.default.upload(stream, to: urlString, method: method, headers: headers)
  281. }
  282. /// Creates an `UploadRequest` using the default `SessionManager` from the specified `urlRequest` for
  283. /// uploading the `stream`.
  284. ///
  285. /// - parameter urlRequest: The URL request.
  286. /// - parameter stream: The stream to upload.
  287. ///
  288. /// - returns: The created `UploadRequest`.
  289. @discardableResult
  290. public func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest {
  291. return SessionManager.default.upload(stream, with: urlRequest)
  292. }
  293. // MARK: MultipartFormData
  294. /// Encodes `multipartFormData` using `encodingMemoryThreshold` with the default `SessionManager` and calls
  295. /// `encodingCompletion` with new `UploadRequest` using the `method`, `urlString` and `headers`.
  296. ///
  297. /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
  298. /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
  299. /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
  300. /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
  301. /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
  302. /// used for larger payloads such as video content.
  303. ///
  304. /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
  305. /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
  306. /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
  307. /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
  308. /// technique was used.
  309. ///
  310. /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
  311. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
  312. /// `multipartFormDataEncodingMemoryThreshold` by default.
  313. /// - parameter urlString: The URL string.
  314. /// - parameter method: The HTTP method. `.post` by default.
  315. /// - parameter headers: The HTTP headers. `nil` by default.
  316. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
  317. public func upload(
  318. multipartFormData: @escaping (MultipartFormData) -> Void,
  319. usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
  320. to urlString: URLStringConvertible,
  321. method: HTTPMethod = .post,
  322. headers: [String: String]? = nil,
  323. encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
  324. {
  325. return SessionManager.default.upload(
  326. multipartFormData: multipartFormData,
  327. usingThreshold: encodingMemoryThreshold,
  328. to: urlString,
  329. method: method,
  330. headers: headers,
  331. encodingCompletion: encodingCompletion
  332. )
  333. }
  334. /// Encodes `multipartFormData` using `encodingMemoryThreshold` and the default `SessionManager` and
  335. /// calls `encodingCompletion` with new `UploadRequest` using the `urlRequest`.
  336. ///
  337. /// It is important to understand the memory implications of uploading `MultipartFormData`. If the cummulative
  338. /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
  339. /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
  340. /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
  341. /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
  342. /// used for larger payloads such as video content.
  343. ///
  344. /// The `encodingMemoryThreshold` parameter allows Alamofire to automatically determine whether to encode in-memory
  345. /// or stream from disk. If the content length of the `MultipartFormData` is below the `encodingMemoryThreshold`,
  346. /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
  347. /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
  348. /// technique was used.
  349. ///
  350. /// - parameter multipartFormData: The closure used to append body parts to the `MultipartFormData`.
  351. /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
  352. /// `multipartFormDataEncodingMemoryThreshold` by default.
  353. /// - parameter urlRequest: The URL request.
  354. /// - parameter encodingCompletion: The closure called when the `MultipartFormData` encoding is complete.
  355. public func upload(
  356. multipartFormData: @escaping (MultipartFormData) -> Void,
  357. usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
  358. with urlRequest: URLRequestConvertible,
  359. encodingCompletion: ((SessionManager.MultipartFormDataEncodingResult) -> Void)?)
  360. {
  361. return SessionManager.default.upload(
  362. multipartFormData: multipartFormData,
  363. usingThreshold: encodingMemoryThreshold,
  364. with: urlRequest,
  365. encodingCompletion: encodingCompletion
  366. )
  367. }
  368. #if !os(watchOS)
  369. // MARK: - Stream Request
  370. // MARK: Hostname and Port
  371. /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `hostname`
  372. /// and `port`.
  373. ///
  374. /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  375. ///
  376. /// - parameter hostName: The hostname of the server to connect to.
  377. /// - parameter port: The port of the server to connect to.
  378. ///
  379. /// - returns: The created `StreamRequest`.
  380. @discardableResult
  381. public func stream(withHostName hostName: String, port: Int) -> StreamRequest {
  382. return SessionManager.default.stream(withHostName: hostName, port: port)
  383. }
  384. // MARK: NetService
  385. /// Creates a `StreamRequest` using the default `SessionManager` for bidirectional streaming with the `netService`.
  386. ///
  387. /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
  388. ///
  389. /// - parameter netService: The net service used to identify the endpoint.
  390. ///
  391. /// - returns: The created `StreamRequest`.
  392. @discardableResult
  393. public func stream(with netService: NetService) -> StreamRequest {
  394. return SessionManager.default.stream(with: netService)
  395. }
  396. #endif