From 9ea6c01e02db924866ada349f91390ccd6b1ee4f Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Fri, 8 Mar 2019 18:41:42 -0800 Subject: [PATCH] feat: promisify session.clearAuthCache() (#17259) * feat: promisify session.clearAuthCache() * remove unused callback runner helpers --- atom/browser/api/atom_api_session.cc | 33 +++++++---------- atom/browser/api/atom_api_session.h | 2 +- docs/api/promisification.md | 2 +- docs/api/session.md | 12 +++++- lib/browser/api/session.js | 1 + spec/api-session-spec.js | 55 +++++++++++++++++++++++++++- 6 files changed, 80 insertions(+), 25 deletions(-) diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 9c885795ae1c..665d306d1b90 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -210,17 +210,6 @@ const char kPersistPrefix[] = "persist:"; // Referenced session objects. std::map> g_sessions; -// Runs the callback in UI thread. -void RunCallbackInUI(const base::Callback& callback) { - base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, callback); -} - -template -void RunCallbackInUI(const base::Callback& callback, T... result) { - base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(callback, result...)); -} - void ResolveOrRejectPromiseInUI(atom::util::Promise promise, int net_error) { if (net_error != net::OK) { std::string err_msg = net::ErrorToString(net_error); @@ -313,7 +302,7 @@ void ClearHostResolverCacheInIO( void ClearAuthCacheInIO( const scoped_refptr& context_getter, const ClearAuthCacheOptions& options, - const base::Closure& callback) { + util::Promise promise) { auto* request_context = context_getter->GetURLRequestContext(); auto* network_session = request_context->http_transaction_factory()->GetSession(); @@ -333,8 +322,9 @@ void ClearAuthCacheInIO( } network_session->CloseAllConnections(); } - if (!callback.is_null()) - RunCallbackInUI(callback); + base::PostTaskWithTraits( + FROM_HERE, {BrowserThread::UI}, + base::BindOnce(util::Promise::ResolveEmptyPromise, std::move(promise))); } void AllowNTLMCredentialsForDomainsInIO( @@ -625,20 +615,23 @@ v8::Local Session::ClearHostResolverCache(mate::Arguments* args) { return handle; } -void Session::ClearAuthCache(mate::Arguments* args) { +v8::Local Session::ClearAuthCache(mate::Arguments* args) { + v8::Isolate* isolate = args->isolate(); + util::Promise promise(isolate); + v8::Local handle = promise.GetHandle(); + ClearAuthCacheOptions options; if (!args->GetNext(&options)) { - args->ThrowError("Must specify options object"); - return; + promise.RejectWithErrorMessage("Must specify options object"); + return handle; } - base::Closure callback; - args->GetNext(&callback); base::PostTaskWithTraits( FROM_HERE, {BrowserThread::IO}, base::BindOnce(&ClearAuthCacheInIO, WrapRefCounted(browser_context_->GetRequestContext()), - options, callback)); + options, std::move(promise))); + return handle; } void Session::AllowNTLMCredentialsForDomains(const std::string& domains) { diff --git a/atom/browser/api/atom_api_session.h b/atom/browser/api/atom_api_session.h index 7debed6c92c0..82c159104c9c 100644 --- a/atom/browser/api/atom_api_session.h +++ b/atom/browser/api/atom_api_session.h @@ -78,7 +78,7 @@ class Session : public mate::TrackableObject, void SetPermissionCheckHandler(v8::Local val, mate::Arguments* args); v8::Local ClearHostResolverCache(mate::Arguments* args); - void ClearAuthCache(mate::Arguments* args); + v8::Local ClearAuthCache(mate::Arguments* args); void AllowNTLMCredentialsForDomains(const std::string& domains); void SetUserAgent(const std::string& user_agent, mate::Arguments* args); std::string GetUserAgent(); diff --git a/docs/api/promisification.md b/docs/api/promisification.md index 8d2a8b0a9b21..9838b378f123 100644 --- a/docs/api/promisification.md +++ b/docs/api/promisification.md @@ -14,7 +14,6 @@ When a majority of affected functions are migrated, this flag will be enabled by - [inAppPurchase.purchaseProduct(productID, quantity, callback)](https://github.com/electron/electron/blob/master/docs/api/in-app-purchase.md#purchaseProduct) - [inAppPurchase.getProducts(productIDs, callback)](https://github.com/electron/electron/blob/master/docs/api/in-app-purchase.md#getProducts) - [ses.getBlobData(identifier, callback)](https://github.com/electron/electron/blob/master/docs/api/session.md#getBlobData) -- [ses.clearAuthCache(options[, callback])](https://github.com/electron/electron/blob/master/docs/api/session.md#clearAuthCache) - [contents.executeJavaScript(code[, userGesture, callback])](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#executeJavaScript) - [contents.print([options], [callback])](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#print) - [webFrame.executeJavaScript(code[, userGesture, callback])](https://github.com/electron/electron/blob/master/docs/api/web-frame.md#executeJavaScript) @@ -46,6 +45,7 @@ When a majority of affected functions are migrated, this flag will be enabled by - [ses.setProxy(config, callback)](https://github.com/electron/electron/blob/master/docs/api/session.md#setProxy) - [ses.resolveProxy(url, callback)](https://github.com/electron/electron/blob/master/docs/api/session.md#resolveProxy) - [ses.getCacheSize(callback)](https://github.com/electron/electron/blob/master/docs/api/session.md#getCacheSize) +- [ses.clearAuthCache(options[, callback])](https://github.com/electron/electron/blob/master/docs/api/session.md#clearAuthCache) - [ses.clearCache(callback)](https://github.com/electron/electron/blob/master/docs/api/session.md#clearCache) - [shell.openExternal(url[, options, callback])](https://github.com/electron/electron/blob/master/docs/api/shell.md#openExternal) - [webviewTag.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#capturePage) diff --git a/docs/api/session.md b/docs/api/session.md index 291794f48853..689fc0d7eab9 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -531,13 +531,21 @@ event. The [DownloadItem](download-item.md) will not have any `WebContents` asso the initial state will be `interrupted`. The download will start only when the `resume` API is called on the [DownloadItem](download-item.md). -#### `ses.clearAuthCache(options[, callback])` +#### `ses.clearAuthCache(options, callback)` * `options` ([RemovePassword](structures/remove-password.md) | [RemoveClientCertificate](structures/remove-client-certificate.md)) -* `callback` Function (optional) - Called when operation is done. +* `callback` Function - Called when operation is done. Clears the session’s HTTP authentication cache. +**[Deprecated Soon](promisification.md)** + +#### `ses.clearAuthCache(options)` + +* `options` ([RemovePassword](structures/remove-password.md) | [RemoveClientCertificate](structures/remove-client-certificate.md)) + +Returns `Promise` - resolves when the session’s HTTP authentication cache has been cleared. + #### `ses.setPreloads(preloads)` * `preloads` String[] - An array of absolute path to preload scripts diff --git a/lib/browser/api/session.js b/lib/browser/api/session.js index 73cafa07c94b..baf54b9b147f 100644 --- a/lib/browser/api/session.js +++ b/lib/browser/api/session.js @@ -29,6 +29,7 @@ Session.prototype.resolveProxy = deprecate.promisify(Session.prototype.resolvePr Session.prototype.setProxy = deprecate.promisify(Session.prototype.setProxy) Session.prototype.getCacheSize = deprecate.promisify(Session.prototype.getCacheSize) Session.prototype.clearCache = deprecate.promisify(Session.prototype.clearCache) +Session.prototype.clearAuthCache = deprecate.promisify(Session.prototype.clearAuthCache) Cookies.prototype.flushStore = deprecate.promisify(Cookies.prototype.flushStore) Cookies.prototype.get = deprecate.promisify(Cookies.prototype.get) diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index 0a91855fba57..f37c7a11462f 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -965,8 +965,61 @@ describe('session module', () => { }) }) - describe('ses.clearAuthCache(options[, callback])', () => { + describe('ses.clearAuthCache(options)', () => { it('can clear http auth info from cache', (done) => { + const ses = session.fromPartition('auth-cache') + const server = http.createServer((req, res) => { + const credentials = auth(req) + if (!credentials || credentials.name !== 'test' || credentials.pass !== 'test') { + res.statusCode = 401 + res.setHeader('WWW-Authenticate', 'Basic realm="Restricted"') + res.end() + } else { + res.end('authenticated') + } + }) + server.listen(0, '127.0.0.1', () => { + const port = server.address().port + function issueLoginRequest (attempt = 1) { + if (attempt > 2) { + server.close() + return done() + } + const request = net.request({ + url: `http://127.0.0.1:${port}`, + session: ses + }) + request.on('login', (info, callback) => { + attempt += 1 + assert.strictEqual(info.scheme, 'basic') + assert.strictEqual(info.realm, 'Restricted') + callback('test', 'test') + }) + request.on('response', (response) => { + let data = '' + response.pause() + response.on('data', (chunk) => { + data += chunk + }) + response.on('end', () => { + assert.strictEqual(data, 'authenticated') + ses.clearAuthCache({ type: 'password' }).then(() => { + issueLoginRequest(attempt) + }) + }) + response.on('error', (error) => { done(error) }) + response.resume() + }) + // Internal api to bypass cache for testing. + request.urlRequest._setLoadFlags(1 << 1) + request.end() + } + issueLoginRequest() + }) + }) + + // TODO(codebytere): remove when promisification complete + it('can clear http auth info from cache (callback)', (done) => { const ses = session.fromPartition('auth-cache') const server = http.createServer((req, res) => { const credentials = auth(req)