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
),
42 recvfrom_addr_resource_(NULL
) {
43 recvfrom_addr_
.size
= 0;
44 memset(recvfrom_addr_
.data
, 0,
45 arraysize(recvfrom_addr_
.data
) * sizeof(*recvfrom_addr_
.data
));
47 memset(bound_addr_
.data
, 0,
48 arraysize(bound_addr_
.data
) * sizeof(*bound_addr_
.data
));
51 SendCreate(BROWSER
, PpapiHostMsg_UDPSocket_CreatePrivate());
53 SendCreate(BROWSER
, PpapiHostMsg_UDPSocket_Create());
55 PluginGlobals::Get()->resource_reply_thread_registrar()->HandleOnIOThread(
56 PpapiPluginMsg_UDPSocket_PushRecvResult::ID
);
59 UDPSocketResourceBase::~UDPSocketResourceBase() {
62 int32_t UDPSocketResourceBase::SetOptionImpl(
63 PP_UDPSocket_Option name
,
65 bool check_bind_state
,
66 scoped_refptr
<TrackedCallback
> callback
) {
68 return PP_ERROR_FAILED
;
70 SocketOptionData option_data
;
72 case PP_UDPSOCKET_OPTION_ADDRESS_REUSE
:
73 case PP_UDPSOCKET_OPTION_BROADCAST
: {
74 if ((check_bind_state
|| name
== PP_UDPSOCKET_OPTION_ADDRESS_REUSE
) &&
76 // SetOption should fail in this case in order to give predictable
77 // behavior while binding. Note that we use |bind_called_| rather
78 // than |bound_| since the latter is only set on successful completion
80 return PP_ERROR_FAILED
;
82 if (value
.type
!= PP_VARTYPE_BOOL
)
83 return PP_ERROR_BADARGUMENT
;
84 option_data
.SetBool(PP_ToBool(value
.value
.as_bool
));
87 case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE
:
88 case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE
: {
89 if (check_bind_state
&& !bound_
)
90 return PP_ERROR_FAILED
;
91 if (value
.type
!= PP_VARTYPE_INT32
)
92 return PP_ERROR_BADARGUMENT
;
93 option_data
.SetInt32(value
.value
.as_int
);
98 return PP_ERROR_BADARGUMENT
;
102 Call
<PpapiPluginMsg_UDPSocket_SetOptionReply
>(
104 PpapiHostMsg_UDPSocket_SetOption(name
, option_data
),
105 base::Bind(&UDPSocketResourceBase::OnPluginMsgSetOptionReply
,
106 base::Unretained(this),
109 return PP_OK_COMPLETIONPENDING
;
112 int32_t UDPSocketResourceBase::BindImpl(
113 const PP_NetAddress_Private
* addr
,
114 scoped_refptr
<TrackedCallback
> callback
) {
116 return PP_ERROR_BADARGUMENT
;
117 if (bound_
|| closed_
)
118 return PP_ERROR_FAILED
;
119 if (TrackedCallback::IsPending(bind_callback_
))
120 return PP_ERROR_INPROGRESS
;
123 bind_callback_
= callback
;
125 // Send the request, the browser will call us back via BindReply.
126 Call
<PpapiPluginMsg_UDPSocket_BindReply
>(
128 PpapiHostMsg_UDPSocket_Bind(*addr
),
129 base::Bind(&UDPSocketResourceBase::OnPluginMsgBindReply
,
130 base::Unretained(this)),
132 return PP_OK_COMPLETIONPENDING
;
135 PP_Bool
UDPSocketResourceBase::GetBoundAddressImpl(
136 PP_NetAddress_Private
* addr
) {
137 if (!addr
|| !bound_
|| closed_
)
144 int32_t UDPSocketResourceBase::RecvFromImpl(
148 scoped_refptr
<TrackedCallback
> callback
) {
149 if (!buffer
|| num_bytes
<= 0)
150 return PP_ERROR_BADARGUMENT
;
152 return PP_ERROR_FAILED
;
153 if (TrackedCallback::IsPending(recvfrom_callback_
))
154 return PP_ERROR_INPROGRESS
;
156 if (recv_buffers_
.empty()) {
157 read_buffer_
= buffer
;
158 bytes_to_read_
= std::min(num_bytes
, kMaxReadSize
);
159 recvfrom_addr_resource_
= addr
;
160 recvfrom_callback_
= callback
;
162 return PP_OK_COMPLETIONPENDING
;
164 RecvBuffer
& front
= recv_buffers_
.front();
166 if (num_bytes
< static_cast<int32_t>(front
.data
.size()))
167 return PP_ERROR_MESSAGE_TOO_BIG
;
169 int32_t result
= SetRecvFromOutput(front
.result
, front
.data
, front
.addr
,
170 buffer
, num_bytes
, addr
);
173 Post(BROWSER
, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
179 PP_Bool
UDPSocketResourceBase::GetRecvFromAddressImpl(
180 PP_NetAddress_Private
* addr
) {
183 *addr
= recvfrom_addr_
;
187 int32_t UDPSocketResourceBase::SendToImpl(
190 const PP_NetAddress_Private
* addr
,
191 scoped_refptr
<TrackedCallback
> callback
) {
192 if (!buffer
|| num_bytes
<= 0 || !addr
)
193 return PP_ERROR_BADARGUMENT
;
195 return PP_ERROR_FAILED
;
196 if (TrackedCallback::IsPending(sendto_callback_
))
197 return PP_ERROR_INPROGRESS
;
199 if (num_bytes
> kMaxWriteSize
)
200 num_bytes
= kMaxWriteSize
;
202 sendto_callback_
= callback
;
204 // Send the request, the browser will call us back via SendToReply.
205 Call
<PpapiPluginMsg_UDPSocket_SendToReply
>(
207 PpapiHostMsg_UDPSocket_SendTo(std::string(buffer
, num_bytes
), *addr
),
208 base::Bind(&UDPSocketResourceBase::OnPluginMsgSendToReply
,
209 base::Unretained(this)),
211 return PP_OK_COMPLETIONPENDING
;
214 void UDPSocketResourceBase::CloseImpl() {
221 Post(BROWSER
, PpapiHostMsg_UDPSocket_Close());
223 PostAbortIfNecessary(&bind_callback_
);
224 PostAbortIfNecessary(&recvfrom_callback_
);
225 PostAbortIfNecessary(&sendto_callback_
);
231 void UDPSocketResourceBase::OnReplyReceived(
232 const ResourceMessageReplyParams
& params
,
233 const IPC::Message
& msg
) {
234 PPAPI_BEGIN_MESSAGE_MAP(UDPSocketResourceBase
, msg
)
235 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
236 PpapiPluginMsg_UDPSocket_PushRecvResult
,
237 OnPluginMsgPushRecvResult
)
238 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
239 PluginResource::OnReplyReceived(params
, msg
))
240 PPAPI_END_MESSAGE_MAP()
243 void UDPSocketResourceBase::PostAbortIfNecessary(
244 scoped_refptr
<TrackedCallback
>* callback
) {
245 if (TrackedCallback::IsPending(*callback
))
246 (*callback
)->PostAbort();
249 void UDPSocketResourceBase::OnPluginMsgSetOptionReply(
250 scoped_refptr
<TrackedCallback
> callback
,
251 const ResourceMessageReplyParams
& params
) {
252 if (TrackedCallback::IsPending(callback
))
253 RunCallback(callback
, params
.result());
256 void UDPSocketResourceBase::OnPluginMsgBindReply(
257 const ResourceMessageReplyParams
& params
,
258 const PP_NetAddress_Private
& bound_addr
) {
259 // It is possible that |bind_callback_| is pending while |closed_| is true:
260 // CloseImpl() has been called, but a BindReply came earlier than the task to
261 // abort |bind_callback_|. We don't want to update |bound_| or |bound_addr_|
263 if (!TrackedCallback::IsPending(bind_callback_
) || closed_
)
266 if (params
.result() == PP_OK
)
268 bound_addr_
= bound_addr
;
269 RunCallback(bind_callback_
, params
.result());
272 void UDPSocketResourceBase::OnPluginMsgPushRecvResult(
273 const ResourceMessageReplyParams
& params
,
275 const std::string
& data
,
276 const PP_NetAddress_Private
& addr
) {
277 // TODO(yzshen): Support passing in a non-const string ref, so that we can
278 // eliminate one copy when storing the data in the buffer.
280 DCHECK_LT(recv_buffers_
.size(), kPluginReceiveBufferSlots
);
282 if (!TrackedCallback::IsPending(recvfrom_callback_
) || !read_buffer_
) {
283 recv_buffers_
.push(RecvBuffer());
284 RecvBuffer
& back
= recv_buffers_
.back();
285 back
.result
= result
;
292 DCHECK_EQ(recv_buffers_
.size(), 0u);
294 if (bytes_to_read_
< static_cast<int32_t>(data
.size())) {
295 recv_buffers_
.push(RecvBuffer());
296 RecvBuffer
& back
= recv_buffers_
.back();
297 back
.result
= result
;
301 result
= PP_ERROR_MESSAGE_TOO_BIG
;
303 result
= SetRecvFromOutput(result
, data
, addr
, read_buffer_
, bytes_to_read_
,
304 recvfrom_addr_resource_
);
305 Post(BROWSER
, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
310 recvfrom_addr_resource_
= NULL
;
312 RunCallback(recvfrom_callback_
, result
);
315 void UDPSocketResourceBase::OnPluginMsgSendToReply(
316 const ResourceMessageReplyParams
& params
,
317 int32_t bytes_written
) {
318 if (!TrackedCallback::IsPending(sendto_callback_
))
321 if (params
.result() == PP_OK
)
322 RunCallback(sendto_callback_
, bytes_written
);
324 RunCallback(sendto_callback_
, params
.result());
327 void UDPSocketResourceBase::RunCallback(scoped_refptr
<TrackedCallback
> callback
,
329 callback
->Run(ConvertNetworkAPIErrorForCompatibility(pp_result
,
333 int32_t UDPSocketResourceBase::SetRecvFromOutput(
334 int32_t browser_result
,
335 const std::string
& data
,
336 const PP_NetAddress_Private
& addr
,
339 PP_Resource
* output_addr
) {
340 DCHECK_GE(num_bytes
, static_cast<int32_t>(data
.size()));
342 int32_t result
= browser_result
;
343 if (result
== PP_OK
&& output_addr
) {
344 thunk::EnterResourceCreationNoLock
enter(pp_instance());
345 if (enter
.succeeded()) {
346 *output_addr
= enter
.functions()->CreateNetAddressFromNetAddressPrivate(
347 pp_instance(), addr
);
349 result
= PP_ERROR_FAILED
;
353 if (result
== PP_OK
&& !data
.empty())
354 memcpy(output_buffer
, data
.c_str(), data
.size());
356 recvfrom_addr_
= addr
;
358 return result
== PP_OK
? static_cast<int32_t>(data
.size()) : result
;