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
@@ -35,11 +35,36 @@ extension HTTPCookie {
35
35
generate cookie-related HTTP header fields.
36
36
*/
37
37
open class HTTPCookieStorage : NSObject {
38
-
39
- public override init ( ) { NSUnimplemented ( ) }
40
-
38
+
39
+ private static var sharedStorage : HTTPCookieStorage ?
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:
@@ -70,19 +102,80 @@ open class HTTPCookieStorage: NSObject {
70
102
@discussion The cookie will override an existing cookie with the
71
103
same name, domain and path, if any.
72
104
*/
73
- open func setCookie( _ cookie: HTTPCookie ) { NSUnimplemented ( ) }
105
+ open func setCookie( _ cookie: HTTPCookie ) {
106
+ workQueue. sync {
107
+ if cookieAcceptPolicy == . never { return }
108
+
109
+ //add or replace
110
+ let key = cookie. domain + cookie. path + cookie. name
111
+ if let _ = allCookies. index ( forKey: key) {
112
+ allCookies. updateValue ( cookie, forKey: key)
113
+ } else {
114
+ allCookies [ key] = cookie
115
+ }
116
+
117
+ //remove stale cookies, these may include the one we just added
118
+ let expired = allCookies. filter { ( _, value) in value. expiresDate != nil && value. expiresDate!. timeIntervalSinceNow < 0 }
119
+ for (key, _) in expired {
120
+ self . allCookies. removeValue ( forKey: key)
121
+ }
122
+
123
+ updatePersistentStore ( )
124
+ }
125
+ }
74
126
127
+ private func createCookie( _ properties: [ String : Any ] ) -> HTTPCookie ? {
128
+ var cookieProperties : [ HTTPCookiePropertyKey : Any ] = [ : ]
129
+ for key in properties. keys {
130
+ if key == " Expires " {
131
+ let value = properties [ key] as! NSNumber
132
+ cookieProperties [ HTTPCookiePropertyKey ( rawValue: key) ] = Date ( timeIntervalSince1970: value. doubleValue)
133
+ } else {
134
+ cookieProperties [ HTTPCookiePropertyKey ( rawValue: key) ] = properties [ key]
135
+ }
136
+ }
137
+ return HTTPCookie ( properties: cookieProperties)
138
+ }
139
+
140
+ private func updatePersistentStore( ) {
141
+ //persist cookies
142
+ var persistDictionary : [ String : [ String : Any ] ] = [ : ]
143
+ let persistable = allCookies. filter { ( _, value) in value. expiresDate != nil &&
144
+ value. isSessionOnly == false &&
145
+ value. expiresDate!. timeIntervalSinceNow > 0
146
+ }
147
+
148
+ for (key, cookie) in persistable {
149
+ persistDictionary [ key] = cookie. simpleDictionary ( )
150
+ }
151
+
152
+ let nsdict = _SwiftValue. store ( persistDictionary) as! NSDictionary
153
+ _ = nsdict. write ( toFile: cookieFilePath, atomically: true )
154
+ }
155
+
75
156
/*!
76
157
@method deleteCookie:
77
158
@abstract Delete the specified cookie
78
159
*/
79
- open func deleteCookie( _ cookie: HTTPCookie ) { NSUnimplemented ( ) }
160
+ open func deleteCookie( _ cookie: HTTPCookie ) {
161
+ workQueue. sync {
162
+ let key = cookie. domain + cookie. path + cookie. name
163
+ self . allCookies. removeValue ( forKey: key)
164
+ updatePersistentStore ( )
165
+ }
166
+ }
80
167
81
168
/*!
82
169
@method removeCookiesSince:
83
170
@abstract Delete all cookies from the cookie storage since the provided date.
84
171
*/
85
- open func removeCookies( since date: Date ) { NSUnimplemented ( ) }
172
+ open func removeCookies( since date: Date ) {
173
+ let cookiesSinceDate = allCookies. values. filter { $0. properties![ . created] as! Double > date. timeIntervalSinceReferenceDate }
174
+ for cookie in cookiesSinceDate {
175
+ deleteCookie ( cookie)
176
+ }
177
+ updatePersistentStore ( )
178
+ }
86
179
87
180
/*!
88
181
@method cookiesForURL:
@@ -94,7 +187,13 @@ open class HTTPCookieStorage: NSObject {
94
187
<tt>+[NSCookie requestHeaderFieldsWithCookies:]</tt> to turn this array
95
188
into a set of header fields to add to a request.
96
189
*/
97
- open func cookies( for url: URL ) -> [ HTTPCookie ] ? { NSUnimplemented ( ) }
190
+ open func cookies( for url: URL ) -> [ HTTPCookie ] ? {
191
+ var cookies : [ HTTPCookie ] ?
192
+ workQueue. sync {
193
+ cookies = Array ( allCookies. values. filter { url. host != nil && $0. domain + $0. path == url. host! + url. path } )
194
+ }
195
+ return cookies
196
+ }
98
197
99
198
/*!
100
199
@method setCookies:forURL:mainDocumentURL:
@@ -113,7 +212,23 @@ open class HTTPCookieStorage: NSObject {
113
212
dictionary and then use this method to store the resulting cookies
114
213
in accordance with policy settings.
115
214
*/
116
- open func setCookies( _ cookies: [ HTTPCookie ] , for url: URL ? , mainDocumentURL: URL ? ) { NSUnimplemented ( ) }
215
+ open func setCookies( _ cookies: [ HTTPCookie ] , for url: URL ? , mainDocumentURL: URL ? ) {
216
+ workQueue. sync {
217
+ guard cookieAcceptPolicy != . never else { return }
218
+ guard let theUrl = url else { return }
219
+
220
+ var validCookies = [ HTTPCookie] ( )
221
+ if mainDocumentURL != nil && cookieAcceptPolicy == . onlyFromMainDocumentDomain {
222
+ NSUnimplemented ( )
223
+ } else {
224
+ validCookies = cookies. filter { theUrl. host != nil && theUrl. host!. hasSuffix ( $0. domain) }
225
+ }
226
+
227
+ for cookie in validCookies {
228
+ setCookie ( cookie)
229
+ }
230
+ }
231
+ }
117
232
118
233
/*!
119
234
@method cookieAcceptPolicy
@@ -129,6 +244,7 @@ open class HTTPCookieStorage: NSObject {
129
244
@discussion proper sorting of cookies may require extensive string conversion, which can be avoided by allowing the system to perform the sorting. This API is to be preferred over the more generic -[NSHTTPCookieStorage cookies] API, if sorting is going to be performed.
130
245
*/
131
246
open func sortedCookies( using sortOrder: [ SortDescriptor ] ) -> [ HTTPCookie ] { NSUnimplemented ( ) }
247
+
132
248
}
133
249
134
250
/*!
@@ -137,3 +253,16 @@ open class HTTPCookieStorage: NSObject {
137
253
*/
138
254
public let NSHTTPCookieManagerCookiesChangedNotification : String = " " // NSUnimplemented
139
255
256
+ extension HTTPCookie {
257
+ public func simpleDictionary( ) -> [ String : Any ] {
258
+ var properties : [ String : Any ] = [ : ]
259
+ properties [ " Name " ] = _name
260
+ properties [ " Path " ] = _path
261
+ properties [ " Value " ] = _value
262
+ properties [ " Secure " ] = _secure
263
+ properties [ " Version " ] = _version
264
+ properties [ " Expires " ] = _expiresDate!. timeIntervalSince1970
265
+ properties [ " Domain " ] = _domain
266
+ return properties
267
+ }
268
+ }
0 commit comments