1// Copyright (c) 2013 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/renderer_host/pepper/pepper_network_proxy_host.h"
6
7#include "base/bind.h"
8#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
9#include "content/browser/renderer_host/pepper/pepper_socket_utils.h"
10#include "content/public/browser/browser_context.h"
11#include "content/public/browser/browser_thread.h"
12#include "content/public/browser/render_process_host.h"
13#include "content/public/common/socket_permission_request.h"
14#include "net/base/net_errors.h"
15#include "net/proxy/proxy_info.h"
16#include "net/url_request/url_request_context.h"
17#include "net/url_request/url_request_context_getter.h"
18#include "ppapi/c/pp_errors.h"
19#include "ppapi/host/dispatch_host_message.h"
20#include "ppapi/host/ppapi_host.h"
21#include "ppapi/proxy/ppapi_messages.h"
22
23namespace content {
24
25PepperNetworkProxyHost::PepperNetworkProxyHost(BrowserPpapiHostImpl* host,
26                                               PP_Instance instance,
27                                               PP_Resource resource)
28    : ResourceHost(host->GetPpapiHost(), instance, resource),
29      proxy_service_(NULL),
30      is_allowed_(false),
31      waiting_for_ui_thread_data_(true),
32      weak_factory_(this) {
33  int render_process_id(0), render_view_id(0);
34  host->GetRenderViewIDsForInstance(instance,
35                                    &render_process_id,
36                                    &render_view_id);
37  BrowserThread::PostTaskAndReplyWithResult(
38      BrowserThread::UI, FROM_HERE,
39      base::Bind(&GetUIThreadDataOnUIThread,
40                 render_process_id,
41                 render_view_id,
42                 host->external_plugin()),
43      base::Bind(&PepperNetworkProxyHost::DidGetUIThreadData,
44                 weak_factory_.GetWeakPtr()));
45}
46
47PepperNetworkProxyHost::~PepperNetworkProxyHost() {
48  while (!pending_requests_.empty()) {
49    // If the proxy_service_ is NULL, we shouldn't have any outstanding
50    // requests.
51    DCHECK(proxy_service_);
52    net::ProxyService::PacRequest* request = pending_requests_.front();
53    proxy_service_->CancelPacRequest(request);
54    pending_requests_.pop();
55  }
56}
57
58PepperNetworkProxyHost::UIThreadData::UIThreadData()
59    : is_allowed(false) {
60}
61
62PepperNetworkProxyHost::UIThreadData::~UIThreadData() {
63}
64
65// static
66PepperNetworkProxyHost::UIThreadData
67PepperNetworkProxyHost::GetUIThreadDataOnUIThread(int render_process_id,
68                                                  int render_view_id,
69                                                  bool is_external_plugin) {
70  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
71  PepperNetworkProxyHost::UIThreadData result;
72  RenderProcessHost* render_process_host =
73      RenderProcessHost::FromID(render_process_id);
74  if (render_process_host && render_process_host->GetBrowserContext()) {
75    result.context_getter = render_process_host->GetBrowserContext()->
76        GetRequestContextForRenderProcess(render_process_id);
77  }
78
79  RenderViewHost* render_view_host =
80      RenderViewHost::FromID(render_process_id, render_view_id);
81  if (render_view_host) {
82    SocketPermissionRequest request(
83        content::SocketPermissionRequest::RESOLVE_PROXY, std::string(), 0);
84    result.is_allowed = pepper_socket_utils::CanUseSocketAPIs(
85        is_external_plugin,
86        false /* is_private_api */,
87        request,
88        render_view_host);
89  }
90  return result;
91}
92
93void PepperNetworkProxyHost::DidGetUIThreadData(
94    const UIThreadData& ui_thread_data) {
95  is_allowed_ = ui_thread_data.is_allowed;
96  if (ui_thread_data.context_getter.get() &&
97      ui_thread_data.context_getter->GetURLRequestContext()) {
98    proxy_service_ =
99        ui_thread_data.context_getter->GetURLRequestContext()->proxy_service();
100  }
101  waiting_for_ui_thread_data_ = false;
102  if (!proxy_service_) {
103    DLOG_IF(WARNING, proxy_service_)
104        << "Failed to find a ProxyService for Pepper plugin.";
105  }
106  TryToSendUnsentRequests();
107}
108
109int32_t PepperNetworkProxyHost::OnResourceMessageReceived(
110    const IPC::Message& msg,
111    ppapi::host::HostMessageContext* context) {
112  IPC_BEGIN_MESSAGE_MAP(PepperNetworkProxyHost, msg)
113    PPAPI_DISPATCH_HOST_RESOURCE_CALL(
114        PpapiHostMsg_NetworkProxy_GetProxyForURL, OnMsgGetProxyForURL)
115  IPC_END_MESSAGE_MAP()
116  return PP_ERROR_FAILED;
117}
118
119int32_t PepperNetworkProxyHost::OnMsgGetProxyForURL(
120    ppapi::host::HostMessageContext* context,
121    const std::string& url) {
122  GURL gurl(url);
123  if (gurl.is_valid()) {
124    UnsentRequest request = { gurl, context->MakeReplyMessageContext() };
125    unsent_requests_.push(request);
126    TryToSendUnsentRequests();
127  } else {
128    SendFailureReply(PP_ERROR_BADARGUMENT,
129                     context->MakeReplyMessageContext());
130  }
131  return PP_OK_COMPLETIONPENDING;
132}
133
134void PepperNetworkProxyHost::TryToSendUnsentRequests() {
135  if (waiting_for_ui_thread_data_)
136    return;
137
138  while (!unsent_requests_.empty()) {
139    const UnsentRequest& request = unsent_requests_.front();
140    if (!proxy_service_) {
141      SendFailureReply(PP_ERROR_FAILED, request.reply_context);
142    } else if (!is_allowed_) {
143      SendFailureReply(PP_ERROR_NOACCESS, request.reply_context);
144    } else {
145      // Everything looks valid, so try to resolve the proxy.
146      net::ProxyInfo* proxy_info = new net::ProxyInfo;
147      net::ProxyService::PacRequest* pending_request = NULL;
148      base::Callback<void (int)> callback =
149          base::Bind(&PepperNetworkProxyHost::OnResolveProxyCompleted,
150                     weak_factory_.GetWeakPtr(),
151                     request.reply_context,
152                     base::Owned(proxy_info));
153      int result = proxy_service_->ResolveProxy(request.url,
154                                                proxy_info,
155                                                callback,
156                                                &pending_request,
157                                                net::BoundNetLog());
158      pending_requests_.push(pending_request);
159      // If it was handled synchronously, we must run the callback now;
160      // proxy_service_ won't run it for us in this case.
161      if (result != net::ERR_IO_PENDING)
162        callback.Run(result);
163    }
164    unsent_requests_.pop();
165  }
166}
167
168void PepperNetworkProxyHost::OnResolveProxyCompleted(
169    ppapi::host::ReplyMessageContext context,
170    net::ProxyInfo* proxy_info,
171    int result) {
172  pending_requests_.pop();
173
174  if (result != net::OK) {
175    // Currently, the only proxy-specific error we could get is
176    // MANDATORY_PROXY_CONFIGURATION_FAILED. There's really no action a plugin
177    // can take in this case, so there's no need to distinguish it from other
178    // failures.
179    context.params.set_result(PP_ERROR_FAILED);
180  }
181  host()->SendReply(context,
182                    PpapiPluginMsg_NetworkProxy_GetProxyForURLReply(
183                        proxy_info->ToPacString()));
184}
185
186void PepperNetworkProxyHost::SendFailureReply(
187    int32_t error,
188    ppapi::host::ReplyMessageContext context) {
189  context.params.set_result(error);
190  host()->SendReply(context,
191                    PpapiPluginMsg_NetworkProxy_GetProxyForURLReply(
192                        std::string()));
193}
194
195}  // namespace content
196