OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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_filter.h" | |
6 | |
7 #include <algorithm> | |
8 #include <cstring> | |
9 | |
10 #include "base/logging.h" | |
11 #include "ppapi/c/pp_errors.h" | |
12 #include "ppapi/proxy/error_conversion.h" | |
13 #include "ppapi/proxy/ppapi_messages.h" | |
14 #include "ppapi/thunk/enter.h" | |
15 #include "ppapi/thunk/resource_creation_api.h" | |
16 | |
17 namespace ppapi { | |
18 namespace proxy { | |
19 | |
20 const int32_t UDPSocketFilter::kMaxReadSize = 128 * 1024; | |
21 const int32_t UDPSocketFilter::kMaxReceiveBufferSize = | |
22 1024 * UDPSocketFilter::kMaxReadSize; | |
23 const size_t UDPSocketFilter::kPluginReceiveBufferSlots = 32u; | |
24 | |
25 namespace { | |
26 | |
27 int32_t SetRecvFromOutput(PP_Instance pp_instance, | |
28 const scoped_ptr<std::string>& data, | |
29 const PP_NetAddress_Private& addr, | |
30 char* output_buffer, | |
31 int32_t num_bytes, | |
32 PP_Resource* output_addr, | |
33 int32_t browser_result) { | |
34 ProxyLock::AssertAcquired(); | |
35 DCHECK_GE(num_bytes, static_cast<int32_t>(data->size())); | |
36 | |
37 int32_t result = browser_result; | |
38 if (result == PP_OK && output_addr) { | |
39 thunk::EnterResourceCreationNoLock enter(pp_instance); | |
40 if (enter.succeeded()) { | |
41 *output_addr = enter.functions()->CreateNetAddressFromNetAddressPrivate( | |
42 pp_instance, addr); | |
43 } else { | |
44 result = PP_ERROR_FAILED; | |
45 } | |
46 } | |
47 | |
48 if (result == PP_OK && !data->empty()) | |
49 memcpy(output_buffer, data->c_str(), data->size()); | |
50 | |
51 result = result == PP_OK ? static_cast<int32_t>(data->size()) : result; | |
52 return result; | |
53 } | |
54 | |
55 } // namespace | |
56 | |
57 UDPSocketFilter::UDPSocketFilter() { | |
58 } | |
59 | |
60 UDPSocketFilter::~UDPSocketFilter() { | |
61 } | |
62 | |
63 void UDPSocketFilter::AddUDPResource( | |
64 PP_Instance instance, | |
65 PP_Resource resource, | |
66 bool private_api, | |
67 const base::Closure& slot_available_callback) { | |
raymes
2015/03/10 02:53:14
Should we assert the proxy lock here too?
dmichael (off chromium)
2015/03/23 20:50:06
Done.
| |
68 base::AutoLock acquire(lock_); | |
69 DCHECK(!queues_.contains(resource)); | |
70 queues_.add(resource, scoped_ptr<RecvQueue>(new RecvQueue( | |
71 instance, private_api, slot_available_callback))); | |
raymes
2015/03/10 02:53:14
nit: indentation
dmichael (off chromium)
2015/03/23 20:50:06
Ah, thanks & sorry. I ran git cl format in the lat
| |
72 } | |
73 | |
74 void UDPSocketFilter::RemoveUDPResource(PP_Resource resource) { | |
raymes
2015/03/10 02:53:14
Should we assert the proxy lock here too?
dmichael (off chromium)
2015/03/23 20:50:06
Done.
| |
75 base::AutoLock acquire(lock_); | |
76 DCHECK(queues_.contains(resource)); | |
77 queues_.erase(resource); | |
78 } | |
79 | |
80 int32_t UDPSocketFilter::RequestData( | |
81 PP_Resource resource, | |
82 int32_t num_bytes, | |
83 char* buffer, | |
84 PP_Resource* addr, | |
85 const scoped_refptr<TrackedCallback>& callback) { | |
raymes
2015/03/10 02:53:14
Should we assert the proxy lock here too?
dmichael (off chromium)
2015/03/23 20:50:06
Done.
| |
86 base::AutoLock acquire(lock_); | |
87 RecvQueue* queue_ptr = queues_.get(resource); | |
88 if (!queue_ptr) { | |
89 NOTREACHED(); | |
90 return PP_ERROR_FAILED; | |
91 } | |
92 return queue_ptr->RequestData(num_bytes, buffer, addr, callback); | |
93 } | |
94 | |
95 bool UDPSocketFilter::OnResourceReplyReceived( | |
96 const ResourceMessageReplyParams& params, | |
97 const IPC::Message& nested_msg) { | |
98 bool handled = true; | |
99 PPAPI_BEGIN_MESSAGE_MAP(UDPSocketFilter, nested_msg) | |
100 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_UDPSocket_PushRecvResult, | |
101 OnPluginMsgPushRecvResult) | |
102 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(handled = false) | |
dmichael (off chromium)
2015/03/23 20:50:06
...Re: git cl format; it does these "wrong" so I "
| |
103 PPAPI_END_MESSAGE_MAP() | |
104 return handled; | |
105 } | |
106 | |
107 PP_NetAddress_Private UDPSocketFilter::GetLastAddrPrivate( | |
108 PP_Resource resource) const { | |
109 base::AutoLock acquire(lock_); | |
110 return queues_.get(resource)->GetLastAddrPrivate(); | |
111 } | |
112 | |
113 void UDPSocketFilter::OnPluginMsgPushRecvResult( | |
114 const ResourceMessageReplyParams& params, | |
115 int32_t result, | |
116 const std::string& data, | |
117 const PP_NetAddress_Private& addr) { | |
raymes
2015/03/10 02:53:14
Is it possible to add an assertion that we're on t
dmichael (off chromium)
2015/03/23 20:50:06
Good idea, done. I had to add a way to get the IPC
| |
118 base::AutoLock acquire(lock_); | |
119 RecvQueue* queue_ptr = queues_.get(params.pp_resource()); | |
120 // The RecvQueue might be gone if there were messages in-flight for a | |
121 // resource that has been destroyed. | |
122 if (queue_ptr) { | |
123 // TODO(yzshen): Support passing in a non-const string ref, so that we can | |
124 // eliminate one copy when storing the data in the buffer. | |
125 queue_ptr->DataReceivedOnIOThread(result, data, addr); | |
126 } | |
127 } | |
128 | |
129 UDPSocketFilter::RecvQueue::RecvQueue( | |
130 PP_Instance pp_instance, | |
131 bool private_api, | |
132 const base::Closure& slot_available_callback) | |
133 : pp_instance_(pp_instance), | |
134 read_buffer_(nullptr), | |
135 bytes_to_read_(0), | |
136 recvfrom_addr_resource_(nullptr), | |
137 last_recvfrom_addr_(), | |
138 private_api_(private_api), | |
139 slot_available_callback_(slot_available_callback) { | |
140 } | |
141 | |
142 UDPSocketFilter::RecvQueue::~RecvQueue() { | |
143 if (TrackedCallback::IsPending(recvfrom_callback_)) | |
144 recvfrom_callback_->PostAbort(); | |
145 } | |
146 | |
147 void UDPSocketFilter::RecvQueue::DataReceivedOnIOThread( | |
148 int32_t result, | |
149 const std::string& data, | |
150 const PP_NetAddress_Private& addr) { | |
raymes
2015/03/10 02:53:14
Is it possible to add an assertion that we're on t
dmichael (off chromium)
2015/03/23 20:50:06
Done.
| |
151 DCHECK_LT(recv_buffers_.size(), UDPSocketFilter::kPluginReceiveBufferSlots); | |
152 | |
153 if (!TrackedCallback::IsPending(recvfrom_callback_) || !read_buffer_) { | |
154 recv_buffers_.push(RecvBuffer()); | |
155 RecvBuffer& back = recv_buffers_.back(); | |
156 back.result = result; | |
157 back.data = data; | |
158 back.addr = addr; | |
159 return; | |
160 } | |
161 DCHECK_EQ(recv_buffers_.size(), 0u); | |
162 | |
163 if (bytes_to_read_ < static_cast<int32_t>(data.size())) { | |
164 recv_buffers_.push(RecvBuffer()); | |
165 RecvBuffer& back = recv_buffers_.back(); | |
166 back.result = result; | |
167 back.data = data; | |
168 back.addr = addr; | |
169 | |
170 result = PP_ERROR_MESSAGE_TOO_BIG; | |
171 } else { | |
172 // Instead of calling SetRecvFromOutput directly, post it as a completion | |
173 // task, so that: | |
174 // 1) It can run with the ProxyLock (we can't lock it on the IO thread.) | |
175 // 2) So that we only write to the output params in the case of success. | |
176 // (Since the callback will complete on another thread, it's possible | |
177 // that the resource will be deleted and abort the callback before it | |
178 // is actually run.) | |
179 scoped_ptr<std::string> data_to_pass(new std::string(data)); | |
180 recvfrom_callback_->set_completion_task(base::Bind( | |
181 &SetRecvFromOutput, pp_instance_, base::Passed(data_to_pass.Pass()), | |
182 addr, base::Unretained(read_buffer_), bytes_to_read_, | |
183 base::Unretained(recvfrom_addr_resource_))); | |
184 last_recvfrom_addr_ = addr; | |
185 slot_available_callback_.Run(); | |
186 } | |
187 | |
188 read_buffer_ = NULL; | |
189 bytes_to_read_ = -1; | |
190 recvfrom_addr_resource_ = NULL; | |
191 | |
192 recvfrom_callback_->Run( | |
193 ConvertNetworkAPIErrorForCompatibility(result, private_api_)); | |
194 } | |
195 | |
196 int32_t UDPSocketFilter::RecvQueue::RequestData( | |
197 int32_t num_bytes, | |
198 char* buffer_out, | |
199 PP_Resource* addr_out, | |
200 const scoped_refptr<TrackedCallback>& callback) { | |
201 ProxyLock::AssertAcquired(); | |
202 if (!buffer_out || num_bytes <= 0) | |
203 return PP_ERROR_BADARGUMENT; | |
204 if (TrackedCallback::IsPending(recvfrom_callback_)) | |
205 return PP_ERROR_INPROGRESS; | |
206 | |
207 if (recv_buffers_.empty()) { | |
208 read_buffer_ = buffer_out; | |
209 bytes_to_read_ = std::min(num_bytes, UDPSocketFilter::kMaxReadSize); | |
210 recvfrom_addr_resource_ = addr_out; | |
211 recvfrom_callback_ = callback; | |
212 return PP_OK_COMPLETIONPENDING; | |
213 } else { | |
214 RecvBuffer& front = recv_buffers_.front(); | |
215 | |
216 if (num_bytes < static_cast<int32_t>(front.data.size())) | |
217 return PP_ERROR_MESSAGE_TOO_BIG; | |
218 | |
219 int32_t result = front.data.size(); | |
220 scoped_ptr<std::string> data_to_pass(new std::string); | |
221 data_to_pass->swap(front.data); | |
222 SetRecvFromOutput(pp_instance_, data_to_pass.Pass(), front.addr, buffer_out, | |
223 num_bytes, addr_out, PP_OK); | |
224 last_recvfrom_addr_ = front.addr; | |
225 recv_buffers_.pop(); | |
226 slot_available_callback_.Run(); | |
227 | |
228 return result; | |
229 } | |
230 } | |
231 | |
232 PP_NetAddress_Private UDPSocketFilter::RecvQueue::GetLastAddrPrivate() const { | |
233 CHECK(private_api_); | |
234 return last_recvfrom_addr_; | |
235 } | |
236 | |
237 } // namespace proxy | |
238 } // namespace ppapi | |
OLD | NEW |