pepper_port_allocator.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
1// Copyright (c) 2012 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.h" 6 7#include "base/bind.h" 8#include "base/strings/string_number_conversions.h" 9#include "net/base/net_util.h" 10#include "ppapi/c/pp_errors.h" 11#include "ppapi/cpp/url_loader.h" 12#include "ppapi/cpp/url_request_info.h" 13#include "ppapi/cpp/url_response_info.h" 14#include "ppapi/utility/completion_callback_factory.h" 15#include "remoting/client/plugin/pepper_network_manager.h" 16#include "remoting/client/plugin/pepper_packet_socket_factory.h" 17#include "remoting/client/plugin/pepper_util.h" 18 19namespace remoting { 20 21namespace { 22 23// Read buffer we allocate per read when reading response from 24// URLLoader. Normally the response from URL loader is smaller than 1kB. 25const int kReadSize = 1024; 26 27class PepperPortAllocatorSession 28 : public cricket::HttpPortAllocatorSessionBase { 29 public: 30 PepperPortAllocatorSession( 31 cricket::HttpPortAllocatorBase* allocator, 32 const std::string& content_name, 33 int component, 34 const std::string& ice_username_fragment, 35 const std::string& ice_password, 36 const std::vector<rtc::SocketAddress>& stun_hosts, 37 const std::vector<std::string>& relay_hosts, 38 const std::string& relay_token, 39 const pp::InstanceHandle& instance); 40 virtual ~PepperPortAllocatorSession(); 41 42 // cricket::HttpPortAllocatorBase overrides. 43 virtual void ConfigReady(cricket::PortConfiguration* config) OVERRIDE; 44 virtual void GetPortConfigurations() OVERRIDE; 45 virtual void SendSessionRequest(const std::string& host, int port) OVERRIDE; 46 47 private: 48 void OnUrlOpened(int32_t result); 49 void ReadResponseBody(); 50 void OnResponseBodyRead(int32_t result); 51 52 pp::InstanceHandle instance_; 53 54 cricket::ServerAddresses stun_hosts_; 55 56 scoped_ptr<pp::URLLoader> relay_url_loader_; 57 std::vector<char> relay_response_body_; 58 bool relay_response_received_; 59 60 pp::CompletionCallbackFactory<PepperPortAllocatorSession> callback_factory_; 61 62 DISALLOW_COPY_AND_ASSIGN(PepperPortAllocatorSession); 63}; 64 65PepperPortAllocatorSession::PepperPortAllocatorSession( 66 cricket::HttpPortAllocatorBase* allocator, 67 const std::string& content_name, 68 int component, 69 const std::string& ice_username_fragment, 70 const std::string& ice_password, 71 const std::vector<rtc::SocketAddress>& stun_hosts, 72 const std::vector<std::string>& relay_hosts, 73 const std::string& relay_token, 74 const pp::InstanceHandle& instance) 75 : HttpPortAllocatorSessionBase(allocator, 76 content_name, 77 component, 78 ice_username_fragment, 79 ice_password, 80 stun_hosts, 81 relay_hosts, 82 relay_token, 83 std::string()), 84 instance_(instance), 85 stun_hosts_(stun_hosts.begin(), stun_hosts.end()), 86 relay_response_received_(false), 87 callback_factory_(this) { 88} 89 90PepperPortAllocatorSession::~PepperPortAllocatorSession() { 91} 92 93void PepperPortAllocatorSession::ConfigReady( 94 cricket::PortConfiguration* config) { 95 // Filter out non-UDP relay ports, so that we don't try using TCP. 96 for (cricket::PortConfiguration::RelayList::iterator relay = 97 config->relays.begin(); relay != config->relays.end(); ++relay) { 98 cricket::PortList filtered_ports; 99 for (cricket::PortList::iterator port = 100 relay->ports.begin(); port != relay->ports.end(); ++port) { 101 if (port->proto == cricket::PROTO_UDP) { 102 filtered_ports.push_back(*port); 103 } 104 } 105 relay->ports = filtered_ports; 106 } 107 cricket::BasicPortAllocatorSession::ConfigReady(config); 108} 109 110void PepperPortAllocatorSession::GetPortConfigurations() { 111 // Add a configuration without relay response first so local and STUN 112 // candidates can be allocated without waiting for the relay response. 113 ConfigReady(new cricket::PortConfiguration( 114 stun_hosts_, std::string(), std::string())); 115 116 TryCreateRelaySession(); 117} 118 119void PepperPortAllocatorSession::SendSessionRequest( 120 const std::string& host, 121 int port) { 122 relay_url_loader_.reset(new pp::URLLoader(instance_)); 123 pp::URLRequestInfo request_info(instance_); 124 std::string url = "https://" + host + ":" + base::IntToString(port) + 125 GetSessionRequestUrl() + "&sn=1"; 126 request_info.SetURL(url); 127 request_info.SetMethod("GET"); 128 std::stringstream headers; 129 headers << "X-Talk-Google-Relay-Auth: " << relay_token() << "\n\r"; 130 headers << "X-Google-Relay-Auth: " << relay_token() << "\n\r"; 131 headers << "X-Stream-Type: " << "chromoting" << "\n\r"; 132 request_info.SetHeaders(headers.str()); 133 134 pp::CompletionCallback callback = 135 callback_factory_.NewCallback(&PepperPortAllocatorSession::OnUrlOpened); 136 int result = relay_url_loader_->Open(request_info, callback); 137 DCHECK_EQ(result, PP_OK_COMPLETIONPENDING); 138} 139 140void PepperPortAllocatorSession::OnUrlOpened(int32_t result) { 141 if (result == PP_ERROR_ABORTED) { 142 return; 143 } 144 145 if (result < 0) { 146 LOG(WARNING) << "URLLoader failed: " << result; 147 // Retry creating session. 148 TryCreateRelaySession(); 149 return; 150 } 151 152 pp::URLResponseInfo response = relay_url_loader_->GetResponseInfo(); 153 DCHECK(!response.is_null()); 154 if (response.GetStatusCode() != 200) { 155 LOG(WARNING) << "Received HTTP status code " << response.GetStatusCode(); 156 // Retry creating session. 157 TryCreateRelaySession(); 158 return; 159 } 160 161 relay_response_body_.clear(); 162 ReadResponseBody(); 163} 164 165void PepperPortAllocatorSession::ReadResponseBody() { 166 int pos = relay_response_body_.size(); 167 relay_response_body_.resize(pos + kReadSize); 168 pp::CompletionCallback callback = callback_factory_.NewCallback( 169 &PepperPortAllocatorSession::OnResponseBodyRead); 170 int result = relay_url_loader_->ReadResponseBody(&relay_response_body_[pos], 171 kReadSize, 172 callback); 173 DCHECK_EQ(result, PP_OK_COMPLETIONPENDING); 174} 175 176void PepperPortAllocatorSession::OnResponseBodyRead(int32_t result) { 177 if (result == PP_ERROR_ABORTED) { 178 return; 179 } 180 181 if (result < 0) { 182 LOG(WARNING) << "Failed to read HTTP response body when " 183 "creating relay session: " << result; 184 // Retry creating session. 185 TryCreateRelaySession(); 186 return; 187 } 188 189 // Resize the buffer in case we've read less than was requested. 190 CHECK_LE(result, kReadSize); 191 CHECK_GE(static_cast<int>(relay_response_body_.size()), kReadSize); 192 relay_response_body_.resize(relay_response_body_.size() - kReadSize + result); 193 194 if (result == 0) { 195 relay_response_received_ = true; 196 ReceiveSessionResponse(std::string(relay_response_body_.begin(), 197 relay_response_body_.end())); 198 return; 199 } 200 201 ReadResponseBody(); 202} 203 204} // namespace 205 206// static 207scoped_ptr<PepperPortAllocator> PepperPortAllocator::Create( 208 const pp::InstanceHandle& instance) { 209 scoped_ptr<rtc::NetworkManager> network_manager( 210 new PepperNetworkManager(instance)); 211 scoped_ptr<rtc::PacketSocketFactory> socket_factory( 212 new PepperPacketSocketFactory(instance)); 213 scoped_ptr<PepperPortAllocator> result(new PepperPortAllocator( 214 instance, network_manager.Pass(), socket_factory.Pass())); 215 return result.Pass(); 216} 217 218PepperPortAllocator::PepperPortAllocator( 219 const pp::InstanceHandle& instance, 220 scoped_ptr<rtc::NetworkManager> network_manager, 221 scoped_ptr<rtc::PacketSocketFactory> socket_factory) 222 : HttpPortAllocatorBase(network_manager.get(), 223 socket_factory.get(), 224 std::string()), 225 instance_(instance), 226 network_manager_(network_manager.Pass()), 227 socket_factory_(socket_factory.Pass()) { 228 // TCP transport is disabled becase PseudoTCP works poorly over 229 // it. ENABLE_SHARED_UFRAG flag is specified so that the same 230 // username fragment is shared between all candidates for this 231 // channel. 232 set_flags(cricket::PORTALLOCATOR_DISABLE_TCP | 233 cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG| 234 cricket::PORTALLOCATOR_ENABLE_IPV6); 235} 236 237PepperPortAllocator::~PepperPortAllocator() { 238} 239 240cricket::PortAllocatorSession* PepperPortAllocator::CreateSessionInternal( 241 const std::string& content_name, 242 int component, 243 const std::string& ice_username_fragment, 244 const std::string& ice_password) { 245 return new PepperPortAllocatorSession( 246 this, content_name, component, ice_username_fragment, ice_password, 247 stun_hosts(), relay_hosts(), relay_token(), instance_); 248} 249 250} // namespace remoting 251