544 lines
22 KiB
Swift
544 lines
22 KiB
Swift
/// Representation of the HTTP headers associated with a `HTTPRequest` or `HTTPResponse`.
|
|
/// Headers are subscriptable using case-insensitive comparison or provide `Name` constants. eg.
|
|
/// ```swift
|
|
/// let contentLength = headers["content-length"]
|
|
/// ```
|
|
/// or
|
|
/// ```swift
|
|
/// let contentLength = headers[.contentLength]
|
|
/// ```
|
|
public struct HTTPHeaders {
|
|
var original: [(name: Name, value: String)]?
|
|
var storage: [Name: [String]] {
|
|
didSet { original = nil }
|
|
}
|
|
|
|
/// :nodoc:
|
|
public subscript(name: Name) -> String? {
|
|
get {
|
|
guard let value = storage[name] else { return nil }
|
|
switch name {
|
|
case Name.setCookie: // Exception, see note in [RFC7230, section 3.2.2]
|
|
return value.isEmpty ? nil : value[0]
|
|
default:
|
|
return value.joined(separator: ",")
|
|
}
|
|
}
|
|
set {
|
|
storage[name] = newValue.map { [$0] }
|
|
}
|
|
}
|
|
|
|
/// :nodoc:
|
|
public subscript(valuesFor name: Name) -> [String] {
|
|
get { return storage[name] ?? [] }
|
|
set { storage[name] = newValue.isEmpty ? nil : newValue }
|
|
}
|
|
}
|
|
|
|
extension HTTPHeaders: ExpressibleByDictionaryLiteral {
|
|
/// Creates HTTP headers.
|
|
public init(dictionaryLiteral: (Name, String)...) {
|
|
storage = [:]
|
|
for (name, value) in dictionaryLiteral {
|
|
#if swift(>=4.0)
|
|
storage[name, default: []].append(value)
|
|
#else
|
|
if storage[name] == nil {
|
|
storage[name] = [value]
|
|
} else {
|
|
storage[name]!.append(value)
|
|
}
|
|
#endif
|
|
}
|
|
original = dictionaryLiteral
|
|
}
|
|
}
|
|
|
|
extension HTTPHeaders {
|
|
// Used instead of HTTPHeaders to save CPU on dictionary construction
|
|
/// :nodoc:
|
|
public struct Literal: ExpressibleByDictionaryLiteral {
|
|
let fields: [(name: Name, value: String)]
|
|
|
|
public init(dictionaryLiteral: (Name, String)...) {
|
|
fields = dictionaryLiteral
|
|
}
|
|
}
|
|
|
|
/// Appends a header to the headers
|
|
public mutating func append(_ literal: HTTPHeaders.Literal) {
|
|
for (name, value) in literal.fields {
|
|
#if swift(>=4.0)
|
|
storage[name, default: []].append(value)
|
|
#else
|
|
if storage[name] == nil {
|
|
storage[name] = [value]
|
|
} else {
|
|
storage[name]!.append(value)
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// Replaces a header in the headers
|
|
public mutating func replace(_ literal: HTTPHeaders.Literal) {
|
|
for (name, _) in literal.fields {
|
|
storage[name] = []
|
|
}
|
|
for (name, value) in literal.fields {
|
|
storage[name]!.append(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension HTTPHeaders: Sequence {
|
|
/// :nodoc:
|
|
public func makeIterator() -> AnyIterator<(name: Name, value: String)> {
|
|
if let original = original {
|
|
return AnyIterator(original.makeIterator())
|
|
} else {
|
|
return AnyIterator(StorageIterator(storage.makeIterator()))
|
|
}
|
|
}
|
|
|
|
struct StorageIterator: IteratorProtocol {
|
|
var headers: DictionaryIterator<Name, [String]>
|
|
var header: (name: Name, values: IndexingIterator<[String]>)?
|
|
|
|
init(_ iterator: DictionaryIterator<Name, [String]>) {
|
|
headers = iterator
|
|
header = headers.next().map { (name: $0.key, values: $0.value.makeIterator()) }
|
|
}
|
|
|
|
mutating func next() -> (name: Name, value: String)? {
|
|
while header != nil {
|
|
if let value = header!.values.next() {
|
|
return (name: header!.name, value: value)
|
|
} else {
|
|
header = headers.next().map { (name: $0.key, values: $0.value.makeIterator()) }
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
/// HTTPHeaders structure.
|
|
extension HTTPHeaders {
|
|
/// Type used for the name of a HTTP header in the `HTTPHeaders` storage.
|
|
public struct Name: Hashable, ExpressibleByStringLiteral, CustomStringConvertible {
|
|
let original: String
|
|
let lowercased: String
|
|
public let hashValue: Int
|
|
|
|
/// Create a HTTP header name with the provided String.
|
|
public init(_ name: String) {
|
|
original = name
|
|
lowercased = name.lowercased()
|
|
hashValue = lowercased.hashValue
|
|
}
|
|
|
|
public init(stringLiteral: String) {
|
|
self.init(stringLiteral)
|
|
}
|
|
|
|
public init(unicodeScalarLiteral: String) {
|
|
self.init(unicodeScalarLiteral)
|
|
}
|
|
|
|
public init(extendedGraphemeClusterLiteral: String) {
|
|
self.init(extendedGraphemeClusterLiteral)
|
|
}
|
|
|
|
/// :nodoc:
|
|
public var description: String {
|
|
return original
|
|
}
|
|
|
|
/// :nodoc:
|
|
public static func == (lhs: Name, rhs: Name) -> Bool {
|
|
return lhs.lowercased == rhs.lowercased
|
|
}
|
|
|
|
// https://www.iana.org/assignments/message-headers/message-headers.xhtml
|
|
// Permanent Message Header Field Names
|
|
|
|
/// A-IM header.
|
|
public static let aIM = Name("A-IM")
|
|
/// Accept header.
|
|
public static let accept = Name("Accept")
|
|
/// Accept-Additions header.
|
|
public static let acceptAdditions = Name("Accept-Additions")
|
|
/// Accept-Charset header.
|
|
public static let acceptCharset = Name("Accept-Charset")
|
|
/// Accept-Datetime header.
|
|
public static let acceptDatetime = Name("Accept-Datetime")
|
|
/// Accept-Encoding header.
|
|
public static let acceptEncoding = Name("Accept-Encoding")
|
|
/// Accept-Features header.
|
|
public static let acceptFeatures = Name("Accept-Features")
|
|
/// Accept-Language header.
|
|
public static let acceptLanguage = Name("Accept-Language")
|
|
/// Accept-Patch header.
|
|
public static let acceptPatch = Name("Accept-Patch")
|
|
/// Accept-Post header.
|
|
public static let acceptPost = Name("Accept-Post")
|
|
/// Accept-Ranges header.
|
|
public static let acceptRanges = Name("Accept-Ranges")
|
|
/// Accept-Age header.
|
|
public static let age = Name("Age")
|
|
/// Accept-Allow header.
|
|
public static let allow = Name("Allow")
|
|
/// ALPN header.
|
|
public static let alpn = Name("ALPN")
|
|
/// Alt-Svc header.
|
|
public static let altSvc = Name("Alt-Svc")
|
|
/// Alt-Used header.
|
|
public static let altUsed = Name("Alt-Used")
|
|
/// Alternatives header.
|
|
public static let alternates = Name("Alternates")
|
|
/// Apply-To-Redirect-Ref header.
|
|
public static let applyToRedirectRef = Name("Apply-To-Redirect-Ref")
|
|
/// Authentication-Control header.
|
|
public static let authenticationControl = Name("Authentication-Control")
|
|
/// Authentication-Info header.
|
|
public static let authenticationInfo = Name("Authentication-Info")
|
|
/// Authorization header.
|
|
public static let authorization = Name("Authorization")
|
|
/// C-Ext header.
|
|
public static let cExt = Name("C-Ext")
|
|
/// C-Man header.
|
|
public static let cMan = Name("C-Man")
|
|
/// C-Opt header.
|
|
public static let cOpt = Name("C-Opt")
|
|
/// C-PEP header.
|
|
public static let cPEP = Name("C-PEP")
|
|
/// C-PEP-Indo header.
|
|
public static let cPEPInfo = Name("C-PEP-Info")
|
|
/// Cache-Control header.
|
|
public static let cacheControl = Name("Cache-Control")
|
|
/// CalDav-Timezones header.
|
|
public static let calDAVTimezones = Name("CalDAV-Timezones")
|
|
/// Close header.
|
|
public static let close = Name("Close")
|
|
/// Connection header.
|
|
public static let connection = Name("Connection")
|
|
/// Content-Base.
|
|
public static let contentBase = Name("Content-Base")
|
|
/// Content-Disposition header.
|
|
public static let contentDisposition = Name("Content-Disposition")
|
|
/// Content-Encoding header.
|
|
public static let contentEncoding = Name("Content-Encoding")
|
|
/// Content-ID header.
|
|
public static let contentID = Name("Content-ID")
|
|
/// Content-Language header.
|
|
public static let contentLanguage = Name("Content-Language")
|
|
/// Content-Length header.
|
|
public static let contentLength = Name("Content-Length")
|
|
/// Content-Location header.
|
|
public static let contentLocation = Name("Content-Location")
|
|
/// Content-MD5 header.
|
|
public static let contentMD5 = Name("Content-MD5")
|
|
/// Content-Range header.
|
|
public static let contentRange = Name("Content-Range")
|
|
/// Content-Script-Type header.
|
|
public static let contentScriptType = Name("Content-Script-Type")
|
|
/// Content-Style-Type header.
|
|
public static let contentStyleType = Name("Content-Style-Type")
|
|
/// Content-Type header.
|
|
public static let contentType = Name("Content-Type")
|
|
/// Content-Version header.
|
|
public static let contentVersion = Name("Content-Version")
|
|
/// Content-Cookie header.
|
|
public static let cookie = Name("Cookie")
|
|
/// Content-Cookie2 header.
|
|
public static let cookie2 = Name("Cookie2")
|
|
/// DASL header.
|
|
public static let dasl = Name("DASL")
|
|
/// DASV header.
|
|
public static let dav = Name("DAV")
|
|
/// Date header.
|
|
public static let date = Name("Date")
|
|
/// Default-Style header.
|
|
public static let defaultStyle = Name("Default-Style")
|
|
/// Delta-Base header.
|
|
public static let deltaBase = Name("Delta-Base")
|
|
/// Depth header.
|
|
public static let depth = Name("Depth")
|
|
/// Derived-From header.
|
|
public static let derivedFrom = Name("Derived-From")
|
|
/// Destination header.
|
|
public static let destination = Name("Destination")
|
|
/// Differential-ID header.
|
|
public static let differentialID = Name("Differential-ID")
|
|
/// Digest header.
|
|
public static let digest = Name("Digest")
|
|
/// ETag header.
|
|
public static let eTag = Name("ETag")
|
|
/// Expect header.
|
|
public static let expect = Name("Expect")
|
|
/// Expires header.
|
|
public static let expires = Name("Expires")
|
|
/// Ext header.
|
|
public static let ext = Name("Ext")
|
|
/// Forwarded header.
|
|
public static let forwarded = Name("Forwarded")
|
|
/// From header.
|
|
public static let from = Name("From")
|
|
/// GetProfile header.
|
|
public static let getProfile = Name("GetProfile")
|
|
/// Hobareg header.
|
|
public static let hobareg = Name("Hobareg")
|
|
/// Host header.
|
|
public static let host = Name("Host")
|
|
/// HTTP2-Settings header.
|
|
public static let http2Settings = Name("HTTP2-Settings")
|
|
/// IM header.
|
|
public static let im = Name("IM")
|
|
/// If header.
|
|
public static let `if` = Name("If")
|
|
/// If-Match header.
|
|
public static let ifMatch = Name("If-Match")
|
|
/// If-Modified-Since header.
|
|
public static let ifModifiedSince = Name("If-Modified-Since")
|
|
/// If-None-Match header.
|
|
public static let ifNoneMatch = Name("If-None-Match")
|
|
/// If-Range header.
|
|
public static let ifRange = Name("If-Range")
|
|
/// If-Schedule-Tag-Match header.
|
|
public static let ifScheduleTagMatch = Name("If-Schedule-Tag-Match")
|
|
/// If-Unmodified-Since header.
|
|
public static let ifUnmodifiedSince = Name("If-Unmodified-Since")
|
|
/// Keep-Alive header.
|
|
public static let keepAlive = Name("Keep-Alive")
|
|
/// Label header.
|
|
public static let label = Name("Label")
|
|
/// Last-Modified header.
|
|
public static let lastModified = Name("Last-Modified")
|
|
/// Link header.
|
|
public static let link = Name("Link")
|
|
/// Location header.
|
|
public static let location = Name("Location")
|
|
/// Lock-Token header.
|
|
public static let lockToken = Name("Lock-Token")
|
|
/// Man header.
|
|
public static let man = Name("Man")
|
|
/// Max-Forwards header.
|
|
public static let maxForwards = Name("Max-Forwards")
|
|
/// Memento-Date header.
|
|
public static let mementoDatetime = Name("Memento-Datetime")
|
|
/// Meter header.
|
|
public static let meter = Name("Meter")
|
|
/// MIME-Version header.
|
|
public static let mimeVersion = Name("MIME-Version")
|
|
/// Negotiate header.
|
|
public static let negotiate = Name("Negotiate")
|
|
/// Opt header.
|
|
public static let opt = Name("Opt")
|
|
/// Optional-WWW-Authenticate header.
|
|
public static let optionalWWWAuthenticate = Name("Optional-WWW-Authenticate")
|
|
/// Ordering-Type header.
|
|
public static let orderingType = Name("Ordering-Type")
|
|
/// Origin header.
|
|
public static let origin = Name("Origin")
|
|
/// Overwrite header.
|
|
public static let overwrite = Name("Overwrite")
|
|
/// P3P header.
|
|
public static let p3p = Name("P3P")
|
|
/// PEP header.
|
|
public static let pep = Name("PEP")
|
|
/// PICS-Label header.
|
|
public static let picsLabel = Name("PICS-Label")
|
|
/// Pep-Info header.
|
|
public static let pepInfo = Name("Pep-Info")
|
|
/// Position header.
|
|
public static let position = Name("Position")
|
|
/// Pragma header.
|
|
public static let pragma = Name("Pragma")
|
|
/// Prefer header.
|
|
public static let prefer = Name("Prefer")
|
|
/// Preference-Applied header.
|
|
public static let preferenceApplied = Name("Preference-Applied")
|
|
/// ProfileObject header.
|
|
public static let profileObject = Name("ProfileObject")
|
|
/// Protocol header.
|
|
public static let `protocol` = Name("Protocol")
|
|
/// Protocol-Info header.
|
|
public static let protocolInfo = Name("Protocol-Info")
|
|
/// Protocol-Query header.
|
|
public static let protocolQuery = Name("Protocol-Query")
|
|
/// Protocol-Request header.
|
|
public static let protocolRequest = Name("Protocol-Request")
|
|
/// Proxy-Authenticate header.
|
|
public static let proxyAuthenticate = Name("Proxy-Authenticate")
|
|
/// Proxy-Authentication-Info header.
|
|
public static let proxyAuthenticationInfo = Name("Proxy-Authentication-Info")
|
|
/// Proxy-Authorization header.
|
|
public static let proxyAuthorization = Name("Proxy-Authorization")
|
|
/// Proxy-Features header.
|
|
public static let proxyFeatures = Name("Proxy-Features")
|
|
/// Proxy-Instruction header.
|
|
public static let proxyInstruction = Name("Proxy-Instruction")
|
|
/// Public header.
|
|
public static let `public` = Name("Public")
|
|
/// Public-Key-Pins header.
|
|
public static let publicKeyPins = Name("Public-Key-Pins")
|
|
/// Public-Key-Pins-Report-Only header.
|
|
public static let publicKeyPinsReportOnly = Name("Public-Key-Pins-Report-Only")
|
|
/// Range header.
|
|
public static let range = Name("Range")
|
|
/// Redirect-Ref header.
|
|
public static let redirectRef = Name("Redirect-Ref")
|
|
/// Referer header.
|
|
public static let referer = Name("Referer")
|
|
/// Retry-After header.
|
|
public static let retryAfter = Name("Retry-After")
|
|
/// Safe header.
|
|
public static let safe = Name("Safe")
|
|
/// Schedule-Reply header.
|
|
public static let scheduleReply = Name("Schedule-Reply")
|
|
/// Schedule-Tag header.
|
|
public static let scheduleTag = Name("Schedule-Tag")
|
|
/// Sec-WebSocket-Accept header.
|
|
public static let secWebSocketAccept = Name("Sec-WebSocket-Accept")
|
|
/// Sec-WebSocket-Extensions header.
|
|
public static let secWebSocketExtensions = Name("Sec-WebSocket-Extensions")
|
|
/// Sec-WebSocket-Key header.
|
|
public static let secWebSocketKey = Name("Sec-WebSocket-Key")
|
|
/// Sec-WebSocket-Protocol header.
|
|
public static let secWebSocketProtocol = Name("Sec-WebSocket-Protocol")
|
|
/// Sec-WebSocket-Version header.
|
|
public static let secWebSocketVersion = Name("Sec-WebSocket-Version")
|
|
/// Security-Scheme header.
|
|
public static let securityScheme = Name("Security-Scheme")
|
|
/// Server header.
|
|
public static let server = Name("Server")
|
|
/// Set-Cookie header.
|
|
public static let setCookie = Name("Set-Cookie")
|
|
/// Set-Cookie2 header.
|
|
public static let setCookie2 = Name("Set-Cookie2")
|
|
/// SetProfile header.
|
|
public static let setProfile = Name("SetProfile")
|
|
/// SLUG header.
|
|
public static let slug = Name("SLUG")
|
|
/// SoapAction header.
|
|
public static let soapAction = Name("SoapAction")
|
|
/// Status-URI header.
|
|
public static let statusURI = Name("Status-URI")
|
|
/// Strict-Transport-Security header.
|
|
public static let strictTransportSecurity = Name("Strict-Transport-Security")
|
|
/// Surrogate-Capability header.
|
|
public static let surrogateCapability = Name("Surrogate-Capability")
|
|
/// Surrogate-Control header.
|
|
public static let surrogateControl = Name("Surrogate-Control")
|
|
/// TCN header.
|
|
public static let tcn = Name("TCN")
|
|
/// TE header.
|
|
public static let te = Name("TE")
|
|
/// Timeout header.
|
|
public static let timeout = Name("Timeout")
|
|
/// Topic header.
|
|
public static let topic = Name("Topic")
|
|
/// Trailer header.
|
|
public static let trailer = Name("Trailer")
|
|
/// Transfer-Encoding header.
|
|
public static let transferEncoding = Name("Transfer-Encoding")
|
|
/// TTL header.
|
|
public static let ttl = Name("TTL")
|
|
/// Urgency header.
|
|
public static let urgency = Name("Urgency")
|
|
/// URI header.
|
|
public static let uri = Name("URI")
|
|
/// Upgrade header.
|
|
public static let upgrade = Name("Upgrade")
|
|
/// User-Agent header.
|
|
public static let userAgent = Name("User-Agent")
|
|
/// Variant-Vary header.
|
|
public static let variantVary = Name("Variant-Vary")
|
|
/// Vary header.
|
|
public static let vary = Name("Vary")
|
|
/// Via header.
|
|
public static let via = Name("Via")
|
|
/// WWW-Authenticate header.
|
|
public static let wwwAuthenticate = Name("WWW-Authenticate")
|
|
/// Want-Digest header.
|
|
public static let wantDigest = Name("Want-Digest")
|
|
/// Warning header.
|
|
public static let warning = Name("Warning")
|
|
/// X-Frame-Options header.
|
|
public static let xFrameOptions = Name("X-Frame-Options")
|
|
|
|
// https://www.iana.org/assignments/message-headers/message-headers.xhtml
|
|
// Provisional Message Header Field Names
|
|
/// Access-Control header.
|
|
public static let accessControl = Name("Access-Control")
|
|
/// Access-Control-Allow-Credentials header.
|
|
public static let accessControlAllowCredentials = Name("Access-Control-Allow-Credentials")
|
|
/// Access-Control-Allow-Headers header.
|
|
public static let accessControlAllowHeaders = Name("Access-Control-Allow-Headers")
|
|
/// Access-Control-Allow-Methods header.
|
|
public static let accessControlAllowMethods = Name("Access-Control-Allow-Methods")
|
|
/// Access-Control-Allow-Origin header.
|
|
public static let accessControlAllowOrigin = Name("Access-Control-Allow-Origin")
|
|
/// Access-Control-Max-Age header.
|
|
public static let accessControlMaxAge = Name("Access-Control-Max-Age")
|
|
/// Access-Control-Request-Method header.
|
|
public static let accessControlRequestMethod = Name("Access-Control-Request-Method")
|
|
/// Access-Control-Request-Headers header.
|
|
public static let accessControlRequestHeaders = Name("Access-Control-Request-Headers")
|
|
/// Compliance header.
|
|
public static let compliance = Name("Compliance")
|
|
/// Content-Transfer-Encoding header.
|
|
public static let contentTransferEncoding = Name("Content-Transfer-Encoding")
|
|
/// Cost header.
|
|
public static let cost = Name("Cost")
|
|
/// EDIINT-Features header.
|
|
public static let ediintFeatures = Name("EDIINT-Features")
|
|
/// Message-ID header.
|
|
public static let messageID = Name("Message-ID")
|
|
/// Method-Check header.
|
|
public static let methodCheck = Name("Method-Check")
|
|
/// Method-Check-Expires header.
|
|
public static let methodCheckExpires = Name("Method-Check-Expires")
|
|
/// Non-Compliance header.
|
|
public static let nonCompliance = Name("Non-Compliance")
|
|
/// Optional header.
|
|
public static let optional = Name("Optional")
|
|
/// Referer-Root header.
|
|
public static let refererRoot = Name("Referer-Root")
|
|
/// Resolution-Hint header.
|
|
public static let resolutionHint = Name("Resolution-Hint")
|
|
/// Resolver-Location header.
|
|
public static let resolverLocation = Name("Resolver-Location")
|
|
/// SubOK header.
|
|
public static let subOK = Name("SubOK")
|
|
/// Subst header.
|
|
public static let subst = Name("Subst")
|
|
/// Title header.
|
|
public static let title = Name("Title")
|
|
/// UA-Color header.
|
|
public static let uaColor = Name("UA-Color")
|
|
/// UA-Media header.
|
|
public static let uaMedia = Name("UA-Media")
|
|
/// UA-Pixels header.
|
|
public static let uaPixels = Name("UA-Pixels")
|
|
/// UA-Resolution header.
|
|
public static let uaResolution = Name("UA-Resolution")
|
|
/// UA-Windowpixels header.
|
|
public static let uaWindowpixels = Name("UA-Windowpixels")
|
|
/// Version header.
|
|
public static let version = Name("Version")
|
|
/// X-Device-Accept header.
|
|
public static let xDeviceAccept = Name("X-Device-Accept")
|
|
/// X-Device-Accept-Charset header.
|
|
public static let xDeviceAcceptCharset = Name("X-Device-Accept-Charset")
|
|
/// X-Device-Accept-Encoding header.
|
|
public static let xDeviceAcceptEncoding = Name("X-Device-Accept-Encoding")
|
|
/// X-Device-Accept-Language header.
|
|
public static let xDeviceAcceptLanguage = Name("X-Device-Accept-Language")
|
|
/// X-Device-User-Agent header.
|
|
public static let xDeviceUserAgent = Name("X-Device-User-Agent")
|
|
}
|
|
}
|