1 // Copyright 2015 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.
5 #include "ppapi/proxy/udp_socket_filter.h"
10 #include "base/logging.h"
11 #include "ppapi/c/pp_errors.h"
12 #include "ppapi/proxy/error_conversion.h"
13 #include "ppapi/proxy/plugin_globals.h"
14 #include "ppapi/proxy/ppapi_messages.h"
15 #include "ppapi/thunk/enter.h"
16 #include "ppapi/thunk/resource_creation_api.h"
21 const int32_t UDPSocketFilter::kMaxReadSize
= 128 * 1024;
22 const int32_t UDPSocketFilter::kMaxReceiveBufferSize
=
23 1024 * UDPSocketFilter::kMaxReadSize
;
24 const size_t UDPSocketFilter::kPluginReceiveBufferSlots
= 32u;
28 int32_t SetRecvFromOutput(PP_Instance pp_instance
,
29 const scoped_ptr
<std::string
>& data
,
30 const PP_NetAddress_Private
& addr
,
33 PP_Resource
* output_addr
,
34 int32_t browser_result
) {
35 ProxyLock::AssertAcquired();
36 DCHECK_GE(num_bytes
, static_cast<int32_t>(data
->size()));
38 int32_t result
= browser_result
;
39 if (result
== PP_OK
&& output_addr
) {
40 thunk::EnterResourceCreationNoLock
enter(pp_instance
);
41 if (enter
.succeeded()) {
42 *output_addr
= enter
.functions()->CreateNetAddressFromNetAddressPrivate(
45 result
= PP_ERROR_FAILED
;
49 if (result
== PP_OK
&& !data
->empty())
50 memcpy(output_buffer
, data
->c_str(), data
->size());
52 return result
== PP_OK
? static_cast<int32_t>(data
->size()) : result
;
57 UDPSocketFilter::UDPSocketFilter() {
60 UDPSocketFilter::~UDPSocketFilter() {
63 void UDPSocketFilter::AddUDPResource(
67 const base::Closure
& slot_available_callback
) {
68 ProxyLock::AssertAcquired();
69 base::AutoLock
acquire(lock_
);
70 DCHECK(!queues_
.contains(resource
));
71 queues_
.add(resource
, scoped_ptr
<RecvQueue
>(new RecvQueue(
72 instance
, private_api
, slot_available_callback
)));
75 void UDPSocketFilter::RemoveUDPResource(PP_Resource resource
) {
76 ProxyLock::AssertAcquired();
77 base::AutoLock
acquire(lock_
);
78 DCHECK(queues_
.contains(resource
));
79 queues_
.erase(resource
);
82 int32_t UDPSocketFilter::RequestData(
87 const scoped_refptr
<TrackedCallback
>& callback
) {
88 ProxyLock::AssertAcquired();
89 base::AutoLock
acquire(lock_
);
90 RecvQueue
* queue_ptr
= queues_
.get(resource
);
93 return PP_ERROR_FAILED
;
95 return queue_ptr
->RequestData(num_bytes
, buffer
, addr
, callback
);
98 bool UDPSocketFilter::OnResourceReplyReceived(
99 const ResourceMessageReplyParams
& params
,
100 const IPC::Message
& nested_msg
) {
102 PPAPI_BEGIN_MESSAGE_MAP(UDPSocketFilter
, nested_msg
)
103 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_UDPSocket_PushRecvResult
,
104 OnPluginMsgPushRecvResult
)
105 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(handled
= false)
106 PPAPI_END_MESSAGE_MAP()
110 PP_NetAddress_Private
UDPSocketFilter::GetLastAddrPrivate(
111 PP_Resource resource
) const {
112 base::AutoLock
acquire(lock_
);
113 return queues_
.get(resource
)->GetLastAddrPrivate();
116 void UDPSocketFilter::OnPluginMsgPushRecvResult(
117 const ResourceMessageReplyParams
& params
,
119 const std::string
& data
,
120 const PP_NetAddress_Private
& addr
) {
121 DCHECK(PluginGlobals::Get()->ipc_task_runner()->RunsTasksOnCurrentThread());
122 base::AutoLock
acquire(lock_
);
123 RecvQueue
* queue_ptr
= queues_
.get(params
.pp_resource());
124 // The RecvQueue might be gone if there were messages in-flight for a
125 // resource that has been destroyed.
127 // TODO(yzshen): Support passing in a non-const string ref, so that we can
128 // eliminate one copy when storing the data in the buffer.
129 queue_ptr
->DataReceivedOnIOThread(result
, data
, addr
);
133 UDPSocketFilter::RecvQueue::RecvQueue(
134 PP_Instance pp_instance
,
136 const base::Closure
& slot_available_callback
)
137 : pp_instance_(pp_instance
),
138 read_buffer_(nullptr),
140 recvfrom_addr_resource_(nullptr),
141 last_recvfrom_addr_(),
142 private_api_(private_api
),
143 slot_available_callback_(slot_available_callback
) {
146 UDPSocketFilter::RecvQueue::~RecvQueue() {
147 if (TrackedCallback::IsPending(recvfrom_callback_
))
148 recvfrom_callback_
->PostAbort();
151 void UDPSocketFilter::RecvQueue::DataReceivedOnIOThread(
153 const std::string
& data
,
154 const PP_NetAddress_Private
& addr
) {
155 DCHECK(PluginGlobals::Get()->ipc_task_runner()->RunsTasksOnCurrentThread());
156 DCHECK_LT(recv_buffers_
.size(), UDPSocketFilter::kPluginReceiveBufferSlots
);
158 if (!TrackedCallback::IsPending(recvfrom_callback_
) || !read_buffer_
) {
159 recv_buffers_
.push(RecvBuffer());
160 RecvBuffer
& back
= recv_buffers_
.back();
161 back
.result
= result
;
166 DCHECK_EQ(recv_buffers_
.size(), 0u);
168 if (bytes_to_read_
< static_cast<int32_t>(data
.size())) {
169 recv_buffers_
.push(RecvBuffer());
170 RecvBuffer
& back
= recv_buffers_
.back();
171 back
.result
= result
;
175 result
= PP_ERROR_MESSAGE_TOO_BIG
;
177 // Instead of calling SetRecvFromOutput directly, post it as a completion
179 // 1) It can run with the ProxyLock (we can't lock it on the IO thread.)
180 // 2) So that we only write to the output params in the case of success.
181 // (Since the callback will complete on another thread, it's possible
182 // that the resource will be deleted and abort the callback before it
184 scoped_ptr
<std::string
> data_to_pass(new std::string(data
));
185 recvfrom_callback_
->set_completion_task(base::Bind(
186 &SetRecvFromOutput
, pp_instance_
, base::Passed(data_to_pass
.Pass()),
187 addr
, base::Unretained(read_buffer_
), bytes_to_read_
,
188 base::Unretained(recvfrom_addr_resource_
)));
189 last_recvfrom_addr_
= addr
;
190 PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
192 RunWhileLocked(slot_available_callback_
));
197 recvfrom_addr_resource_
= NULL
;
199 recvfrom_callback_
->Run(
200 ConvertNetworkAPIErrorForCompatibility(result
, private_api_
));
203 int32_t UDPSocketFilter::RecvQueue::RequestData(
206 PP_Resource
* addr_out
,
207 const scoped_refptr
<TrackedCallback
>& callback
) {
208 ProxyLock::AssertAcquired();
209 if (!buffer_out
|| num_bytes
<= 0)
210 return PP_ERROR_BADARGUMENT
;
211 if (TrackedCallback::IsPending(recvfrom_callback_
))
212 return PP_ERROR_INPROGRESS
;
214 if (recv_buffers_
.empty()) {
215 read_buffer_
= buffer_out
;
216 bytes_to_read_
= std::min(num_bytes
, UDPSocketFilter::kMaxReadSize
);
217 recvfrom_addr_resource_
= addr_out
;
218 recvfrom_callback_
= callback
;
219 return PP_OK_COMPLETIONPENDING
;
221 RecvBuffer
& front
= recv_buffers_
.front();
223 if (static_cast<size_t>(num_bytes
) < front
.data
.size())
224 return PP_ERROR_MESSAGE_TOO_BIG
;
226 int32_t result
= static_cast<int32_t>(front
.data
.size());
227 scoped_ptr
<std::string
> data_to_pass(new std::string
);
228 data_to_pass
->swap(front
.data
);
229 SetRecvFromOutput(pp_instance_
, data_to_pass
.Pass(), front
.addr
, buffer_out
,
230 num_bytes
, addr_out
, PP_OK
);
231 last_recvfrom_addr_
= front
.addr
;
233 slot_available_callback_
.Run();
239 PP_NetAddress_Private
UDPSocketFilter::RecvQueue::GetLastAddrPrivate() const {
241 return last_recvfrom_addr_
;