import Foundation open class HTTPHeader { public enum HTTPHeaderError: Error { case malformedHeader, invalidRequestLine, invalidHeaderField, invalidConnectURL, invalidConnectPort, invalidURL, missingHostField, invalidHostField, invalidHostPort, invalidContentLength, illegalEncoding } open var HTTPVersion: String open var method: String open var isConnect: Bool = false open var path: String open var foundationURL: Foundation.URL? open var homemadeURL: HTTPURL? open var host: String open var port: Int // just assume that `Content-Length` is given as of now. // Chunk is not supported yet. open var contentLength: Int = 0 open var headers: [(String, String)] = [] open var rawHeader: Data? public init(headerString: String) throws { let lines = headerString.components(separatedBy: "\r\n") guard lines.count >= 3 else { throw HTTPHeaderError.malformedHeader } let request = lines[0].components(separatedBy: " ") guard request.count == 3 else { throw HTTPHeaderError.invalidRequestLine } method = request[0] path = request[1] HTTPVersion = request[2] for line in lines[1.. String? { get { for (key, value) in headers { if index.caseInsensitiveCompare(key) == .orderedSame { return value } } return nil } } open func toData() -> Data { return toString().data(using: String.Encoding.utf8)! } open func toString() -> String { var strRep = "\(method) \(path) \(HTTPVersion)\r\n" for (key, value) in headers { strRep += "\(key): \(value)\r\n" } strRep += "\r\n" return strRep } open func addHeader(_ key: String, value: String) { headers.append((key, value)) } open func rewriteToRelativePath() { if path[path.startIndex] != "/" { guard let rewrotePath = URL.matchRelativePath(path) else { return } path = rewrotePath } } open func removeHeader(_ key: String) -> String? { for i in 0.. String? { if let result = relativePathRegex.firstMatch(in: url, options: NSRegularExpression.MatchingOptions(), range: NSRange(location: 0, length: url.count)) { return (url as NSString).substring(with: result.range(at: 1)) } else { return nil } } } }