     306: "Switch Proxy",
     307: "Temporary Redirect",
     308: "Permanent Redirect",
-
+    
     // 4xx (Client Error)
     400: "Bad Request",
     401: "Unauthorized",

let ReqresRequestHandledKey = "ReqresRequestHandledKey"
let ReqresRequestTimeKey = "ReqresRequestTimeKey"

open class Reqres: URLProtocol {
    var connection: NSURLConnection?
    var data: NSMutableData?
    var response: URLResponse?
    var newRequest: NSMutableURLRequest?

    open static var allowUTF8Emoji: Bool = true

    open static var logger: ReqresLogging = ReqresDefaultLogger()

    open class func register() {
        URLProtocol.registerClass(self)
    }

    open class func unregister() {
        URLProtocol.unregisterClass(self)
    }

    open class func defaultSessionConfiguration() -> URLSessionConfiguration {
        let config = URLSessionConfiguration.default
        config.protocolClasses?.insert(Reqres.self, at: 0)
        return config
    }

    // MARK: - NSURLProtocol

    open override class func canInit(with request: URLRequest) -> Bool {
        guard self.property(forKey: ReqresRequestHandledKey, in: request) == nil && self.logger.logLevel != .none else {
            return false
        }
        return true
    }

    open override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        return request
    }

    open override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool {
        return super.requestIsCacheEquivalent(a, to: b)
    }

    open override func startLoading() {
        guard let req = (request as NSURLRequest).mutableCopy() as? NSMutableURLRequest , newRequest == nil else { return }

        self.newRequest = req

        URLProtocol.setProperty(true, forKey: ReqresRequestHandledKey, in: newRequest!)
        URLProtocol.setProperty(Date(), forKey: ReqresRequestTimeKey, in: newRequest!)

        connection = NSURLConnection(request: newRequest! as URLRequest, delegate: self)

        logRequest(newRequest! as URLRequest)
    }

    open override func stopLoading() {
        connection = nil
    }

    // MARK: NSURLConnectionDelegate

    func connection(_ connection: NSURLConnection!, didReceiveResponse response: URLResponse!) {
        let policy = URLCache.StoragePolicy(rawValue: request.cachePolicy.rawValue) ?? .notAllowed
        client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: policy)

        self.response = response
        self.data = NSMutableData()
    }

    func connection(_ connection: NSURLConnection!, didReceiveData data: Data!) {
        client?.urlProtocol(self, didLoad: data)
        self.data?.append(data)
    }

    func connectionDidFinishLoading(_ connection: NSURLConnection!) {
        client?.urlProtocolDidFinishLoading(self)

        if let response = response {
            logResponse(response, method: connection.originalRequest.httpMethod, data: data as Data?)
        }
    }

    func connection(_ connection: NSURLConnection!, didFailWithError error: NSError!) {
        client?.urlProtocol(self, didFailWithError: error)
        logError(connection.originalRequest, error: error)
    }

    // MARK: - Logging

    open func logError(_ request: URLRequest, error: NSError) {

        var s = ""

        if let method = request.httpMethod {
            s += "\(method) "
        }

        if let url = request.url?.absoluteString {
            s += "\(url) "
        }

        s += "ERROR: \(error.localizedDescription)"

        if let reason = error.localizedFailureReason {
            s += "\nReason: \(reason)"
        }

        if let suggestion = error.localizedRecoverySuggestion {
            s += "\nSuggestion: \(suggestion)"
        }

        type(of: self).logger.logError(s)
    }

    open func logRequest(_ request: URLRequest) {

        var s = ""

        if type(of: self).allowUTF8Emoji {
            s += "⬆️ "
        }

        if let method = request.httpMethod {
            s += "\(method) "
        }

        if let url = request.url?.absoluteString {
            s += "'\(url)' "
        }

        if type(of: self).logger.logLevel == .verbose {

            if let headers = request.allHTTPHeaderFields , headers.count > 0 {
                s += "\n" + logHeaders(headers as [String : AnyObject])
            }

            s += "\nBody: \(bodyString(request.httpBodyData))"

            type(of: self).logger.logVerbose(s)
        } else {

            type(of: self).logger.logLight(s)
        }
    }

    open func logResponse(_ response: URLResponse, method: String?, data: Data? = nil) {

        var s = ""

        if type(of: self).allowUTF8Emoji {
            s += "⬇️ "
        }

        if let method = newRequest?.httpMethod {
            s += "\(method) "
        }

        if let url = response.url?.absoluteString {
            s += "\(url) "
        }

        if let httpResponse = response as? HTTPURLResponse {
            s += "("
            if type(of: self).allowUTF8Emoji {

        s += "]"
        return s
    }
    
    func bodyString(_ body: Data?) -> String {
        
        if let body = body {
            if let json = try? JSONSerialization.jsonObject(with: body, options: .mutableContainers),
                let pretty = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted),
                let string = String(data: pretty, encoding: String.Encoding.utf8) {
                return string
            } else if let string = String(data: body, encoding: String.Encoding.utf8) {
                return string
            } else {
                return body.description
            }
        } else {
            return "nil"
