Skip to content

Commit bf7e3ff

Browse files
committed
Merge pr/34 and pr/56
2 parents addc5d2 + 99fe799 commit bf7e3ff

File tree

4 files changed

+285
-34
lines changed

4 files changed

+285
-34
lines changed

Foundation.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@
200200
C93559291C12C49F009FD6A9 /* TestNSAffineTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = C93559281C12C49F009FD6A9 /* TestNSAffineTransform.swift */; };
201201
DCDBB8331C1768AC00313299 /* TestNSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDBB8321C1768AC00313299 /* TestNSData.swift */; };
202202
E876A73E1C1180E000F279EC /* TestNSRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = E876A73D1C1180E000F279EC /* TestNSRange.swift */; };
203+
848A30581C137B3500C83206 /* TestNSHTTPCookie.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848A30571C137B3500C83206 /* TestNSHTTPCookie.swift */; };
203204
EA66F6361BEED03E00136161 /* TargetConditionals.h in Headers */ = {isa = PBXBuildFile; fileRef = EA66F6351BEED03E00136161 /* TargetConditionals.h */; settings = {ATTRIBUTES = (Public, ); }; };
204205
EA66F6441BF1619600136161 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA66F6381BF1619600136161 /* main.swift */; };
205206
EA66F6481BF1619600136161 /* NSURLTestData.plist in Resources */ = {isa = PBXBuildFile; fileRef = EA66F63B1BF1619600136161 /* NSURLTestData.plist */; };
@@ -526,6 +527,7 @@
526527
C93559281C12C49F009FD6A9 /* TestNSAffineTransform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSAffineTransform.swift; sourceTree = "<group>"; };
527528
DCDBB8321C1768AC00313299 /* TestNSData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSData.swift; sourceTree = "<group>"; };
528529
E876A73D1C1180E000F279EC /* TestNSRange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSRange.swift; sourceTree = "<group>"; };
530+
848A30571C137B3500C83206 /* TestNSHTTPCookie.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TestNSHTTPCookie.swift; path = TestFoundation/TestNSHTTPCookie.swift; sourceTree = SOURCE_ROOT; };
529531
EA313DFC1BE7F2E90060A403 /* CFURLComponents_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFURLComponents_Internal.h; sourceTree = "<group>"; };
530532
EA313DFD1BE7F2E90060A403 /* CFURLComponents_URIParser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CFURLComponents_URIParser.c; sourceTree = "<group>"; };
531533
EA313DFE1BE7F2E90060A403 /* CFURLComponents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CFURLComponents.c; sourceTree = "<group>"; };
@@ -1016,6 +1018,7 @@
10161018
EA66F63C1BF1619600136161 /* TestNSArray.swift */,
10171019
52829AD61C160D64003BC4EF /* TestNSCalendar.swift */,
10181020
EA66F63D1BF1619600136161 /* TestNSDictionary.swift */,
1021+
848A30571C137B3500C83206 /* TestNSHTTPCookie.swift */,
10191022
EA66F63E1BF1619600136161 /* TestNSIndexSet.swift */,
10201023
EA66F63F1BF1619600136161 /* TestNSNumber.swift */,
10211024
4DC1D07F1C12EEEF00B5948A /* TestNSPipe.swift */,
@@ -1725,6 +1728,7 @@
17251728
DCDBB8331C1768AC00313299 /* TestNSData.swift in Sources */,
17261729
EA66F64E1BF1619600136161 /* TestNSIndexSet.swift in Sources */,
17271730
22B9C1E11C165D7A00DECFF9 /* TestNSDate.swift in Sources */,
1731+
848A30581C137B3500C83206 /* TestNSHTTPCookie.swift in Sources */,
17281732
EA66F6541BF1619600136161 /* TestNSSet.swift in Sources */,
17291733
EA66F64A1BF1619600136161 /* TestNSArray.swift in Sources */,
17301734
5BC1D8BE1BF3B09E009D3973 /* TestNSCharacterSet.swift in Sources */,

