AutorÃa | Ultima modificación | Ver Log |
//// HTTPHeaders.swift//// Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to deal// in the Software without restriction, including without limitation the rights// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell// copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN// THE SOFTWARE.//import Foundation/// An order-preserving and case-insensitive representation of HTTP headers.public struct HTTPHeaders {private var headers: [HTTPHeader] = []/// Creates an empty instance.public init() {}/// Creates an instance from an array of `HTTPHeader`s. Duplicate case-insensitive names are collapsed into the last/// name and value encountered.public init(_ headers: [HTTPHeader]) {self.init()headers.forEach { update($0) }}/// Creates an instance from a `[String: String]`. Duplicate case-insensitive names are collapsed into the last name/// and value encountered.public init(_ dictionary: [String: String]) {self.init()dictionary.forEach { update(HTTPHeader(name: $0.key, value: $0.value)) }}/// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`.////// - Parameters:/// - name: The `HTTPHeader` name./// - value: The `HTTPHeader value.public mutating func add(name: String, value: String) {update(HTTPHeader(name: name, value: value))}/// Case-insensitively updates or appends the provided `HTTPHeader` into the instance.////// - Parameter header: The `HTTPHeader` to update or append.public mutating func add(_ header: HTTPHeader) {update(header)}/// Case-insensitively updates or appends an `HTTPHeader` into the instance using the provided `name` and `value`.////// - Parameters:/// - name: The `HTTPHeader` name./// - value: The `HTTPHeader value.public mutating func update(name: String, value: String) {update(HTTPHeader(name: name, value: value))}/// Case-insensitively updates or appends the provided `HTTPHeader` into the instance.////// - Parameter header: The `HTTPHeader` to update or append.public mutating func update(_ header: HTTPHeader) {guard let index = headers.index(of: header.name) else {headers.append(header)return}headers.replaceSubrange(index...index, with: [header])}/// Case-insensitively removes an `HTTPHeader`, if it exists, from the instance.////// - Parameter name: The name of the `HTTPHeader` to remove.public mutating func remove(name: String) {guard let index = headers.index(of: name) else { return }headers.remove(at: index)}/// Sort the current instance by header name, case insensitively.public mutating func sort() {headers.sort { $0.name.lowercased() < $1.name.lowercased() }}/// Returns an instance sorted by header name.////// - Returns: A copy of the current instance sorted by name.public func sorted() -> HTTPHeaders {var headers = selfheaders.sort()return headers}/// Case-insensitively find a header's value by name.////// - Parameter name: The name of the header to search for, case-insensitively.////// - Returns: The value of header, if it exists.public func value(for name: String) -> String? {guard let index = headers.index(of: name) else { return nil }return headers[index].value}/// Case-insensitively access the header with the given name.////// - Parameter name: The name of the header.public subscript(_ name: String) -> String? {get { value(for: name) }set {if let value = newValue {update(name: name, value: value)} else {remove(name: name)}}}/// The dictionary representation of all headers.////// This representation does not preserve the current order of the instance.public var dictionary: [String: String] {let namesAndValues = headers.map { ($0.name, $0.value) }return Dictionary(namesAndValues, uniquingKeysWith: { _, last in last })}}extension HTTPHeaders: ExpressibleByDictionaryLiteral {public init(dictionaryLiteral elements: (String, String)...) {self.init()elements.forEach { update(name: $0.0, value: $0.1) }}}extension HTTPHeaders: ExpressibleByArrayLiteral {public init(arrayLiteral elements: HTTPHeader...) {self.init(elements)}}extension HTTPHeaders: Sequence {public func makeIterator() -> IndexingIterator<[HTTPHeader]> {headers.makeIterator()}}extension HTTPHeaders: Collection {public var startIndex: Int {headers.startIndex}public var endIndex: Int {headers.endIndex}public subscript(position: Int) -> HTTPHeader {headers[position]}public func index(after i: Int) -> Int {headers.index(after: i)}}extension HTTPHeaders: CustomStringConvertible {public var description: String {headers.map(\.description).joined(separator: "\n")}}// MARK: - HTTPHeader/// A representation of a single HTTP header's name / value pair.public struct HTTPHeader: Hashable {/// Name of the header.public let name: String/// Value of the header.public let value: String/// Creates an instance from the given `name` and `value`.////// - Parameters:/// - name: The name of the header./// - value: The value of the header.public init(name: String, value: String) {self.name = nameself.value = value}}extension HTTPHeader: CustomStringConvertible {public var description: String {"\(name): \(value)"}}extension HTTPHeader {/// Returns an `Accept` header.////// - Parameter value: The `Accept` value./// - Returns: The header.public static func accept(_ value: String) -> HTTPHeader {HTTPHeader(name: "Accept", value: value)}/// Returns an `Accept-Charset` header.////// - Parameter value: The `Accept-Charset` value./// - Returns: The header.public static func acceptCharset(_ value: String) -> HTTPHeader {HTTPHeader(name: "Accept-Charset", value: value)}/// Returns an `Accept-Language` header.////// Alamofire offers a default Accept-Language header that accumulates and encodes the system's preferred languages./// Use `HTTPHeader.defaultAcceptLanguage`.////// - Parameter value: The `Accept-Language` value.////// - Returns: The header.public static func acceptLanguage(_ value: String) -> HTTPHeader {HTTPHeader(name: "Accept-Language", value: value)}/// Returns an `Accept-Encoding` header.////// Alamofire offers a default accept encoding value that provides the most common values. Use/// `HTTPHeader.defaultAcceptEncoding`.////// - Parameter value: The `Accept-Encoding` value.////// - Returns: The headerpublic static func acceptEncoding(_ value: String) -> HTTPHeader {HTTPHeader(name: "Accept-Encoding", value: value)}/// Returns a `Basic` `Authorization` header using the `username` and `password` provided.////// - Parameters:/// - username: The username of the header./// - password: The password of the header.////// - Returns: The header.public static func authorization(username: String, password: String) -> HTTPHeader {let credential = Data("\(username):\(password)".utf8).base64EncodedString()return authorization("Basic \(credential)")}/// Returns a `Bearer` `Authorization` header using the `bearerToken` provided////// - Parameter bearerToken: The bearer token.////// - Returns: The header.public static func authorization(bearerToken: String) -> HTTPHeader {authorization("Bearer \(bearerToken)")}/// Returns an `Authorization` header.////// Alamofire provides built-in methods to produce `Authorization` headers. For a Basic `Authorization` header use/// `HTTPHeader.authorization(username:password:)`. For a Bearer `Authorization` header, use/// `HTTPHeader.authorization(bearerToken:)`.////// - Parameter value: The `Authorization` value.////// - Returns: The header.public static func authorization(_ value: String) -> HTTPHeader {HTTPHeader(name: "Authorization", value: value)}/// Returns a `Content-Disposition` header.////// - Parameter value: The `Content-Disposition` value.////// - Returns: The header.public static func contentDisposition(_ value: String) -> HTTPHeader {HTTPHeader(name: "Content-Disposition", value: value)}/// Returns a `Content-Type` header.////// All Alamofire `ParameterEncoding`s and `ParameterEncoder`s set the `Content-Type` of the request, so it may not be necessary to manually/// set this value.////// - Parameter value: The `Content-Type` value.////// - Returns: The header.public static func contentType(_ value: String) -> HTTPHeader {HTTPHeader(name: "Content-Type", value: value)}/// Returns a `User-Agent` header.////// - Parameter value: The `User-Agent` value.////// - Returns: The header.public static func userAgent(_ value: String) -> HTTPHeader {HTTPHeader(name: "User-Agent", value: value)}}extension Array where Element == HTTPHeader {/// Case-insensitively finds the index of an `HTTPHeader` with the provided name, if it exists.func index(of name: String) -> Int? {let lowercasedName = name.lowercased()return firstIndex { $0.name.lowercased() == lowercasedName }}}// MARK: - Defaultsextension HTTPHeaders {/// The default set of `HTTPHeaders` used by Alamofire. Includes `Accept-Encoding`, `Accept-Language`, and/// `User-Agent`.public static let `default`: HTTPHeaders = [.defaultAcceptEncoding,.defaultAcceptLanguage,.defaultUserAgent]}extension HTTPHeader {/// Returns Alamofire's default `Accept-Encoding` header, appropriate for the encodings supported by particular OS/// versions.////// See the [Accept-Encoding HTTP header documentation](https://tools.ietf.org/html/rfc7230#section-4.2.3) .public static let defaultAcceptEncoding: HTTPHeader = {let encodings: [String]if #available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, *) {encodings = ["br", "gzip", "deflate"]} else {encodings = ["gzip", "deflate"]}return .acceptEncoding(encodings.qualityEncoded())}()/// Returns Alamofire's default `Accept-Language` header, generated by querying `Locale` for the user's/// `preferredLanguages`.////// See the [Accept-Language HTTP header documentation](https://tools.ietf.org/html/rfc7231#section-5.3.5).public static let defaultAcceptLanguage: HTTPHeader = .acceptLanguage(Locale.preferredLanguages.prefix(6).qualityEncoded())/// Returns Alamofire's default `User-Agent` header.////// See the [User-Agent header documentation](https://tools.ietf.org/html/rfc7231#section-5.5.3).////// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 13.0.0) Alamofire/5.0.0`public static let defaultUserAgent: HTTPHeader = {let info = Bundle.main.infoDictionarylet executable = (info?["CFBundleExecutable"] as? String) ??(ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init)) ??"Unknown"let bundle = info?["CFBundleIdentifier"] as? String ?? "Unknown"let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown"let appBuild = info?["CFBundleVersion"] as? String ?? "Unknown"let osNameVersion: String = {let version = ProcessInfo.processInfo.operatingSystemVersionlet versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"let osName: String = {#if os(iOS)#if targetEnvironment(macCatalyst)return "macOS(Catalyst)"#elsereturn "iOS"#endif#elseif os(watchOS)return "watchOS"#elseif os(tvOS)return "tvOS"#elseif os(macOS)return "macOS"#elseif os(Linux)return "Linux"#elseif os(Windows)return "Windows"#elsereturn "Unknown"#endif}()return "\(osName) \(versionString)"}()let alamofireVersion = "Alamofire/\(version)"let userAgent = "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(alamofireVersion)"return .userAgent(userAgent)}()}extension Collection where Element == String {func qualityEncoded() -> String {enumerated().map { index, encoding inlet quality = 1.0 - (Double(index) * 0.1)return "\(encoding);q=\(quality)"}.joined(separator: ", ")}}// MARK: - System Type Extensionsextension URLRequest {/// Returns `allHTTPHeaderFields` as `HTTPHeaders`.public var headers: HTTPHeaders {get { allHTTPHeaderFields.map(HTTPHeaders.init) ?? HTTPHeaders() }set { allHTTPHeaderFields = newValue.dictionary }}}extension HTTPURLResponse {/// Returns `allHeaderFields` as `HTTPHeaders`.public var headers: HTTPHeaders {(allHeaderFields as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders()}}extension URLSessionConfiguration {/// Returns `httpAdditionalHeaders` as `HTTPHeaders`.public var headers: HTTPHeaders {get { (httpAdditionalHeaders as? [String: String]).map(HTTPHeaders.init) ?? HTTPHeaders() }set { httpAdditionalHeaders = newValue.dictionary }}}