<commit_before>//
//  HttpClient.swift
//  Pods
//
//  Created by <NAME> on 05/06/17.
//
//

import UIKit

public typealias HttpCompletionHandler = (_ result: Result<Data>) -> Void

public class HttpClient: NSObject {
    
    // MARK: Variables
    
    static public var builder   : HttpClient.Builder {
        return HttpClient.Builder()
    }
    
    //Http Properties
    
    fileprivate var contentType : String!
    fileprivate var timeout     : TimeInterval!
    fileprivate var encoding    : String.Encoding!
    
    fileprivate var header      : [String: String]!
    fileprivate var parameters  : [String: Any]!
    
    //Basic Authorization
    
    fileprivate var username    : String?
    fileprivate var password    : String?
    
    //Certificates
    
    fileprivate var certificateMode     : CertificateMode = .none
    
    fileprivate var certificate         : Data?
    fileprivate var certificatePassword : String?
    
    fileprivate var trustAllSSL         : Bool = false
    
    fileprivate var hostDomain          : String?
    
    //Request
    
    fileprivate var url : String?
    
    //Response
    
    fileprivate var completionHandler   : HttpCompletionHandler?
    
    // MARK: Inits
    
    fileprivate override init() {
        super.init()
        
        contentType = "application/json"
        timeout = 60
        encoding = .utf8
        
        header = [:]
        parameters = [:]
    }
    
    // MARK: Conversions
    
    private func convertBasicAuthToBase64(username: String, password: String) throws -> String {
        let credentials = "\(username):\(password)"
        
        guard let data = credentials.data(using: encoding) else {
            throw MochaError.descriptive(message: "Error formatting the basic authentication provided.")
        }
        
        let base64Credential = data.base64EncodedString(options: .lineLength64Characters)
        let authValue = "Basic \(base64Credential)"
        return authValue
    }
    
    private func string(fromDictionary dictionary: [String: Any]) -> String {
        var resource = ""
        
        for (key, value) in dictionary {
            resource = resource.isEmpty ? "" : resource + "&"
            resource += key + "=" + "\(value)"
        }
        
        return resource
    }
    
    // MARK: Requests
    
    public func get() {
        send(httpMethod: "get")
    }
    
    public func delete() {
        send(httpMethod: "delete")
    }
    
    public func post() {
        send(httpMethod: "post")
    }
    
    public func update() {
        send(httpMethod: "update")
    }
    
    private func handleGenericError(with message: String) {
        completionHandler?(.failure(.descriptive(message: message)))
    }
    
