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,20 +101,82 @@ 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 ( ) }
86
-
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
+ }
179
+
87
180
/*!
88
181
@method cookiesForURL:
89
182
@abstract Returns an array of cookies to send to the given URL.
@@ -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
@@ -138,3 +258,24 @@ public extension Notification.Name {
138
258
*/
139
259
public static let NSHTTPCookieManagerCookiesChanged = Notification . Name ( rawValue: " NSHTTPCookieManagerCookiesChangedNotification " )
140
260
}
261
+
262
+ extension HTTPCookie {
263
+ internal func dictionary( ) -> [ String : Any ] {
264
+ var properties : [ String : Any ] = [ : ]
265
+ properties [ HTTPCookiePropertyKey . name. rawValue] = name
266
+ properties [ HTTPCookiePropertyKey . path. rawValue] = path
267
+ properties [ HTTPCookiePropertyKey . value. rawValue] = _value
268
+ properties [ HTTPCookiePropertyKey . secure. rawValue] = _secure
269
+ properties [ HTTPCookiePropertyKey . version. rawValue] = _version
270
+ properties [ HTTPCookiePropertyKey . expires. rawValue] = _expiresDate!. timeIntervalSince1970
271
+ properties [ HTTPCookiePropertyKey . domain. rawValue] = _domain
272
+ if let commentURL = _commentURL {
273
+ properties [ HTTPCookiePropertyKey . commentURL. rawValue] = commentURL. absoluteString
274
+ }
275
+ if let comment = _comment {
276
+ properties [ HTTPCookiePropertyKey . comment. rawValue] = comment
277
+ }
278
+ properties [ HTTPCookiePropertyKey . port. rawValue] = portList
279
+ return properties
280
+ }
281
+ }
0 commit comments