// Copyright (c) 2012 The Chromium 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 "content/browser/resolve_proxy_msg_helper.h" #include "base/bind.h" #include "base/compiler_specific.h" #include "content/common/view_messages.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/storage_partition.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "net/proxy_resolution/proxy_info.h" #include "services/network/public/mojom/network_context.mojom.h" namespace content { ResolveProxyMsgHelper::ResolveProxyMsgHelper(int render_process_host_id) : BrowserMessageFilter(ViewMsgStart), render_process_host_id_(render_process_host_id), binding_(this) {} void ResolveProxyMsgHelper::OverrideThreadForMessage( const IPC::Message& message, BrowserThread::ID* thread) { if (message.type() == ViewHostMsg_ResolveProxy::ID) *thread = BrowserThread::UI; } bool ResolveProxyMsgHelper::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(ResolveProxyMsgHelper, message) IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ResolveProxy, OnResolveProxy) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void ResolveProxyMsgHelper::OnResolveProxy(const GURL& url, IPC::Message* reply_msg) { DCHECK_CURRENTLY_ON(BrowserThread::UI); // Enqueue the pending request. pending_requests_.push_back(PendingRequest(url, reply_msg)); // If nothing is in progress, start. if (!binding_.is_bound()) { DCHECK_EQ(1u, pending_requests_.size()); StartPendingRequest(); } } ResolveProxyMsgHelper::~ResolveProxyMsgHelper() { DCHECK(!owned_self_); DCHECK(!binding_.is_bound()); } void ResolveProxyMsgHelper::StartPendingRequest() { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!binding_.is_bound()); DCHECK(!pending_requests_.empty()); // Start the request. network::mojom::ProxyLookupClientPtr proxy_lookup_client; binding_.Bind(mojo::MakeRequest(&proxy_lookup_client)); binding_.set_connection_error_handler( base::BindOnce(&ResolveProxyMsgHelper::OnProxyLookupComplete, base::Unretained(this), net::ERR_ABORTED, base::nullopt)); owned_self_ = this; if (!SendRequestToNetworkService(pending_requests_.front().url, std::move(proxy_lookup_client))) { OnProxyLookupComplete(net::ERR_FAILED, base::nullopt); } } bool ResolveProxyMsgHelper::SendRequestToNetworkService( const GURL& url, network::mojom::ProxyLookupClientPtr proxy_lookup_client) { DCHECK_CURRENTLY_ON(BrowserThread::UI); RenderProcessHost* render_process_host = RenderProcessHost::FromID(render_process_host_id_); // Fail the request if there's no such RenderProcessHost; if (!render_process_host) return false; render_process_host->GetStoragePartition() ->GetNetworkContext() ->LookUpProxyForURL(url, std::move(proxy_lookup_client)); return true; } void ResolveProxyMsgHelper::OnProxyLookupComplete( int32_t net_error, const base::Optional<net::ProxyInfo>& proxy_info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!pending_requests_.empty()); binding_.Close(); // Need to keep |this| alive until the end of this method, and then release // this reference. StartPendingRequest(), if called, will grab other // reference, and a reference may be owned by the IO thread or by other // posted tasks, so |this| may or may not be deleted at the end of this // method. scoped_refptr<ResolveProxyMsgHelper> owned_self = std::move(owned_self_); // If all references except |owned_self| have been released, then there's // nothing waiting for pending requests to complete. So just exit this method, // which will release the last reference, destroying |this|. if (HasOneRef()) return; // Clear the current (completed) request. PendingRequest completed_req = std::move(pending_requests_.front()); pending_requests_.pop_front(); ViewHostMsg_ResolveProxy::WriteReplyParams( completed_req.reply_msg.get(), !!proxy_info, proxy_info ? proxy_info->ToPacString() : std::string()); Send(completed_req.reply_msg.release()); // Start the next request. if (!pending_requests_.empty()) StartPendingRequest(); } ResolveProxyMsgHelper::PendingRequest::PendingRequest(const GURL& url, IPC::Message* reply_msg) : url(url), reply_msg(reply_msg) {} ResolveProxyMsgHelper::PendingRequest::PendingRequest( PendingRequest&& pending_request) noexcept = default; ResolveProxyMsgHelper::PendingRequest::~PendingRequest() noexcept = default; ResolveProxyMsgHelper::PendingRequest& ResolveProxyMsgHelper::PendingRequest:: operator=(PendingRequest&& pending_request) noexcept = default; } // namespace content