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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: remoting/client/plugin/pepper_port_allocator_session.cc
diff --git a/remoting/client/plugin/pepper_port_allocator_session.cc b/remoting/client/plugin/pepper_port_allocator_session.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2aff2d2bef1016f2cc40aee52e8cae2d9a6bd63b
--- /dev/null
+++ b/remoting/client/plugin/pepper_port_allocator_session.cc
@@ -0,0 +1,430 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/client/plugin/pepper_port_allocator_session.h"
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/trusted/ppb_url_loader_trusted.h"
+#include "ppapi/cpp/url_loader.h"
+#include "ppapi/cpp/url_request_info.h"
+#include "ppapi/cpp/url_response_info.h"
+#include "remoting/jingle_glue/http_port_allocator.h"
+#include "remoting/client/plugin/chromoting_instance.h"
+#include "remoting/client/plugin/pepper_entrypoints.h"
+#include "remoting/client/plugin/pepper_util.h"
+
+namespace {
+
+static const int kHostPort = 80;
+static const int kNumRetries = 5;
+static const std::string kCreateSessionURL = "/create_session";
+
+// Define a SessionFactory in the anonymouse namespace so we have a
+// shorter name.
+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.
+ public:
+ SessionFactory(remoting::ChromotingInstance* instance,
+ MessageLoop* message_loop)
+ : instance_(instance),
+ jingle_message_loop_(message_loop) {
+ }
+
+ virtual cricket::PortAllocatorSession* CreateSession(
+ cricket::BasicPortAllocator* allocator,
+ const std::string& name,
+ const std::string& session_type,
+ const std::vector<talk_base::SocketAddress>& stun_hosts,
+ const std::vector<std::string>& relay_hosts,
+ const std::string& relay,
+ const std::string& agent) {
+ return new remoting::PepperPortAllocatorSession(
+ instance_, jingle_message_loop_, allocator, name, session_type,
+ stun_hosts, relay_hosts, relay, agent);
+ }
+
+ private:
+ remoting::ChromotingInstance* instance_;
+
+ // Message loop that jingle runs on.
+ MessageLoop* jingle_message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(SessionFactory);
+};
+
+typedef Callback3<bool, int, const std::string&>::Type FetchCallback;
+
+// Helper routine to remove whitespace from the ends of a string.
+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.
+ size_t first = str.find_first_not_of(" \t\r\n");
+ if (first == std::string::npos) {
+ str.clear();
+ return;
+ }
+
+ ASSERT(str.find_last_not_of(" \t\r\n") != std::string::npos);
+}
+
+// Parses the lines in the result of the HTTP request that are of the form
+// 'a=b' and returns them in a map.
+typedef std::map<std::string, std::string> StringMap;
+void ParseMap(const std::string& string, StringMap& map) {
+ size_t start_of_line = 0;
+ size_t end_of_line = 0;
+
+ for (;;) { // for each line
+ start_of_line = string.find_first_not_of("\r\n", end_of_line);
+ if (start_of_line == std::string::npos)
+ break;
+
+ 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
+ if (end_of_line == std::string::npos) {
+ end_of_line = string.length();
+ }
+
+ 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
+ if ((equals >= end_of_line) || (equals == std::string::npos))
+ continue;
+
+ std::string key(string, start_of_line, equals - start_of_line);
+ std::string value(string, equals + 1, end_of_line - equals - 1);
+
+ Trim(key);
Alpha Left Google 2011/03/07 18:02:29 I did change this to base::TrimString().
+ Trim(value);
+
+ if ((key.size() > 0) && (value.size() > 0))
+ map[key] = value;
+ }
+}
+
+} // namespace
+
+namespace remoting {
+
+// A URL Fetcher using Pepper.
+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.
+ public:
+ PepperURLFetcher() : fetch_callback_(NULL) {
+ callback_factory_.Initialize(this);
+ }
+
+ void Start(const pp::Instance& instance,
+ pp::URLRequestInfo request,
+ FetchCallback* fetch_callback) {
+ loader_ = pp::URLLoader(instance);
+
+ // Grant access to external origins.
+ const struct PPB_URLLoaderTrusted* trusted_loader_interface =
+ reinterpret_cast<const PPB_URLLoaderTrusted*>(
+ PPP_GetBrowserInterface(PPB_URLLOADERTRUSTED_INTERFACE));
+ trusted_loader_interface->GrantUniversalAccess(
+ loader_.pp_resource());
+
+ fetch_callback_.reset(fetch_callback);
+
+ pp::CompletionCallback callback =
+ callback_factory_.NewCallback(&PepperURLFetcher::DidOpen);
+ int rv = loader_.Open(request, callback);
+ if (rv != PP_ERROR_WOULDBLOCK)
+ callback.Run(rv);
+ }
+
+ private:
+ void ReadMore() {
+ pp::CompletionCallback callback =
+ callback_factory_.NewCallback(&PepperURLFetcher::DidRead);
+ int rv = loader_.ReadResponseBody(buf_, sizeof(buf_), callback);
+ if (rv != PP_ERROR_WOULDBLOCK)
+ callback.Run(rv);
+ }
+
+ void DidOpen(int32_t result) {
+ if (result == PP_OK) {
+ ReadMore();
+ } else {
+ DidFinish(result);
+ }
+ }
+
+ void DidRead(int32_t result) {
+ if (result > 0) {
+ data_.append(buf_, result);
+ ReadMore();
+ } else {
+ DidFinish(result);
+ }
+ }
+
+ void DidFinish(int32_t result) {
+ if (fetch_callback_.get()) {
+ bool success = result == PP_OK;
+ int status_code = 0;
+ if (success)
+ status_code = loader_.GetResponseInfo().GetStatusCode();
+ fetch_callback_->Run(success, status_code, data_);
+ }
+ }
+
+ pp::CompletionCallbackFactory<PepperURLFetcher> callback_factory_;
+ pp::URLLoader loader_;
+ scoped_ptr<FetchCallback> fetch_callback_;
+ char buf_[4096];
+ std::string data_;
+};
+
+// A helper class to do HTTP request on the pepper thread and then delegate the
+// result to PepperPortAllocatorSession on jingle thread safely.
+class PepperCreateSessionTask
+ : public base::RefCountedThreadSafe<PepperCreateSessionTask> {
+ public:
+ PepperCreateSessionTask(
+ MessageLoop* jingle_message_loop,
+ PepperPortAllocatorSession* allocator_session,
+ ChromotingInstance* instance,
+ const std::string& host,
+ int port,
+ const std::string& relay_token,
+ const std::string& session_type,
+ const std::string& name)
+ : jingle_message_loop_(jingle_message_loop),
+ allocator_session_(allocator_session),
+ instance_(instance),
+ host_(host),
+ port_(port),
+ relay_token_(relay_token),
+ session_type_(session_type),
+ name_(name) {
+ }
+
+ // Start doing the request. The request will start on the pepper thread.
+ void Start() {
+ if (!instance_->CurrentlyOnPluginThread()) {
+ RunTaskOnPluginThread(
+ NewRunnableMethod(this, &PepperCreateSessionTask::Start));
+ return;
+ }
+
+ // Perform the request here.
+ std::string url = base::StringPrintf("http://%s:%d/create_session",
+ host_.c_str(), port_);
+ pp::URLRequestInfo request(instance_);
+ request.SetURL(url.c_str());
+ request.SetMethod("GET");
+ request.SetHeaders(base::StringPrintf(
+ "X-Talk-Google-Relay-Auth: %s\r\n"
+ "X-Google-Relay-Auth: %s\r\n"
+ "X-Session-Type: %s\r\n"
+ "X-Stream-Type: %s\r\n",
+ relay_token_.c_str(), relay_token_.c_str(), session_type_.c_str(),
+ name_.c_str()));
+
+ url_fetcher_.reset(new PepperURLFetcher());
+ url_fetcher_->Start(
+ *instance_, request,
+ NewCallback(this, &PepperCreateSessionTask::OnRequestDone));
+ }
+
+ // Detach this task. This class will not access PepperPortAllocatorSession
+ // anymore.
+ void Detach() {
+ // Set the pointers to zero.
+ {
+ base::AutoLock auto_lock(lock_);
+ jingle_message_loop_ = NULL;
+ allocator_session_ = NULL;
+ instance_ = NULL;
+ }
+
+ // IMPORTANT!
+ // Post a dummy task so that we destruct this class and associated pepper
+ // resources on pepper thread.
+ RunTaskOnPluginThread(
+ 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.
+ }
+
+ private:
+ void OnRequestDone(bool success, int status_code,
+ const std::string& response) {
+ // IMPORTANT!
+ // This method is called on the pepper thread and we want the response to
+ // be delegated to the jingle thread. However jignle thread might have
+ // been destroyed and |allocator_session_| might be dangling too. So we
+ // put a lock here to access |jingle_message_loop_| and then do the
+ // remaining work on the jingle thread.
+ 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
+ if (!jingle_message_loop_)
+ return;
+
+ jingle_message_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &PepperCreateSessionTask::DelegateRequestDone,
+ success, status_code, response));
+ }
+
+ void DelegateRequestDone(bool success, int status_code,
+ const std::string& response) {
+ if (!allocator_session_)
+ return;
+ allocator_session_->OnRequestDone(success, status_code, response);
+ }
+
+ // This is a dummy function just so that we can deref this class on pepper
+ // thread.
+ void Dummy() {}
+
+ // Protects |jingle_message_loop_|.
+ base::Lock lock_;
+
+ MessageLoop* jingle_message_loop_;
+ PepperPortAllocatorSession* allocator_session_;
+ ChromotingInstance* instance_;
+ std::string host_;
+ int port_;
+ std::string relay_token_;
+ std::string session_type_;
+ std::string name_;
+
+ // Pepper resources for URL fetching.
+ scoped_ptr<PepperURLFetcher> url_fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(PepperCreateSessionTask);
+};
+
+PortAllocatorSessionFactory* PepperPortAllocatorSession::CreateFactory(
+ ChromotingInstance* instance) {
+ return new SessionFactory(instance, MessageLoop::current());
+}
+
+PepperPortAllocatorSession::PepperPortAllocatorSession(
+ ChromotingInstance* instance,
+ MessageLoop* message_loop,
+ cricket::BasicPortAllocator* allocator,
+ const std::string &name,
+ const std::string& session_type,
+ const std::vector<talk_base::SocketAddress>& stun_hosts,
+ const std::vector<std::string>& relay_hosts,
+ const std::string& relay_token,
+ const std::string& user_agent)
+ : BasicPortAllocatorSession(allocator, name, session_type),
+ instance_(instance), jingle_message_loop_(message_loop),
+ relay_hosts_(relay_hosts), stun_hosts_(stun_hosts),
+ relay_token_(relay_token), agent_(user_agent), attempts_(0) {
+}
+
+PepperPortAllocatorSession::~PepperPortAllocatorSession() {
+ if (create_session_task_) {
+ create_session_task_->Detach();
+ create_session_task_ = NULL;
+ }
+}
+
+void PepperPortAllocatorSession::GetPortConfigurations() {
+ // Creating relay sessions can take time and is done asynchronously.
+ // Creating stun sessions could also take time and could be done aysnc also,
+ // but for now is done here and added to the initial config. Note any later
+ // configs will have unresolved stun ips and will be discarded by the
+ // AllocationSequence.
+ cricket::PortConfiguration* config =
+ new cricket::PortConfiguration(stun_hosts_[0], "", "", "");
+ ConfigReady(config);
+ TryCreateRelaySession();
+}
+
+void PepperPortAllocatorSession::TryCreateRelaySession() {
+ if (attempts_ == kNumRetries) {
+ LOG(ERROR) << "PepperPortAllocator: maximum number of requests reached; "
+ << "giving up on relay.";
+ return;
+ }
+
+ if (relay_hosts_.size() == 0) {
+ LOG(ERROR) << "PepperPortAllocator: no relay hosts configured.";
+ return;
+ }
+
+ // Choose the next host to try.
+ std::string host = relay_hosts_[attempts_ % relay_hosts_.size()];
+ attempts_++;
+ LOG(INFO) << "PepperPortAllocator: sending to relay host " << host;
+ if (relay_token_.empty()) {
+ LOG(WARNING) << "No relay auth token found.";
+ }
+
+ SendSessionRequest(host, kHostPort);
+}
+
+void PepperPortAllocatorSession::SendSessionRequest(const std::string& host,
+ int port) {
+ // Destroy the old PepperCreateSessionTask first.
+ if (create_session_task_) {
+ create_session_task_->Detach();
+ create_session_task_ = NULL;
+ }
+
+ // Construct a new one and start it. OnRequestDone() will be called when
+ // task has completed.
+ create_session_task_ = new PepperCreateSessionTask(
+ jingle_message_loop_, this, instance_, host, port, relay_token_,
+ session_type(), name());
+ create_session_task_->Start();
+}
+
+void PepperPortAllocatorSession::OnRequestDone(bool success,
+ int status_code,
+ const std::string& response) {
+ DCHECK_EQ(jingle_message_loop_, MessageLoop::current());
+
+ if (!success || status_code != 200) {
+ LOG(WARNING) << "PepperPortAllocatorSession: failed.";
+ TryCreateRelaySession();
+ return;
+ }
+
+ LOG(INFO) << "PepperPortAllocatorSession: request succeeded.";
+ ReceiveSessionResponse(response);
+}
+
+void PepperPortAllocatorSession::ReceiveSessionResponse(
+ const std::string& response) {
+ StringMap map;
+ ParseMap(response, map);
+
+ std::string username = map["username"];
+ std::string password = map["password"];
+ std::string magic_cookie = map["magic_cookie"];
+
+ std::string relay_ip = map["relay.ip"];
+ std::string relay_udp_port = map["relay.udp_port"];
+ std::string relay_tcp_port = map["relay.tcp_port"];
+ std::string relay_ssltcp_port = map["relay.ssltcp_port"];
+
+ cricket::PortConfiguration* config =
+ new cricket::PortConfiguration(stun_hosts_[0], username,
+ password, magic_cookie);
+
+ cricket::PortConfiguration::PortList ports;
+ if (!relay_udp_port.empty()) {
+ talk_base::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str()));
+ ports.push_back(cricket::ProtocolAddress(address, cricket::PROTO_UDP));
+ }
+ if (!relay_tcp_port.empty()) {
+ talk_base::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str()));
+ ports.push_back(cricket::ProtocolAddress(address, cricket::PROTO_TCP));
+ }
+ if (!relay_ssltcp_port.empty()) {
+ talk_base::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str()));
+ ports.push_back(cricket::ProtocolAddress(address, cricket::PROTO_SSLTCP));
+ }
+ config->AddRelay(ports, 0.0f);
+ ConfigReady(config);
+}
+
+} // namespace remoting
+
+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?

Powered by Google App Engine
This is Rietveld 408576698