Skip to content

Commit 73a8350

Browse files
authored
Merge pull request #2125 from pushkarnk/urlsession-config-ephemeral
2 parents 4c0fef0 + b25ca86 commit 73a8350

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
@@ -41,7 +41,7 @@ open class HTTPCookieStorage: NSObject {
4141
private static let sharedSyncQ = DispatchQueue(label: "org.swift.HTTPCookieStorage.sharedSyncQ")
4242

4343
/* only modified in init */
44-
private var cookieFilePath: String!
44+
private var cookieFilePath: String?
4545

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

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

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

116+
// `URLSessionConfiguration.ephemeral` needs an ephemeral cookie storage.
117+
// Ephemeral cookie storage is an in-memory store and does not load from, and store to, a persistent store.
118+
internal class func ephemeralStorage() -> HTTPCookieStorage {
119+
return HTTPCookieStorage(cookieStorageName: "Ephemeral", isEphemeral: true)
120+
}
121+
111122
open var cookies: [HTTPCookie]? {
112123
return Array(self.syncQ.sync { self.allCookies.values })
113124
}
@@ -122,7 +133,7 @@ open class HTTPCookieStorage: NSObject {
122133
open class var shared: HTTPCookieStorage {
123134
return sharedStorage
124135
}
125-
136+
126137
/*!
127138
@method sharedCookieStorageForGroupContainerIdentifier:
128139
@abstract Get the cookie storage for the container associated with the specified application group identifier
@@ -175,7 +186,7 @@ open class HTTPCookieStorage: NSObject {
175186
}
176187

177188
open override var description: String {
178-
return "<NSHTTPCookieStorage cookies count:\(cookies?.count ?? 0)>"
189+
return "\(self.isEphemeral ? "Ephemeral" : "")<NSHTTPCookieStorage cookies count:\(cookies?.count ?? 0)>"
179190
}
180191

181192
private func createCookie(_ properties: [String: Any]) -> HTTPCookie? {
@@ -192,6 +203,11 @@ open class HTTPCookieStorage: NSObject {
192203
}
193204

194205
private func updatePersistentStore() {
206+
// No persistence if this is an ephemeral storage
207+
if self.isEphemeral { return }
208+
209+
guard let cookieFilePath = self.cookieFilePath else { return }
210+
195211
if #available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) {
196212
dispatchPrecondition(condition: DispatchPredicate.onQueue(self.syncQ))
197213
}

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)