From 4731816471712f1092d9db102fe485dbaf5aa083 Mon Sep 17 00:00:00 2001 From: Tony Li Date: Tue, 5 Nov 2024 01:10:32 +1300 Subject: [PATCH] Use ephemeral URLSession to send .org REST API requests (#23715) * Use ephemeral URLSession to send .org REST API requests * Fix grammar issues in code comment * Change static functions to initialisers * Remove an unnecessary throw --- .../Classes/Networking/WordPressClient.swift | 34 ++++++++++++++----- .../ViewModel/BlogDashboardViewModel.swift | 2 +- ...DetailsViewController+SectionHelpers.swift | 6 ++-- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/WordPress/Classes/Networking/WordPressClient.swift b/WordPress/Classes/Networking/WordPressClient.swift index 85066b40da8d..e3176f6df152 100644 --- a/WordPress/Classes/Networking/WordPressClient.swift +++ b/WordPress/Classes/Networking/WordPressClient.swift @@ -8,20 +8,20 @@ struct WordPressSite { case selfHosted(username: String, authToken: String) } - let baseUrl: String + let baseUrl: URL let type: WordPressSite.SiteType init(baseUrl: ParsedUrl, type: WordPressSite.SiteType) { - self.baseUrl = baseUrl.url() + self.baseUrl = baseUrl.asURL() self.type = type } - static func from(blog: Blog) throws -> WordPressSite { + init(blog: Blog) throws { let url = try ParsedUrl.parse(input: blog.getUrlString()) if let account = blog.account { - return WordPressSite(baseUrl: url, type: .dotCom(authToken: account.authToken)) + self.init(baseUrl: url, type: .dotCom(authToken: account.authToken)) } else { - return WordPressSite(baseUrl: url, type: .selfHosted( + self.init(baseUrl: url, type: .selfHosted( username: try blog.getUsername(), authToken: try blog.getApplicationToken()) ) @@ -45,16 +45,32 @@ actor WordPressClient { self.rootUrl = rootUrl.url() } - static func `for`(site: WordPressSite, in session: URLSession) throws -> WordPressClient { - let parsedUrl = try ParsedUrl.parse(input: site.baseUrl) + init(site: WordPressSite) { + // `site.barUrl` is a legal HTTP URL, which should be convertable to the `ParsedUrl` type. + let parsedUrl: ParsedUrl + do { + parsedUrl = try ParsedUrl.parse(input: site.baseUrl.absoluteString) + } catch { + fatalError("Failed to cast URL (\(site.baseUrl.absoluteString)) to ParsedUrl: \(error)") + } + + // Currently, the app supports both account passwords and application passwords. + // When a site is initially signed in with an account password, WordPress login cookies are stored + // in `URLSession.shared`. After switching the site to application password authentication, + // the stored cookies may interfere with application-password authentication, resulting in 401 + // errors from the REST API. + // + // To avoid this issue, we'll use an ephemeral URLSession for now (which stores cookies in memory + // rather than using the shared one on disk). + let session = URLSession(configuration: .ephemeral) switch site.type { case let .dotCom(authToken): let api = WordPressAPI(urlSession: session, baseUrl: parsedUrl, authenticationStategy: .authorizationHeader(token: authToken)) - return WordPressClient(api: api, rootUrl: parsedUrl) + self.init(api: api, rootUrl: parsedUrl) case .selfHosted(let username, let authToken): let api = WordPressAPI.init(urlSession: session, baseUrl: parsedUrl, authenticationStategy: .init(username: username, password: authToken)) - return WordPressClient(api: api, rootUrl: parsedUrl) + self.init(api: api, rootUrl: parsedUrl) } } diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/ViewModel/BlogDashboardViewModel.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/ViewModel/BlogDashboardViewModel.swift index a87434da349b..be64e977adc0 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/ViewModel/BlogDashboardViewModel.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/ViewModel/BlogDashboardViewModel.swift @@ -108,7 +108,7 @@ final class BlogDashboardViewModel { var _error: Error? do { - self.wordpressClient = try WordPressClient.for(site: .from(blog: self.blog), in: .shared) + self.wordpressClient = try WordPressClient(site: .init(blog: self.blog)) } catch { _error = error self.wordpressClient = nil diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Details/BlogDetailsViewController+SectionHelpers.swift b/WordPress/Classes/ViewRelated/Blog/Blog Details/BlogDetailsViewController+SectionHelpers.swift index 26f43f0f0937..4523322e5c2b 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Details/BlogDetailsViewController+SectionHelpers.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Details/BlogDetailsViewController+SectionHelpers.swift @@ -120,7 +120,7 @@ extension BlogDetailsViewController { @objc func shouldShowApplicationPasswordRow() -> Bool { // Only available for application-password authenticated self-hosted sites. - return self.blog.account == nil && self.blog.userID != nil && (try? WordPressSite.from(blog: self.blog)) != nil + return self.blog.account == nil && self.blog.userID != nil && (try? WordPressSite(blog: self.blog)) != nil } private func createApplicationPasswordService() -> ApplicationPasswordService? { @@ -129,8 +129,8 @@ extension BlogDetailsViewController { } do { - let site = try WordPressSite.from(blog: self.blog) - let client = try WordPressClient.for(site: site, in: .shared) + let site = try WordPressSite(blog: self.blog) + let client = WordPressClient(site: site) return ApplicationPasswordService(api: client, currentUserId: userId) } catch { DDLogError("Failed to create WordPressClient: \(error)")