diff --git a/BUILD.gn b/BUILD.gn index 8b801e82e..4d51c2e66 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -407,6 +407,8 @@ static_library("libcef_static") { "libcef/browser/net_service/cookie_helper.h", "libcef/browser/net_service/cookie_manager_impl.cc", "libcef/browser/net_service/cookie_manager_impl.h", + "libcef/browser/net_service/login_delegate.cc", + "libcef/browser/net_service/login_delegate.h", "libcef/browser/net_service/proxy_url_loader_factory.cc", "libcef/browser/net_service/proxy_url_loader_factory.h", "libcef/browser/net_service/resource_handler_wrapper.cc", diff --git a/include/capi/cef_request_context_capi.h b/include/capi/cef_request_context_capi.h index 728d82b06..23fbd7ef1 100644 --- a/include/capi/cef_request_context_capi.h +++ b/include/capi/cef_request_context_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=59376c6298df4489ecc34f3509a8d0e77e9d97f7$ +// $hash=fdfce3e4e33a1d4e1170497d2a476f0837994060$ // #ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_CONTEXT_CAPI_H_ @@ -238,6 +238,15 @@ typedef struct _cef_request_context_t { struct _cef_request_context_t* self, struct _cef_completion_callback_t* callback); + /// + // Clears all HTTP authentication credentials that were added as part of + // handling GetAuthCredentials. If |callback| is non-NULL it will be executed + // on the UI thread after completion. + /// + void(CEF_CALLBACK* clear_http_auth_credentials)( + struct _cef_request_context_t* self, + struct _cef_completion_callback_t* callback); + /// // Clears all active and idle connections that Chromium currently has. This is // only recommended if you have released all other CEF objects but don't yet diff --git a/include/capi/cef_request_handler_capi.h b/include/capi/cef_request_handler_capi.h index 19da99598..d030fa0dd 100644 --- a/include/capi/cef_request_handler_capi.h +++ b/include/capi/cef_request_handler_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=fce8beb9d3e8709a512077681455cb4ef92ef76d$ +// $hash=d9c4e8591ee39bd9d8c1714c0ca2417a7d2a38ea$ // #ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_HANDLER_CAPI_H_ @@ -154,10 +154,11 @@ typedef struct _cef_request_handler_t { /// // Called on the IO thread when the browser needs credentials from the user. - // |isProxy| indicates whether the host is a proxy server. |host| contains the - // hostname and |port| contains the port number. |realm| is the realm of the - // challenge and may be NULL. |scheme| is the authentication scheme used, such - // as "basic" or "digest", and will be NULL if the source of the request is an + // |origin_url| is the origin making this authentication request. |isProxy| + // indicates whether the host is a proxy server. |host| contains the hostname + // and |port| contains the port number. |realm| is the realm of the challenge + // and may be NULL. |scheme| is the authentication scheme used, such as + // "basic" or "digest", and will be NULL if the source of the request is an // FTP server. Return true (1) to continue the request and call // cef_auth_callback_t::cont() either in this function or at a later time when // the authentication information is available. Return false (0) to cancel the @@ -166,7 +167,7 @@ typedef struct _cef_request_handler_t { int(CEF_CALLBACK* get_auth_credentials)( struct _cef_request_handler_t* self, struct _cef_browser_t* browser, - struct _cef_frame_t* frame, + const cef_string_t* origin_url, int isProxy, const cef_string_t* host, int port, diff --git a/include/capi/cef_urlrequest_capi.h b/include/capi/cef_urlrequest_capi.h index 968f6b01a..841551303 100644 --- a/include/capi/cef_urlrequest_capi.h +++ b/include/capi/cef_urlrequest_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=3d31b2d1bad50deeb68a5e6318344024c2f3f5be$ +// $hash=77ac3a2aaea32b649185a58e4c2bbb13b7fe0540$ // #ifndef CEF_INCLUDE_CAPI_CEF_URLREQUEST_CAPI_H_ @@ -193,9 +193,11 @@ typedef struct _cef_urlrequest_client_t { // |isProxy| indicates whether the host is a proxy server. |host| contains the // hostname and |port| contains the port number. Return true (1) to continue // the request and call cef_auth_callback_t::cont() when the authentication - // information is available. Return false (0) to cancel the request. This - // function will only be called for requests initiated from the browser - // process. + // information is available. If the request has an associated browser/frame + // then returning false (0) will result in a call to GetAuthCredentials on the + // cef_request_tHandler associated with that browser, if any. Otherwise, + // returning false (0) will cancel the request immediately. This function will + // only be called for requests initiated from the browser process. /// int(CEF_CALLBACK* get_auth_credentials)( struct _cef_urlrequest_client_t* self, diff --git a/include/cef_api_hash.h b/include/cef_api_hash.h index 6d0e5c1eb..5bc34ab88 100644 --- a/include/cef_api_hash.h +++ b/include/cef_api_hash.h @@ -34,7 +34,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=0bc0d1faa22392b4245dc6eaf56337e7847a1900$ +// $hash=467550bc7fb025b069edb9dc65988d8cb9c56fd2$ // #ifndef CEF_INCLUDE_API_HASH_H_ @@ -47,13 +47,13 @@ // way that may cause binary incompatibility with other builds. The universal // hash value will change if any platform is affected whereas the platform hash // values will change only if that particular platform is affected. -#define CEF_API_HASH_UNIVERSAL "31b55dcbcf52e9f51fe423741f64e5c77e71c65a" +#define CEF_API_HASH_UNIVERSAL "3f2fd465a9dc95ef7d645c0b3c8bdf4e26d9b26e" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "afb8ea794d1bb0f56ba5ce1718a16e5dfd64229e" +#define CEF_API_HASH_PLATFORM "37fc3765fe0ef3ef7542e6568e5689b3575968a8" #elif defined(OS_MACOSX) -#define CEF_API_HASH_PLATFORM "e3a279cf087095495c08fd9fa88daef5dbafff3a" +#define CEF_API_HASH_PLATFORM "dc61db096a4b62365b16d450fe4f898a9552da6a" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "d25833941c670d82cff0aa0ccf47b557d6813634" +#define CEF_API_HASH_PLATFORM "059d7243ae34aa5503c96a21ce24a673dddf4e64" #endif #ifdef __cplusplus diff --git a/include/cef_request_context.h b/include/cef_request_context.h index 4a8cfa916..2cda5be4e 100644 --- a/include/cef_request_context.h +++ b/include/cef_request_context.h @@ -249,6 +249,15 @@ class CefRequestContext : public virtual CefBaseRefCounted { virtual void ClearCertificateExceptions( CefRefPtr callback) = 0; + /// + // Clears all HTTP authentication credentials that were added as part of + // handling GetAuthCredentials. If |callback| is non-NULL it will be executed + // on the UI thread after completion. + /// + /*--cef(optional_param=callback)--*/ + virtual void ClearHttpAuthCredentials( + CefRefPtr callback) = 0; + /// // Clears all active and idle connections that Chromium currently has. // This is only recommended if you have released all other CEF objects but diff --git a/include/cef_request_handler.h b/include/cef_request_handler.h index 7352a5d1e..337ddfff7 100644 --- a/include/cef_request_handler.h +++ b/include/cef_request_handler.h @@ -150,18 +150,19 @@ class CefRequestHandler : public virtual CefBaseRefCounted { /// // Called on the IO thread when the browser needs credentials from the user. - // |isProxy| indicates whether the host is a proxy server. |host| contains the - // hostname and |port| contains the port number. |realm| is the realm of the - // challenge and may be empty. |scheme| is the authentication scheme used, - // such as "basic" or "digest", and will be empty if the source of the request - // is an FTP server. Return true to continue the request and call + // |origin_url| is the origin making this authentication request. |isProxy| + // indicates whether the host is a proxy server. |host| contains the hostname + // and |port| contains the port number. |realm| is the realm of the challenge + // and may be empty. |scheme| is the authentication scheme used, such as + // "basic" or "digest", and will be empty if the source of the request is an + // FTP server. Return true to continue the request and call // CefAuthCallback::Continue() either in this method or at a later time when // the authentication information is available. Return false to cancel the // request immediately. /// /*--cef(optional_param=realm,optional_param=scheme)--*/ virtual bool GetAuthCredentials(CefRefPtr browser, - CefRefPtr frame, + const CefString& origin_url, bool isProxy, const CefString& host, int port, diff --git a/include/cef_urlrequest.h b/include/cef_urlrequest.h index 5b98b1ee3..8966eb8d3 100644 --- a/include/cef_urlrequest.h +++ b/include/cef_urlrequest.h @@ -186,8 +186,11 @@ class CefURLRequestClient : public virtual CefBaseRefCounted { // |isProxy| indicates whether the host is a proxy server. |host| contains the // hostname and |port| contains the port number. Return true to continue the // request and call CefAuthCallback::Continue() when the authentication - // information is available. Return false to cancel the request. This method - // will only be called for requests initiated from the browser process. + // information is available. If the request has an associated browser/frame + // then returning false will result in a call to GetAuthCredentials on the + // CefRequestHandler associated with that browser, if any. Otherwise, + // returning false will cancel the request immediately. This method will only + // be called for requests initiated from the browser process. /// /*--cef(optional_param=realm)--*/ virtual bool GetAuthCredentials(bool isProxy, diff --git a/libcef/browser/content_browser_client.cc b/libcef/browser/content_browser_client.cc index bbadf9b80..a46087f3b 100644 --- a/libcef/browser/content_browser_client.cc +++ b/libcef/browser/content_browser_client.cc @@ -22,6 +22,7 @@ #include "libcef/browser/media_capture_devices_dispatcher.h" #include "libcef/browser/net/chrome_scheme_handler.h" #include "libcef/browser/net/net_util.h" +#include "libcef/browser/net_service/login_delegate.h" #include "libcef/browser/net_service/proxy_url_loader_factory.h" #include "libcef/browser/net_service/resource_request_handler_wrapper.h" #include "libcef/browser/plugins/plugin_service_filter.h" @@ -113,6 +114,7 @@ #include "extensions/common/constants.h" #include "extensions/common/switches.h" #include "mojo/public/cpp/bindings/remote.h" +#include "net/base/auth.h" #include "net/ssl/ssl_cert_request_info.h" #include "ppapi/host/ppapi_host.h" #include "services/network/public/cpp/network_switches.h" @@ -1222,6 +1224,21 @@ CefContentBrowserClient::CreateClientCertStore( ->CreateClientCertStore(); } +std::unique_ptr +CefContentBrowserClient::CreateLoginDelegate( + const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + const content::GlobalRequestID& request_id, + bool is_request_for_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt, + LoginAuthRequiredCallback auth_required_callback) { + return std::make_unique( + auth_info, web_contents, request_id, url, + std::move(auth_required_callback)); +} + void CefContentBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories( int frame_tree_node_id, NonNetworkURLLoaderFactoryMap* factories) { diff --git a/libcef/browser/content_browser_client.h b/libcef/browser/content_browser_client.h index 88efc2e2e..d661f60b1 100644 --- a/libcef/browser/content_browser_client.h +++ b/libcef/browser/content_browser_client.h @@ -155,10 +155,17 @@ class CefContentBrowserClient : public content::ContentBrowserClient { service_manager::BinderRegistry* registry, blink::AssociatedInterfaceRegistry* associated_registry, content::RenderProcessHost* render_process_host) override; - std::unique_ptr CreateClientCertStore( content::ResourceContext* resource_context) override; - + std::unique_ptr CreateLoginDelegate( + const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + const content::GlobalRequestID& request_id, + bool is_request_for_main_frame, + const GURL& url, + scoped_refptr response_headers, + bool first_auth_attempt, + LoginAuthRequiredCallback auth_required_callback) override; void RegisterNonNetworkNavigationURLLoaderFactories( int frame_tree_node_id, NonNetworkURLLoaderFactoryMap* factories) override; diff --git a/libcef/browser/net/network_delegate.cc b/libcef/browser/net/network_delegate.cc index 8900a8628..04c0ce933 100644 --- a/libcef/browser/net/network_delegate.cc +++ b/libcef/browser/net/network_delegate.cc @@ -395,13 +395,10 @@ net::NetworkDelegate::AuthRequiredResponse CefNetworkDelegate::OnAuthRequired( if (client.get()) { CefRefPtr handler = client->GetRequestHandler(); if (handler.get()) { - CefRefPtr frame = - net_util::GetFrameForRequest(browser->browser_info(), request); - CefRefPtr callbackPtr( new CefAuthCallbackImpl(std::move(callback), credentials)); if (handler->GetAuthCredentials( - browser.get(), frame, auth_info.is_proxy, + browser.get(), request->url().spec(), auth_info.is_proxy, auth_info.challenger.host(), auth_info.challenger.port(), auth_info.realm, auth_info.scheme, callbackPtr.get())) { request->SetUserData( diff --git a/libcef/browser/net_service/browser_urlrequest_impl.cc b/libcef/browser/net_service/browser_urlrequest_impl.cc index 465ff42aa..07398daf1 100644 --- a/libcef/browser/net_service/browser_urlrequest_impl.cc +++ b/libcef/browser/net_service/browser_urlrequest_impl.cc @@ -22,12 +22,88 @@ #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" +#include "content/public/browser/global_request_id.h" +#include "content/public/browser/render_frame_host.h" #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/simple_url_loader.h" #include "services/network/public/cpp/simple_url_loader_stream_consumer.h" +namespace { + +const int32_t kInitialRequestID = -2; + +// Request ID for requests initiated by CefBrowserURLRequest. request_ids +// generated by child processes are counted up from 0, while browser +// created requests start at -2 and go down from there. (We need to start at -2 +// because -1 is used as a special value all over the resource_dispatcher_host +// for uninitialized variables.) The resource_dispatcher_host code path is not +// used when NetworkService is enabled so it's safe to repurpose the -2 and +// below range here. +// This method is only called on the UI thread. +int32_t MakeRequestID() { + static int32_t request_id = kInitialRequestID; + return --request_id; +} + +// Manages the mapping of request IDs to request objects. +class RequestManager { + public: + RequestManager() {} + ~RequestManager() { DCHECK(map_.empty()); } + + void Add(int32_t request_id, + CefRefPtr request, + CefRefPtr client) { + DCHECK_LE(request_id, kInitialRequestID); + + base::AutoLock lock_scope(lock_); + DCHECK(map_.find(request_id) == map_.end()); + map_.insert(std::make_pair(request_id, std::make_pair(request, client))); + } + + void Remove(int32_t request_id) { + if (request_id > kInitialRequestID) + return; + + base::AutoLock lock_scope(lock_); + RequestMap::iterator it = map_.find(request_id); + DCHECK(it != map_.end()); + map_.erase(it); + } + + base::Optional Get(int32_t request_id) { + if (request_id > kInitialRequestID) + return base::nullopt; + + base::AutoLock lock_scope(lock_); + RequestMap::const_iterator it = map_.find(request_id); + if (it != map_.end()) { + return it->second; + } + return base::nullopt; + } + + private: + base::Lock lock_; + + using RequestMap = std::map; + RequestMap map_; + + DISALLOW_COPY_AND_ASSIGN(RequestManager); +}; + +#if DCHECK_IS_ON() +// Because of DCHECK()s in the object destructor. +base::LazyInstance::DestructorAtExit g_manager = + LAZY_INSTANCE_INITIALIZER; +#else +base::LazyInstance::Leaky g_manager = LAZY_INSTANCE_INITIALIZER; +#endif + +} // namespace + // CefBrowserURLRequest::Context ---------------------------------------------- class CefBrowserURLRequest::Context @@ -113,6 +189,7 @@ class CefBrowserURLRequest::Context request_context_impl->GetBrowserContext(); DCHECK(browser_context); + int render_frame_id = MSG_ROUTING_NONE; scoped_refptr loader_factory_getter; if (frame) { // The request will be associated with this frame/browser if it's valid, @@ -120,6 +197,14 @@ class CefBrowserURLRequest::Context content::RenderFrameHost* rfh = static_cast(frame.get())->GetRenderFrameHost(); if (rfh) { + // In cases where authentication is required this value will be passed + // as the |routing_id| parameter to + // NetworkServiceClient::OnAuthRequired. Despite the naming the + // GetWebContents method in network_service_client.cc expects it to be a + // FrameTreeNodeId. The |process_id| parameter will always be + // network::mojom::kBrowserProcessId (value 0) for these requests. + render_frame_id = rfh->GetFrameTreeNodeId(); + loader_factory_getter = net_service::URLLoaderFactoryGetter::Create(rfh, browser_context); } @@ -132,10 +217,12 @@ class CefBrowserURLRequest::Context FROM_HERE, base::BindOnce( &CefBrowserURLRequest::Context::ContinueOnOriginatingThread, self, - loader_factory_getter)); + render_frame_id, MakeRequestID(), loader_factory_getter)); } void ContinueOnOriginatingThread( + int render_frame_id, + int32_t request_id, scoped_refptr loader_factory_getter) { DCHECK(CalledOnValidThread()); @@ -163,6 +250,8 @@ class CefBrowserURLRequest::Context static_cast(request_.get()) ->Get(resource_request.get(), false); + resource_request->render_frame_id = render_frame_id; + // SimpleURLLoader is picky about the body contents. Try to populate them // correctly below. auto request_body = resource_request->request_body; @@ -187,6 +276,11 @@ class CefBrowserURLRequest::Context loader_ = network::SimpleURLLoader::Create(std::move(resource_request), NO_TRAFFIC_ANNOTATION_YET); + // Associate the request with |request_id|. + request_id_ = request_id; + loader_->SetRequestId(request_id); + g_manager.Get().Add(request_id, url_request_, client_); + if (request_body) { if (request_body->elements()->size() == 1) { const auto& element = (*request_body->elements())[0]; @@ -294,6 +388,7 @@ class CefBrowserURLRequest::Context DCHECK(request_->GetFlags() | UR_FLAG_STOP_ON_REDIRECT); response_->SetReadOnly(false); + response_->SetURL(redirect_info.new_url.spec()); response_->SetResponseHeaders(*response_head.headers); response_->SetReadOnly(true); @@ -403,6 +498,8 @@ class CefBrowserURLRequest::Context DCHECK(CalledOnValidThread()); DCHECK(url_request_); + g_manager.Get().Remove(request_id_); + client_ = nullptr; request_context_ = nullptr; @@ -439,6 +536,8 @@ class CefBrowserURLRequest::Context scoped_refptr loader_factory_getter_; std::unique_ptr loader_; + int32_t request_id_ = 0; + CefURLRequest::Status status_ = UR_IO_PENDING; CefRefPtr response_; bool response_was_cached_ = false; @@ -453,6 +552,22 @@ class CefBrowserURLRequest::Context // CefBrowserURLRequest ------------------------------------------------------- +// static +base::Optional +CefBrowserURLRequest::FromRequestID(int32_t request_id) { + return g_manager.Get().Get(request_id); +} + +// static +base::Optional +CefBrowserURLRequest::FromRequestID( + const content::GlobalRequestID& request_id) { + if (request_id.child_id == network::mojom::kBrowserProcessId) { + return FromRequestID(request_id.request_id); + } + return base::nullopt; +} + CefBrowserURLRequest::CefBrowserURLRequest( CefRefPtr frame, CefRefPtr request, diff --git a/libcef/browser/net_service/browser_urlrequest_impl.h b/libcef/browser/net_service/browser_urlrequest_impl.h index 839a95daa..45f5a673c 100644 --- a/libcef/browser/net_service/browser_urlrequest_impl.h +++ b/libcef/browser/net_service/browser_urlrequest_impl.h @@ -9,10 +9,28 @@ #include "include/cef_urlrequest.h" +#include "base/optional.h" + +namespace content { +struct GlobalRequestID; +} + class CefBrowserURLRequest : public CefURLRequest { public: class Context; + // TODO(network): After the old network code path is deleted move the + // CefURLRequestClient::GetAuthCredentials callback to the context thread and + // return just the CefBrowserURLRequest object here. The *Client object can + // then be retrieved by calling GetClient() from the required thread. + using RequestInfo = std::pair, + CefRefPtr>; + + // Retrieve the request objects, if any, associated with |request_id|. + static base::Optional FromRequestID(int32_t request_id); + static base::Optional FromRequestID( + const content::GlobalRequestID& request_id); + // If |frame| is nullptr requests can still be intercepted but no // browser/frame will be associated with them. CefBrowserURLRequest(CefRefPtr frame, diff --git a/libcef/browser/net_service/login_delegate.cc b/libcef/browser/net_service/login_delegate.cc new file mode 100644 index 000000000..0b24ab18e --- /dev/null +++ b/libcef/browser/net_service/login_delegate.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that can +// be found in the LICENSE file. + +#include "libcef/browser/net_service/login_delegate.h" + +#include "libcef/browser/browser_host_impl.h" +#include "libcef/browser/net_service/browser_urlrequest_impl.h" +#include "libcef/browser/thread_util.h" + +#include "base/memory/scoped_refptr.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "content/public/browser/global_request_id.h" +#include "content/public/browser/web_contents.h" + +namespace net_service { + +namespace { + +class AuthCallbackImpl : public CefAuthCallback { + public: + explicit AuthCallbackImpl(base::WeakPtr delegate) + : delegate_(delegate), + task_runner_(base::SequencedTaskRunnerHandle::Get()) {} + + ~AuthCallbackImpl() override { + if (delegate_.MaybeValid()) { + // If |delegate_| isn't valid this will be a no-op. + task_runner_->PostTask(FROM_HERE, + base::BindOnce(&LoginDelegate::Cancel, delegate_)); + } + } + + void Continue(const CefString& username, const CefString& password) override { + if (!task_runner_->RunsTasksInCurrentSequence()) { + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&AuthCallbackImpl::Continue, this, username, + password)); + return; + } + + if (delegate_) { + delegate_->Continue(username, password); + delegate_ = NULL; + } + } + + void Cancel() override { + if (!task_runner_->RunsTasksInCurrentSequence()) { + task_runner_->PostTask(FROM_HERE, + base::BindOnce(&AuthCallbackImpl::Cancel, this)); + return; + } + + if (delegate_) { + delegate_->Cancel(); + delegate_ = NULL; + } + } + + private: + base::WeakPtr delegate_; + scoped_refptr task_runner_; + + IMPLEMENT_REFCOUNTING(AuthCallbackImpl); + DISALLOW_COPY_AND_ASSIGN(AuthCallbackImpl); +}; + +void RunCallbackOnIOThread( + CefRefPtr browser, + base::Optional url_request_info, + const net::AuthChallengeInfo& auth_info, + const GURL& origin_url, + CefRefPtr callback_impl) { + CEF_REQUIRE_IOT(); + + // TODO(network): After the old network code path is deleted move this + // callback to the BrowserURLRequest's context thread. + if (url_request_info) { + bool handled = url_request_info->second->GetAuthCredentials( + auth_info.is_proxy, auth_info.challenger.host(), + auth_info.challenger.port(), auth_info.realm, auth_info.scheme, + callback_impl.get()); + if (handled) { + // The user will execute the callback, or the request will be canceled on + // AuthCallbackImpl destruction. + return; + } + } + + if (browser) { + CefRefPtr client = browser->GetClient(); + if (client) { + CefRefPtr handler = client->GetRequestHandler(); + if (handler) { + bool handled = handler->GetAuthCredentials( + browser.get(), origin_url.spec(), auth_info.is_proxy, + auth_info.challenger.host(), auth_info.challenger.port(), + auth_info.realm, auth_info.scheme, callback_impl.get()); + if (handled) { + // The user will execute the callback, or the request will be canceled + // on AuthCallbackImpl destruction. + return; + } + } + } + } + + callback_impl->Cancel(); +} +} // namespace + +LoginDelegate::LoginDelegate(const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + const content::GlobalRequestID& request_id, + const GURL& origin_url, + LoginAuthRequiredCallback callback) + : callback_(std::move(callback)), weak_ptr_factory_(this) { + CEF_REQUIRE_UIT(); + + // May be nullptr for requests originating from CefURLRequest. + CefRefPtr browser; + if (web_contents) { + browser = CefBrowserHostImpl::GetBrowserForContents(web_contents); + } + + // |callback| needs to be executed asynchronously. + CEF_POST_TASK(CEF_UIT, base::BindOnce(&LoginDelegate::Start, + weak_ptr_factory_.GetWeakPtr(), browser, + auth_info, request_id, origin_url)); +} + +void LoginDelegate::Continue(const CefString& username, + const CefString& password) { + CEF_REQUIRE_UIT(); + if (!callback_.is_null()) { + std::move(callback_).Run( + net::AuthCredentials(username.ToString16(), password.ToString16())); + } +} + +void LoginDelegate::Cancel() { + CEF_REQUIRE_UIT(); + if (!callback_.is_null()) { + std::move(callback_).Run(base::nullopt); + } +} + +void LoginDelegate::Start(CefRefPtr browser, + const net::AuthChallengeInfo& auth_info, + const content::GlobalRequestID& request_id, + const GURL& origin_url) { + CEF_REQUIRE_UIT(); + + auto url_request_info = CefBrowserURLRequest::FromRequestID(request_id); + + if (browser || url_request_info) { + // AuthCallbackImpl is bound to the current thread. + CefRefPtr callbackImpl = + new AuthCallbackImpl(weak_ptr_factory_.GetWeakPtr()); + + // Execute callbacks on the IO thread to maintain the "old" + // network_delegate callback behaviour. + CEF_POST_TASK(CEF_IOT, base::BindOnce(&RunCallbackOnIOThread, browser, + url_request_info, auth_info, + origin_url, callbackImpl)); + } else { + Cancel(); + } +} + +} // namespace net_service \ No newline at end of file diff --git a/libcef/browser/net_service/login_delegate.h b/libcef/browser/net_service/login_delegate.h new file mode 100644 index 000000000..1f831aa15 --- /dev/null +++ b/libcef/browser/net_service/login_delegate.h @@ -0,0 +1,50 @@ +// Copyright (c) 2019 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that can +// be found in the LICENSE file. + +#ifndef CEF_LIBCEF_BROWSER_NET_SERVICE_LOGIN_DELEGATE_H_ +#define CEF_LIBCEF_BROWSER_NET_SERVICE_LOGIN_DELEGATE_H_ + +#include "include/cef_base.h" + +#include "base/memory/weak_ptr.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/browser/login_delegate.h" +#include "net/base/auth.h" + +namespace content { +struct GlobalRequestID; +class WebContents; +} // namespace content + +class CefBrowserHostImpl; +class GURL; + +namespace net_service { + +class LoginDelegate : public content::LoginDelegate { + public: + // This object will be deleted when |callback| is executed or the request is + // canceled. |callback| should not be executed after this object is deleted. + LoginDelegate(const net::AuthChallengeInfo& auth_info, + content::WebContents* web_contents, + const content::GlobalRequestID& request_id, + const GURL& origin_url, + LoginAuthRequiredCallback callback); + + void Continue(const CefString& username, const CefString& password); + void Cancel(); + + private: + void Start(CefRefPtr browser, + const net::AuthChallengeInfo& auth_info, + const content::GlobalRequestID& request_id, + const GURL& origin_url); + + LoginAuthRequiredCallback callback_; + base::WeakPtrFactory weak_ptr_factory_; +}; + +} // namespace net_service + +#endif // CEF_LIBCEF_BROWSER_NET_SERVICE_LOGIN_DELEGATE_H_ diff --git a/libcef/browser/request_context_impl.cc b/libcef/browser/request_context_impl.cc index 3d6510acf..922308f4f 100644 --- a/libcef/browser/request_context_impl.cc +++ b/libcef/browser/request_context_impl.cc @@ -546,6 +546,21 @@ void CefRequestContextImpl::ClearCertificateExceptions( this, callback)); } +void CefRequestContextImpl::ClearHttpAuthCredentials( + CefRefPtr callback) { + if (net_service::IsEnabled()) { + GetBrowserContext( + base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI}), + base::Bind(&CefRequestContextImpl::ClearHttpAuthCredentialsInternal, + this, callback)); + } else { + GetRequestContextImpl( + base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}), + base::Bind(&CefRequestContextImpl::ClearHttpAuthCredentialsInternalOld, + this, callback)); + } +} + void CefRequestContextImpl::CloseAllConnections( CefRefPtr callback) { if (net_service::IsEnabled()) { @@ -847,6 +862,36 @@ void CefRequestContextImpl::ClearCertificateExceptionsInternal( } } +void CefRequestContextImpl::ClearHttpAuthCredentialsInternal( + CefRefPtr callback, + CefBrowserContext* browser_context) { + CEF_REQUIRE_UIT(); + + browser_context->GetNetworkContext()->ClearHttpAuthCache( + base::Time(), base::Bind(&CefCompletionCallback::OnComplete, callback)); +} + +void CefRequestContextImpl::ClearHttpAuthCredentialsInternalOld( + CefRefPtr callback, + scoped_refptr request_context) { + CEF_REQUIRE_IOT(); + + net::URLRequestContext* url_context = request_context->GetURLRequestContext(); + if (url_context) { + net::HttpNetworkSession* http_session = + url_context->http_transaction_factory()->GetSession(); + DCHECK(http_session); + + http_session->http_auth_cache()->ClearEntriesAddedSince(base::Time()); + http_session->CloseAllConnections(); + } + + if (callback) { + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefCompletionCallback::OnComplete, callback)); + } +} + void CefRequestContextImpl::CloseAllConnectionsInternal( CefRefPtr callback, CefBrowserContext* browser_context) { diff --git a/libcef/browser/request_context_impl.h b/libcef/browser/request_context_impl.h index 4e945a934..f0a93bdc0 100644 --- a/libcef/browser/request_context_impl.h +++ b/libcef/browser/request_context_impl.h @@ -73,6 +73,8 @@ class CefRequestContextImpl : public CefRequestContext { CefString& error) override; void ClearCertificateExceptions( CefRefPtr callback) override; + void ClearHttpAuthCredentials( + CefRefPtr callback) override; void CloseAllConnections(CefRefPtr callback) override; void ResolveHost(const CefString& origin, CefRefPtr callback) override; @@ -157,6 +159,12 @@ class CefRequestContextImpl : public CefRequestContext { void ClearCertificateExceptionsInternal( CefRefPtr callback, CefBrowserContext* browser_context); + void ClearHttpAuthCredentialsInternal( + CefRefPtr callback, + CefBrowserContext* browser_context); + void ClearHttpAuthCredentialsInternalOld( + CefRefPtr callback, + scoped_refptr request_context); void CloseAllConnectionsInternal(CefRefPtr callback, CefBrowserContext* browser_context); void CloseAllConnectionsInternalOld( diff --git a/libcef_dll/cpptoc/request_context_cpptoc.cc b/libcef_dll/cpptoc/request_context_cpptoc.cc index bd0fab1c6..572e01ebe 100644 --- a/libcef_dll/cpptoc/request_context_cpptoc.cc +++ b/libcef_dll/cpptoc/request_context_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=29ac900a92c4ec966cc115aae96c49b545bc9db9$ +// $hash=95cc8d1445764e1d1610b2047844b138e60542e6$ // #include "libcef_dll/cpptoc/request_context_cpptoc.h" @@ -373,6 +373,21 @@ void CEF_CALLBACK request_context_clear_certificate_exceptions( CefCompletionCallbackCToCpp::Wrap(callback)); } +void CEF_CALLBACK request_context_clear_http_auth_credentials( + struct _cef_request_context_t* self, + cef_completion_callback_t* callback) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Unverified params: callback + + // Execute + CefRequestContextCppToC::Get(self)->ClearHttpAuthCredentials( + CefCompletionCallbackCToCpp::Wrap(callback)); +} + void CEF_CALLBACK request_context_close_all_connections(struct _cef_request_context_t* self, cef_completion_callback_t* callback) { @@ -549,6 +564,8 @@ CefRequestContextCppToC::CefRequestContextCppToC() { GetStruct()->set_preference = request_context_set_preference; GetStruct()->clear_certificate_exceptions = request_context_clear_certificate_exceptions; + GetStruct()->clear_http_auth_credentials = + request_context_clear_http_auth_credentials; GetStruct()->close_all_connections = request_context_close_all_connections; GetStruct()->resolve_host = request_context_resolve_host; GetStruct()->load_extension = request_context_load_extension; diff --git a/libcef_dll/cpptoc/request_handler_cpptoc.cc b/libcef_dll/cpptoc/request_handler_cpptoc.cc index e760d667a..67b6a2b6f 100644 --- a/libcef_dll/cpptoc/request_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/request_handler_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=370a54c21bec2aff5cf62b5897f5d43401e1ec31$ +// $hash=ab373b720fe3c700867f86ad29ffc8b3e518a92c$ // #include "libcef_dll/cpptoc/request_handler_cpptoc.h" @@ -159,7 +159,7 @@ request_handler_get_resource_request_handler( int CEF_CALLBACK request_handler_get_auth_credentials(struct _cef_request_handler_t* self, cef_browser_t* browser, - cef_frame_t* frame, + const cef_string_t* origin_url, int isProxy, const cef_string_t* host, int port, @@ -177,9 +177,9 @@ request_handler_get_auth_credentials(struct _cef_request_handler_t* self, DCHECK(browser); if (!browser) return 0; - // Verify param: frame; type: refptr_diff - DCHECK(frame); - if (!frame) + // Verify param: origin_url; type: string_byref_const + DCHECK(origin_url); + if (!origin_url) return 0; // Verify param: host; type: string_byref_const DCHECK(host); @@ -193,7 +193,7 @@ request_handler_get_auth_credentials(struct _cef_request_handler_t* self, // Execute bool _retval = CefRequestHandlerCppToC::Get(self)->GetAuthCredentials( - CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame), + CefBrowserCToCpp::Wrap(browser), CefString(origin_url), isProxy ? true : false, CefString(host), port, CefString(realm), CefString(scheme), CefAuthCallbackCToCpp::Wrap(callback)); diff --git a/libcef_dll/ctocpp/request_context_ctocpp.cc b/libcef_dll/ctocpp/request_context_ctocpp.cc index 781c3766a..1f462f2ab 100644 --- a/libcef_dll/ctocpp/request_context_ctocpp.cc +++ b/libcef_dll/ctocpp/request_context_ctocpp.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=8155639f093ff697fd30a9e330ae3470108251e5$ +// $hash=5036c24365fa0fdb3d7efc54670f3123a7104fa0$ // #include "libcef_dll/ctocpp/request_context_ctocpp.h" @@ -355,6 +355,22 @@ void CefRequestContextCToCpp::ClearCertificateExceptions( _struct, CefCompletionCallbackCppToC::Wrap(callback)); } +NO_SANITIZE("cfi-icall") +void CefRequestContextCToCpp::ClearHttpAuthCredentials( + CefRefPtr callback) { + cef_request_context_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, clear_http_auth_credentials)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Unverified params: callback + + // Execute + _struct->clear_http_auth_credentials( + _struct, CefCompletionCallbackCppToC::Wrap(callback)); +} + NO_SANITIZE("cfi-icall") void CefRequestContextCToCpp::CloseAllConnections( CefRefPtr callback) { diff --git a/libcef_dll/ctocpp/request_context_ctocpp.h b/libcef_dll/ctocpp/request_context_ctocpp.h index 8dd4ea5b3..92d934762 100644 --- a/libcef_dll/ctocpp/request_context_ctocpp.h +++ b/libcef_dll/ctocpp/request_context_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=00889895e17ac49f55668e395aa98a5f8312ee31$ +// $hash=dd07d0157b7e9128b240ed2b059f2358ebf9fc09$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_CONTEXT_CTOCPP_H_ @@ -63,6 +63,8 @@ class CefRequestContextCToCpp CefString& error) OVERRIDE; void ClearCertificateExceptions( CefRefPtr callback) OVERRIDE; + void ClearHttpAuthCredentials( + CefRefPtr callback) OVERRIDE; void CloseAllConnections(CefRefPtr callback) OVERRIDE; void ResolveHost(const CefString& origin, CefRefPtr callback) OVERRIDE; diff --git a/libcef_dll/ctocpp/request_handler_ctocpp.cc b/libcef_dll/ctocpp/request_handler_ctocpp.cc index f7fb4fe73..3460268e3 100644 --- a/libcef_dll/ctocpp/request_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/request_handler_ctocpp.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=f286ec7520ee6b589b3ecafa409eb3f71c081e27$ +// $hash=ad2fb4fa8e7c8e265de6e03e2c8c8d3d28a7fd39$ // #include "libcef_dll/ctocpp/request_handler_ctocpp.h" @@ -151,7 +151,7 @@ CefRequestHandlerCToCpp::GetResourceRequestHandler( NO_SANITIZE("cfi-icall") bool CefRequestHandlerCToCpp::GetAuthCredentials( CefRefPtr browser, - CefRefPtr frame, + const CefString& origin_url, bool isProxy, const CefString& host, int port, @@ -170,9 +170,9 @@ bool CefRequestHandlerCToCpp::GetAuthCredentials( DCHECK(browser.get()); if (!browser.get()) return false; - // Verify param: frame; type: refptr_diff - DCHECK(frame.get()); - if (!frame.get()) + // Verify param: origin_url; type: string_byref_const + DCHECK(!origin_url.empty()); + if (origin_url.empty()) return false; // Verify param: host; type: string_byref_const DCHECK(!host.empty()); @@ -186,8 +186,8 @@ bool CefRequestHandlerCToCpp::GetAuthCredentials( // Execute int _retval = _struct->get_auth_credentials( - _struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame), - isProxy, host.GetStruct(), port, realm.GetStruct(), scheme.GetStruct(), + _struct, CefBrowserCppToC::Wrap(browser), origin_url.GetStruct(), isProxy, + host.GetStruct(), port, realm.GetStruct(), scheme.GetStruct(), CefAuthCallbackCppToC::Wrap(callback)); // Return type: bool diff --git a/libcef_dll/ctocpp/request_handler_ctocpp.h b/libcef_dll/ctocpp/request_handler_ctocpp.h index 5d4c5e65c..8ecef5154 100644 --- a/libcef_dll/ctocpp/request_handler_ctocpp.h +++ b/libcef_dll/ctocpp/request_handler_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=1e21398533e04c0dbd4143a3847b44fb43c64bc5$ +// $hash=dab1901a0bbeda97ef1efc8e5852964c318775f6$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_ @@ -54,7 +54,7 @@ class CefRequestHandlerCToCpp const CefString& request_initiator, bool& disable_default_handling) override; bool GetAuthCredentials(CefRefPtr browser, - CefRefPtr frame, + const CefString& origin_url, bool isProxy, const CefString& host, int port, diff --git a/patch/patch.cfg b/patch/patch.cfg index 6b465daf2..f82cf28b5 100644 --- a/patch/patch.cfg +++ b/patch/patch.cfg @@ -418,6 +418,14 @@ patches = [ # https://bitbucket.org/chromiumembedded/cef/issues/2622 'name': 'services_network_2622', }, + { + # Enhancements to NetworkService: + # - Support customization of request_id via SimpleURLLoader. + # - Remove the non-nullptr WebContents requirement from + # NetworkServiceClient::OnAuthRequired. + # https://bitbucket.org/chromiumembedded/cef/issues/2718 + 'name': 'services_network_request_id_2718', + }, { # Windows: Remove the base::Value is_standard_layout assert which will fail # for the cef_sandbox build, and which is no longer required. diff --git a/patch/patches/services_network_request_id_2718.patch b/patch/patches/services_network_request_id_2718.patch new file mode 100644 index 000000000..61c00673c --- /dev/null +++ b/patch/patches/services_network_request_id_2718.patch @@ -0,0 +1,84 @@ +diff --git content/browser/network_service_client.cc content/browser/network_service_client.cc +index 2c2795aeaf51..7cded67a3fe3 100644 +--- content/browser/network_service_client.cc ++++ content/browser/network_service_client.cc +@@ -205,10 +205,6 @@ class LoginHandlerDelegate { + } + + WebContents* web_contents = web_contents_getter_.Run(); +- if (!web_contents) { +- OnAuthCredentials(base::nullopt); +- return; +- } + + // WeakPtr is not strictly necessary here due to OnRequestCancelled. + creating_login_delegate_ = true; +@@ -500,11 +496,6 @@ void NetworkServiceClient::OnAuthRequired( + base::RepeatingCallback web_contents_getter = + base::BindRepeating(GetWebContents, process_id, routing_id); + +- if (!web_contents_getter.Run()) { +- std::move(auth_challenge_responder)->OnAuthCredentials(base::nullopt); +- return; +- } +- + bool is_request_for_main_frame = IsMainFrameRequest(process_id, routing_id); + new LoginHandlerDelegate(std::move(auth_challenge_responder), + std::move(web_contents_getter), auth_info, +diff --git services/network/public/cpp/simple_url_loader.cc services/network/public/cpp/simple_url_loader.cc +index 068b86169fab..b8488726b6a7 100644 +--- services/network/public/cpp/simple_url_loader.cc ++++ services/network/public/cpp/simple_url_loader.cc +@@ -227,6 +227,7 @@ class SimpleURLLoaderImpl : public SimpleURLLoader, + uint64_t length = std::numeric_limits::max()) override; + void SetRetryOptions(int max_retries, int retry_mode) override; + void SetTimeoutDuration(base::TimeDelta timeout_duration) override; ++ void SetRequestId(int32_t request_id) override; + + int NetError() const override; + const ResourceResponseHead* ResponseInfo() const override; +@@ -344,6 +345,8 @@ class SimpleURLLoaderImpl : public SimpleURLLoader, + // The next values contain all the information required to restart the + // request. + ++ int32_t request_id_ = 0; ++ + // Populated in the constructor, and cleared once no longer needed, when no + // more retries are possible. + std::unique_ptr resource_request_; +@@ -1368,6 +1371,12 @@ void SimpleURLLoaderImpl::SetTimeoutDuration(base::TimeDelta timeout_duration) { + timeout_duration_ = timeout_duration; + } + ++void SimpleURLLoaderImpl::SetRequestId(int32_t request_id) { ++ // Check if a request has not yet been started. ++ DCHECK(!body_handler_); ++ request_id_ = request_id; ++} ++ + int SimpleURLLoaderImpl::NetError() const { + // Should only be called once the request is compelete. + DCHECK(request_state_->finished); +@@ -1516,7 +1525,7 @@ void SimpleURLLoaderImpl::StartRequest( + string_upload_data_pipe_getter_->GetPtrForNewUpload()); + } + url_loader_factory->CreateLoaderAndStart( +- mojo::MakeRequest(&url_loader_), 0 /* routing_id */, 0 /* request_id */, ++ mojo::MakeRequest(&url_loader_), 0 /* routing_id */, request_id_, + 0 /* options */, *resource_request_, std::move(client_ptr), + net::MutableNetworkTrafficAnnotationTag(annotation_tag_)); + +diff --git services/network/public/cpp/simple_url_loader.h services/network/public/cpp/simple_url_loader.h +index f81788d25fda..ba8948be5ebf 100644 +--- services/network/public/cpp/simple_url_loader.h ++++ services/network/public/cpp/simple_url_loader.h +@@ -324,6 +324,9 @@ class COMPONENT_EXPORT(NETWORK_CPP) SimpleURLLoader { + // as much time as it wants. + virtual void SetTimeoutDuration(base::TimeDelta timeout_duration) = 0; + ++ // Set the ID that will be associated with the request. ++ virtual void SetRequestId(int32_t request_id) = 0; ++ + // Returns the net::Error representing the final status of the request. May + // only be called once the loader has informed the caller of completion. + virtual int NetError() const = 0; diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index d1b9b5e45..1ca44526e 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -702,6 +702,32 @@ CefRefPtr ClientHandler::GetResourceRequestHandler( return this; } +bool ClientHandler::GetAuthCredentials(CefRefPtr browser, + const CefString& origin_url, + bool isProxy, + const CefString& host, + int port, + const CefString& realm, + const CefString& scheme, + CefRefPtr callback) { + CEF_REQUIRE_IO_THREAD(); + + // Used for testing authentication with a proxy server. + // For example, CCProxy on Windows. + if (isProxy) { + callback->Continue("guest", "guest"); + return true; + } + + // Used for testing authentication with https://jigsaw.w3.org/HTTP/. + if (host == "jigsaw.w3.org") { + callback->Continue("guest", "guest"); + return true; + } + + return false; +} + bool ClientHandler::OnQuotaRequest(CefRefPtr browser, const CefString& origin_url, int64 new_size, diff --git a/tests/cefclient/browser/client_handler.h b/tests/cefclient/browser/client_handler.h index eba05dd21..b45778cdb 100644 --- a/tests/cefclient/browser/client_handler.h +++ b/tests/cefclient/browser/client_handler.h @@ -229,6 +229,14 @@ class ClientHandler : public CefClient, bool is_download, const CefString& request_initiator, bool& disable_default_handling) OVERRIDE; + bool GetAuthCredentials(CefRefPtr browser, + const CefString& origin_url, + bool isProxy, + const CefString& host, + int port, + const CefString& realm, + const CefString& scheme, + CefRefPtr callback) OVERRIDE; bool OnQuotaRequest(CefRefPtr browser, const CefString& origin_url, int64 new_size, diff --git a/tests/cefclient/browser/urlrequest_test.cc b/tests/cefclient/browser/urlrequest_test.cc index bb59edd63..e7145fafb 100644 --- a/tests/cefclient/browser/urlrequest_test.cc +++ b/tests/cefclient/browser/urlrequest_test.cc @@ -126,9 +126,10 @@ class Handler : public CefMessageRouterBrowserSide::Handler { const RequestClient::Callback& request_callback = base::Bind(&Handler::OnRequestComplete, base::Unretained(this)); - // Create and start the new CefURLRequest. - urlrequest_ = CefURLRequest::Create( - cef_request, new RequestClient(request_callback), NULL); + // Create and start a new CefURLRequest associated with the frame, so + // that it shares authentication with ClientHandler::GetAuthCredentials. + urlrequest_ = frame->CreateURLRequest( + cef_request, new RequestClient(request_callback)); return true; } diff --git a/tests/cefclient/resources/other_tests.html b/tests/cefclient/resources/other_tests.html index 5626b470c..798aebe46 100644 --- a/tests/cefclient/resources/other_tests.html +++ b/tests/cefclient/resources/other_tests.html @@ -7,11 +7,13 @@ diff --git a/tests/ceftests/urlrequest_unittest.cc b/tests/ceftests/urlrequest_unittest.cc index 1d36dc71d..0df2d465e 100644 --- a/tests/ceftests/urlrequest_unittest.cc +++ b/tests/ceftests/urlrequest_unittest.cc @@ -7,6 +7,7 @@ #include #include "include/base/cef_bind.h" +#include "include/cef_parser.h" #include "include/cef_request_context_handler.h" #include "include/cef_scheme.h" #include "include/cef_server.h" @@ -65,6 +66,7 @@ enum RequestTestMode { REQTEST_GET_REDIRECT_STOP, REQTEST_GET_REDIRECT_LOCATION, REQTEST_GET_REFERRER, + REQTEST_GET_AUTH, REQTEST_POST, REQTEST_POST_FILE, REQTEST_POST_WITHPROGRESS, @@ -144,6 +146,16 @@ struct RequestRunSettings { // If true the response cookie should be saved. bool expect_save_cookie = false; + // If true the test will begin by requiring Basic authentication and then + // continue with the actual request. The UR_FLAG_ALLOW_STORED_CREDENTIALS + // flag must be set on the request. When using the global request context + // CefRequestContext::ClearHttpAuthCredentials should be called to avoid + // leaking state across test runs. Authentication is only supported with + // browser-initiated requests and the server backend. + bool expect_authentication = false; + std::string username; + std::string password; + // If specified the test will begin with this redirect request and response. CefRefPtr redirect_request; CefRefPtr redirect_response; @@ -266,6 +278,24 @@ class RequestDataMap { RedirectDataMap redirect_data_map_; }; +class TestCompletionCallback : public CefCompletionCallback { + public: + explicit TestCompletionCallback(const base::Closure& complete_callback) + : complete_callback_(complete_callback) { + EXPECT_FALSE(complete_callback_.is_null()); + } + + void OnComplete() override { + complete_callback_.Run(); + complete_callback_.Reset(); + } + + private: + base::Closure complete_callback_; + + IMPLEMENT_REFCOUNTING(TestCompletionCallback); +}; + std::string GetRequestScheme(bool server_backend) { return server_backend ? kRequestSchemeServer : kRequestSchemeCustom; } @@ -472,6 +502,47 @@ void GetNormalResponse(const RequestRunSettings* settings, response->SetHeaderMap(headerMap); } +// Based on https://en.wikipedia.org/wiki/Basic_access_authentication#Protocol +void GetAuthResponse(CefRefPtr response) { + response->SetStatus(401); + response->SetStatusText("Unauthorized"); + response->SetMimeType("text/html"); + + CefResponse::HeaderMap headerMap; + headerMap.insert( + std::make_pair("WWW-Authenticate", "Basic realm=\"Test Realm\"")); + response->SetHeaderMap(headerMap); +} + +bool IsAuthorized(CefRefPtr request, + const std::string& username, + const std::string& password) { + const std::string& authHeader = request->GetHeaderByName("Authorization"); + if (authHeader.empty()) + return false; + + if (authHeader.find("Basic ") == 0) { + const std::string& base64 = authHeader.substr(6); + CefRefPtr data = CefBase64Decode(base64); + EXPECT_TRUE(data); + if (!data) { + LOG(ERROR) << "Failed to decode Authorization value: " << base64; + return false; + } + + std::string decoded; + decoded.resize(data->GetSize()); + data->GetData(&decoded[0], data->GetSize(), 0); + + const std::string& expected = username + ":" + password; + EXPECT_STREQ(expected.c_str(), decoded.c_str()); + return decoded == expected; + } + + LOG(ERROR) << "Unexpected Authorization value: " << authHeader; + return false; +} + // SCHEME HANDLER BACKEND // Serves request responses. @@ -1011,6 +1082,14 @@ class RequestServerHandler : public CefServerHandler { CefRefPtr request) { RequestDataMap::Entry entry = data_map_.Find(request->GetURL()); if (entry.type == RequestDataMap::Entry::TYPE_NORMAL) { + const bool needs_auth = entry.settings->expect_authentication && + !IsAuthorized(request, entry.settings->username, + entry.settings->password); + if (needs_auth) { + HandleAuthRequest(server, connection_id, request); + return; + } + HandleNormalRequest(server, connection_id, request, entry.settings); } else if (entry.type == RequestDataMap::Entry::TYPE_REDIRECT) { HandleRedirectRequest(server, connection_id, request, @@ -1022,6 +1101,14 @@ class RequestServerHandler : public CefServerHandler { } } + void HandleAuthRequest(CefRefPtr server, + int connection_id, + CefRefPtr request) { + CefRefPtr response = CefResponse::Create(); + GetAuthResponse(response); + SendResponse(server, connection_id, response, std::string()); + } + void HandleNormalRequest(CefRefPtr server, int connection_id, CefRefPtr request, @@ -1189,6 +1276,11 @@ class RequestClient : public CefURLRequestClient { const CefString& realm, const CefString& scheme, CefRefPtr callback) override { + auth_credentials_ct_++; + if (has_authentication_) { + callback->Continue(username_, password_); + return true; + } return false; } @@ -1196,10 +1288,15 @@ class RequestClient : public CefURLRequestClient { const RequestCompleteCallback complete_callback_; public: + bool has_authentication_ = false; + std::string username_; + std::string password_; + int request_complete_ct_ = 0; int upload_progress_ct_ = 0; int download_progress_ct_ = 0; int download_data_ct_ = 0; + int auth_credentials_ct_ = 0; int64 upload_total_ = 0; int64 download_total_ = 0; @@ -1251,6 +1348,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe { REGISTER_TEST(REQTEST_GET_REDIRECT_LOCATION, SetupGetRedirectLocationTest, SingleRunTest); REGISTER_TEST(REQTEST_GET_REFERRER, SetupGetReferrerTest, SingleRunTest); + REGISTER_TEST(REQTEST_GET_AUTH, SetupGetAuthTest, SingleRunTest); REGISTER_TEST(REQTEST_POST, SetupPostTest, SingleRunTest); REGISTER_TEST(REQTEST_POST_FILE, SetupPostFileTest, SingleRunTest); REGISTER_TEST(REQTEST_POST_WITHPROGRESS, SetupPostWithProgressTest, @@ -1530,6 +1628,28 @@ class RequestTestRunner : public base::RefCountedThreadSafe { complete_callback.Run(); } + void SetupGetAuthTest(const base::Closure& complete_callback) { + // Start with the normal get test. + SetupGetTestShared(); + + // Require Basic authentication. + settings_.expect_authentication = true; + settings_.username = "user"; + settings_.password = "pass"; + + // This flag is required to support credentials, which means we'll also get + // the cookies. + settings_.request->SetFlags(UR_FLAG_ALLOW_STORED_CREDENTIALS); + settings_.expect_save_cookie = true; + settings_.expect_send_cookie = true; + + // The authentication request will come first, then the actual request. + settings_.expected_receive_count = 2; + settings_.expected_send_count = 2; + + complete_callback.Run(); + } + void SetupPostTestShared() { settings_.request = CefRequest::Create(); settings_.request->SetURL(GetTestURL("PostTest.html")); @@ -2053,6 +2173,16 @@ class RequestTestRunner : public base::RefCountedThreadSafe { EXPECT_TRUE(request.get()); CefRefPtr client = new RequestClient(complete_callback); + + // Delegation to CefRequestHandler::GetAuthCredentials is only supported + // with NetworkService. + if ((!IsNetworkServiceEnabled() || !use_frame_method_) && + settings_.expect_authentication) { + client->has_authentication_ = true; + client->username_ = settings_.username; + client->password_ = settings_.password; + } + if (use_frame_method_) { EXPECT_TRUE(frame_); frame_->CreateURLRequest(request, client.get()); @@ -2123,6 +2253,12 @@ class RequestTestRunner : public base::RefCountedThreadSafe { EXPECT_EQ(0, client->download_data_ct_); EXPECT_TRUE(client->download_data_.empty()); } + + if (settings_.expect_authentication) { + EXPECT_EQ(1, client->auth_credentials_ct_); + } else { + EXPECT_EQ(0, client->auth_credentials_ct_); + } } // Run a test with a single request. @@ -2530,7 +2666,7 @@ class RequestTestHandler : public TestHandler { // Continue the test once supported schemes has been set. request_context->GetCookieManager(NULL)->SetSupportedSchemes( supported_schemes, true, - new SupportedSchemesCompletionCallback( + new TestCompletionCallback( base::Bind(&RequestTestHandler::PreSetupComplete, this))); } else { PreSetupComplete(); @@ -2603,6 +2739,25 @@ class RequestTestHandler : public TestHandler { return TestHandler::OnBeforeResourceLoad(browser, frame, request, callback); } + bool GetAuthCredentials(CefRefPtr browser, + const CefString& origin_url, + bool isProxy, + const CefString& host, + int port, + const CefString& realm, + const CefString& scheme, + CefRefPtr callback) override { + EXPECT_TRUE(test_in_browser_); + EXPECT_TRUE(test_frame_method_); + auth_credentials_ct_++; + if (test_runner_->settings_.expect_authentication) { + callback->Continue(test_runner_->settings_.username, + test_runner_->settings_.password); + return true; + } + return false; + } + void OnLoadEnd(CefRefPtr browser, CefRefPtr frame, int httpStatusCode) override { @@ -2713,7 +2868,21 @@ class RequestTestHandler : public TestHandler { // Shut down the browser side of the test. test_runner_->ShutdownTest( - base::Bind(&RequestTestHandler::DestroyTest, this)); + base::Bind(&RequestTestHandler::MaybeClearAuthCredentials, this)); + } + + void MaybeClearAuthCredentials() { + if (test_runner_->settings_.expect_authentication && + context_mode_ == CONTEXT_GLOBAL) { + // Clear the HTTP authentication cache to avoid leaking state between + // test runs when using the global request context. + test_runner_->GetRequestContext()->ClearHttpAuthCredentials( + new TestCompletionCallback( + base::Bind(&RequestTestHandler::DestroyTest, this))); + return; + } + + DestroyTest(); } void DestroyTest() override { @@ -2730,6 +2899,16 @@ class RequestTestHandler : public TestHandler { } } + // CefRequestHandler::GetAuthCredentials should be called after + // CefURLRequestClient::GetAuthCredentials when the request has an + // associated frame. + if (IsNetworkServiceEnabled() && test_in_browser_ && test_frame_method_ && + test_runner_->settings_.expect_authentication) { + EXPECT_EQ(1, auth_credentials_ct_); + } else { + EXPECT_EQ(0, auth_credentials_ct_); + } + TestHandler::DestroyTest(); // For non-global contexts OnTestComplete() will be called when the @@ -2799,26 +2978,6 @@ class RequestTestHandler : public TestHandler { IMPLEMENT_REFCOUNTING(RequestContextHandler); }; - // Continue the rest once supported schemes have been set. - class SupportedSchemesCompletionCallback : public CefCompletionCallback { - public: - explicit SupportedSchemesCompletionCallback( - const base::Closure& complete_callback) - : complete_callback_(complete_callback) { - EXPECT_FALSE(complete_callback_.is_null()); - } - - void OnComplete() override { - complete_callback_.Run(); - complete_callback_.Reset(); - } - - private: - base::Closure complete_callback_; - - IMPLEMENT_REFCOUNTING(SupportedSchemesCompletionCallback); - }; - const RequestTestMode test_mode_; const ContextTestMode context_mode_; const bool test_in_browser_; @@ -2841,6 +3000,7 @@ class RequestTestHandler : public TestHandler { TrackCallback got_message_; TrackCallback got_success_; + int auth_credentials_ct_ = 0; TrackCallback got_on_test_complete_; IMPLEMENT_REFCOUNTING(RequestTestHandler); @@ -3007,7 +3167,7 @@ REQ_TEST_SET(WithFrame, true) REQ_TEST_FRAME_SET() -// Cache tests can only be run with the server backend. +// Cache and authentication tests can only be run with the server backend. #define REQ_TEST_CACHE_SET_EX(suffix, context_mode, test_frame_method) \ REQ_TEST(BrowserGETCacheWithControl##suffix, REQTEST_CACHE_WITH_CONTROL, \ context_mode, true, true, test_frame_method) \ @@ -3039,6 +3199,8 @@ REQ_TEST_FRAME_SET() REQ_TEST(RendererGETCacheWithoutControl##suffix, \ REQTEST_CACHE_WITHOUT_CONTROL, context_mode, false, true, \ test_frame_method) \ + REQ_TEST(BrowserGETAuth##suffix, REQTEST_GET_AUTH, context_mode, true, true, \ + test_frame_method) \ REQ_TEST(RendererGETCacheSkipFlag##suffix, REQTEST_CACHE_SKIP_FLAG, \ context_mode, false, true, test_frame_method) \ REQ_TEST(RendererGETCacheSkipHeader##suffix, REQTEST_CACHE_SKIP_HEADER, \