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;
32 UDPSocketResourceBase::UDPSocketResourceBase(Connection connection
,
35 : PluginResource(connection
, instance
),
36 private_api_(private_api
),
41 recvfrom_addr_resource_(NULL
) {
42 recvfrom_addr_
.size
= 0;
43 memset(recvfrom_addr_
.data
, 0,
44 arraysize(recvfrom_addr_
.data
) * sizeof(*recvfrom_addr_
.data
));
46 memset(bound_addr_
.data
, 0,
47 arraysize(bound_addr_
.data
) * sizeof(*bound_addr_
.data
));
50 SendCreate(BROWSER
, PpapiHostMsg_UDPSocket_CreatePrivate());
52 SendCreate(BROWSER
, PpapiHostMsg_UDPSocket_Create());
54 PluginGlobals::Get()->resource_reply_thread_registrar()->HandleOnIOThread(
55 PpapiPluginMsg_UDPSocket_PushRecvResult::ID
);
58 UDPSocketResourceBase::~UDPSocketResourceBase() {
61 int32_t UDPSocketResourceBase::SetOptionImpl(
62 PP_UDPSocket_Option name
,
64 scoped_refptr
<TrackedCallback
> callback
) {
66 return PP_ERROR_FAILED
;
68 SocketOptionData option_data
;
70 case PP_UDPSOCKET_OPTION_ADDRESS_REUSE
:
71 case PP_UDPSOCKET_OPTION_BROADCAST
: {
73 return PP_ERROR_FAILED
;
74 if (value
.type
!= PP_VARTYPE_BOOL
)
75 return PP_ERROR_BADARGUMENT
;
76 option_data
.SetBool(PP_ToBool(value
.value
.as_bool
));
79 case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE
:
80 case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE
: {
82 return PP_ERROR_FAILED
;
83 if (value
.type
!= PP_VARTYPE_INT32
)
84 return PP_ERROR_BADARGUMENT
;
85 option_data
.SetInt32(value
.value
.as_int
);
90 return PP_ERROR_BADARGUMENT
;
94 Call
<PpapiPluginMsg_UDPSocket_SetOptionReply
>(
96 PpapiHostMsg_UDPSocket_SetOption(name
, option_data
),
97 base::Bind(&UDPSocketResourceBase::OnPluginMsgSetOptionReply
,
98 base::Unretained(this),
101 return PP_OK_COMPLETIONPENDING
;
104 int32_t UDPSocketResourceBase::BindImpl(
105 const PP_NetAddress_Private
* addr
,
106 scoped_refptr
<TrackedCallback
> callback
) {
108 return PP_ERROR_BADARGUMENT
;
109 if (bound_
|| closed_
)
110 return PP_ERROR_FAILED
;
111 if (TrackedCallback::IsPending(bind_callback_
))
112 return PP_ERROR_INPROGRESS
;
114 bind_callback_
= callback
;
116 // Send the request, the browser will call us back via BindReply.
117 Call
<PpapiPluginMsg_UDPSocket_BindReply
>(
119 PpapiHostMsg_UDPSocket_Bind(*addr
),
120 base::Bind(&UDPSocketResourceBase::OnPluginMsgBindReply
,
121 base::Unretained(this)),
123 return PP_OK_COMPLETIONPENDING
;
126 PP_Bool
UDPSocketResourceBase::GetBoundAddressImpl(
127 PP_NetAddress_Private
* addr
) {
128 if (!addr
|| !bound_
|| closed_
)
135 int32_t UDPSocketResourceBase::RecvFromImpl(
139 scoped_refptr
<TrackedCallback
> callback
) {
140 if (!buffer
|| num_bytes
<= 0)
141 return PP_ERROR_BADARGUMENT
;
143 return PP_ERROR_FAILED
;
144 if (TrackedCallback::IsPending(recvfrom_callback_
))
145 return PP_ERROR_INPROGRESS
;
147 if (recv_buffers_
.empty()) {
148 read_buffer_
= buffer
;
149 bytes_to_read_
= std::min(num_bytes
, kMaxReadSize
);
150 recvfrom_addr_resource_
= addr
;
151 recvfrom_callback_
= callback
;
153 return PP_OK_COMPLETIONPENDING
;
155 RecvBuffer
& front
= recv_buffers_
.front();
157 if (num_bytes
< static_cast<int32_t>(front
.data
.size()))
158 return PP_ERROR_MESSAGE_TOO_BIG
;
160 int32_t result
= SetRecvFromOutput(front
.result
, front
.data
, front
.addr
,
161 buffer
, num_bytes
, addr
);
164 Post(BROWSER
, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
170 PP_Bool
UDPSocketResourceBase::GetRecvFromAddressImpl(
171 PP_NetAddress_Private
* addr
) {
174 *addr
= recvfrom_addr_
;
178 int32_t UDPSocketResourceBase::SendToImpl(
181 const PP_NetAddress_Private
* addr
,
182 scoped_refptr
<TrackedCallback
> callback
) {
183 if (!buffer
|| num_bytes
<= 0 || !addr
)
184 return PP_ERROR_BADARGUMENT
;
186 return PP_ERROR_FAILED
;
187 if (TrackedCallback::IsPending(sendto_callback_
))
188 return PP_ERROR_INPROGRESS
;
190 if (num_bytes
> kMaxWriteSize
)
191 num_bytes
= kMaxWriteSize
;
193 sendto_callback_
= callback
;
195 // Send the request, the browser will call us back via SendToReply.
196 Call
<PpapiPluginMsg_UDPSocket_SendToReply
>(
198 PpapiHostMsg_UDPSocket_SendTo(std::string(buffer
, num_bytes
), *addr
),
199 base::Bind(&UDPSocketResourceBase::OnPluginMsgSendToReply
,
200 base::Unretained(this)),
202 return PP_OK_COMPLETIONPENDING
;
205 void UDPSocketResourceBase::CloseImpl() {
212 Post(BROWSER
, PpapiHostMsg_UDPSocket_Close());
214 PostAbortIfNecessary(&bind_callback_
);
215 PostAbortIfNecessary(&recvfrom_callback_
);
216 PostAbortIfNecessary(&sendto_callback_
);
222 void UDPSocketResourceBase::OnReplyReceived(
223 const ResourceMessageReplyParams
& params
,
224 const IPC::Message
& msg
) {
225 PPAPI_BEGIN_MESSAGE_MAP(UDPSocketResourceBase
, msg
)
226 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
227 PpapiPluginMsg_UDPSocket_PushRecvResult
,
228 OnPluginMsgPushRecvResult
)
229 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
230 PluginResource::OnReplyReceived(params
, msg
))
231 PPAPI_END_MESSAGE_MAP()
234 void UDPSocketResourceBase::PostAbortIfNecessary(
235 scoped_refptr
<TrackedCallback
>* callback
) {
236 if (TrackedCallback::IsPending(*callback
))
237 (*callback
)->PostAbort();
240 void UDPSocketResourceBase::OnPluginMsgSetOptionReply(
241 scoped_refptr
<TrackedCallback
> callback
,
242 const ResourceMessageReplyParams
& params
) {
243 if (TrackedCallback::IsPending(callback
))
244 RunCallback(callback
, params
.result());
247 void UDPSocketResourceBase::OnPluginMsgBindReply(
248 const ResourceMessageReplyParams
& params
,
249 const PP_NetAddress_Private
& bound_addr
) {
250 // It is possible that |bind_callback_| is pending while |closed_| is true:
251 // CloseImpl() has been called, but a BindReply came earlier than the task to
252 // abort |bind_callback_|. We don't want to update |bound_| or |bound_addr_|
254 if (!TrackedCallback::IsPending(bind_callback_
) || closed_
)
257 if (params
.result() == PP_OK
)
259 bound_addr_
= bound_addr
;
260 RunCallback(bind_callback_
, params
.result());
263 void UDPSocketResourceBase::OnPluginMsgPushRecvResult(
264 const ResourceMessageReplyParams
& params
,
266 const std::string
& data
,
267 const PP_NetAddress_Private
& addr
) {
268 // TODO(yzshen): Support passing in a non-const string ref, so that we can
269 // eliminate one copy when storing the data in the buffer.
271 DCHECK_LT(recv_buffers_
.size(), kPluginReceiveBufferSlots
);
273 if (!TrackedCallback::IsPending(recvfrom_callback_
) || !read_buffer_
) {
274 recv_buffers_
.push(RecvBuffer());
275 RecvBuffer
& back
= recv_buffers_
.back();
276 back
.result
= result
;
283 DCHECK_EQ(recv_buffers_
.size(), 0u);
285 if (bytes_to_read_
< static_cast<int32_t>(data
.size())) {
286 recv_buffers_
.push(RecvBuffer());
287 RecvBuffer
& back
= recv_buffers_
.back();
288 back
.result
= result
;
292 result
= PP_ERROR_MESSAGE_TOO_BIG
;
294 result
= SetRecvFromOutput(result
, data
, addr
, read_buffer_
, bytes_to_read_
,
295 recvfrom_addr_resource_
);
296 Post(BROWSER
, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
301 recvfrom_addr_resource_
= NULL
;
303 RunCallback(recvfrom_callback_
, result
);
306 void UDPSocketResourceBase::OnPluginMsgSendToReply(
307 const ResourceMessageReplyParams
& params
,
308 int32_t bytes_written
) {
309 if (!TrackedCallback::IsPending(sendto_callback_
))
312 if (params
.result() == PP_OK
)
313 RunCallback(sendto_callback_
, bytes_written
);
315 RunCallback(sendto_callback_
, params
.result());
318 void UDPSocketResourceBase::RunCallback(scoped_refptr
<TrackedCallback
> callback
,
320 callback
->Run(ConvertNetworkAPIErrorForCompatibility(pp_result
,
324 int32_t UDPSocketResourceBase::SetRecvFromOutput(
325 int32_t browser_result
,
326 const std::string
& data
,
327 const PP_NetAddress_Private
& addr
,
330 PP_Resource
* output_addr
) {
331 DCHECK_GE(num_bytes
, static_cast<int32_t>(data
.size()));
333 int32_t result
= browser_result
;
334 if (result
== PP_OK
&& output_addr
) {
335 thunk::EnterResourceCreationNoLock
enter(pp_instance());
336 if (enter
.succeeded()) {
337 *output_addr
= enter
.functions()->CreateNetAddressFromNetAddressPrivate(
338 pp_instance(), addr
);
340 result
= PP_ERROR_FAILED
;
344 if (result
== PP_OK
&& !data
.empty())
345 memcpy(output_buffer
, data
.c_str(), data
.size());
347 recvfrom_addr_
= addr
;
349 return result
== PP_OK
? static_cast<int32_t>(data
.size()) : result
;