udp_socket_resource_base.cc revision d57369da7c6519fef57db42085f7b42d4c8845c1
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.
4
5#include "ppapi/proxy/udp_socket_resource_base.h"
6
7#include <algorithm>
8#include <cstring>
9
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/ppapi_messages.h"
16#include "ppapi/shared_impl/socket_option_data.h"
17#include "ppapi/thunk/enter.h"
18#include "ppapi/thunk/resource_creation_api.h"
19
20namespace ppapi {
21namespace proxy {
22
23const int32_t UDPSocketResourceBase::kMaxReadSize = 1024 * 1024;
24const int32_t UDPSocketResourceBase::kMaxWriteSize = 1024 * 1024;
25const int32_t UDPSocketResourceBase::kMaxSendBufferSize =
26    1024 * UDPSocketResourceBase::kMaxWriteSize;
27const int32_t UDPSocketResourceBase::kMaxReceiveBufferSize =
28    1024 * UDPSocketResourceBase::kMaxReadSize;
29
30
31UDPSocketResourceBase::UDPSocketResourceBase(Connection connection,
32                                             PP_Instance instance,
33                                             bool private_api)
34    : PluginResource(connection, instance),
35      private_api_(private_api),
36      bound_(false),
37      closed_(false),
38      read_buffer_(NULL),
39      bytes_to_read_(-1) {
40  recvfrom_addr_.size = 0;
41  memset(recvfrom_addr_.data, 0,
42         arraysize(recvfrom_addr_.data) * sizeof(*recvfrom_addr_.data));
43  bound_addr_.size = 0;
44  memset(bound_addr_.data, 0,
45         arraysize(bound_addr_.data) * sizeof(*bound_addr_.data));
46
47  if (private_api)
48    SendCreate(BROWSER, PpapiHostMsg_UDPSocket_CreatePrivate());
49  else
50    SendCreate(BROWSER, PpapiHostMsg_UDPSocket_Create());
51}
52
53UDPSocketResourceBase::~UDPSocketResourceBase() {
54}
55
56int32_t UDPSocketResourceBase::SetOptionImpl(
57    PP_UDPSocket_Option name,
58    const PP_Var& value,
59    scoped_refptr<TrackedCallback> callback) {
60  if (closed_)
61    return PP_ERROR_FAILED;
62
63  SocketOptionData option_data;
64  switch (name) {
65    case PP_UDPSOCKET_OPTION_ADDRESS_REUSE:
66    case PP_UDPSOCKET_OPTION_BROADCAST: {
67      if (bound_)
68        return PP_ERROR_FAILED;
69      if (value.type != PP_VARTYPE_BOOL)
70        return PP_ERROR_BADARGUMENT;
71      option_data.SetBool(PP_ToBool(value.value.as_bool));
72      break;
73    }
74    case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE:
75    case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
76      if (!bound_)
77        return PP_ERROR_FAILED;
78      if (value.type != PP_VARTYPE_INT32)
79        return PP_ERROR_BADARGUMENT;
80      option_data.SetInt32(value.value.as_int);
81      break;
82    }
83    default: {
84      NOTREACHED();
85      return PP_ERROR_BADARGUMENT;
86    }
87  }
88
89  Call<PpapiPluginMsg_UDPSocket_SetOptionReply>(
90      BROWSER,
91      PpapiHostMsg_UDPSocket_SetOption(name, option_data),
92      base::Bind(&UDPSocketResourceBase::OnPluginMsgSetOptionReply,
93                 base::Unretained(this),
94                 callback),
95      callback);
96  return PP_OK_COMPLETIONPENDING;
97}
98
99int32_t UDPSocketResourceBase::BindImpl(
100    const PP_NetAddress_Private* addr,
101    scoped_refptr<TrackedCallback> callback) {
102  if (!addr)
103    return PP_ERROR_BADARGUMENT;
104  if (bound_ || closed_)
105    return PP_ERROR_FAILED;
106  if (TrackedCallback::IsPending(bind_callback_))
107    return PP_ERROR_INPROGRESS;
108
109  bind_callback_ = callback;
110
111  // Send the request, the browser will call us back via BindReply.
112  Call<PpapiPluginMsg_UDPSocket_BindReply>(
113      BROWSER,
114      PpapiHostMsg_UDPSocket_Bind(*addr),
115      base::Bind(&UDPSocketResourceBase::OnPluginMsgBindReply,
116                 base::Unretained(this)),
117      callback);
118  return PP_OK_COMPLETIONPENDING;
119}
120
121PP_Bool UDPSocketResourceBase::GetBoundAddressImpl(
122    PP_NetAddress_Private* addr) {
123  if (!addr || !bound_ || closed_)
124    return PP_FALSE;
125
126  *addr = bound_addr_;
127  return PP_TRUE;
128}
129
130int32_t UDPSocketResourceBase::RecvFromImpl(
131    char* buffer,
132    int32_t num_bytes,
133    PP_Resource* addr,
134    scoped_refptr<TrackedCallback> callback) {
135  if (!buffer || num_bytes <= 0)
136    return PP_ERROR_BADARGUMENT;
137  if (!bound_)
138    return PP_ERROR_FAILED;
139  if (TrackedCallback::IsPending(recvfrom_callback_))
140    return PP_ERROR_INPROGRESS;
141
142  read_buffer_ = buffer;
143  bytes_to_read_ = std::min(num_bytes, kMaxReadSize);
144  recvfrom_callback_ = callback;
145
146  // Send the request, the browser will call us back via RecvFromReply.
147  Call<PpapiPluginMsg_UDPSocket_RecvFromReply>(
148      BROWSER,
149      PpapiHostMsg_UDPSocket_RecvFrom(bytes_to_read_),
150      base::Bind(&UDPSocketResourceBase::OnPluginMsgRecvFromReply,
151                 base::Unretained(this), addr),
152      callback);
153  return PP_OK_COMPLETIONPENDING;
154}
155
156PP_Bool UDPSocketResourceBase::GetRecvFromAddressImpl(
157    PP_NetAddress_Private* addr) {
158  if (!addr)
159    return PP_FALSE;
160  *addr = recvfrom_addr_;
161  return PP_TRUE;
162}
163
164int32_t UDPSocketResourceBase::SendToImpl(
165    const char* buffer,
166    int32_t num_bytes,
167    const PP_NetAddress_Private* addr,
168    scoped_refptr<TrackedCallback> callback) {
169  if (!buffer || num_bytes <= 0 || !addr)
170    return PP_ERROR_BADARGUMENT;
171  if (!bound_)
172    return PP_ERROR_FAILED;
173  if (TrackedCallback::IsPending(sendto_callback_))
174    return PP_ERROR_INPROGRESS;
175
176  if (num_bytes > kMaxWriteSize)
177    num_bytes = kMaxWriteSize;
178
179  sendto_callback_ = callback;
180
181  // Send the request, the browser will call us back via SendToReply.
182  Call<PpapiPluginMsg_UDPSocket_SendToReply>(
183      BROWSER,
184      PpapiHostMsg_UDPSocket_SendTo(std::string(buffer, num_bytes), *addr),
185      base::Bind(&UDPSocketResourceBase::OnPluginMsgSendToReply,
186                 base::Unretained(this)),
187      callback);
188  return PP_OK_COMPLETIONPENDING;
189}
190
191void UDPSocketResourceBase::CloseImpl() {
192  if(closed_)
193    return;
194
195  bound_ = false;
196  closed_ = true;
197
198  Post(BROWSER, PpapiHostMsg_UDPSocket_Close());
199
200  PostAbortIfNecessary(&bind_callback_);
201  PostAbortIfNecessary(&recvfrom_callback_);
202  PostAbortIfNecessary(&sendto_callback_);
203
204  read_buffer_ = NULL;
205  bytes_to_read_ = -1;
206}
207
208void UDPSocketResourceBase::PostAbortIfNecessary(
209    scoped_refptr<TrackedCallback>* callback) {
210  if (TrackedCallback::IsPending(*callback))
211    (*callback)->PostAbort();
212}
213
214void UDPSocketResourceBase::OnPluginMsgSetOptionReply(
215    scoped_refptr<TrackedCallback> callback,
216    const ResourceMessageReplyParams& params) {
217  if (TrackedCallback::IsPending(callback))
218    RunCallback(callback, params.result());
219}
220
221void UDPSocketResourceBase::OnPluginMsgBindReply(
222    const ResourceMessageReplyParams& params,
223    const PP_NetAddress_Private& bound_addr) {
224  // It is possible that |bind_callback_| is pending while |closed_| is true:
225  // CloseImpl() has been called, but a BindReply came earlier than the task to
226  // abort |bind_callback_|. We don't want to update |bound_| or |bound_addr_|
227  // in that case.
228  if (!TrackedCallback::IsPending(bind_callback_) || closed_)
229    return;
230
231  if (params.result() == PP_OK)
232    bound_ = true;
233  bound_addr_ = bound_addr;
234  RunCallback(bind_callback_, params.result());
235}
236
237void UDPSocketResourceBase::OnPluginMsgRecvFromReply(
238    PP_Resource* output_addr,
239    const ResourceMessageReplyParams& params,
240    const std::string& data,
241    const PP_NetAddress_Private& addr) {
242  // It is possible that |recvfrom_callback_| is pending while |read_buffer_| is
243  // NULL: CloseImpl() has been called, but a RecvFromReply came earlier than
244  // the task to abort |recvfrom_callback_|. We shouldn't access the buffer in
245  // that case. The user may have released it.
246  if (!TrackedCallback::IsPending(recvfrom_callback_) || !read_buffer_)
247    return;
248
249  int32_t result = params.result();
250  if (result == PP_OK && output_addr) {
251    thunk::EnterResourceCreationNoLock enter(pp_instance());
252    if (enter.succeeded()) {
253      *output_addr = enter.functions()->CreateNetAddressFromNetAddressPrivate(
254          pp_instance(), addr);
255    } else {
256      result = PP_ERROR_FAILED;
257    }
258  }
259
260  if (result == PP_OK) {
261    CHECK_LE(static_cast<int32_t>(data.size()), bytes_to_read_);
262    if (!data.empty())
263      memcpy(read_buffer_, data.c_str(), data.size());
264  }
265
266  read_buffer_ = NULL;
267  bytes_to_read_ = -1;
268  recvfrom_addr_ = addr;
269
270  if (result == PP_OK)
271    RunCallback(recvfrom_callback_, static_cast<int32_t>(data.size()));
272  else
273    RunCallback(recvfrom_callback_, result);
274}
275
276void UDPSocketResourceBase::OnPluginMsgSendToReply(
277    const ResourceMessageReplyParams& params,
278    int32_t bytes_written) {
279  if (!TrackedCallback::IsPending(sendto_callback_))
280    return;
281
282  if (params.result() == PP_OK)
283    RunCallback(sendto_callback_, bytes_written);
284  else
285    RunCallback(sendto_callback_, params.result());
286}
287
288void UDPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback,
289                                        int32_t pp_result) {
290  callback->Run(ConvertNetworkAPIErrorForCompatibility(pp_result,
291                                                       private_api_));
292}
293
294}  // namespace proxy
295}  // namespace ppapi
296