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