    private func send(httpMethod: String) {
        
        guard let url = self.url else {
            handleGenericError(with: "URL cannot be `nil`")
            return
        }
        
        guard let nsurl = URL(string: url) else {
            handleGenericError(with: "Invalid URL.")
            return
        }
        
        var request = URLRequest(url: nsurl, cachePolicy: .useProtocolCachePolicy)
        request.httpMethod = httpMethod
        
        if let username = self.username, username.isNotEmpty, let password = <PASSWORD>, <PASSWORD> {
            do {
                let encodedBasicAuth = try convertBasicAuthToBase64(username: username, password: password)
                request.setValue(encodedBasicAuth, forHTTPHeaderField: "Authorization")
            } catch MochaError.descriptive(let message) {
                MochaLogger.log(message)
            } catch {}
        }
        
        if header.count > 0 {
            for (key, value) in header {
                request.setValue(value, forHTTPHeaderField: key)
            }
        }
        
        if httpMethod.equalsIgnoreCase("post") || httpMethod.equalsIgnoreCase("update") {
            if parameters.isEmpty {
                handleGenericError(with: "Http request (\(httpMethod.lowercased()) without parameters.")
                return
            }
            
            if request.value(forHTTPHeaderField: "Content-Type") == nil {
                request.setValue(contentType, forHTTPHeaderField: "Content-Type")
            }
            
            if contentType == "application/x-www-form-urlencoded" {
                let formString = string(fromDictionary: parameters)
                let length = "\(formString.length)"
                
                request.setValue(length, forHTTPHeaderField: "Content-Length")
                request.httpBody = formString.data(using: encoding)
            } else {
                do {
                    let data = try JSONSerialization.data(withJSONObject: parameters, options: [])
                    request.httpBody = data
                } catch {
                    completionHandler?(.failure(.serialization))
                    return
                }
            }
        }
        
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = timeout
        
        let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
        
        let dataTask = session.dataTask(with: request, completionHandler: {
            (data: Data?, response: URLResponse?, error: Error?) in
            
            if let httpResponse = response as? HTTPURLResponse {
                if httpResponse.statusCode == -1022 {
                    self.completionHandler?(.failure(.appSecurityTransport))
                } else if httpResponse.statusCode != 200 {
                    self.completionHandler?(
                        .failure(
                        MochaError.httpResponse(statusCode: httpResponse.statusCode,
                                                data: nil)))
                }
            }
            
            if let error = error {
                MochaLogger.log("Http error: \(error.localizedDescription)")
                self.completionHandler?(.failure(.error(error: error)))
            }
            
            if let data = data {
                self.completionHandler?(.success(data))
            }
        })
        
        dataTask.resume()
        
        session.finishTasksAndInvalidate()
    }<commit_msg>Add new helpers methods for creating auth header and request body<commit_after>//
//  HttpClient.swift
//  Pods
//
//  Created by <NAME> on 05/06/17.
//
//

import UIKit

public typealias HttpCompletionHandler = (_ result: Result<Data>) -> Void

public class HttpClient: NSObject {
    
    // MARK: Variables
    
    static public var builder   : HttpClient.Builder {
        return HttpClient.Builder()
    }
    
    //Http Properties
    
    fileprivate var contentType : String!
    fileprivate var timeout     : TimeInterval!
    fileprivate var encoding    : String.Encoding!
    
    fileprivate var header      : [String: String]!
    fileprivate var parameters  : [String: Any]!
    
    //Basic Authorization
    
    fileprivate var username    : String?
    fileprivate var password    : String?
    
    //Certificates
    
    fileprivate var certificateMode     : CertificateMode = .none
    
    fileprivate var certificate         : Data?
    fileprivate var certificatePassword : String?
    
    fileprivate var trustAllSSL         : Bool = false
    
    fileprivate var hostDomain          : String?
    
    //Request
    
    fileprivate var url : String?
    
    //Response
    
    fileprivate var completionHandler   : HttpCompletionHandler?
    
    // MARK: Inits
    
    fileprivate override init() {
        super.init()
        
        contentType = "application/json"
        timeout = 60
        encoding = .utf8
        
        header = [:]
        parameters = [:]
    }
    
    // MARK: Conversions
    
    private func createBasicAuth() -> Result<String> {
        guard let username = self.username, username.isNotEmpty else {
            return .failure(.descriptive(message:
                "Username not informed for Basic Authorization"))
        }
        
        guard let password = self.password, password.isNotEmpty else {
            return .failure(.descriptive(message:
                "Password not informed for Basic Authorization"))
        }
        
        let credentials = "\(username):\(password)"
        
        guard let data = credentials.data(using: encoding) else {
            return .failure(.descriptive(message:
                "Error formatting the basic authentication provided."))
        }
        
        let base64Credential = data.base64EncodedString(options: .lineLength64Characters)
        return .success("Basic \(base64Credential)")
    }
    
    private func string(fromDictionary dictionary: [String: Any]) -> String {
        var resource = ""
        
        for (key, value) in dictionary {
            resource = resource.isEmpty ? "" : resource + "&"
            resource += key + "=" + "\(value)"
        }
        
        return resource
    }
    
    // MARK: Requests
    
    public func get() {
        send(httpMethod: "get")
    }
    
