 		return appendBody(string: string)
 	}
 	/// Encodes the Codable as a JSON string and converts that to a UTF-8 encoded [UInt8].
-    /// Uses a default JSONEncoder in case none is specified.
+	/// Uses a default JSONEncoder in case none is specified.
 	/// Adds the "application/json" content type unless `skipContentType` is true.
 	@discardableResult
-    func setBody<T: Encodable>(json: T, encoder: JSONEncoder = JSONEncoder(), skipContentType: Bool = false) throws -> Self {
+	func setBody<T: Encodable>(json: T, encoder: JSONEncoder = JSONEncoder(), skipContentType: Bool = false) throws -> Self {
 		let data = try encoder.encode(json)
 		if !skipContentType {
 			setHeader(.contentType, value: applicationJson)
 		}
 		return setBody(bytes: Array(data))
 	}
-	/// Encodes the Dictionary as a JSON string and converts that to a UTF-8 encoded [UInt8].
-	/// Adds the "application/json" content type unless `skipContentType` is true.
-//	@discardableResult
-//	func setBody(json: [String:Any], skipContentType: Bool = false) throws -> Self {
-//		let data = try JSONSerialization.data(withJSONObject: json)
-//		if !skipContentType {
-//			setHeader(.contentType, value: applicationJson)
-//		}
-//		return setBody(bytes: Array(data))
-//	}
+	
 	/// Encodes the JSONConvertible as a JSON string and converts that to a UTF-8 encoded [UInt8].
 	/// Adds the "application/json" content type unless `skipContentType` is true.
 	@discardableResult


/// HTTP response status code/msg.
public enum HTTPResponseStatus: CustomStringConvertible {
    case `continue`
    case switchingProtocols
    case ok
    case created
    case accepted
    case nonAuthoritativeInformation
    case noContent
    case resetContent
    case partialContent
    case multipleChoices
    case movedPermanently
    case found
    case seeOther
    case notModified
    case useProxy
    case temporaryRedirect
    case badRequest
    case unauthorized
    case paymentRequired
    case forbidden
    case notFound
    case methodNotAllowed
    case notAcceptable
    case proxyAuthenticationRequired
    case requestTimeout
    case conflict
    case gone
    case lengthRequired
    case preconditionFailed
    case requestEntityTooLarge
    case requestURITooLong
    case unsupportedMediaType
    case requestedRangeNotSatisfiable
    case expectationFailed
    case internalServerError
    case notImplemented
    case badGateway
    case serviceUnavailable
    case gatewayTimeout
    case httpVersionNotSupported
    case custom(code: Int, message: String)
    /// Returna the textual code and message pair.
    public var description: String {
        switch self {
        case .continue              : return "100 Continue"
        case .switchingProtocols	: return "101 Switching Protocols"
        case .ok					: return "200 OK"
        case .created               : return "201 Created"
        case .accepted              : return "202 Accepted"
        case .nonAuthoritativeInformation	: return "203 Non-Authoritative Information"
        case .noContent             : return "204 No Content"
        case .resetContent          : return "205 Reset Content"
        case .partialContent		: return "206 Partial Content"
        case .multipleChoices       : return "300 Multiple Choices"
        case .movedPermanently      : return "301 Moved Permanently"
        case .found                 : return "302 Found"
        case .seeOther              : return "303 See Other"
        case .notModified           : return "304 Not Modified"
        case .useProxy              : return "305 Use Proxy"
        case .temporaryRedirect     : return "307 Temporary Redirect"
        case .badRequest			: return "400 Bad Request"
        case .unauthorized          : return "401 Unauthorized"
        case .paymentRequired       : return "402 Payment Required"
        case .forbidden             : return "403 Forbidden"
        case .notFound              : return "404 Not Found"
        case .methodNotAllowed      : return "405 Method Not Allowed"
        case .notAcceptable         : return "406 Not Acceptable"
        case .proxyAuthenticationRequired	: return "407 Proxy Authentication Required"
        case .requestTimeout		: return "408 Request Timeout"
        case .conflict              : return "409 Conflict"
        case .gone                  : return "410 Gone"
        case .lengthRequired		: return "411 Length Required"
        case .preconditionFailed	: return "412 Precondition Failed"
        case .requestEntityTooLarge	: return "413 Request Entity Too Large"
        case .requestURITooLong	: return "414 Request-URI Too Long"
        case .unsupportedMediaType	: return "415 Unsupported Media Type"
        case .requestedRangeNotSatisfiable	: return "416 Requested Range Not Satisfiable"
        case .expectationFailed	: return "417 Expectation Failed"
        case .internalServerError	: return "500 Internal Server Error"
        case .notImplemented		: return "501 Not Implemented"
        case .badGateway			: return "502 Bad Gateway"
        case .serviceUnavailable	: return "503 Service Unavailable"
        case .gatewayTimeout		: return "504 Gateway Timeout"
        case .httpVersionNotSupported	: return "505 HTTP Version Not Supported"
        case .custom(let code, let message): return "\(code) \(message)"
        }
    }
    
    public static func statusFrom(code: Int) -> HTTPResponseStatus {
        switch code {
        case 100: return .continue
        case 101: return .switchingProtocols
        case 200: return .ok
        case 201: return .created
        case 202: return .accepted
        case 203: return .nonAuthoritativeInformation
        case 204: return .noContent
        case 205: return .resetContent
        case 206: return .partialContent
        case 300: return .multipleChoices
        case 301: return .movedPermanently
        case 302: return .found
        case 303: return .seeOther
        case 304: return .notModified
        case 305: return .useProxy
        case 307: return .temporaryRedirect
        case 400: return .badRequest
        case 401: return .unauthorized
        case 402: return .paymentRequired
        case 403: return .forbidden
        case 404: return .notFound
        case 405: return .methodNotAllowed
        case 406: return .notAcceptable
        case 407: return .proxyAuthenticationRequired
        case 408: return .requestTimeout
        case 409: return .conflict
        case 410: return .gone
        case 411: return .lengthRequired
        case 412: return .preconditionFailed
        case 413: return .requestEntityTooLarge
        case 414: return .requestURITooLong
        case 415: return .unsupportedMediaType
        case 416: return .requestedRangeNotSatisfiable
        case 417: return .expectationFailed
        case 500: return .internalServerError
        case 501: return .notImplemented
        case 502: return .badGateway
        case 503: return .serviceUnavailable
        case 504: return .gatewayTimeout
        case 505: return .httpVersionNotSupported
        default:
            return .custom(code: code, message: "Custom")
        }
    }
	
	/// The numeric code for this response status.
    public var code: Int {
        switch self {
        case .continue: return 100
        case .switchingProtocols: return 101
        case .ok: return 200
        case .created: return 201
        case .accepted: return 202
        case .nonAuthoritativeInformation: return 203
        case .noContent: return 204
        case .resetContent: return 205
        case .partialContent: return 206
        case .multipleChoices: return 300
        case .movedPermanently: return 301
        case .found: return 302
        case .seeOther: return 303
        case .notModified: return 304
        case .useProxy: return 305
        case .temporaryRedirect: return 307
        case .badRequest: return 400
        case .unauthorized: return 401
        case .paymentRequired: return 402
        case .forbidden: return 403
        case .notFound: return 404
        case .methodNotAllowed: return 405
        case .notAcceptable: return 406
        case .proxyAuthenticationRequired: return 407
        case .requestTimeout: return 408
        case .conflict: return 409
        case .gone: return 410
        case .lengthRequired: return 411
        case .preconditionFailed: return 412
        case .requestEntityTooLarge: return 413
        case .requestURITooLong: return 414
        case .unsupportedMediaType: return 415
        case .requestedRangeNotSatisfiable: return 416
        case .expectationFailed: return 417
        case .internalServerError: return 500
        case .notImplemented: return 501
        case .badGateway: return 502
        case .serviceUnavailable: return 503
        case .gatewayTimeout: return 504
        case .httpVersionNotSupported: return 505
        case .custom(let code, _): return code
        }
    }
}

/// This bundles together the values which will be used to set a cookie in the outgoing response
public struct HTTPCookie {
    /// Cookie expiration type
    public enum Expiration {
        /// Session cookie with no explicit expiration
        case session
        /// Expiratiuon in a number of seconds from now
        case relativeSeconds(Int)
        /// Expiration at an absolute time given in seconds from epoch
        case absoluteSeconds(Int)
        /// Custom expiration date string
        case absoluteDate(String)
    }
    public enum SameSite {
        // https://tools.ietf.org/html/draft-west-first-party-cookies-07
        // Cross-site usage is allowed
        case lax
        // The cookie is withheld with any cross-site usage
        case strict
    }
    
    /// Cookie name
    public let name: String
    /// Cookie value
    public let value: String
    /// Cookie domain
    public let domain: String?
    /// Cookie expiration
    public let expires: Expiration?
    /// Cookie path
    public let path: String?
    /// Cookie secure flag
    public let secure: Bool?
    /// Cookie http only flag
    public let httpOnly: Bool?
    /// Cookie samesite flag
    public let sameSite: SameSite?
    
    /// Cookie public initializer
    public init(name: String,
                value: String,
                domain: String? = nil,
                expires: Expiration? = nil,
                path: String? = nil,
                secure: Bool? = nil,
                httpOnly: Bool? = nil,
                sameSite: SameSite? = nil) {
        self.name = name
        self.value = value
        self.domain = domain
        self.expires = expires
        self.path = path
        self.secure = secure
        self.httpOnly = httpOnly
        self.sameSite = sameSite
    }
}

/// An HTTP based response object.
/// Contains all header and body data which will be delivered to the client.
public protocol HTTPResponse: class {
	/// The request object which instigated this response.
    var request: HTTPRequest { get }
	/// The HTTP response status.
    var status: HTTPResponseStatus { get set }
	/// Indicate that the response should attempt to stream all outgoing data.
	/// This is primarily used when the resulting content length can not be known.
    var isStreaming: Bool { get set }
	/// Body data waiting to be sent to the client.
	/// This will be emptied after each chunk is sent.
	var bodyBytes: [UInt8] { get set }
    /// Returns the requested outgoing header value.
    func header(_ named: HTTPResponseHeader.Name) -> String?
	/// Add a header to the outgoing response.
	/// No check for duplicate or repeated headers will be made.
	@discardableResult
    func addHeader(_ named: HTTPResponseHeader.Name, value: String) -> Self
	/// Set the indicated header value. 
	/// If the header already exists then the existing value will be replaced.
	@discardableResult
    func setHeader(_ named: HTTPResponseHeader.Name, value: String) -> Self
    /// Provide access to all current header values.
    var headers: AnyIterator<(HTTPResponseHeader.Name, String)> { get }
	/// Push all currently available headers and body data to the client.
	/// May be called multiple times.
	func push(callback: @escaping (Bool) -> ())

//

#if os(Linux)
import SwiftGlibc
#else
import Darwin
#endif

import PerfectLib
