mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			109 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Swift
		
	
		
		
			
		
	
	
			109 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Swift
		
	
| 
								 
											5 years ago
										 
									 | 
							
								import Foundation
							 | 
						||
| 
								 | 
							
								import PromiseKit
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								public enum HTTP {
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    private static let sslURLSession = URLSession(configuration: .ephemeral)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    private static let defaultURLSession = URLSession(configuration: .ephemeral, delegate: defaultURLSessionDelegate, delegateQueue: nil)
							 | 
						||
| 
								 | 
							
								    private static let defaultURLSessionDelegate = DefaultURLSessionDelegateImplementation()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // MARK: Settings
							 | 
						||
| 
								 | 
							
								    public static let timeout: TimeInterval = 10
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // MARK: URL Session Delegate Implementation
							 | 
						||
| 
								 | 
							
								    private final class DefaultURLSessionDelegateImplementation : NSObject, URLSessionDelegate {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
							 | 
						||
| 
								 | 
							
								            // Snode to snode communication uses self-signed certificates but clients can safely ignore this
							 | 
						||
| 
								 | 
							
								            completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // MARK: Verb
							 | 
						||
| 
								 | 
							
								    public enum Verb : String {
							 | 
						||
| 
								 | 
							
								        case get = "GET"
							 | 
						||
| 
								 | 
							
								        case put = "PUT"
							 | 
						||
| 
								 | 
							
								        case post = "POST"
							 | 
						||
| 
								 | 
							
								        case delete = "DELETE"
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // MARK: Error
							 | 
						||
| 
								 | 
							
								    public enum Error : LocalizedError {
							 | 
						||
| 
								 | 
							
								        case generic
							 | 
						||
| 
								 | 
							
								        case httpRequestFailed(statusCode: UInt, json: JSON?)
							 | 
						||
| 
								 | 
							
								        case invalidJSON
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        public var errorDescription: String? {
							 | 
						||
| 
								 | 
							
								            switch self {
							 | 
						||
| 
								 | 
							
								            case .generic: return "An error occurred."
							 | 
						||
| 
								 | 
							
								            case .httpRequestFailed(let statusCode, _): return "HTTP request failed with status code: \(statusCode)."
							 | 
						||
| 
								 | 
							
								            case .invalidJSON: return "Invalid JSON."
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // MARK: Main
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    public static func execute(_ verb: Verb, _ url: String, timeout: TimeInterval = HTTP.timeout, useSSLURLSession: Bool = false) -> Promise<JSON> {
							 | 
						||
| 
								 | 
							
								        return execute(verb, url, body: nil, timeout: timeout, useSSLURLSession: useSSLURLSession)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    public static func execute(_ verb: Verb, _ url: String, parameters: JSON?, timeout: TimeInterval = HTTP.timeout, useSSLURLSession: Bool = false) -> Promise<JSON> {
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								        if let parameters = parameters {
							 | 
						||
| 
								 | 
							
								            do {
							 | 
						||
| 
								 | 
							
								                guard JSONSerialization.isValidJSONObject(parameters) else { return Promise(error: Error.invalidJSON) }
							 | 
						||
| 
								 | 
							
								                let body = try JSONSerialization.data(withJSONObject: parameters, options: [ .fragmentsAllowed ])
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								                return execute(verb, url, body: body, timeout: timeout, useSSLURLSession: useSSLURLSession)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								            } catch (let error) {
							 | 
						||
| 
								 | 
							
								                return Promise(error: error)
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								            return execute(verb, url, body: nil, timeout: timeout, useSSLURLSession: useSSLURLSession)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    public static func execute(_ verb: Verb, _ url: String, body: Data?, timeout: TimeInterval = HTTP.timeout, useSSLURLSession: Bool = false) -> Promise<JSON> {
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								        var request = URLRequest(url: URL(string: url)!)
							 | 
						||
| 
								 | 
							
								        request.httpMethod = verb.rawValue
							 | 
						||
| 
								 | 
							
								        request.httpBody = body
							 | 
						||
| 
								 | 
							
								        request.timeoutInterval = timeout
							 | 
						||
| 
								 | 
							
								        request.allHTTPHeaderFields?.removeValue(forKey: "User-Agent")
							 | 
						||
| 
								 | 
							
								        let (promise, seal) = Promise<JSON>.pending()
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								        let urlSession = useSSLURLSession ? sslURLSession : defaultURLSession
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								        let task = urlSession.dataTask(with: request) { data, response, error in
							 | 
						||
| 
								 | 
							
								            guard let data = data, let response = response as? HTTPURLResponse else {
							 | 
						||
| 
								 | 
							
								                if let error = error {
							 | 
						||
| 
								 | 
							
								                    SNLog("\(verb.rawValue) request to \(url) failed due to error: \(error).")
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    SNLog("\(verb.rawValue) request to \(url) failed.")
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                // Override the actual error so that we can correctly catch failed requests in sendOnionRequest(invoking:on:with:)
							 | 
						||
| 
								 | 
							
								                return seal.reject(Error.httpRequestFailed(statusCode: 0, json: nil))
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if let error = error {
							 | 
						||
| 
								 | 
							
								                SNLog("\(verb.rawValue) request to \(url) failed due to error: \(error).")
							 | 
						||
| 
								 | 
							
								                // Override the actual error so that we can correctly catch failed requests in sendOnionRequest(invoking:on:with:)
							 | 
						||
| 
								 | 
							
								                return seal.reject(Error.httpRequestFailed(statusCode: 0, json: nil))
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            let statusCode = UInt(response.statusCode)
							 | 
						||
| 
								 | 
							
								            var json: JSON? = nil
							 | 
						||
| 
								 | 
							
								            if let j = try? JSONSerialization.jsonObject(with: data, options: [ .fragmentsAllowed ]) as? JSON {
							 | 
						||
| 
								 | 
							
								                json = j
							 | 
						||
| 
								 | 
							
								            } else if let result = String(data: data, encoding: .utf8) {
							 | 
						||
| 
								 | 
							
								                json = [ "result" : result ]
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            guard 200...299 ~= statusCode else {
							 | 
						||
| 
								 | 
							
								                let jsonDescription = json?.prettifiedDescription ?? "no debugging info provided"
							 | 
						||
| 
								 | 
							
								                SNLog("\(verb.rawValue) request to \(url) failed with status code: \(statusCode) (\(jsonDescription)).")
							 | 
						||
| 
								 | 
							
								                return seal.reject(Error.httpRequestFailed(statusCode: statusCode, json: json))
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if let json = json {
							 | 
						||
| 
								 | 
							
								                seal.fulfill(json)
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                SNLog("Couldn't parse JSON returned by \(verb.rawValue) request to \(url).")
							 | 
						||
| 
								 | 
							
								                return seal.reject(Error.invalidJSON)
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        task.resume()
							 | 
						||
| 
								 | 
							
								        return promise
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |