Skip to content

Commit b25ca86

Browse files
author
Pushkar N Kulkarni
committed
Implement URLSessionConfiguration.ephemeral
The difference between `default` and `ephemeral` `URLSessionConfiguration`s is in the storage aspect. There are three kinds of stores related to a `URLSession` - a `URLCache` - a `URLCredentialStorage` - an `HTTPCookieStorage` While the `default` configuration mandates persistent stores, the `ephemeral` configuration mandates only in-memory, ephemeral stores. As of today, `URLCache` and `URLCredentialStorage` are unimplemented and not configured in the `default` configuration. `HTTPCookieStorage` is implemented and we use the `shared` storage in the `default` configuration. This commit includes: - an ephemeral `HTTPCookieStorage` that does not read from, and write to, a persistent store - an initial `URLSessionConfiguration.ephemeral` implementation which configures an ephmeral `HTTPCookieStorage`, but no `URLCache` and `URLCredentialStorage`
1 parent dffdfe6 commit b25ca86

File tree

3 files changed

+61
-13
lines changed

3 files changed

+61
-13
lines changed

Foundation/HTTPCookieStorage.swift

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ open class HTTPCookieStorage: NSObject {
4242
private static let sharedSyncQ = DispatchQueue(label: "org.swift.HTTPCookieStorage.sharedSyncQ")
4343

4444
/* only modified in init */
45-
private var cookieFilePath: String!
45+
private var cookieFilePath: String?
4646

4747
/* synchronized on syncQ, please don't use _allCookies directly outside of init/deinit */
4848
private var _allCookies: [String: HTTPCookie]
@@ -62,22 +62,27 @@ open class HTTPCookieStorage: NSObject {
6262
}
6363
private let syncQ = DispatchQueue(label: "org.swift.HTTPCookieStorage.syncQ")
6464

65-
private init(cookieStorageName: String) {
65+
private let isEphemeral: Bool
66+
67+
private init(cookieStorageName: String, isEphemeral: Bool = false) {
6668
_allCookies = [:]
6769
cookieAcceptPolicy = .always
70+
self.isEphemeral = isEphemeral
6871
super.init()
69-
let bundlePath = Bundle.main.bundlePath
70-
var bundleName = bundlePath.components(separatedBy: "/").last!
71-
if let range = bundleName.range(of: ".", options: .backwards, range: nil, locale: nil) {
72-
bundleName = String(bundleName[..<range.lowerBound])
72+
if !isEphemeral {
73+
let bundlePath = Bundle.main.bundlePath
74+
var bundleName = bundlePath.components(separatedBy: "/").last!
75+
if let range = bundleName.range(of: ".", options: .backwards, range: nil, locale: nil) {
76+
bundleName = String(bundleName[..<range.lowerBound])
77+
}
78+
let cookieFolderPath = _CFXDGCreateDataHomePath()._swiftObject + "/" + bundleName
79+
cookieFilePath = filePath(path: cookieFolderPath, fileName: "/.cookies." + cookieStorageName, bundleName: bundleName)
80+
loadPersistedCookies()
7381
}
74-
let cookieFolderPath = _CFXDGCreateDataHomePath()._swiftObject + "/" + bundleName
75-
cookieFilePath = filePath(path: cookieFolderPath, fileName: "/.cookies." + cookieStorageName, bundleName: bundleName)
76-
loadPersistedCookies()
7782
}
7883

7984
private func loadPersistedCookies() {
80-
guard let cookiesData = try? Data(contentsOf: URL(fileURLWithPath: cookieFilePath)) else { return }
85+
guard let cookieFilePath = self.cookieFilePath, let cookiesData = try? Data(contentsOf: URL(fileURLWithPath: cookieFilePath)) else { return }
8186
guard let cookies = try? PropertyListSerialization.propertyList(from: cookiesData, format: nil) else { return }
8287
var cookies0 = cookies as? [String: [String: Any]] ?? [:]
8388
self.syncQ.sync {
@@ -109,6 +114,12 @@ open class HTTPCookieStorage: NSObject {
109114
return FileManager.default.currentDirectoryPath + "/" + bundleName + fileName
110115
}
111116

117+
// `URLSessionConfiguration.ephemeral` needs an ephemeral cookie storage.
118+
// Ephemeral cookie storage is an in-memory store and does not load from, and store to, a persistent store.
119+
internal class func ephemeralStorage() -> HTTPCookieStorage {
120+
return HTTPCookieStorage(cookieStorageName: "Ephemeral", isEphemeral: true)
121+
}
122+
112123
open var cookies: [HTTPCookie]? {
113124
return Array(self.syncQ.sync { self.allCookies.values })
114125
}
@@ -130,7 +141,7 @@ open class HTTPCookieStorage: NSObject {
130141
}
131142
}
132143
}
133-
144+
134145
/*!
135146
@method sharedCookieStorageForGroupContainerIdentifier:
136147
@abstract Get the cookie storage for the container associated with the specified application group identifier
@@ -183,7 +194,7 @@ open class HTTPCookieStorage: NSObject {
183194
}
184195

185196
open override var description: String {
186-
return "<NSHTTPCookieStorage cookies count:\(cookies?.count ?? 0)>"
197+
return "\(self.isEphemeral ? "Ephemeral" : "")<NSHTTPCookieStorage cookies count:\(cookies?.count ?? 0)>"
187198
}
188199

189200
private func createCookie(_ properties: [String: Any]) -> HTTPCookie? {
@@ -200,6 +211,11 @@ open class HTTPCookieStorage: NSObject {
200211
}
201212

202213
private func updatePersistentStore() {
214+
// No persistence if this is an ephemeral storage
215+
if self.isEphemeral { return }
216+
217+
guard let cookieFilePath = self.cookieFilePath else { return }
218+
203219
if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
204220
dispatchPrecondition(condition: DispatchPredicate.onQueue(self.syncQ))
205221
}

Foundation/URLSession/URLSessionConfiguration.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,15 @@ open class URLSessionConfiguration : NSObject, NSCopying {
119119
open class var `default`: URLSessionConfiguration {
120120
return URLSessionConfiguration()
121121
}
122-
open class var ephemeral: URLSessionConfiguration { NSUnimplemented() }
122+
123+
open class var ephemeral: URLSessionConfiguration {
124+
// Return a new ephemeral URLSessionConfiguration every time this property is invoked
125+
// TODO: urlCache and urlCredentialStorage should also be ephemeral/in-memory
126+
// URLCache and URLCredentialStorage are still unimplemented
127+
let ephemeralConfiguration = URLSessionConfiguration()
128+
ephemeralConfiguration.httpCookieStorage = HTTPCookieStorage.ephemeralStorage()
129+
return ephemeralConfiguration
130+
}
123131

124132
open class func background(withIdentifier identifier: String) -> URLSessionConfiguration { NSUnimplemented() }
125133

TestFoundation/TestURLSession.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class TestURLSession : LoopbackServerTest {
4343
("test_concurrentRequests", test_concurrentRequests),
4444
("test_disableCookiesStorage", test_disableCookiesStorage),
4545
("test_cookiesStorage", test_cookiesStorage),
46+
("test_cookieStorageForEphmeralConfiguration", test_cookieStorageForEphmeralConfiguration),
4647
("test_setCookies", test_setCookies),
4748
("test_dontSetCookies", test_dontSetCookies),
4849
("test_initURLSessionConfiguration", test_initURLSessionConfiguration),
@@ -707,6 +708,29 @@ class TestURLSession : LoopbackServerTest {
707708
waitForExpectations(timeout: 30)
708709
}
709710

711+
func test_cookieStorageForEphmeralConfiguration() {
712+
let config = URLSessionConfiguration.ephemeral
713+
config.timeoutIntervalForRequest = 5
714+
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/requestCookies"
715+
let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
716+
var expect = expectation(description: "POST \(urlString)")
717+
var req = URLRequest(url: URL(string: urlString)!)
718+
req.httpMethod = "POST"
719+
var task = session.dataTask(with: req) { (data, _, error) -> Void in
720+
defer { expect.fulfill() }
721+
XCTAssertNotNil(data)
722+
XCTAssertNil(error as? URLError, "error = \(error as! URLError)")
723+
}
724+
task.resume()
725+
waitForExpectations(timeout: 30)
726+
let cookies = config.httpCookieStorage?.cookies
727+
XCTAssertEqual(cookies?.count, 1)
728+
729+
let config2 = URLSessionConfiguration.ephemeral
730+
let cookies2 = config2.httpCookieStorage?.cookies
731+
XCTAssertEqual(cookies2?.count, 0)
732+
}
733+
710734
func test_dontSetCookies() {
711735
let config = URLSessionConfiguration.default
712736
config.timeoutIntervalForRequest = 5

0 commit comments

Comments
 (0)