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,36 @@ 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
+
40
+ private let cookieFilePath : String = NSHomeDirectory ( ) + " /.cookies "
41
+ private let workQueue : DispatchQueue = DispatchQueue ( label: " HTTPCookieStorage.workqueue " )
42
+ var allCookies : [ String : HTTPCookie ]
43
+
44
+ public override init ( ) {
45
+ allCookies = [ : ]
46
+ cookieAcceptPolicy = . always
47
+ super. init ( )
48
+ loadPersistedCookies ( )
49
+ }
50
+
51
+ private func loadPersistedCookies( ) {
52
+ guard let cookies = NSMutableDictionary ( contentsOfFile: cookieFilePath) else { return }
53
+ var cookies0 = _SwiftValue. fetch ( cookies) as? [ String : [ String : Any ] ] ?? [ : ]
54
+ for key in cookies0. keys {
55
+ if let cookie = createCookie ( cookies0 [ key] !) {
56
+ allCookies [ key] = cookie
57
+ }
58
+ }
59
+ }
60
+
41
61
open var cookies : [ HTTPCookie ] ? {
42
- NSUnimplemented ( )
62
+ var theCookies : [ HTTPCookie ] ?
63
+ workQueue. sync {
64
+ theCookies = Array ( self . allCookies. values)
65
+ }
66
+ return theCookies
43
67
}
44
68
45
69
/*!
@@ -49,7 +73,14 @@ open class HTTPCookieStorage: NSObject {
49
73
@discussion Starting in OS X 10.11, each app has its own sharedHTTPCookieStorage singleton,
50
74
which will not be shared with other applications.
51
75
*/
52
- class var shared : HTTPCookieStorage { get { NSUnimplemented ( ) } }
76
+ open class var shared : HTTPCookieStorage {
77
+ get {
78
+ if sharedStorage == nil {
79
+ sharedStorage = HTTPCookieStorage ( )
80
+ }
81
+ return sharedStorage!
82
+ }
83
+ }
53
84
54
85
/*!
55
86
@method sharedCookieStorageForGroupContainerIdentifier:
@@ -70,19 +101,81 @@ open class HTTPCookieStorage: NSObject {
70
101
@discussion The cookie will override an existing cookie with the
71
102
same name, domain and path, if any.
72
103
*/
73
- open func setCookie( _ cookie: HTTPCookie ) { NSUnimplemented ( ) }
104
+ open func setCookie( _ cookie: HTTPCookie ) {
105
+ workQueue. sync {
106
+ if cookieAcceptPolicy == . never { return }
107
+
108
+ //add or replace
109
+ let key = cookie. domain + cookie. path + cookie. name
110
+ if let _ = allCookies. index ( forKey: key) {
111
+ allCookies. updateValue ( cookie, forKey: key)
112
+ } else {
113
+ allCookies [ key] = cookie
114
+ }
115
+
116
+ //remove stale cookies, these may include the one we just added
117
+ let expired = allCookies. filter { ( _, value) in value. expiresDate != nil && value. expiresDate!. timeIntervalSinceNow < 0 }
118
+ for (key, _) in expired {
119
+ self . allCookies. removeValue ( forKey: key)
120
+ }
121
+
122
+ updatePersistentStore ( )
123
+ }
124
+ }
74
125
126
+ private func createCookie( _ properties: [ String : Any ] ) -> HTTPCookie ? {
127
+ var cookieProperties : [ HTTPCookiePropertyKey : Any ] = [ : ]
128
+ for key in properties. keys {
129
+ if key == " Expires " {
130
+ let value = properties [ key] as! NSNumber
131
+ cookieProperties [ HTTPCookiePropertyKey ( rawValue: key) ] = Date ( timeIntervalSince1970: value. doubleValue)
132
+ } else {
133
+ cookieProperties [ HTTPCookiePropertyKey ( rawValue: key) ] = properties [ key]
134
+ }
135
+ }
136
+ return HTTPCookie ( properties: cookieProperties)
137
+ }
138
+
139
+ private func updatePersistentStore( ) {
140
+ //persist cookies
141
+ var persistDictionary : [ String : [ String : Any ] ] = [ : ]
142
+ let persistable = allCookies. filter { ( _, value) in value. expiresDate != nil &&
143
+ value. isSessionOnly == false &&
144
+ value. expiresDate!. timeIntervalSinceNow > 0 }
145
+
146
+ for (key, cookie) in persistable {
147
+ persistDictionary [ key] = cookie. dictionary ( )
148
+ }
149
+
150
+ let nsdict = _SwiftValue. store ( persistDictionary) as! NSDictionary
151
+ _ = nsdict. write ( toFile: cookieFilePath, atomically: true )
152
+ }
153
+
75
154
/*!
76
155
@method deleteCookie:
77
156
@abstract Delete the specified cookie
78
157
*/
79
- open func deleteCookie( _ cookie: HTTPCookie ) { NSUnimplemented ( ) }
158
+ open func deleteCookie( _ cookie: HTTPCookie ) {
159
+ workQueue. sync {
160
+ let key = cookie. domain + cookie. path + cookie. name
161
+ self . allCookies. removeValue ( forKey: key)
162
+ updatePersistentStore ( )
163
+ }
164
+ }
80
165
81
166
/*!
82
167
@method removeCookiesSince:
83
168
@abstract Delete all cookies from the cookie storage since the provided date.
84
169
*/
85
- open func removeCookies( since date: Date ) { NSUnimplemented ( ) }
170
+ open func removeCookies( since date: Date ) {
171
+ let cookiesSinceDate = allCookies. values. filter {
172
+ $0. properties![ . created] as! Double > date. timeIntervalSinceReferenceDate
173
+ }
174
+ for cookie in cookiesSinceDate {
175
+ deleteCookie ( cookie)
176
+ }
177
+ updatePersistentStore ( )
178
+ }
86
179
87
180
/*!
88
181
@method cookiesForURL:
@@ -94,7 +187,17 @@ 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
+ guard let host = url. host else { return nil }
193
+ let path = url. path == " " ? " / " : url. path
194
+ workQueue. sync {
195
+ cookies = Array ( allCookies. values. filter {
196
+ $0. domain + $0. path == host + path
197
+ } )
198
+ }
199
+ return cookies
200
+ }
98
201
99
202
/*!
100
203
@method setCookies:forURL:mainDocumentURL:
@@ -113,7 +216,24 @@ open class HTTPCookieStorage: NSObject {
113
216
dictionary and then use this method to store the resulting cookies
114
217
in accordance with policy settings.
115
218
*/
116
- open func setCookies( _ cookies: [ HTTPCookie ] , for url: URL ? , mainDocumentURL: URL ? ) { NSUnimplemented ( ) }
219
+ open func setCookies( _ cookies: [ HTTPCookie ] , for url: URL ? , mainDocumentURL: URL ? ) {
220
+ guard cookieAcceptPolicy != . never else { return }
221
+ guard let theUrl = url else { return }
222
+
223
+ var validCookies = [ HTTPCookie] ( )
224
+ if mainDocumentURL != nil && cookieAcceptPolicy == . onlyFromMainDocumentDomain {
225
+ //TODO: needs a careful study of the behaviour on Darwin
226
+ NSUnimplemented ( )
227
+ } else {
228
+ validCookies = cookies. filter {
229
+ theUrl. host != nil && theUrl. host!. hasSuffix ( $0. domain)
230
+ }
231
+ }
232
+
233
+ for cookie in validCookies {
234
+ setCookie ( cookie)
235
+ }
236
+ }
117
237
118
238
/*!
119
239
@method cookieAcceptPolicy
@@ -129,6 +249,7 @@ open class HTTPCookieStorage: NSObject {
129
249
@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
250
*/
131
251
open func sortedCookies( using sortOrder: [ SortDescriptor ] ) -> [ HTTPCookie ] { NSUnimplemented ( ) }
252
+
132
253
}
133
254
134
255
/*!
@@ -137,3 +258,23 @@ open class HTTPCookieStorage: NSObject {
137
258
*/
138
259
public let NSHTTPCookieManagerCookiesChangedNotification : String = " " // NSUnimplemented
139
260
261
+ extension HTTPCookie {
262
+ internal func dictionary( ) -> [ String : Any ] {
263
+ var properties : [ String : Any ] = [ : ]
264
+ properties [ HTTPCookiePropertyKey . name. rawValue] = name
265
+ properties [ HTTPCookiePropertyKey . path. rawValue] = path
266
+ properties [ HTTPCookiePropertyKey . value. rawValue] = _value
267
+ properties [ HTTPCookiePropertyKey . secure. rawValue] = _secure
268
+ properties [ HTTPCookiePropertyKey . version. rawValue] = _version
269
+ properties [ HTTPCookiePropertyKey . expires. rawValue] = _expiresDate!. timeIntervalSince1970
270
+ properties [ HTTPCookiePropertyKey . domain. rawValue] = _domain
271
+ if let commentURL = _commentURL {
272
+ properties [ HTTPCookiePropertyKey . commentURL. rawValue] = commentURL. absoluteString
273
+ }
274
+ if let comment = _comment {
275
+ properties [ HTTPCookiePropertyKey . comment. rawValue] = comment
276
+ }
277
+ properties [ HTTPCookiePropertyKey . port. rawValue] = portList
278
+ return properties
279
+ }
280
+ }
0 commit comments