1 // Copyright 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.
5 #include "ppapi/proxy/udp_socket_resource_base.h"
10 #include "base/logging.h"
11 #include "ppapi/c/pp_bool.h"
12 #include "ppapi/c/pp_completion_callback.h"
13 #include "ppapi/c/pp_errors.h"
14 #include "ppapi/proxy/error_conversion.h"
15 #include "ppapi/proxy/plugin_globals.h"
16 #include "ppapi/proxy/ppapi_messages.h"
17 #include "ppapi/shared_impl/socket_option_data.h"
18 #include "ppapi/thunk/enter.h"
19 #include "ppapi/thunk/resource_creation_api.h"
24 const int32_t UDPSocketResourceBase::kMaxReadSize
= 128 * 1024;
25 const int32_t UDPSocketResourceBase::kMaxWriteSize
= 128 * 1024;
26 const int32_t UDPSocketResourceBase::kMaxSendBufferSize
=
27 1024 * UDPSocketResourceBase::kMaxWriteSize
;
28 const int32_t UDPSocketResourceBase::kMaxReceiveBufferSize
=
29 1024 * UDPSocketResourceBase::kMaxReadSize
;
30 const size_t UDPSocketResourceBase::kPluginReceiveBufferSlots
= 32u;
31 const size_t UDPSocketResourceBase::kPluginSendBufferSlots
= 8u;
33 UDPSocketResourceBase::UDPSocketResourceBase(Connection connection
,
36 : PluginResource(connection
, instance
),
37 private_api_(private_api
),
43 recvfrom_addr_resource_(NULL
) {
44 recvfrom_addr_
.size
= 0;
45 memset(recvfrom_addr_
.data
, 0,
46 arraysize(recvfrom_addr_
.data
) * sizeof(*recvfrom_addr_
.data
));
48 memset(bound_addr_
.data
, 0,
49 arraysize(bound_addr_
.data
) * sizeof(*bound_addr_
.data
));
52 SendCreate(BROWSER
, PpapiHostMsg_UDPSocket_CreatePrivate());
54 SendCreate(BROWSER
, PpapiHostMsg_UDPSocket_Create());
56 PluginGlobals::Get()->resource_reply_thread_registrar()->HandleOnIOThread(
57 PpapiPluginMsg_UDPSocket_PushRecvResult::ID
);
60 UDPSocketResourceBase::~UDPSocketResourceBase() {
63 int32_t UDPSocketResourceBase::SetOptionImpl(
64 PP_UDPSocket_Option name
,
66 bool check_bind_state
,
67 scoped_refptr
<TrackedCallback
> callback
) {
69 return PP_ERROR_FAILED
;
71 SocketOptionData option_data
;
73 case PP_UDPSOCKET_OPTION_ADDRESS_REUSE
:
74 case PP_UDPSOCKET_OPTION_BROADCAST
: {
75 if ((check_bind_state
|| name
== PP_UDPSOCKET_OPTION_ADDRESS_REUSE
) &&
77 // SetOption should fail in this case in order to give predictable
78 // behavior while binding. Note that we use |bind_called_| rather
79 // than |bound_| since the latter is only set on successful completion
81 return PP_ERROR_FAILED
;
83 if (value
.type
!= PP_VARTYPE_BOOL
)
84 return PP_ERROR_BADARGUMENT
;
85 option_data
.SetBool(PP_ToBool(value
.value
.as_bool
));
88 case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE
:
89 case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE
: {
90 if (check_bind_state
&& !bound_
)
91 return PP_ERROR_FAILED
;
92 if (value
.type
!= PP_VARTYPE_INT32
)
93 return PP_ERROR_BADARGUMENT
;
94 option_data
.SetInt32(value
.value
.as_int
);
99 return PP_ERROR_BADARGUMENT
;
103 Call
<PpapiPluginMsg_UDPSocket_SetOptionReply
>(
105 PpapiHostMsg_UDPSocket_SetOption(name
, option_data
),
106 base::Bind(&UDPSocketResourceBase::OnPluginMsgSetOptionReply
,
107 base::Unretained(this),
110 return PP_OK_COMPLETIONPENDING
;
113 int32_t UDPSocketResourceBase::BindImpl(
114 const PP_NetAddress_Private
* addr
,
115 scoped_refptr
<TrackedCallback
> callback
) {
117 return PP_ERROR_BADARGUMENT
;
118 if (bound_
|| closed_
)
119 return PP_ERROR_FAILED
;
120 if (TrackedCallback::IsPending(bind_callback_
))
121 return PP_ERROR_INPROGRESS
;
124 bind_callback_
= callback
;
126 // Send the request, the browser will call us back via BindReply.
127 Call
<PpapiPluginMsg_UDPSocket_BindReply
>(
129 PpapiHostMsg_UDPSocket_Bind(*addr
),
130 base::Bind(&UDPSocketResourceBase::OnPluginMsgBindReply
,
131 base::Unretained(this)),
133 return PP_OK_COMPLETIONPENDING
;
136 PP_Bool
UDPSocketResourceBase::GetBoundAddressImpl(
137 PP_NetAddress_Private
* addr
) {
138 if (!addr
|| !bound_
|| closed_
)
145 int32_t UDPSocketResourceBase::RecvFromImpl(
149 scoped_refptr
<TrackedCallback
> callback
) {
150 if (!buffer
|| num_bytes
<= 0)
151 return PP_ERROR_BADARGUMENT
;
153 return PP_ERROR_FAILED
;
154 if (TrackedCallback::IsPending(recvfrom_callback_
))
155 return PP_ERROR_INPROGRESS
;
157 if (recv_buffers_
.empty()) {
158 read_buffer_
= buffer
;
159 bytes_to_read_
= std::min(num_bytes
, kMaxReadSize
);
160 recvfrom_addr_resource_
= addr
;
161 recvfrom_callback_
= callback
;
163 return PP_OK_COMPLETIONPENDING
;
165 RecvBuffer
& front
= recv_buffers_
.front();
167 if (num_bytes
< static_cast<int32_t>(front
.data
.size()))
168 return PP_ERROR_MESSAGE_TOO_BIG
;
170 int32_t result
= SetRecvFromOutput(front
.result
, front
.data
, front
.addr
,
171 buffer
, num_bytes
, addr
);
174 Post(BROWSER
, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
180 PP_Bool
UDPSocketResourceBase::GetRecvFromAddressImpl(
181 PP_NetAddress_Private
* addr
) {
184 *addr
= recvfrom_addr_
;
188 int32_t UDPSocketResourceBase::SendToImpl(
191 const PP_NetAddress_Private
* addr
,
192 scoped_refptr
<TrackedCallback
> callback
) {
193 if (!buffer
|| num_bytes
<= 0 || !addr
)
194 return PP_ERROR_BADARGUMENT
;
196 return PP_ERROR_FAILED
;
197 if (sendto_callbacks_
.size() == kPluginSendBufferSlots
)
198 return PP_ERROR_INPROGRESS
;
200 if (num_bytes
> kMaxWriteSize
)
201 num_bytes
= kMaxWriteSize
;
203 sendto_callbacks_
.push(callback
);
205 // Send the request, the browser will call us back via SendToReply.
206 Call
<PpapiPluginMsg_UDPSocket_SendToReply
>(
208 PpapiHostMsg_UDPSocket_SendTo(std::string(buffer
, num_bytes
), *addr
),
209 base::Bind(&UDPSocketResourceBase::OnPluginMsgSendToReply
,
210 base::Unretained(this)),
212 return PP_OK_COMPLETIONPENDING
;
215 void UDPSocketResourceBase::CloseImpl() {
222 Post(BROWSER
, PpapiHostMsg_UDPSocket_Close());
224 PostAbortIfNecessary(&bind_callback_
);
225 PostAbortIfNecessary(&recvfrom_callback_
);
226 while (!sendto_callbacks_
.empty()) {
227 scoped_refptr
<TrackedCallback
> callback
= sendto_callbacks_
.front();
228 sendto_callbacks_
.pop();
229 PostAbortIfNecessary(&callback
);
236 void UDPSocketResourceBase::OnReplyReceived(
237 const ResourceMessageReplyParams
& params
,
238 const IPC::Message
& msg
) {
239 PPAPI_BEGIN_MESSAGE_MAP(UDPSocketResourceBase
, msg
)
240 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
241 PpapiPluginMsg_UDPSocket_PushRecvResult
,
242 OnPluginMsgPushRecvResult
)
243 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
244 PluginResource::OnReplyReceived(params
, msg
))
245 PPAPI_END_MESSAGE_MAP()
248 void UDPSocketResourceBase::PostAbortIfNecessary(
249 scoped_refptr
<TrackedCallback
>* callback
) {
250 if (TrackedCallback::IsPending(*callback
))
251 (*callback
)->PostAbort();
254 void UDPSocketResourceBase::OnPluginMsgSetOptionReply(
255 scoped_refptr
<TrackedCallback
> callback
,
256 const ResourceMessageReplyParams
& params
) {
257 if (TrackedCallback::IsPending(callback
))
258 RunCallback(callback
, params
.result());
261 void UDPSocketResourceBase::OnPluginMsgBindReply(
262 const ResourceMessageReplyParams
& params
,
263 const PP_NetAddress_Private
& bound_addr
) {
264 // It is possible that |bind_callback_| is pending while |closed_| is true:
265 // CloseImpl() has been called, but a BindReply came earlier than the task to
266 // abort |bind_callback_|. We don't want to update |bound_| or |bound_addr_|
268 if (!TrackedCallback::IsPending(bind_callback_
) || closed_
)
271 if (params
.result() == PP_OK
)
273 bound_addr_
= bound_addr
;
274 RunCallback(bind_callback_
, params
.result());
277 void UDPSocketResourceBase::OnPluginMsgPushRecvResult(
278 const ResourceMessageReplyParams
& params
,
280 const std::string
& data
,
281 const PP_NetAddress_Private
& addr
) {
282 // TODO(yzshen): Support passing in a non-const string ref, so that we can
283 // eliminate one copy when storing the data in the buffer.
285 DCHECK_LT(recv_buffers_
.size(), kPluginReceiveBufferSlots
);
287 if (!TrackedCallback::IsPending(recvfrom_callback_
) || !read_buffer_
) {
288 recv_buffers_
.push(RecvBuffer());
289 RecvBuffer
& back
= recv_buffers_
.back();
290 back
.result
= result
;
297 DCHECK_EQ(recv_buffers_
.size(), 0u);
299 if (bytes_to_read_
< static_cast<int32_t>(data
.size())) {
300 recv_buffers_
.push(RecvBuffer());
301 RecvBuffer
& back
= recv_buffers_
.back();
302 back
.result
= result
;
306 result
= PP_ERROR_MESSAGE_TOO_BIG
;
308 result
= SetRecvFromOutput(result
, data
, addr
, read_buffer_
, bytes_to_read_
,
309 recvfrom_addr_resource_
);
310 Post(BROWSER
, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
315 recvfrom_addr_resource_
= NULL
;
317 RunCallback(recvfrom_callback_
, result
);
320 void UDPSocketResourceBase::OnPluginMsgSendToReply(
321 const ResourceMessageReplyParams
& params
,
322 int32_t bytes_written
) {
323 // This can be empty if the socket was closed, but there are still tasks
324 // to be posted for this resource.
325 if (sendto_callbacks_
.empty())
328 scoped_refptr
<TrackedCallback
> callback
= sendto_callbacks_
.front();
329 sendto_callbacks_
.pop();
330 if (!TrackedCallback::IsPending(callback
))
333 if (params
.result() == PP_OK
)
334 RunCallback(callback
, bytes_written
);
336 RunCallback(callback
, params
.result());
339 void UDPSocketResourceBase::RunCallback(scoped_refptr
<TrackedCallback
> callback
,
341 callback
->Run(ConvertNetworkAPIErrorForCompatibility(pp_result
,
345 int32_t UDPSocketResourceBase::SetRecvFromOutput(
346 int32_t browser_result
,
347 const std::string
& data
,
348 const PP_NetAddress_Private
& addr
,
351 PP_Resource
* output_addr
) {
352 DCHECK_GE(num_bytes
, static_cast<int32_t>(data
.size()));
354 int32_t result
= browser_result
;
355 if (result
== PP_OK
&& output_addr
) {
356 thunk::EnterResourceCreationNoLock
enter(pp_instance());
357 if (enter
.succeeded()) {
358 *output_addr
= enter
.functions()->CreateNetAddressFromNetAddressPrivate(
359 pp_instance(), addr
);
361 result
= PP_ERROR_FAILED
;
365 if (result
== PP_OK
&& !data
.empty())
366 memcpy(output_buffer
, data
.c_str(), data
.size());
368 recvfrom_addr_
= addr
;
370 return result
== PP_OK
? static_cast<int32_t>(data
.size()) : result
;