1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/browser/resolve_proxy_msg_helper.h"
6 
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "content/common/view_messages.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/render_process_host.h"
12 #include "content/public/browser/storage_partition.h"
13 #include "net/base/network_isolation_key.h"
14 #include "net/proxy_resolution/proxy_info.h"
15 #include "services/network/public/mojom/network_context.mojom.h"
16 
17 namespace content {
18 
ResolveProxyMsgHelper(int render_process_host_id)19 ResolveProxyMsgHelper::ResolveProxyMsgHelper(int render_process_host_id)
20     : BrowserMessageFilter(ViewMsgStart),
21       render_process_host_id_(render_process_host_id) {}
22 
OverrideThreadForMessage(const IPC::Message & message,BrowserThread::ID * thread)23 void ResolveProxyMsgHelper::OverrideThreadForMessage(
24     const IPC::Message& message,
25     BrowserThread::ID* thread) {
26   if (message.type() == ViewHostMsg_ResolveProxy::ID)
27     *thread = BrowserThread::UI;
28 }
29 
OnMessageReceived(const IPC::Message & message)30 bool ResolveProxyMsgHelper::OnMessageReceived(const IPC::Message& message) {
31   bool handled = true;
32   IPC_BEGIN_MESSAGE_MAP(ResolveProxyMsgHelper, message)
33     IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ResolveProxy, OnResolveProxy)
34     IPC_MESSAGE_UNHANDLED(handled = false)
35   IPC_END_MESSAGE_MAP()
36   return handled;
37 }
38 
OnResolveProxy(const GURL & url,IPC::Message * reply_msg)39 void ResolveProxyMsgHelper::OnResolveProxy(const GURL& url,
40                                            IPC::Message* reply_msg) {
41   DCHECK_CURRENTLY_ON(BrowserThread::UI);
42 
43   // Enqueue the pending request.
44   pending_requests_.push_back(PendingRequest(url, reply_msg));
45 
46   // If nothing is in progress, start.
47   if (!receiver_.is_bound()) {
48     DCHECK_EQ(1u, pending_requests_.size());
49     StartPendingRequest();
50   }
51 }
52 
~ResolveProxyMsgHelper()53 ResolveProxyMsgHelper::~ResolveProxyMsgHelper() {
54   DCHECK(!owned_self_);
55   DCHECK(!receiver_.is_bound());
56 }
57 
StartPendingRequest()58 void ResolveProxyMsgHelper::StartPendingRequest() {
59   DCHECK_CURRENTLY_ON(BrowserThread::UI);
60   DCHECK(!receiver_.is_bound());
61   DCHECK(!pending_requests_.empty());
62 
63   // Start the request.
64   mojo::PendingRemote<network::mojom::ProxyLookupClient> proxy_lookup_client =
65       receiver_.BindNewPipeAndPassRemote();
66   receiver_.set_disconnect_handler(
67       base::BindOnce(&ResolveProxyMsgHelper::OnProxyLookupComplete,
68                      base::Unretained(this), net::ERR_ABORTED, base::nullopt));
69   owned_self_ = this;
70   if (!SendRequestToNetworkService(pending_requests_.front().url,
71                                    std::move(proxy_lookup_client))) {
72     OnProxyLookupComplete(net::ERR_FAILED, base::nullopt);
73   }
74 }
75 
SendRequestToNetworkService(const GURL & url,mojo::PendingRemote<network::mojom::ProxyLookupClient> proxy_lookup_client)76 bool ResolveProxyMsgHelper::SendRequestToNetworkService(
77     const GURL& url,
78     mojo::PendingRemote<network::mojom::ProxyLookupClient>
79         proxy_lookup_client) {
80   DCHECK_CURRENTLY_ON(BrowserThread::UI);
81 
82   RenderProcessHost* render_process_host =
83       RenderProcessHost::FromID(render_process_host_id_);
84   // Fail the request if there's no such RenderProcessHost;
85   if (!render_process_host)
86     return false;
87   // TODO(https://crbug.com/1021661): Pass in a non-empty NetworkIsolationKey.
88   render_process_host->GetStoragePartition()
89       ->GetNetworkContext()
90       ->LookUpProxyForURL(url, net::NetworkIsolationKey::Todo(),
91                           std::move(proxy_lookup_client));
92   return true;
93 }
94 
OnProxyLookupComplete(int32_t net_error,const base::Optional<net::ProxyInfo> & proxy_info)95 void ResolveProxyMsgHelper::OnProxyLookupComplete(
96     int32_t net_error,
97     const base::Optional<net::ProxyInfo>& proxy_info) {
98   DCHECK_CURRENTLY_ON(BrowserThread::UI);
99   DCHECK(!pending_requests_.empty());
100 
101   receiver_.reset();
102 
103   // Need to keep |this| alive until the end of this method, and then release
104   // this reference. StartPendingRequest(), if called, will grab other
105   // reference, and a reference may be owned by the IO thread or by other
106   // posted tasks, so |this| may or may not be deleted at the end of this
107   // method.
108   scoped_refptr<ResolveProxyMsgHelper> owned_self = std::move(owned_self_);
109 
110   // If all references except |owned_self| have been released, then there's
111   // nothing waiting for pending requests to complete. So just exit this method,
112   // which will release the last reference, destroying |this|.
113   if (HasOneRef())
114     return;
115 
116   // Clear the current (completed) request.
117   PendingRequest completed_req = std::move(pending_requests_.front());
118   pending_requests_.pop_front();
119 
120   ViewHostMsg_ResolveProxy::WriteReplyParams(
121       completed_req.reply_msg.get(), !!proxy_info,
122       proxy_info ? proxy_info->ToPacString() : std::string());
123   Send(completed_req.reply_msg.release());
124 
125   // Start the next request.
126   if (!pending_requests_.empty())
127     StartPendingRequest();
128 }
129 
PendingRequest(const GURL & url,IPC::Message * reply_msg)130 ResolveProxyMsgHelper::PendingRequest::PendingRequest(const GURL& url,
131                                                       IPC::Message* reply_msg)
132     : url(url), reply_msg(reply_msg) {}
133 
134 ResolveProxyMsgHelper::PendingRequest::PendingRequest(
135     PendingRequest&& pending_request) noexcept = default;
136 
137 ResolveProxyMsgHelper::PendingRequest::~PendingRequest() noexcept = default;
138 
139 ResolveProxyMsgHelper::PendingRequest& ResolveProxyMsgHelper::PendingRequest::
140 operator=(PendingRequest&& pending_request) noexcept = default;
141 
142 }  // namespace content
143