6
6
// See http://swift.org/LICENSE.txt for license information
7
7
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8
8
//
9
-
9
+ import Dispatch
10
10
11
11
/*!
12
12
@enum NSHTTPCookieAcceptPolicy
18
18
*/
19
19
extension HTTPCookie {
20
20
public enum AcceptPolicy : UInt {
21
-
22
21
case always
23
22
case never
24
23
case onlyFromMainDocumentDomain
@@ -35,11 +34,37 @@ extension HTTPCookie {
35
34
generate cookie-related HTTP header fields.
36
35
*/
37
36
open class HTTPCookieStorage : NSObject {
38
-
39
- public override init ( ) { NSUnimplemented ( ) }
40
-
37
+
38
+ private static var sharedStorage : HTTPCookieStorage ?
39
+ private static var sharedCookieStorages : [ String : HTTPCookieStorage ] = [ : ] //for group storage containers
40
+
41
+ private let cookieFilePath : String = NSHomeDirectory ( ) + " /.cookies "
42
+ private let workQueue : DispatchQueue = DispatchQueue ( label: " HTTPCookieStorage.workqueue " )
43
+ var allCookies : [ String : HTTPCookie ]
44
+
45
+ public override init ( ) {
46
+ allCookies = [ : ]
47
+ cookieAcceptPolicy = . always
48
+ super. init ( )
49
+ loadPersistedCookies ( )
50
+ }
51
+
52
+ private func loadPersistedCookies( ) {
53
+ guard let cookies = NSMutableDictionary ( contentsOfFile: cookieFilePath) else { return }
54
+ var cookies0 = _SwiftValue. fetch ( cookies) as? [ String : [ String : Any ] ] ?? [ : ]
55
+ for key in cookies0. keys {
56
+ if let cookie = createCookie ( cookies0 [ key] !) {
57
+ allCookies [ key] = cookie
58
+ }
59
+ }
60
+ }
61
+
41
62
open var cookies : [ HTTPCookie ] ? {
42
- NSUnimplemented ( )
63
+ var theCookies : [ HTTPCookie ] ?
64
+ workQueue. sync {
65
+ theCookies = Array ( self . allCookies. values)
66
+ }
67
+ return theCookies
43
68
}
44
69
45
70
/*!
@@ -49,7 +74,14 @@ open class HTTPCookieStorage: NSObject {
49
74
@discussion Starting in OS X 10.11, each app has its own sharedHTTPCookieStorage singleton,
50
75
which will not be shared with other applications.
51
76
*/
52
- class var shared : HTTPCookieStorage { get { NSUnimplemented ( ) } }
77
+ open class var shared : HTTPCookieStorage {
78
+ get {
79
+ if sharedStorage == nil {
80
+ sharedStorage = HTTPCookieStorage ( )
81
+ }
82
+ return sharedStorage!
83
+ }
84
+ }
53
85
54
86
/*!
55
87
@method sharedCookieStorageForGroupContainerIdentifier:
@@ -62,28 +94,100 @@ open class HTTPCookieStorage: NSObject {
62
94
shared among all applications and extensions with access to the same application group. Subsequent calls to this
63
95
method with the same identifier will return the same cookie storage instance.
64
96
*/
65
- class func sharedCookieStorage( forGroupContainerIdentifier identifier: String ) -> HTTPCookieStorage { NSUnimplemented ( ) }
97
+ class func sharedCookieStorage( forGroupContainerIdentifier identifier: String ) -> HTTPCookieStorage {
98
+ guard let cookieStorage = sharedCookieStorages [ identifier] else {
99
+ let newCookieStorage = HTTPCookieStorage ( )
100
+ sharedCookieStorages [ identifier] = newCookieStorage
101
+ return newCookieStorage
102
+ }
103
+ return cookieStorage
104
+ }
105
+
66
106
67
107
/*!
68
108
@method setCookie:
69
109
@abstract Set a cookie
70
110
@discussion The cookie will override an existing cookie with the
71
111
same name, domain and path, if any.
72
112
*/
73
- open func setCookie( _ cookie: HTTPCookie ) { NSUnimplemented ( ) }
113
+ open func setCookie( _ cookie: HTTPCookie ) {
114
+ workQueue. sync {
115
+ if cookieAcceptPolicy == . never { return }
116
+
117
+ //add or replace
118
+ let key = cookie. domain + cookie. path + cookie. name
119
+ if let _ = allCookies. index ( forKey: key) {
120
+ allCookies. updateValue ( cookie, forKey: key)
121
+ } else {
122
+ allCookies [ key] = cookie
123
+ }
124
+
125
+ //remove stale cookies, these may include the one we just added
126
+ let expired = allCookies. filter { ( _, value) in value. expiresDate != nil && value. expiresDate!. timeIntervalSinceNow < 0 }
127
+ for (key, _) in expired {
128
+ self . allCookies. removeValue ( forKey: key)
129
+ }
130
+
131
+ updatePersistentStore ( )
132
+ }
133
+ }
74
134
135
+ private func createCookie( _ properties: [ String : Any ] ) -> HTTPCookie ? {
136
+ var cookieProperties : [ HTTPCookiePropertyKey : Any ] = [ : ]
137
+ for (key, value) in properties {
138
+ if key == " Expires " {
139
+ guard let timestamp = value as? NSNumber else { continue }
140
+ cookieProperties [ HTTPCookiePropertyKey ( rawValue: key) ] = Date ( timeIntervalSince1970: timestamp. doubleValue)
141
+ } else {
142
+ cookieProperties [ HTTPCookiePropertyKey ( rawValue: key) ] = properties [ key]
143
+ }
144
+ }
145
+ return HTTPCookie ( properties: cookieProperties)
146
+ }
147
+
148
+ private func updatePersistentStore( ) {
149
+ //persist cookies
150
+ var persistDictionary : [ String : [ String : Any ] ] = [ : ]
151
+ let persistable = allCookies. filter { ( _, value) in
152
+ value. expiresDate != nil &&
153
+ value. isSessionOnly == false &&
154
+ value. expiresDate!. timeIntervalSinceNow > 0
155
+ }
156
+
157
+ for (key, cookie) in persistable {
158
+ persistDictionary [ key] = cookie. persistableDictionary ( )
159
+ }
160
+
161
+ let nsdict = _SwiftValue. store ( persistDictionary) as! NSDictionary
162
+ _ = nsdict. write ( toFile: cookieFilePath, atomically: true )
163
+ }
164
+
75
165
/*!
76
166
@method deleteCookie:
77
167
@abstract Delete the specified cookie
78
168
*/
79
- open func deleteCookie( _ cookie: HTTPCookie ) { NSUnimplemented ( ) }
169
+ open func deleteCookie( _ cookie: HTTPCookie ) {
170
+ let key = cookie. domain + cookie. path + cookie. name
171
+ workQueue. sync {
172
+ self . allCookies. removeValue ( forKey: key)
173
+ updatePersistentStore ( )
174
+ }
175
+ }
80
176
81
177
/*!
82
178
@method removeCookiesSince:
83
179
@abstract Delete all cookies from the cookie storage since the provided date.
84
180
*/
85
- open func removeCookies( since date: Date ) { NSUnimplemented ( ) }
86
-
181
+ open func removeCookies( since date: Date ) {
182
+ let cookiesSinceDate = allCookies. values. filter {
183
+ $0. properties![ . created] as! Double > date. timeIntervalSinceReferenceDate
184
+ }
185
+ for cookie in cookiesSinceDate {
186
+ deleteCookie ( cookie)
187
+ }
188
+ updatePersistentStore ( )
189
+ }
190
+
87
191
/*!
88
192
@method cookiesForURL:
89
193
@abstract Returns an array of cookies to send to the given URL.
@@ -94,7 +198,15 @@ open class HTTPCookieStorage: NSObject {
94
198
<tt>+[NSCookie requestHeaderFieldsWithCookies:]</tt> to turn this array
95
199
into a set of header fields to add to a request.
96
200
*/
97
- open func cookies( for url: URL ) -> [ HTTPCookie ] ? { NSUnimplemented ( ) }
201
+ open func cookies( for url: URL ) -> [ HTTPCookie ] ? {
202
+ var cookies : [ HTTPCookie ] ?
203
+ guard let host = url. host else { return nil }
204
+ let path = url. path == " " ? " / " : url. path
205
+ workQueue. sync {
206
+ cookies = Array ( allCookies. values. filter { $0. domain + $0. path == host + path } )
207
+ }
208
+ return cookies
209
+ }
98
210
99
211
/*!
100
212
@method setCookies:forURL:mainDocumentURL:
@@ -113,7 +225,22 @@ open class HTTPCookieStorage: NSObject {
113
225
dictionary and then use this method to store the resulting cookies
114
226
in accordance with policy settings.
115
227
*/
116
- open func setCookies( _ cookies: [ HTTPCookie ] , for url: URL ? , mainDocumentURL: URL ? ) { NSUnimplemented ( ) }
228
+ open func setCookies( _ cookies: [ HTTPCookie ] , for url: URL ? , mainDocumentURL: URL ? ) {
229
+ guard cookieAcceptPolicy != . never else { return }
230
+ guard let theUrl = url else { return }
231
+
232
+ var validCookies = [ HTTPCookie] ( )
233
+ if mainDocumentURL != nil && cookieAcceptPolicy == . onlyFromMainDocumentDomain {
234
+ //TODO: needs a careful study of the behaviour on Darwin
235
+ NSUnimplemented ( )
236
+ } else {
237
+ validCookies = cookies. filter { theUrl. host != nil && theUrl. host!. hasSuffix ( $0. domain) }
238
+ }
239
+
240
+ for cookie in validCookies {
241
+ setCookie ( cookie)
242
+ }
243
+ }
117
244
118
245
/*!
119
246
@method cookieAcceptPolicy
@@ -138,3 +265,24 @@ public extension Notification.Name {
138
265
*/
139
266
public static let NSHTTPCookieManagerCookiesChanged = Notification . Name ( rawValue: " NSHTTPCookieManagerCookiesChangedNotification " )
140
267
}
268
+
269
+ extension HTTPCookie {
270
+ internal func persistableDictionary( ) -> [ String : Any ] {
271
+ var properties : [ String : Any ] = [ : ]
272
+ properties [ HTTPCookiePropertyKey . name. rawValue] = name
273
+ properties [ HTTPCookiePropertyKey . path. rawValue] = path
274
+ properties [ HTTPCookiePropertyKey . value. rawValue] = _value
275
+ properties [ HTTPCookiePropertyKey . secure. rawValue] = _secure
276
+ properties [ HTTPCookiePropertyKey . version. rawValue] = _version
277
+ properties [ HTTPCookiePropertyKey . expires. rawValue] = _expiresDate? . timeIntervalSince1970 ?? Date ( ) . timeIntervalSince1970 //OK?
278
+ properties [ HTTPCookiePropertyKey . domain. rawValue] = _domain
279
+ if let commentURL = _commentURL {
280
+ properties [ HTTPCookiePropertyKey . commentURL. rawValue] = commentURL. absoluteString
281
+ }
282
+ if let comment = _comment {
283
+ properties [ HTTPCookiePropertyKey . comment. rawValue] = comment
284
+ }
285
+ properties [ HTTPCookiePropertyKey . port. rawValue] = portList
286
+ return properties
287
+ }
288
+ }
0 commit comments