| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See http://swift.org/LICENSE.txt for license information |
| // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| |
| /// Key for cookie name |
| public let NSHTTPCookieName: String = "Name" |
| |
| /// Key for cookie value |
| public let NSHTTPCookieValue: String = "Value" |
| |
| /// Key for cookie origin URL |
| public let NSHTTPCookieOriginURL: String = "OriginURL" |
| |
| /// Key for cookie version |
| public let NSHTTPCookieVersion: String = "Version" |
| |
| /// Key for cookie domain |
| public let NSHTTPCookieDomain: String = "Domain" |
| |
| /// Key for cookie path |
| public let NSHTTPCookiePath: String = "Path" |
| |
| /// Key for cookie secure flag |
| public let NSHTTPCookieSecure: String = "Secure" |
| |
| /// Key for cookie expiration date |
| public let NSHTTPCookieExpires: String = "Expires" |
| |
| /// Key for cookie comment text |
| public let NSHTTPCookieComment: String = "Comment" |
| |
| /// Key for cookie comment URL |
| public let NSHTTPCookieCommentURL: String = "CommentURL" |
| |
| /// Key for cookie discard (session-only) flag |
| public let NSHTTPCookieDiscard: String = "Discard" |
| |
| /// Key for cookie maximum age (an alternate way of specifying the expiration) |
| public let NSHTTPCookieMaximumAge: String = "Max-Age" |
| |
| /// Key for cookie ports |
| public let NSHTTPCookiePort: String = "Port" |
| |
| |
| /// `NSHTTPCookie` represents an http cookie. |
| /// |
| /// An `NSHTTPCookie` instance represents a single http cookie. It is |
| /// an immutable object initialized from a dictionary that contains |
| /// the various cookie attributes. It has accessors to get the various |
| /// attributes of a cookie. |
| public class NSHTTPCookie : NSObject { |
| |
| let _comment: String? |
| let _commentURL: NSURL? |
| let _domain: String |
| let _expiresDate: NSDate? |
| let _HTTPOnly: Bool |
| let _secure: Bool |
| let _sessionOnly: Bool |
| let _name: String |
| let _path: String |
| let _portList: [NSNumber]? |
| let _value: String |
| let _version: Int |
| var _properties: [String : Any] |
| |
| /// Initialize a NSHTTPCookie object with a dictionary of parameters |
| /// |
| /// - Parameter properties: The dictionary of properties to be used to |
| /// initialize this cookie. |
| /// |
| /// Supported dictionary keys and value types for the |
| /// given dictionary are as follows. |
| /// |
| /// All properties can handle an NSString value, but some can also |
| /// handle other types. |
| /// |
| /// <table border=1 cellspacing=2 cellpadding=4> |
| /// <tr> |
| /// <th>Property key constant</th> |
| /// <th>Type of value</th> |
| /// <th>Required</th> |
| /// <th>Description</th> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieComment</td> |
| /// <td>NSString</td> |
| /// <td>NO</td> |
| /// <td>Comment for the cookie. Only valid for version 1 cookies and |
| /// later. Default is nil.</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieCommentURL</td> |
| /// <td>NSURL or NSString</td> |
| /// <td>NO</td> |
| /// <td>Comment URL for the cookie. Only valid for version 1 cookies |
| /// and later. Default is nil.</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieDomain</td> |
| /// <td>NSString</td> |
| /// <td>Special, a value for either NSHTTPCookieOriginURL or |
| /// NSHTTPCookieDomain must be specified.</td> |
| /// <td>Domain for the cookie. Inferred from the value for |
| /// NSHTTPCookieOriginURL if not provided.</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieDiscard</td> |
| /// <td>NSString</td> |
| /// <td>NO</td> |
| /// <td>A string stating whether the cookie should be discarded at |
| /// the end of the session. String value must be either "TRUE" or |
| /// "FALSE". Default is "FALSE", unless this is cookie is version |
| /// 1 or greater and a value for NSHTTPCookieMaximumAge is not |
| /// specified, in which case it is assumed "TRUE".</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieExpires</td> |
| /// <td>NSDate or NSString</td> |
| /// <td>NO</td> |
| /// <td>Expiration date for the cookie. Used only for version 0 |
| /// cookies. Ignored for version 1 or greater.</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieMaximumAge</td> |
| /// <td>NSString</td> |
| /// <td>NO</td> |
| /// <td>A string containing an integer value stating how long in |
| /// seconds the cookie should be kept, at most. Only valid for |
| /// version 1 cookies and later. Default is "0".</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieName</td> |
| /// <td>NSString</td> |
| /// <td>YES</td> |
| /// <td>Name of the cookie</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieOriginURL</td> |
| /// <td>NSURL or NSString</td> |
| /// <td>Special, a value for either NSHTTPCookieOriginURL or |
| /// NSHTTPCookieDomain must be specified.</td> |
| /// <td>URL that set this cookie. Used as default for other fields |
| /// as noted.</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookiePath</td> |
| /// <td>NSString</td> |
| /// <td>NO</td> |
| /// <td>Path for the cookie. Inferred from the value for |
| /// NSHTTPCookieOriginURL if not provided. Default is "/".</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookiePort</td> |
| /// <td>NSString</td> |
| /// <td>NO</td> |
| /// <td>comma-separated integer values specifying the ports for the |
| /// cookie. Only valid for version 1 cookies and later. Default is |
| /// empty string ("").</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieSecure</td> |
| /// <td>NSString</td> |
| /// <td>NO</td> |
| /// <td>A string stating whether the cookie should be transmitted |
| /// only over secure channels. String value must be either "TRUE" |
| /// or "FALSE". Default is "FALSE".</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieValue</td> |
| /// <td>NSString</td> |
| /// <td>YES</td> |
| /// <td>Value of the cookie</td> |
| /// </tr> |
| /// <tr> |
| /// <td>NSHTTPCookieVersion</td> |
| /// <td>NSString</td> |
| /// <td>NO</td> |
| /// <td>Specifies the version of the cookie. Must be either "0" or |
| /// "1". Default is "0".</td> |
| /// </tr> |
| /// </table> |
| /// |
| /// All other keys are ignored. |
| /// |
| /// - Returns: An initialized `NSHTTPCookie`, or nil if the set of |
| /// dictionary keys is invalid, for example because a required key is |
| /// missing, or a recognized key maps to an illegal value. |
| /// |
| /// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative |
| /// - Note: Since this API is under consideration it may be either removed or revised in the near future |
| public init?(properties: [String : Any]) { |
| guard let |
| path = properties[NSHTTPCookiePath] as? String, |
| name = properties[NSHTTPCookieName] as? String, |
| value = properties[NSHTTPCookieValue] as? String |
| else { |
| return nil |
| } |
| |
| let canonicalDomain: String |
| if let domain = properties[NSHTTPCookieDomain] as? String { |
| canonicalDomain = domain |
| } else if let |
| originURL = properties[NSHTTPCookieOriginURL] as? NSURL, |
| host = originURL.host |
| { |
| canonicalDomain = host |
| } else { |
| return nil |
| } |
| |
| _path = path |
| _name = name |
| _value = value |
| _domain = canonicalDomain |
| |
| if let |
| secureString = properties[NSHTTPCookieSecure] as? String |
| where secureString.characters.count > 0 |
| { |
| _secure = true |
| } else { |
| _secure = false |
| } |
| |
| let version: Int |
| if let |
| versionString = properties[NSHTTPCookieVersion] as? String |
| where versionString == "1" |
| { |
| version = 1 |
| } else { |
| version = 0 |
| } |
| _version = version |
| |
| if let portString = properties[NSHTTPCookiePort] as? String |
| where _version == 1 { |
| _portList = portString.characters |
| .split(separator: ",") |
| .flatMap { Int(String($0)) } |
| .map { NSNumber(value: $0) } |
| } else { |
| _portList = nil |
| } |
| |
| // TODO: factor into a utility function |
| if version == 0 { |
| let expiresProperty = properties[NSHTTPCookieExpires] |
| if let date = expiresProperty as? NSDate { |
| _expiresDate = date |
| } else if let dateString = expiresProperty as? String { |
| let formatter = NSDateFormatter() |
| formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss O" // per RFC 6265 '<rfc1123-date, defined in [RFC2616], Section 3.3.1>' |
| let timeZone = NSTimeZone(abbreviation: "GMT") |
| formatter.timeZone = timeZone |
| _expiresDate = formatter.dateFromString(dateString) |
| } else { |
| _expiresDate = nil |
| } |
| } else if let |
| maximumAge = properties[NSHTTPCookieMaximumAge] as? String, |
| secondsFromNow = Int(maximumAge) |
| where _version == 1 { |
| _expiresDate = NSDate(timeIntervalSinceNow: Double(secondsFromNow)) |
| } else { |
| _expiresDate = nil |
| } |
| |
| |
| if let discardString = properties[NSHTTPCookieDiscard] as? String { |
| _sessionOnly = discardString == "TRUE" |
| } else { |
| _sessionOnly = properties[NSHTTPCookieMaximumAge] == nil && version >= 1 |
| } |
| if version == 0 { |
| _comment = nil |
| _commentURL = nil |
| } else { |
| _comment = properties[NSHTTPCookieComment] as? String |
| if let commentURL = properties[NSHTTPCookieCommentURL] as? NSURL { |
| _commentURL = commentURL |
| } else if let commentURL = properties[NSHTTPCookieCommentURL] as? String { |
| _commentURL = NSURL(string: commentURL) |
| } else { |
| _commentURL = nil |
| } |
| } |
| _HTTPOnly = false |
| _properties = [NSHTTPCookieComment : properties[NSHTTPCookieComment], |
| NSHTTPCookieCommentURL : properties[NSHTTPCookieCommentURL], |
| "Created" : NSDate().timeIntervalSinceReferenceDate, // Cocoa Compatibility |
| NSHTTPCookieDiscard : _sessionOnly, |
| NSHTTPCookieDomain : _domain, |
| NSHTTPCookieExpires : _expiresDate, |
| NSHTTPCookieMaximumAge : properties[NSHTTPCookieMaximumAge], |
| NSHTTPCookieName : _name, |
| NSHTTPCookieOriginURL : properties[NSHTTPCookieOriginURL], |
| NSHTTPCookiePath : _path, |
| NSHTTPCookiePort : _portList, |
| NSHTTPCookieSecure : _secure, |
| NSHTTPCookieValue : _value, |
| NSHTTPCookieVersion : _version |
| ] |
| } |
| |
| /// Return a dictionary of header fields that can be used to add the |
| /// specified cookies to the request. |
| /// |
| /// - Parameter cookies: The cookies to turn into request headers. |
| /// - Returns: A dictionary where the keys are header field names, and the values |
| /// are the corresponding header field values. |
| public class func requestHeaderFields(with cookies: [NSHTTPCookie]) -> [String : String] { |
| var cookieString = cookies.reduce("") { (sum, next) -> String in |
| return sum + "\(next._name)=\(next._value); " |
| } |
| //Remove the final trailing semicolon and whitespace |
| if ( cookieString.length > 0 ) { |
| cookieString.characters.removeLast() |
| cookieString.characters.removeLast() |
| } |
| return ["Cookie": cookieString] |
| } |
| |
| /// Return an array of cookies parsed from the specified response header fields and URL. |
| /// |
| /// This method will ignore irrelevant header fields so |
| /// you can pass a dictionary containing data other than cookie data. |
| /// - Parameter headerFields: The response header fields to check for cookies. |
| /// - Parameter URL: The URL that the cookies came from - relevant to how the cookies are interpeted. |
| /// - Returns: An array of NSHTTPCookie objects |
| public class func cookies(withResponseHeaderFields headerFields: [String : String], forURL URL: NSURL) -> [NSHTTPCookie] { NSUnimplemented() } |
| |
| /// Returns a dictionary representation of the receiver. |
| /// |
| /// This method returns a dictionary representation of the |
| /// `NSHTTPCookie` which can be saved and passed to |
| /// `init(properties:)` later to reconstitute an equivalent cookie. |
| /// |
| /// See the `NSHTTPCookie` `init(properties:)` method for |
| /// more information on the constraints imposed on the dictionary, and |
| /// for descriptions of the supported keys and values. |
| /// |
| /// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative |
| /// - Note: Since this API is under consideration it may be either removed or revised in the near future |
| public var properties: [String : Any]? { |
| return _properties |
| } |
| |
| /// The version of the receiver. |
| /// |
| /// Version 0 maps to "old-style" Netscape cookies. |
| /// Version 1 maps to RFC2965 cookies. There may be future versions. |
| public var version: Int { |
| return _version |
| } |
| |
| /// The name of the receiver. |
| public var name: String { |
| return _name |
| } |
| |
| /// The value of the receiver. |
| public var value: String { |
| return _value |
| } |
| |
| /// Returns The expires date of the receiver. |
| /// |
| /// The expires date is the date when the cookie should be |
| /// deleted. The result will be nil if there is no specific expires |
| /// date. This will be the case only for *session-only* cookies. |
| /*@NSCopying*/ public var expiresDate: NSDate? { |
| return _expiresDate |
| } |
| |
| /// Whether the receiver is session-only. |
| /// |
| /// `true` if this receiver should be discarded at the end of the |
| /// session (regardless of expiration date), `false` if receiver need not |
| /// be discarded at the end of the session. |
| public var isSessionOnly: Bool { |
| return _sessionOnly |
| } |
| |
| /// The domain of the receiver. |
| /// |
| /// This value specifies URL domain to which the cookie |
| /// should be sent. A domain with a leading dot means the cookie |
| /// should be sent to subdomains as well, assuming certain other |
| /// restrictions are valid. See RFC 2965 for more detail. |
| public var domain: String { |
| return _domain |
| } |
| |
| /// The path of the receiver. |
| /// |
| /// This value specifies the URL path under the cookie's |
| /// domain for which this cookie should be sent. The cookie will also |
| /// be sent for children of that path, so `"/"` is the most general. |
| public var path: String { |
| return _path |
| } |
| |
| /// Whether the receiver should be sent only over secure channels |
| /// |
| /// Cookies may be marked secure by a server (or by a javascript). |
| /// Cookies marked as such must only be sent via an encrypted connection to |
| /// trusted servers (i.e. via SSL or TLS), and should not be delievered to any |
| /// javascript applications to prevent cross-site scripting vulnerabilities. |
| public var isSecure: Bool { |
| return _secure |
| } |
| |
| /// Whether the receiver should only be sent to HTTP servers per RFC 2965 |
| /// |
| /// Cookies may be marked as HTTPOnly by a server (or by a javascript). |
| /// Cookies marked as such must only be sent via HTTP Headers in HTTP Requests |
| /// for URL's that match both the path and domain of the respective Cookies. |
| /// Specifically these cookies should not be delivered to any javascript |
| /// applications to prevent cross-site scripting vulnerabilities. |
| public var isHTTPOnly: Bool { |
| return _HTTPOnly |
| } |
| |
| /// The comment of the receiver. |
| /// |
| /// This value specifies a string which is suitable for |
| /// presentation to the user explaining the contents and purpose of this |
| /// cookie. It may be nil. |
| public var comment: String? { |
| return _comment |
| } |
| |
| /// The comment URL of the receiver. |
| /// |
| /// This value specifies a URL which is suitable for |
| /// presentation to the user as a link for further information about |
| /// this cookie. It may be nil. |
| /*@NSCopying*/ public var commentURL: NSURL? { |
| return _commentURL |
| } |
| |
| /// The list ports to which the receiver should be sent. |
| /// |
| /// This value specifies an NSArray of NSNumbers |
| /// (containing integers) which specify the only ports to which this |
| /// cookie should be sent. |
| /// |
| /// The array may be nil, in which case this cookie can be sent to any |
| /// port. |
| public var portList: [NSNumber]? { |
| return _portList |
| } |
| } |