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"
9 #include "base/logging.h"
10 #include "ppapi/c/pp_bool.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/shared_impl/socket_option_data.h"
16 #include "ppapi/thunk/enter.h"
21 const int32_t UDPSocketResourceBase::kMaxWriteSize
= 128 * 1024;
22 const int32_t UDPSocketResourceBase::kMaxSendBufferSize
=
23 1024 * UDPSocketResourceBase::kMaxWriteSize
;
24 const size_t UDPSocketResourceBase::kPluginSendBufferSlots
= 8u;
28 void RunCallback(scoped_refptr
<TrackedCallback
> callback
,
31 callback
->Run(ConvertNetworkAPIErrorForCompatibility(pp_result
, private_api
));
34 void PostAbortIfNecessary(const scoped_refptr
<TrackedCallback
>& callback
) {
35 if (TrackedCallback::IsPending(callback
))
36 callback
->PostAbort();
41 UDPSocketResourceBase::UDPSocketResourceBase(Connection connection
,
44 : PluginResource(connection
, instance
),
45 private_api_(private_api
),
49 recv_filter_(PluginGlobals::Get()->udp_socket_filter()),
51 recv_filter_
->AddUDPResource(
52 pp_instance(), pp_resource(), private_api
,
53 base::Bind(&UDPSocketResourceBase::SlotBecameAvailable
, pp_resource()));
55 SendCreate(BROWSER
, PpapiHostMsg_UDPSocket_CreatePrivate());
57 SendCreate(BROWSER
, PpapiHostMsg_UDPSocket_Create());
60 UDPSocketResourceBase::~UDPSocketResourceBase() {
64 int32_t UDPSocketResourceBase::SetOptionImpl(
65 PP_UDPSocket_Option name
,
67 bool check_bind_state
,
68 scoped_refptr
<TrackedCallback
> callback
) {
70 return PP_ERROR_FAILED
;
72 // Check if socket is expected to be bound or not according to the option.
74 case PP_UDPSOCKET_OPTION_ADDRESS_REUSE
:
75 case PP_UDPSOCKET_OPTION_BROADCAST
:
76 case PP_UDPSOCKET_OPTION_MULTICAST_LOOP
:
77 case PP_UDPSOCKET_OPTION_MULTICAST_TTL
: {
78 if ((check_bind_state
|| name
== PP_UDPSOCKET_OPTION_ADDRESS_REUSE
) &&
80 // SetOption should fail in this case in order to give predictable
81 // behavior while binding. Note that we use |bind_called_| rather
82 // than |bound_| since the latter is only set on successful completion
84 return PP_ERROR_FAILED
;
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
;
96 SocketOptionData option_data
;
98 case PP_UDPSOCKET_OPTION_ADDRESS_REUSE
:
99 case PP_UDPSOCKET_OPTION_BROADCAST
:
100 case PP_UDPSOCKET_OPTION_MULTICAST_LOOP
: {
101 if (value
.type
!= PP_VARTYPE_BOOL
)
102 return PP_ERROR_BADARGUMENT
;
103 option_data
.SetBool(PP_ToBool(value
.value
.as_bool
));
106 case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE
:
107 case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE
: {
108 if (value
.type
!= PP_VARTYPE_INT32
)
109 return PP_ERROR_BADARGUMENT
;
110 option_data
.SetInt32(value
.value
.as_int
);
113 case PP_UDPSOCKET_OPTION_MULTICAST_TTL
: {
114 int32_t ival
= value
.value
.as_int
;
115 if (value
.type
!= PP_VARTYPE_INT32
&& (ival
< 0 || ival
> 255))
116 return PP_ERROR_BADARGUMENT
;
117 option_data
.SetInt32(ival
);
122 return PP_ERROR_BADARGUMENT
;
126 Call
<PpapiPluginMsg_UDPSocket_SetOptionReply
>(
128 PpapiHostMsg_UDPSocket_SetOption(name
, option_data
),
129 base::Bind(&UDPSocketResourceBase::OnPluginMsgGeneralReply
,
130 base::Unretained(this),
133 return PP_OK_COMPLETIONPENDING
;
136 int32_t UDPSocketResourceBase::BindImpl(
137 const PP_NetAddress_Private
* addr
,
138 scoped_refptr
<TrackedCallback
> callback
) {
140 return PP_ERROR_BADARGUMENT
;
141 if (bound_
|| closed_
)
142 return PP_ERROR_FAILED
;
143 if (TrackedCallback::IsPending(bind_callback_
))
144 return PP_ERROR_INPROGRESS
;
147 bind_callback_
= callback
;
149 // Send the request, the browser will call us back via BindReply.
150 Call
<PpapiPluginMsg_UDPSocket_BindReply
>(
152 PpapiHostMsg_UDPSocket_Bind(*addr
),
153 base::Bind(&UDPSocketResourceBase::OnPluginMsgBindReply
,
154 base::Unretained(this)),
156 return PP_OK_COMPLETIONPENDING
;
159 PP_Bool
UDPSocketResourceBase::GetBoundAddressImpl(
160 PP_NetAddress_Private
* addr
) {
161 if (!addr
|| !bound_
|| closed_
)
168 int32_t UDPSocketResourceBase::RecvFromImpl(
172 scoped_refptr
<TrackedCallback
> callback
) {
174 return PP_ERROR_FAILED
;
175 return recv_filter_
->RequestData(pp_resource(), num_bytes
, buffer_out
, addr
,
179 PP_Bool
UDPSocketResourceBase::GetRecvFromAddressImpl(
180 PP_NetAddress_Private
* addr
) {
183 *addr
= recv_filter_
->GetLastAddrPrivate(pp_resource());
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 (sendto_callbacks_
.size() == kPluginSendBufferSlots
)
197 return PP_ERROR_INPROGRESS
;
199 if (num_bytes
> kMaxWriteSize
)
200 num_bytes
= kMaxWriteSize
;
202 sendto_callbacks_
.push(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 while (!sendto_callbacks_
.empty()) {
225 scoped_refptr
<TrackedCallback
> callback
= sendto_callbacks_
.front();
226 sendto_callbacks_
.pop();
227 PostAbortIfNecessary(callback
);
229 recv_filter_
->RemoveUDPResource(pp_resource());
232 int32_t UDPSocketResourceBase::JoinGroupImpl(
233 const PP_NetAddress_Private
*group
,
234 scoped_refptr
<TrackedCallback
> callback
) {
237 Call
<PpapiPluginMsg_UDPSocket_JoinGroupReply
>(
239 PpapiHostMsg_UDPSocket_JoinGroup(*group
),
240 base::Bind(&UDPSocketResourceBase::OnPluginMsgGeneralReply
,
241 base::Unretained(this),
244 return PP_OK_COMPLETIONPENDING
;
247 int32_t UDPSocketResourceBase::LeaveGroupImpl(
248 const PP_NetAddress_Private
*group
,
249 scoped_refptr
<TrackedCallback
> callback
) {
252 Call
<PpapiPluginMsg_UDPSocket_LeaveGroupReply
>(
254 PpapiHostMsg_UDPSocket_LeaveGroup(*group
),
255 base::Bind(&UDPSocketResourceBase::OnPluginMsgGeneralReply
,
256 base::Unretained(this),
259 return PP_OK_COMPLETIONPENDING
;
262 void UDPSocketResourceBase::OnPluginMsgGeneralReply(
263 scoped_refptr
<TrackedCallback
> callback
,
264 const ResourceMessageReplyParams
& params
) {
265 if (TrackedCallback::IsPending(callback
))
266 RunCallback(callback
, params
.result(), private_api_
);
269 void UDPSocketResourceBase::OnPluginMsgBindReply(
270 const ResourceMessageReplyParams
& params
,
271 const PP_NetAddress_Private
& bound_addr
) {
272 // It is possible that |bind_callback_| is pending while |closed_| is true:
273 // CloseImpl() has been called, but a BindReply came earlier than the task to
274 // abort |bind_callback_|. We don't want to update |bound_| or |bound_addr_|
276 if (!TrackedCallback::IsPending(bind_callback_
) || closed_
)
279 if (params
.result() == PP_OK
)
281 bound_addr_
= bound_addr
;
282 RunCallback(bind_callback_
, params
.result(), private_api_
);
285 void UDPSocketResourceBase::OnPluginMsgSendToReply(
286 const ResourceMessageReplyParams
& params
,
287 int32_t bytes_written
) {
288 // This can be empty if the socket was closed, but there are still tasks
289 // to be posted for this resource.
290 if (sendto_callbacks_
.empty())
293 scoped_refptr
<TrackedCallback
> callback
= sendto_callbacks_
.front();
294 sendto_callbacks_
.pop();
295 if (!TrackedCallback::IsPending(callback
))
298 if (params
.result() == PP_OK
)
299 RunCallback(callback
, bytes_written
, private_api_
);
301 RunCallback(callback
, params
.result(), private_api_
);
305 void UDPSocketResourceBase::SlotBecameAvailable(PP_Resource resource
) {
306 ProxyLock::AssertAcquired();
307 UDPSocketResourceBase
* thiz
= nullptr;
308 // We have to try to enter all subclasses of UDPSocketResourceBase. Currently,
309 // these are the public and private resources.
310 thunk::EnterResourceNoLock
<thunk::PPB_UDPSocket_API
> enter(resource
, false);
311 if (enter
.succeeded()) {
312 thiz
= static_cast<UDPSocketResourceBase
*>(enter
.resource());
314 thunk::EnterResourceNoLock
<thunk::PPB_UDPSocket_Private_API
> enter_private(
316 if (enter_private
.succeeded())
317 thiz
= static_cast<UDPSocketResourceBase
*>(enter_private
.resource());
320 if (thiz
&& !thiz
->closed_
)
321 thiz
->Post(BROWSER
, PpapiHostMsg_UDPSocket_RecvSlotAvailable());