Foundation/NSHTTPCookie.swift

Lines changed: 162 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,23 @@ public let NSHTTPCookiePort: String = "Port"
9595
attributes of a cookie.
9696
*/
9797
public class NSHTTPCookie : NSObject {
98-
var _properties: [String : AnyObject]
99-
98+
private struct Cookie {
99+
let comment: String?
100+
let commentURL: NSURL?
101+
let domain: String
102+
let expiresDate: NSDate?
103+
let HTTPOnly: Bool
104+
let secure: Bool
105+
let sessionOnly: Bool
106+
let name: String
107+
let path: String
108+
let portList: [NSNumber]?
109+
let properties: [String: Any]?
110+
let value: String
111+
let version: Int
112+
}
113+
private let cookieRepresentation: Cookie
114+
100115
/*!
101116
@method initWithProperties:
102117
@abstract Initialize a NSHTTPCookie object with a dictionary of
@@ -220,20 +235,121 @@ public class NSHTTPCookie : NSObject {
220235
dictionary keys is invalid, for example because a required key is
221236
missing, or a recognized key maps to an illegal value.
222237
*/
223-
public init?(properties: [String : AnyObject]) {
224-
func isValidProperty(property: String?) -> Bool {
225-
if let propertyValue = property {
226-
return propertyValue.length > 0 && (propertyValue as NSString).rangeOfString("/n").location == NSNotFound
238+
/// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative
239+
/// - Note: Since this API is under consideration it may be either removed or revised in the near future
240+
public init?(properties: [String : Any]) {
241+
guard let
242+
path = properties[NSHTTPCookiePath] as? String,
243+
name = properties[NSHTTPCookieName] as? String,
244+
value = properties[NSHTTPCookieValue] as? String
245+
else {
246+
return nil
247+
}
248+
249+
let canonicalDomain: String
250+
if let domain = properties[NSHTTPCookieDomain] as? String {
251+
canonicalDomain = domain
252+
} else if let
253+
originURL = properties[NSHTTPCookieOriginURL] as? NSURL,
254+
host = originURL.host
255+
{
256+
canonicalDomain = host
257+
} else {
258+
return nil
259+
}
260+
261+
let secure: Bool
262+
if let
263+
secureString = properties[NSHTTPCookieSecure] as? String
264+
where secureString.characters.count > 0
265+
{
266+
secure = true
267+
} else {
268+
secure = false
269+
}
270+
271+
let version: Int
272+
if let
273+
versionString = properties[NSHTTPCookieSecure] as? String
274+
where versionString == "1"
275+
{
276+
version = 1
277+
} else {
278+
version = 0
279+
}
280+
281+
let portList: [NSNumber]?
282+
if let portString = properties[NSHTTPCookiePort] as? String {
283+
portList = portString.characters
284+
.split(",")
285+
.flatMap { Int(String($0)) }
286+
.map { NSNumber(integer: $0) }
287+
} else {
288+
portList = nil
289+
}
290+
291+
// TODO: factor into a utility function
292+
let expiresDate: NSDate?
293+
if version == 0 {
294+
let expiresProperty = properties[NSHTTPCookieExpires]
295+
if let date = expiresProperty as? NSDate {
296+
// If the dictionary value is already an NSDate,
297+
// nothing left to do
298+
expiresDate = date
299+
} else if let dateString = expiresProperty as? String {
300+
// If the dictionary value is a string, parse it
301+
let formatter = NSDateFormatter()
302+
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm"
303+
304+
let timeZone = NSTimeZone(abbreviation: "GMT")
305+
formatter.timeZone = timeZone
306+
307+
expiresDate = formatter.dateFromString(dateString)
308+
} else {
309+
expiresDate = nil
310+
}
311+
} else if version == 1 {
312+
if let
313+
maximumAge = properties[NSHTTPCookieMaximumAge] as? String,
314+
secondsFromNow = Double(maximumAge)
315+
{
316+
expiresDate = NSDate(timeIntervalSinceNow: secondsFromNow)
317+
} else {
318+
expiresDate = nil
227319
}
228-
return false
320+
} else {
321+
expiresDate = nil
229322
}
230-
if !isValidProperty(properties[NSHTTPCookiePath] as? String)
231-
|| !isValidProperty(properties[NSHTTPCookieDomain] as? String)
232-
|| !isValidProperty(properties[NSHTTPCookieName] as? String)
233-
|| !isValidProperty(properties[NSHTTPCookieValue] as? String) {
234-
return nil
323+
324+
var discard = false
325+
if let discardString = properties[NSHTTPCookieDiscard] as? String {
326+
discard = discardString == "TRUE"
327+
} else if let
328+
_ = properties[NSHTTPCookieMaximumAge] as? String
329+
where version >= 1
330+
{
331+
discard = false
235332
}
236-
_properties = properties
333+
334+
// TODO: commentURL can be a string or NSURL
335+
336+
self.cookieRepresentation = Cookie(
337+
comment: version == 1 ?
338+
properties[NSHTTPCookieComment] as? String : nil,
339+
commentURL: version == 1 ?
340+
properties[NSHTTPCookieCommentURL] as? NSURL : nil,
341+
domain: canonicalDomain,
342+
expiresDate: expiresDate,
343+
HTTPOnly: secure,
344+
secure: secure,
345+
sessionOnly: discard,
346+
name: name,
347+
path: path,
348+
portList: version == 1 ? portList : nil,
349+
properties: properties,
350+
value: value,
351+
version: version
352+
)
237353
}
238354

239355
/*!
@@ -258,7 +374,17 @@ public class NSHTTPCookie : NSObject {
258374
@result An NSDictionary where the keys are header field names, and the values
259375
are the corresponding header field values.
260376
*/
261-
public class func requestHeaderFieldsWithCookies(cookies: [NSHTTPCookie]) -> [String : String] { NSUnimplemented() }
377+
public class func requestHeaderFieldsWithCookies(cookies: [NSHTTPCookie]) -> [String : String] {
378+
var cookieString = cookies.reduce("") { (sum, next) -> String in
379+
return sum + "\(next.cookieRepresentation.name)=\(next.cookieRepresentation.value); "
380+
}
381+
//Remove the final trailing semicolon and whitespace
382+
if ( cookieString.length > 0 ) {
383+
cookieString.removeAtIndex(cookieString.endIndex.predecessor())
384+
cookieString.removeAtIndex(cookieString.endIndex.predecessor())
385+
}
386+
return ["Cookie": cookieString]
387+
}
262388

263389
/*!
264390
@method cookiesWithResponseHeaderFields:forURL:
@@ -283,8 +409,10 @@ public class NSHTTPCookie : NSObject {
283409
for descriptions of the supported keys and values.
284410
@result The dictionary representation of the receiver.
285411
*/
286-
public var properties: [String : AnyObject]? {
287-
return _properties
412+
/// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative
413+
/// - Note: Since this API is under consideration it may be either removed or revised in the near future
414+
public var properties: [String : Any]? {
415+
return self.cookieRepresentation.properties
288416
}
289417

290418
/*!
@@ -295,7 +423,7 @@ public class NSHTTPCookie : NSObject {
295423
@result the version of the receiver.
296424
*/
297425
public var version: Int {
298-
return (_properties[NSHTTPCookieVersion] as! Int)
426+
return self.cookieRepresentation.version
299427
}
300428

301429
/*!
@@ -304,7 +432,7 @@ public class NSHTTPCookie : NSObject {
304432
@result the name of the receiver.
305433
*/
306434
public var name: String {
307-
return (_properties[NSHTTPCookieName] as! NSString)._swiftObject
435+
return self.cookieRepresentation.name
308436
}
309437

310438
/*!
@@ -313,7 +441,7 @@ public class NSHTTPCookie : NSObject {
313441
@result the value of the receiver.
314442
*/
315443
public var value: String {
316-
return (_properties[NSHTTPCookieValue] as! NSString)._swiftObject
444+
return self.cookieRepresentation.value
317445
}
318446

319447
/*!
@@ -326,10 +454,7 @@ public class NSHTTPCookie : NSObject {
326454
@result The expires date of the receiver.
327455
*/
328456
/*@NSCopying*/ public var expiresDate: NSDate? {
329-
if let expiresDate = _properties[NSHTTPCookieExpires] as? NSDate {
330-
return expiresDate
331-
}
332-
return nil
457+
return self.cookieRepresentation.expiresDate
333458
}
334459

335460
/*!
@@ -340,7 +465,7 @@ public class NSHTTPCookie : NSObject {
340465
be discarded at the end of the session.
341466
*/
342467
public var sessionOnly: Bool {
343-
return !(_properties[NSHTTPCookieExpires] is NSDate)
468+
return self.cookieRepresentation.sessionOnly
344469
}
345470

346471
/*!
@@ -353,7 +478,7 @@ public class NSHTTPCookie : NSObject {
353478
@result The domain of the receiver.
354479
*/
355480
public var domain: String {
356-
return (_properties[NSHTTPCookieDomain] as! NSString)._swiftObject
481+
return self.cookieRepresentation.domain
357482
}
358483

359484
/*!
@@ -365,7 +490,7 @@ public class NSHTTPCookie : NSObject {
365490
@result The path of the receiver.
366491
*/
367492
public var path: String {
368-
return (_properties[NSHTTPCookiePath] as! NSString)._swiftObject
493+
return self.cookieRepresentation.path
369494
}
370495

371496
/*!
@@ -379,7 +504,9 @@ public class NSHTTPCookie : NSObject {
379504
@result YES if this cookie should be sent only over secure channels,
380505
NO otherwise.
381506
*/
382-
public var secure: Bool { NSUnimplemented() }
507+
public var secure: Bool {
508+
return self.cookieRepresentation.secure
509+
}
383510

384511
/*!
385512
@method isHTTPOnly
@@ -393,7 +520,9 @@ public class NSHTTPCookie : NSObject {
393520
@result YES if this cookie should only be sent via HTTP headers,
394521
NO otherwise.
395522
*/
396-
public var HTTPOnly: Bool { NSUnimplemented() }
523+
public var HTTPOnly: Bool {
524+
return self.cookieRepresentation.HTTPOnly
525+
}
397526

398527
/*!
399528
@method comment
@@ -405,7 +534,7 @@ public class NSHTTPCookie : NSObject {
405534
comment.
406535
*/
407536
public var comment: String? {
408-
return (_properties[NSHTTPCookieComment] as! NSString)._swiftObject
537+
return self.cookieRepresentation.comment
409538
}
410539

411540
/*!
@@ -418,10 +547,7 @@ public class NSHTTPCookie : NSObject {
418547
has no comment URL.
419548
*/
420549
/*@NSCopying*/ public var commentURL: NSURL? {
421-
if let commentURL = _properties[NSHTTPCookieCommentURL] as? NSURL {
422-
return commentURL
423-
}
424-
return nil
550+
return self.cookieRepresentation.commentURL
425551
}
426552

427553
/*!
@@ -435,6 +561,8 @@ public class NSHTTPCookie : NSObject {
435561
array may be nil, in which case this cookie can be sent to any
436562
port.
437563
*/
438-
public var portList: [NSNumber]? { NSUnimplemented() }
564+
public var portList: [NSNumber]? {
565+
return self.cookieRepresentation.portList
566+
}
439567
}
440568

0 commit comments

Comments
 (0)