Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Side by Side Diff: remoting/client/plugin/pepper_port_allocator_session.cc

Issue 6623048: Replace libjingle's HttpPortAllocatorSession with Pepper's http client (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: thread switching Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "remoting/client/plugin/pepper_port_allocator_session.h"
6
7 #include <map>
8
9 #include "base/callback.h"
10 #include "base/logging.h"
11 #include "base/stringprintf.h"
12 #include "base/synchronization/lock.h"
13 #include "ppapi/c/pp_errors.h"
14 #include "ppapi/c/trusted/ppb_url_loader_trusted.h"
15 #include "ppapi/cpp/url_loader.h"
16 #include "ppapi/cpp/url_request_info.h"
17 #include "ppapi/cpp/url_response_info.h"
18 #include "remoting/jingle_glue/http_port_allocator.h"
19 #include "remoting/client/plugin/chromoting_instance.h"
20 #include "remoting/client/plugin/pepper_entrypoints.h"
21 #include "remoting/client/plugin/pepper_util.h"
22
23 namespace {
24
25 static const int kHostPort = 80;
26 static const int kNumRetries = 5;
27 static const std::string kCreateSessionURL = "/create_session";
28
29 // Define a SessionFactory in the anonymouse namespace so we have a
30 // shorter name.
31 class SessionFactory : public remoting::PortAllocatorSessionFactory {
Sergey Ulanov 2011/03/07 12:58:45 TODO to move this to a separate file?
Alpha Left Google 2011/03/07 18:02:29 Done.
32 public:
33 SessionFactory(remoting::ChromotingInstance* instance,
34 MessageLoop* message_loop)
35 : instance_(instance),
36 jingle_message_loop_(message_loop) {
37 }
38
39 virtual cricket::PortAllocatorSession* CreateSession(
40 cricket::BasicPortAllocator* allocator,
41 const std::string& name,
42 const std::string& session_type,
43 const std::vector<talk_base::SocketAddress>& stun_hosts,
44 const std::vector<std::string>& relay_hosts,
45 const std::string& relay,
46 const std::string& agent) {
47 return new remoting::PepperPortAllocatorSession(
48 instance_, jingle_message_loop_, allocator, name, session_type,
49 stun_hosts, relay_hosts, relay, agent);
50 }
51
52 private:
53 remoting::ChromotingInstance* instance_;
54
55 // Message loop that jingle runs on.
56 MessageLoop* jingle_message_loop_;
57
58 DISALLOW_COPY_AND_ASSIGN(SessionFactory);
59 };
60
61 typedef Callback3<bool, int, const std::string&>::Type FetchCallback;
62
63 // Helper routine to remove whitespace from the ends of a string.
64 void Trim(std::string& str) {
Sergey Ulanov 2011/03/07 12:58:45 Can we use base::TrimString() instead?
Alpha Left Google 2011/03/07 18:02:29 Done.
65 size_t first = str.find_first_not_of(" \t\r\n");
66 if (first == std::string::npos) {
67 str.clear();
68 return;
69 }
70
71 ASSERT(str.find_last_not_of(" \t\r\n") != std::string::npos);
72 }
73
74 // Parses the lines in the result of the HTTP request that are of the form
75 // 'a=b' and returns them in a map.
76 typedef std::map<std::string, std::string> StringMap;
77 void ParseMap(const std::string& string, StringMap& map) {
78 size_t start_of_line = 0;
79 size_t end_of_line = 0;
80
81 for (;;) { // for each line
82 start_of_line = string.find_first_not_of("\r\n", end_of_line);
83 if (start_of_line == std::string::npos)
84 break;
85
86 end_of_line = string.find_first_of("\r\n", start_of_line);
Sergey Ulanov 2011/03/07 12:58:45 Could it be that the result we receive from the se
Alpha Left Google 2011/03/07 18:02:29 HTTP use \r\n as line breaks and this is from libj
87 if (end_of_line == std::string::npos) {
88 end_of_line = string.length();
89 }
90
91 size_t equals = string.find('=', start_of_line);
Sergey Ulanov 2011/03/07 12:58:45 equals_pos?
Alpha Left Google 2011/03/07 18:02:29 I'm reluctant to change this at the moment since t
92 if ((equals >= end_of_line) || (equals == std::string::npos))
93 continue;
94
95 std::string key(string, start_of_line, equals - start_of_line);
96 std::string value(string, equals + 1, end_of_line - equals - 1);
97
98 Trim(key);
Alpha Left Google 2011/03/07 18:02:29 I did change this to base::TrimString().
99 Trim(value);
100
101 if ((key.size() > 0) && (value.size() > 0))
102 map[key] = value;
103 }
104 }
105
106 } // namespace
107
108 namespace remoting {
109
110 // A URL Fetcher using Pepper.
111 class PepperURLFetcher {
Sergey Ulanov 2011/03/07 12:58:45 TODO to move this to a separate file?
Alpha Left Google 2011/03/07 18:02:29 Done.
112 public:
113 PepperURLFetcher() : fetch_callback_(NULL) {
114 callback_factory_.Initialize(this);
115 }
116
117 void Start(const pp::Instance& instance,
118 pp::URLRequestInfo request,
119 FetchCallback* fetch_callback) {
120 loader_ = pp::URLLoader(instance);
121
122 // Grant access to external origins.
123 const struct PPB_URLLoaderTrusted* trusted_loader_interface =
124 reinterpret_cast<const PPB_URLLoaderTrusted*>(
125 PPP_GetBrowserInterface(PPB_URLLOADERTRUSTED_INTERFACE));
126 trusted_loader_interface->GrantUniversalAccess(
127 loader_.pp_resource());
128
129 fetch_callback_.reset(fetch_callback);
130
131 pp::CompletionCallback callback =
132 callback_factory_.NewCallback(&PepperURLFetcher::DidOpen);
133 int rv = loader_.Open(request, callback);
134 if (rv != PP_ERROR_WOULDBLOCK)
135 callback.Run(rv);
136 }
137
138 private:
139 void ReadMore() {
140 pp::CompletionCallback callback =
141 callback_factory_.NewCallback(&PepperURLFetcher::DidRead);
142 int rv = loader_.ReadResponseBody(buf_, sizeof(buf_), callback);
143 if (rv != PP_ERROR_WOULDBLOCK)
144 callback.Run(rv);
145 }
146
147 void DidOpen(int32_t result) {
148 if (result == PP_OK) {
149 ReadMore();
150 } else {
151 DidFinish(result);
152 }
153 }
154
155 void DidRead(int32_t result) {
156 if (result > 0) {
157 data_.append(buf_, result);
158 ReadMore();
159 } else {
160 DidFinish(result);
161 }
162 }
163
164 void DidFinish(int32_t result) {
165 if (fetch_callback_.get()) {
166 bool success = result == PP_OK;
167 int status_code = 0;
168 if (success)
169 status_code = loader_.GetResponseInfo().GetStatusCode();
170 fetch_callback_->Run(success, status_code, data_);
171 }
172 }
173
174 pp::CompletionCallbackFactory<PepperURLFetcher> callback_factory_;
175 pp::URLLoader loader_;
176 scoped_ptr<FetchCallback> fetch_callback_;
177 char buf_[4096];
178 std::string data_;
179 };
180
181 // A helper class to do HTTP request on the pepper thread and then delegate the
182 // result to PepperPortAllocatorSession on jingle thread safely.
183 class PepperCreateSessionTask
184 : public base::RefCountedThreadSafe<PepperCreateSessionTask> {
185 public:
186 PepperCreateSessionTask(
187 MessageLoop* jingle_message_loop,
188 PepperPortAllocatorSession* allocator_session,
189 ChromotingInstance* instance,
190 const std::string& host,
191 int port,
192 const std::string& relay_token,
193 const std::string& session_type,
194 const std::string& name)
195 : jingle_message_loop_(jingle_message_loop),
196 allocator_session_(allocator_session),
197 instance_(instance),
198 host_(host),
199 port_(port),
200 relay_token_(relay_token),
201 session_type_(session_type),
202 name_(name) {
203 }
204
205 // Start doing the request. The request will start on the pepper thread.
206 void Start() {
207 if (!instance_->CurrentlyOnPluginThread()) {
208 RunTaskOnPluginThread(
209 NewRunnableMethod(this, &PepperCreateSessionTask::Start));
210 return;
211 }
212
213 // Perform the request here.
214 std::string url = base::StringPrintf("http://%s:%d/create_session",
215 host_.c_str(), port_);
216 pp::URLRequestInfo request(instance_);
217 request.SetURL(url.c_str());
218 request.SetMethod("GET");
219 request.SetHeaders(base::StringPrintf(
220 "X-Talk-Google-Relay-Auth: %s\r\n"
221 "X-Google-Relay-Auth: %s\r\n"
222 "X-Session-Type: %s\r\n"
223 "X-Stream-Type: %s\r\n",
224 relay_token_.c_str(), relay_token_.c_str(), session_type_.c_str(),
225 name_.c_str()));
226
227 url_fetcher_.reset(new PepperURLFetcher());
228 url_fetcher_->Start(
229 *instance_, request,
230 NewCallback(this, &PepperCreateSessionTask::OnRequestDone));
231 }
232
233 // Detach this task. This class will not access PepperPortAllocatorSession
234 // anymore.
235 void Detach() {
236 // Set the pointers to zero.
237 {
238 base::AutoLock auto_lock(lock_);
239 jingle_message_loop_ = NULL;
240 allocator_session_ = NULL;
241 instance_ = NULL;
242 }
243
244 // IMPORTANT!
245 // Post a dummy task so that we destruct this class and associated pepper
246 // resources on pepper thread.
247 RunTaskOnPluginThread(
248 NewRunnableMethod(this, &PepperCreateSessionTask::Dummy));
Sergey Ulanov 2011/03/07 12:58:45 This doesn't guarantee that the object will be des
Alpha Left Google 2011/03/07 18:02:29 sounds good.
249 }
250
251 private:
252 void OnRequestDone(bool success, int status_code,
253 const std::string& response) {
254 // IMPORTANT!
255 // This method is called on the pepper thread and we want the response to
256 // be delegated to the jingle thread. However jignle thread might have
257 // been destroyed and |allocator_session_| might be dangling too. So we
258 // put a lock here to access |jingle_message_loop_| and then do the
259 // remaining work on the jingle thread.
260 base::AutoLock auto_lock(lock_);
Sergey Ulanov 2011/03/07 12:58:45 Can we use MessageLoopProxy here? You would not ne
Alpha Left Google 2011/03/07 18:02:29 I believe we have to use message loop proxy everyw
261 if (!jingle_message_loop_)
262 return;
263
264 jingle_message_loop_->PostTask(
265 FROM_HERE,
266 NewRunnableMethod(this, &PepperCreateSessionTask::DelegateRequestDone,
267 success, status_code, response));
268 }
269
270 void DelegateRequestDone(bool success, int status_code,
271 const std::string& response) {
272 if (!allocator_session_)
273 return;
274 allocator_session_->OnRequestDone(success, status_code, response);
275 }
276
277 // This is a dummy function just so that we can deref this class on pepper
278 // thread.
279 void Dummy() {}
280
281 // Protects |jingle_message_loop_|.
282 base::Lock lock_;
283
284 MessageLoop* jingle_message_loop_;
285 PepperPortAllocatorSession* allocator_session_;
286 ChromotingInstance* instance_;
287 std::string host_;
288 int port_;
289 std::string relay_token_;
290 std::string session_type_;
291 std::string name_;
292
293 // Pepper resources for URL fetching.
294 scoped_ptr<PepperURLFetcher> url_fetcher_;
295
296 DISALLOW_COPY_AND_ASSIGN(PepperCreateSessionTask);
297 };
298
299 PortAllocatorSessionFactory* PepperPortAllocatorSession::CreateFactory(
300 ChromotingInstance* instance) {
301 return new SessionFactory(instance, MessageLoop::current());
302 }
303
304 PepperPortAllocatorSession::PepperPortAllocatorSession(
305 ChromotingInstance* instance,
306 MessageLoop* message_loop,
307 cricket::BasicPortAllocator* allocator,
308 const std::string &name,
309 const std::string& session_type,
310 const std::vector<talk_base::SocketAddress>& stun_hosts,
311 const std::vector<std::string>& relay_hosts,
312 const std::string& relay_token,
313 const std::string& user_agent)
314 : BasicPortAllocatorSession(allocator, name, session_type),
315 instance_(instance), jingle_message_loop_(message_loop),
316 relay_hosts_(relay_hosts), stun_hosts_(stun_hosts),
317 relay_token_(relay_token), agent_(user_agent), attempts_(0) {
318 }
319
320 PepperPortAllocatorSession::~PepperPortAllocatorSession() {
321 if (create_session_task_) {
322 create_session_task_->Detach();
323 create_session_task_ = NULL;
324 }
325 }
326
327 void PepperPortAllocatorSession::GetPortConfigurations() {
328 // Creating relay sessions can take time and is done asynchronously.
329 // Creating stun sessions could also take time and could be done aysnc also,
330 // but for now is done here and added to the initial config. Note any later
331 // configs will have unresolved stun ips and will be discarded by the
332 // AllocationSequence.
333 cricket::PortConfiguration* config =
334 new cricket::PortConfiguration(stun_hosts_[0], "", "", "");
335 ConfigReady(config);
336 TryCreateRelaySession();
337 }
338
339 void PepperPortAllocatorSession::TryCreateRelaySession() {
340 if (attempts_ == kNumRetries) {
341 LOG(ERROR) << "PepperPortAllocator: maximum number of requests reached; "
342 << "giving up on relay.";
343 return;
344 }
345
346 if (relay_hosts_.size() == 0) {
347 LOG(ERROR) << "PepperPortAllocator: no relay hosts configured.";
348 return;
349 }
350
351 // Choose the next host to try.
352 std::string host = relay_hosts_[attempts_ % relay_hosts_.size()];
353 attempts_++;
354 LOG(INFO) << "PepperPortAllocator: sending to relay host " << host;
355 if (relay_token_.empty()) {
356 LOG(WARNING) << "No relay auth token found.";
357 }
358
359 SendSessionRequest(host, kHostPort);
360 }
361
362 void PepperPortAllocatorSession::SendSessionRequest(const std::string& host,
363 int port) {
364 // Destroy the old PepperCreateSessionTask first.
365 if (create_session_task_) {
366 create_session_task_->Detach();
367 create_session_task_ = NULL;
368 }
369
370 // Construct a new one and start it. OnRequestDone() will be called when
371 // task has completed.
372 create_session_task_ = new PepperCreateSessionTask(
373 jingle_message_loop_, this, instance_, host, port, relay_token_,
374 session_type(), name());
375 create_session_task_->Start();
376 }
377
378 void PepperPortAllocatorSession::OnRequestDone(bool success,
379 int status_code,
380 const std::string& response) {
381 DCHECK_EQ(jingle_message_loop_, MessageLoop::current());
382
383 if (!success || status_code != 200) {
384 LOG(WARNING) << "PepperPortAllocatorSession: failed.";
385 TryCreateRelaySession();
386 return;
387 }
388
389 LOG(INFO) << "PepperPortAllocatorSession: request succeeded.";
390 ReceiveSessionResponse(response);
391 }
392
393 void PepperPortAllocatorSession::ReceiveSessionResponse(
394 const std::string& response) {
395 StringMap map;
396 ParseMap(response, map);
397
398 std::string username = map["username"];
399 std::string password = map["password"];
400 std::string magic_cookie = map["magic_cookie"];
401
402 std::string relay_ip = map["relay.ip"];
403 std::string relay_udp_port = map["relay.udp_port"];
404 std::string relay_tcp_port = map["relay.tcp_port"];
405 std::string relay_ssltcp_port = map["relay.ssltcp_port"];
406
407 cricket::PortConfiguration* config =
408 new cricket::PortConfiguration(stun_hosts_[0], username,
409 password, magic_cookie);
410
411 cricket::PortConfiguration::PortList ports;
412 if (!relay_udp_port.empty()) {
413 talk_base::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str()));
414 ports.push_back(cricket::ProtocolAddress(address, cricket::PROTO_UDP));
415 }
416 if (!relay_tcp_port.empty()) {
417 talk_base::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str()));
418 ports.push_back(cricket::ProtocolAddress(address, cricket::PROTO_TCP));
419 }
420 if (!relay_ssltcp_port.empty()) {
421 talk_base::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str()));
422 ports.push_back(cricket::ProtocolAddress(address, cricket::PROTO_SSLTCP));
423 }
424 config->AddRelay(ports, 0.0f);
425 ConfigReady(config);
426 }
427
428 } // namespace remoting
429
430 DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::PepperPortAllocatorSession);
Sergey Ulanov 2011/03/07 12:58:45 I don't see why we would need this. Remove it?
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698