    public func delete() {
        send(httpMethod: "delete")
    }
    
    public func post() {
        send(httpMethod: "post")
    }
    
    public func update() {
        send(httpMethod: "update")
    }
    
    private func handleGenericError(with message: String) {
        completionHandler?(.failure(.descriptive(message: message)))
    }
    
    private func createHttpBody(for method: Method) -> Result<Data> {
        guard !parameters.isEmpty else {
            return .failure(.descriptive(message:
                "HttpClient cannot request (\(method.rawValue.uppercased())) without parameters."))
        }
        
        switch contentType {
        case "application/x-www-form-urlencoded":
            let form = string(fromDictionary: parameters)
            return form.data(using: encoding).map {
                Result.success($0)
            } ?? .failure(.serialization)
        default:
            do {
                let data = try JSONSerialization.data(withJSONObject: parameters,
                                                      options: [])
                return .success(data)
            } catch {
                return .failure(.serialization)
            }
        }
    }
    
    private func send(httpMethod: String) {
        guard let url = self.url else {
            handleGenericError(with: "URL cannot be `nil`")
            return
        }
        
        guard let nsurl = URL(string: url) else {
            handleGenericError(with: "Invalid URL.")
            return
        }
        
        var request = URLRequest(url: nsurl, cachePolicy: .useProtocolCachePolicy)
        request.httpMethod = httpMethod
        
        //basic auth
        let basicAuthResult = createBasicAuth()
        switch basicAuthResult {
        case .success(let basicAuth):
            request.setValue(basicAuth, forHTTPHeaderField: "Authorization")
        case .failure(let error):
            MochaLogger.log(error.description)
        }
        
        //headers
        if header.count > 0 {
            for (key, value) in header {
                request.setValue(value, forHTTPHeaderField: key)
            }
        }
        
        if httpMethod.equalsIgnoreCase("post") || httpMethod.equalsIgnoreCase("update") {
            if parameters.isEmpty {
                handleGenericError(with: "Http request (\(httpMethod.lowercased()) without parameters.")
                return
            }
            
            if request.value(forHTTPHeaderField: "Content-Type") == nil {
                request.setValue(contentType, forHTTPHeaderField: "Content-Type")
            }
            
            if contentType == "application/x-www-form-urlencoded" {
                let formString = string(fromDictionary: parameters)
                let length = "\(formString.length)"
                
                request.setValue(length, forHTTPHeaderField: "Content-Length")
                request.httpBody = formString.data(using: encoding)
            } else {
                do {
                    let data = try JSONSerialization.data(withJSONObject: parameters, options: [])
                    request.httpBody = data
                } catch {
                    completionHandler?(.failure(.serialization))
                    return
                }
            }
        }
        
        //configuration
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = timeout
        
        //session
        let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
        
        let dataTask = session.dataTask(with: request, completionHandler: {
            (data: Data?, response: URLResponse?, error: Error?) in
            
            if let httpResponse = response as? HTTPURLResponse {
                if httpResponse.statusCode == -1022 {
                    self.completionHandler?(.failure(.appSecurityTransport))
                } else if httpResponse.statusCode != 200 {
                    self.completionHandler?(
                        .failure(
                        MochaError.httpResponse(statusCode: httpResponse.statusCode,
                                                data: nil)))
                }
            }
            
            if let error = error {
                MochaLogger.log("Http error: \(error.localizedDescription)")
                self.completionHandler?(.failure(.error(error: error)))
            }
            
            if let data = data {
                self.completionHandler?(.success(data))
            }
        })
        
        dataTask.resume()
        
        session.finishTasksAndInvalidate()
    }
{'fraction_non_alphanumeric': 0.088317455769644, 'fraction_numerical': 0.00393954587780245, 'mean_word_length': 1.6092319192674267, 'pattern_counts': {'":': 1, '<': 11, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '10168', 'n_tokens_mistral': 3488, 'n_tokens_neox': 3344, 'n_words': 824}