123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899 |
- import Foundation
- open class SessionManager {
-
-
-
-
-
-
-
-
- public enum MultipartFormDataEncodingResult {
- case success(request: UploadRequest, streamingFromDisk: Bool, streamFileURL: URL?)
- case failure(Error)
- }
-
-
-
- public static let `default`: SessionManager = {
- let configuration = URLSessionConfiguration.default
- configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
- return SessionManager(configuration: configuration)
- }()
-
- public static let defaultHTTPHeaders: HTTPHeaders = {
-
- let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"
-
- let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
- let quality = 1.0 - (Double(index) * 0.1)
- return "\(languageCode);q=\(quality)"
- }.joined(separator: ", ")
-
-
- let userAgent: String = {
- if let info = Bundle.main.infoDictionary {
- let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
- let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
- let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
- let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
- let osNameVersion: String = {
- let version = ProcessInfo.processInfo.operatingSystemVersion
- let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
- let osName: String = {
- #if os(iOS)
- return "iOS"
- #elseif os(watchOS)
- return "watchOS"
- #elseif os(tvOS)
- return "tvOS"
- #elseif os(macOS)
- return "OS X"
- #elseif os(Linux)
- return "Linux"
- #else
- return "Unknown"
- #endif
- }()
- return "\(osName) \(versionString)"
- }()
- let alamofireVersion: String = {
- guard
- let afInfo = Bundle(for: SessionManager.self).infoDictionary,
- let build = afInfo["CFBundleShortVersionString"]
- else { return "Unknown" }
- return "Alamofire/\(build)"
- }()
- return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"
- }
- return "Alamofire"
- }()
- return [
- "Accept-Encoding": acceptEncoding,
- "Accept-Language": acceptLanguage,
- "User-Agent": userAgent
- ]
- }()
-
- public static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000
-
- public let session: URLSession
-
- public let delegate: SessionDelegate
-
- open var startRequestsImmediately: Bool = true
-
- open var adapter: RequestAdapter?
-
- open var retrier: RequestRetrier? {
- get { return delegate.retrier }
- set { delegate.retrier = newValue }
- }
-
-
-
-
-
-
-
-
-
- open var backgroundCompletionHandler: (() -> Void)?
- let queue = DispatchQueue(label: "org.alamofire.session-manager." + UUID().uuidString)
-
-
-
-
-
-
-
-
-
-
-
- public init(
- configuration: URLSessionConfiguration = URLSessionConfiguration.default,
- delegate: SessionDelegate = SessionDelegate(),
- serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
- {
- self.delegate = delegate
- self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
- commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
- }
-
-
-
-
-
-
-
-
- public init?(
- session: URLSession,
- delegate: SessionDelegate,
- serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
- {
- guard delegate === session.delegate else { return nil }
- self.delegate = delegate
- self.session = session
- commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
- }
- private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
- session.serverTrustPolicyManager = serverTrustPolicyManager
- delegate.sessionManager = self
- delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
- guard let strongSelf = self else { return }
- DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
- }
- }
- deinit {
- session.invalidateAndCancel()
- }
-
-
-
-
-
-
-
-
-
-
-
- @discardableResult
- open func request(
- _ url: URLConvertible,
- method: HTTPMethod = .get,
- parameters: Parameters? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: HTTPHeaders? = nil)
- -> DataRequest
- {
- var originalRequest: URLRequest?
- do {
- originalRequest = try URLRequest(url: url, method: method, headers: headers)
- let encodedURLRequest = try encoding.encode(originalRequest!, with: parameters)
- return request(encodedURLRequest)
- } catch {
- return request(originalRequest, failedWith: error)
- }
- }
-
-
-
-
-
-
-
- @discardableResult
- open func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
- var originalRequest: URLRequest?
- do {
- originalRequest = try urlRequest.asURLRequest()
- let originalTask = DataRequest.Requestable(urlRequest: originalRequest!)
- let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
- let request = DataRequest(session: session, requestTask: .data(originalTask, task))
- delegate[task] = request
- if startRequestsImmediately { request.resume() }
- return request
- } catch {
- return request(originalRequest, failedWith: error)
- }
- }
-
- private func request(_ urlRequest: URLRequest?, failedWith error: Error) -> DataRequest {
- var requestTask: Request.RequestTask = .data(nil, nil)
- if let urlRequest = urlRequest {
- let originalTask = DataRequest.Requestable(urlRequest: urlRequest)
- requestTask = .data(originalTask, nil)
- }
- let underlyingError = error.underlyingAdaptError ?? error
- let request = DataRequest(session: session, requestTask: requestTask, error: underlyingError)
- if let retrier = retrier, error is AdaptError {
- allowRetrier(retrier, toRetry: request, with: underlyingError)
- } else {
- if startRequestsImmediately { request.resume() }
- }
- return request
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @discardableResult
- open func download(
- _ url: URLConvertible,
- method: HTTPMethod = .get,
- parameters: Parameters? = nil,
- encoding: ParameterEncoding = URLEncoding.default,
- headers: HTTPHeaders? = nil,
- to destination: DownloadRequest.DownloadFileDestination? = nil)
- -> DownloadRequest
- {
- do {
- let urlRequest = try URLRequest(url: url, method: method, headers: headers)
- let encodedURLRequest = try encoding.encode(urlRequest, with: parameters)
- return download(encodedURLRequest, to: destination)
- } catch {
- return download(nil, to: destination, failedWith: error)
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
- @discardableResult
- open func download(
- _ urlRequest: URLRequestConvertible,
- to destination: DownloadRequest.DownloadFileDestination? = nil)
- -> DownloadRequest
- {
- do {
- let urlRequest = try urlRequest.asURLRequest()
- return download(.request(urlRequest), to: destination)
- } catch {
- return download(nil, to: destination, failedWith: error)
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @discardableResult
- open func download(
- resumingWith resumeData: Data,
- to destination: DownloadRequest.DownloadFileDestination? = nil)
- -> DownloadRequest
- {
- return download(.resumeData(resumeData), to: destination)
- }
-
- private func download(
- _ downloadable: DownloadRequest.Downloadable,
- to destination: DownloadRequest.DownloadFileDestination?)
- -> DownloadRequest
- {
- do {
- let task = try downloadable.task(session: session, adapter: adapter, queue: queue)
- let download = DownloadRequest(session: session, requestTask: .download(downloadable, task))
- download.downloadDelegate.destination = destination
- delegate[task] = download
- if startRequestsImmediately { download.resume() }
- return download
- } catch {
- return download(downloadable, to: destination, failedWith: error)
- }
- }
- private func download(
- _ downloadable: DownloadRequest.Downloadable?,
- to destination: DownloadRequest.DownloadFileDestination?,
- failedWith error: Error)
- -> DownloadRequest
- {
- var downloadTask: Request.RequestTask = .download(nil, nil)
- if let downloadable = downloadable {
- downloadTask = .download(downloadable, nil)
- }
- let underlyingError = error.underlyingAdaptError ?? error
- let download = DownloadRequest(session: session, requestTask: downloadTask, error: underlyingError)
- download.downloadDelegate.destination = destination
- if let retrier = retrier, error is AdaptError {
- allowRetrier(retrier, toRetry: download, with: underlyingError)
- } else {
- if startRequestsImmediately { download.resume() }
- }
- return download
- }
-
-
-
-
-
-
-
-
-
-
-
-
- @discardableResult
- open func upload(
- _ fileURL: URL,
- to url: URLConvertible,
- method: HTTPMethod = .post,
- headers: HTTPHeaders? = nil)
- -> UploadRequest
- {
- do {
- let urlRequest = try URLRequest(url: url, method: method, headers: headers)
- return upload(fileURL, with: urlRequest)
- } catch {
- return upload(nil, failedWith: error)
- }
- }
-
-
-
-
-
-
-
-
- @discardableResult
- open func upload(_ fileURL: URL, with urlRequest: URLRequestConvertible) -> UploadRequest {
- do {
- let urlRequest = try urlRequest.asURLRequest()
- return upload(.file(fileURL, urlRequest))
- } catch {
- return upload(nil, failedWith: error)
- }
- }
-
-
-
-
-
-
-
-
-
-
-
- @discardableResult
- open func upload(
- _ data: Data,
- to url: URLConvertible,
- method: HTTPMethod = .post,
- headers: HTTPHeaders? = nil)
- -> UploadRequest
- {
- do {
- let urlRequest = try URLRequest(url: url, method: method, headers: headers)
- return upload(data, with: urlRequest)
- } catch {
- return upload(nil, failedWith: error)
- }
- }
-
-
-
-
-
-
-
-
- @discardableResult
- open func upload(_ data: Data, with urlRequest: URLRequestConvertible) -> UploadRequest {
- do {
- let urlRequest = try urlRequest.asURLRequest()
- return upload(.data(data, urlRequest))
- } catch {
- return upload(nil, failedWith: error)
- }
- }
-
-
-
-
-
-
-
-
-
-
-
- @discardableResult
- open func upload(
- _ stream: InputStream,
- to url: URLConvertible,
- method: HTTPMethod = .post,
- headers: HTTPHeaders? = nil)
- -> UploadRequest
- {
- do {
- let urlRequest = try URLRequest(url: url, method: method, headers: headers)
- return upload(stream, with: urlRequest)
- } catch {
- return upload(nil, failedWith: error)
- }
- }
-
-
-
-
-
-
-
-
- @discardableResult
- open func upload(_ stream: InputStream, with urlRequest: URLRequestConvertible) -> UploadRequest {
- do {
- let urlRequest = try urlRequest.asURLRequest()
- return upload(.stream(stream, urlRequest))
- } catch {
- return upload(nil, failedWith: error)
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- open func upload(
- multipartFormData: @escaping (MultipartFormData) -> Void,
- usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
- to url: URLConvertible,
- method: HTTPMethod = .post,
- headers: HTTPHeaders? = nil,
- queue: DispatchQueue? = nil,
- encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
- {
- do {
- let urlRequest = try URLRequest(url: url, method: method, headers: headers)
- return upload(
- multipartFormData: multipartFormData,
- usingThreshold: encodingMemoryThreshold,
- with: urlRequest,
- queue: queue,
- encodingCompletion: encodingCompletion
- )
- } catch {
- (queue ?? DispatchQueue.main).async { encodingCompletion?(.failure(error)) }
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- open func upload(
- multipartFormData: @escaping (MultipartFormData) -> Void,
- usingThreshold encodingMemoryThreshold: UInt64 = SessionManager.multipartFormDataEncodingMemoryThreshold,
- with urlRequest: URLRequestConvertible,
- queue: DispatchQueue? = nil,
- encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
- {
- DispatchQueue.global(qos: .utility).async {
- let formData = MultipartFormData()
- multipartFormData(formData)
- var tempFileURL: URL?
- do {
- var urlRequestWithContentType = try urlRequest.asURLRequest()
- urlRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type")
- let isBackgroundSession = self.session.configuration.identifier != nil
- if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession {
- let data = try formData.encode()
- let encodingResult = MultipartFormDataEncodingResult.success(
- request: self.upload(data, with: urlRequestWithContentType),
- streamingFromDisk: false,
- streamFileURL: nil
- )
- (queue ?? DispatchQueue.main).async { encodingCompletion?(encodingResult) }
- } else {
- let fileManager = FileManager.default
- let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory())
- let directoryURL = tempDirectoryURL.appendingPathComponent("org.alamofire.manager/multipart.form.data")
- let fileName = UUID().uuidString
- let fileURL = directoryURL.appendingPathComponent(fileName)
- tempFileURL = fileURL
- var directoryError: Error?
-
- self.queue.sync {
- do {
- try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
- } catch {
- directoryError = error
- }
- }
- if let directoryError = directoryError { throw directoryError }
- try formData.writeEncodedData(to: fileURL)
- let upload = self.upload(fileURL, with: urlRequestWithContentType)
-
- upload.delegate.queue.addOperation {
- do {
- try FileManager.default.removeItem(at: fileURL)
- } catch {
-
- }
- }
- (queue ?? DispatchQueue.main).async {
- let encodingResult = MultipartFormDataEncodingResult.success(
- request: upload,
- streamingFromDisk: true,
- streamFileURL: fileURL
- )
- encodingCompletion?(encodingResult)
- }
- }
- } catch {
-
- if let tempFileURL = tempFileURL {
- do {
- try FileManager.default.removeItem(at: tempFileURL)
- } catch {
-
- }
- }
- (queue ?? DispatchQueue.main).async { encodingCompletion?(.failure(error)) }
- }
- }
- }
-
- private func upload(_ uploadable: UploadRequest.Uploadable) -> UploadRequest {
- do {
- let task = try uploadable.task(session: session, adapter: adapter, queue: queue)
- let upload = UploadRequest(session: session, requestTask: .upload(uploadable, task))
- if case let .stream(inputStream, _) = uploadable {
- upload.delegate.taskNeedNewBodyStream = { _, _ in inputStream }
- }
- delegate[task] = upload
- if startRequestsImmediately { upload.resume() }
- return upload
- } catch {
- return upload(uploadable, failedWith: error)
- }
- }
- private func upload(_ uploadable: UploadRequest.Uploadable?, failedWith error: Error) -> UploadRequest {
- var uploadTask: Request.RequestTask = .upload(nil, nil)
- if let uploadable = uploadable {
- uploadTask = .upload(uploadable, nil)
- }
- let underlyingError = error.underlyingAdaptError ?? error
- let upload = UploadRequest(session: session, requestTask: uploadTask, error: underlyingError)
- if let retrier = retrier, error is AdaptError {
- allowRetrier(retrier, toRetry: upload, with: underlyingError)
- } else {
- if startRequestsImmediately { upload.resume() }
- }
- return upload
- }
- #if !os(watchOS)
-
-
-
-
-
-
-
-
-
-
- @discardableResult
- @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
- open func stream(withHostName hostName: String, port: Int) -> StreamRequest {
- return stream(.stream(hostName: hostName, port: port))
- }
-
-
-
-
-
-
-
-
- @discardableResult
- @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
- open func stream(with netService: NetService) -> StreamRequest {
- return stream(.netService(netService))
- }
-
- @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
- private func stream(_ streamable: StreamRequest.Streamable) -> StreamRequest {
- do {
- let task = try streamable.task(session: session, adapter: adapter, queue: queue)
- let request = StreamRequest(session: session, requestTask: .stream(streamable, task))
- delegate[task] = request
- if startRequestsImmediately { request.resume() }
- return request
- } catch {
- return stream(failedWith: error)
- }
- }
- @available(iOS 9.0, macOS 10.11, tvOS 9.0, *)
- private func stream(failedWith error: Error) -> StreamRequest {
- let stream = StreamRequest(session: session, requestTask: .stream(nil, nil), error: error)
- if startRequestsImmediately { stream.resume() }
- return stream
- }
- #endif
-
- func retry(_ request: Request) -> Bool {
- guard let originalTask = request.originalTask else { return false }
- do {
- let task = try originalTask.task(session: session, adapter: adapter, queue: queue)
- if let originalTask = request.task {
- delegate[originalTask] = nil
- }
- request.delegate.task = task
- request.retryCount += 1
- request.startTime = CFAbsoluteTimeGetCurrent()
- request.endTime = nil
- task.resume()
- return true
- } catch {
- request.delegate.error = error.underlyingAdaptError ?? error
- return false
- }
- }
- private func allowRetrier(_ retrier: RequestRetrier, toRetry request: Request, with error: Error) {
- DispatchQueue.utility.async { [weak self] in
- guard let strongSelf = self else { return }
- retrier.should(strongSelf, retry: request, with: error) { shouldRetry, timeDelay in
- guard let strongSelf = self else { return }
- guard shouldRetry else {
- if strongSelf.startRequestsImmediately { request.resume() }
- return
- }
- DispatchQueue.utility.after(timeDelay) {
- guard let strongSelf = self else { return }
- let retrySucceeded = strongSelf.retry(request)
- if retrySucceeded, let task = request.task {
- strongSelf.delegate[task] = request
- } else {
- if strongSelf.startRequestsImmediately { request.resume() }
- }
- }
- }
- }
- }
- }
|