pepper_port_allocator.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/string_number_conversions.h" 9#include "net/base/net_util.h" 10#include "ppapi/c/pp_errors.h" 11#include "ppapi/cpp/completion_callback.h" 12#include "ppapi/cpp/private/host_resolver_private.h" 13#include "ppapi/cpp/url_loader.h" 14#include "ppapi/cpp/url_request_info.h" 15#include "ppapi/cpp/url_response_info.h" 16#include "remoting/client/plugin/pepper_network_manager.h" 17#include "remoting/client/plugin/pepper_packet_socket_factory.h" 18#include "remoting/client/plugin/pepper_util.h" 19 20namespace remoting { 21 22namespace { 23 24// URL used to create a relay session. 25const char kCreateRelaySessionURL[] = "/create_session"; 26 27// Read buffer we allocate per read when reading response from 28// URLLoader. Normally the response from URL loader is smaller than 1kB. 29const int kReadSize = 1024; 30 31class PepperPortAllocatorSession 32 : public cricket::HttpPortAllocatorSessionBase { 33 public: 34 PepperPortAllocatorSession( 35 cricket::HttpPortAllocatorBase* allocator, 36 const std::string& content_name, 37 int component, 38 const std::string& ice_username_fragment, 39 const std::string& ice_password, 40 const std::vector<talk_base::SocketAddress>& stun_hosts, 41 const std::vector<std::string>& relay_hosts, 42 const std::string& relay_token, 43 const pp::InstanceHandle& instance); 44 virtual ~PepperPortAllocatorSession(); 45 46 // cricket::HttpPortAllocatorBase overrides. 47 virtual void ConfigReady(cricket::PortConfiguration* config) OVERRIDE; 48 virtual void GetPortConfigurations() OVERRIDE; 49 virtual void SendSessionRequest(const std::string& host, int port) OVERRIDE; 50 51 private: 52 void ResolveStunServerAddress(); 53 void OnStunAddressResolved(int32_t result); 54 55 void OnUrlOpened(int32_t result); 56 void ReadResponseBody(); 57 void OnResponseBodyRead(int32_t result); 58 59 pp::InstanceHandle instance_; 60 61 pp::HostResolverPrivate stun_address_resolver_; 62 talk_base::SocketAddress stun_address_; 63 int stun_port_; 64 65 scoped_ptr<pp::URLLoader> relay_url_loader_; 66 std::vector<char> relay_response_body_; 67 bool relay_response_received_; 68 69 // Used to safely cancel completion callbacks from PPAPI calls. 70 base::WeakPtrFactory<PepperPortAllocatorSession> weak_factory_; 71 72 DISALLOW_COPY_AND_ASSIGN(PepperPortAllocatorSession); 73}; 74 75PepperPortAllocatorSession::PepperPortAllocatorSession( 76 cricket::HttpPortAllocatorBase* allocator, 77 const std::string& content_name, 78 int component, 79 const std::string& ice_username_fragment, 80 const std::string& ice_password, 81 const std::vector<talk_base::SocketAddress>& stun_hosts, 82 const std::vector<std::string>& relay_hosts, 83 const std::string& relay_token, 84 const pp::InstanceHandle& instance) 85 : HttpPortAllocatorSessionBase(allocator, 86 content_name, 87 component, 88 ice_username_fragment, 89 ice_password, 90 stun_hosts, 91 relay_hosts, 92 relay_token, 93 std::string()), 94 instance_(instance), 95 stun_address_resolver_(instance_), 96 stun_port_(0), 97 relay_response_received_(false), 98 weak_factory_(this) { 99 if (stun_hosts.size() > 0) { 100 stun_address_ = stun_hosts[0]; 101 } 102} 103 104PepperPortAllocatorSession::~PepperPortAllocatorSession() { 105} 106 107void PepperPortAllocatorSession::ConfigReady( 108 cricket::PortConfiguration* config) { 109 if (config->stun_address.IsUnresolved()) { 110 // Make sure that the address that we pass to ConfigReady() is 111 // always resolved. 112 if (stun_address_.IsUnresolved()) { 113 config->stun_address.Clear(); 114 } else { 115 config->stun_address = stun_address_; 116 } 117 } 118 119 // Filter out non-UDP relay ports, so that we don't try using TCP. 120 for (cricket::PortConfiguration::RelayList::iterator relay = 121 config->relays.begin(); relay != config->relays.end(); ++relay) { 122 cricket::PortList filtered_ports; 123 for (cricket::PortList::iterator port = 124 relay->ports.begin(); port != relay->ports.end(); ++port) { 125 if (port->proto == cricket::PROTO_UDP) { 126 filtered_ports.push_back(*port); 127 } 128 } 129 relay->ports = filtered_ports; 130 } 131 cricket::BasicPortAllocatorSession::ConfigReady(config); 132} 133 134void PepperPortAllocatorSession::GetPortConfigurations() { 135 // Add an empty configuration synchronously, so a local connection 136 // can be started immediately. 137 ConfigReady(new cricket::PortConfiguration( 138 talk_base::SocketAddress(), std::string(), std::string())); 139 140 ResolveStunServerAddress(); 141 TryCreateRelaySession(); 142} 143 144void PepperPortAllocatorSession::ResolveStunServerAddress() { 145 if (stun_address_.IsNil()) { 146 return; 147 } 148 149 if (!stun_address_.IsUnresolved()) { 150 return; 151 } 152 153 std::string hostname = stun_address_.hostname(); 154 uint16 port = stun_address_.port(); 155 156 PP_HostResolver_Private_Hint hint; 157 hint.flags = 0; 158 hint.family = PP_NETADDRESSFAMILY_IPV4; 159 int result = stun_address_resolver_.Resolve( 160 hostname, port, hint, 161 PpCompletionCallback(base::Bind( 162 &PepperPortAllocatorSession::OnStunAddressResolved, 163 weak_factory_.GetWeakPtr()))); 164 165 DCHECK_EQ(result, PP_OK_COMPLETIONPENDING); 166} 167 168void PepperPortAllocatorSession::OnStunAddressResolved(int32_t result) { 169 if (result < 0) { 170 LOG(ERROR) << "Failed to resolve stun address " 171 << stun_address_.hostname() << ": " << result; 172 return; 173 } 174 175 if (!stun_address_resolver_.GetSize()) { 176 LOG(WARNING) << "Received 0 addresses for stun server " 177 << stun_address_.hostname(); 178 return; 179 } 180 181 PP_NetAddress_Private address; 182 if (!stun_address_resolver_.GetNetAddress(0, &address) || 183 !PpAddressToSocketAddress(address, &stun_address_)) { 184 LOG(ERROR) << "Failed to get address for STUN server " 185 << stun_address_.hostname(); 186 return; 187 } 188 189 DCHECK(!stun_address_.IsUnresolved()); 190 191 if (relay_response_received_) { 192 // If we've finished reading the response, then resubmit it to 193 // HttpPortAllocatorSessionBase. This is necessary because STUN 194 // and Relay parameters are stored together in PortConfiguration 195 // and ReceiveSessionResponse() doesn't save relay session 196 // configuration for the case we resolve STUN address later. This 197 // method invokes overriden ConfigReady() which then submits 198 // resolved |stun_address_|. 199 // 200 // TODO(sergeyu): Refactor HttpPortAllocatorSessionBase to fix this. 201 ReceiveSessionResponse(std::string(relay_response_body_.begin(), 202 relay_response_body_.end())); 203 } else { 204 ConfigReady(new cricket::PortConfiguration( 205 stun_address_, std::string(), std::string())); 206 } 207} 208 209void PepperPortAllocatorSession::SendSessionRequest( 210 const std::string& host, 211 int port) { 212 relay_url_loader_.reset(new pp::URLLoader(instance_)); 213 pp::URLRequestInfo request_info(instance_); 214 std::string url = "https://" + host + ":" + base::IntToString(port) + 215 GetSessionRequestUrl() + "&sn=1"; 216 request_info.SetURL(url); 217 request_info.SetMethod("GET"); 218 std::stringstream headers; 219 headers << "X-Talk-Google-Relay-Auth: " << relay_token() << "\n\r"; 220 headers << "X-Google-Relay-Auth: " << relay_token() << "\n\r"; 221 headers << "X-Stream-Type: " << "chromoting" << "\n\r"; 222 request_info.SetHeaders(headers.str()); 223 224 int result = relay_url_loader_->Open( 225 request_info, PpCompletionCallback(base::Bind( 226 &PepperPortAllocatorSession::OnUrlOpened, 227 weak_factory_.GetWeakPtr()))); 228 229 DCHECK_EQ(result, PP_OK_COMPLETIONPENDING); 230} 231 232void PepperPortAllocatorSession::OnUrlOpened(int32_t result) { 233 if (result == PP_ERROR_ABORTED) { 234 return; 235 } 236 237 if (result < 0) { 238 LOG(WARNING) << "URLLoader failed: " << result; 239 // Retry creating session. 240 TryCreateRelaySession(); 241 return; 242 } 243 244 pp::URLResponseInfo response = relay_url_loader_->GetResponseInfo(); 245 DCHECK(!response.is_null()); 246 if (response.GetStatusCode() != 200) { 247 LOG(WARNING) << "Received HTTP status code " << response.GetStatusCode(); 248 // Retry creating session. 249 TryCreateRelaySession(); 250 return; 251 } 252 253 relay_response_body_.clear(); 254 ReadResponseBody(); 255} 256 257void PepperPortAllocatorSession::ReadResponseBody() { 258 int pos = relay_response_body_.size(); 259 relay_response_body_.resize(pos + kReadSize); 260 int result = relay_url_loader_->ReadResponseBody( 261 &relay_response_body_[pos], kReadSize, 262 PpCompletionCallback(base::Bind( 263 &PepperPortAllocatorSession::OnResponseBodyRead, 264 weak_factory_.GetWeakPtr()))); 265 DCHECK_EQ(result, PP_OK_COMPLETIONPENDING); 266} 267 268void PepperPortAllocatorSession::OnResponseBodyRead(int32_t result) { 269 if (result == PP_ERROR_ABORTED) { 270 return; 271 } 272 273 if (result < 0) { 274 LOG(WARNING) << "Failed to read HTTP response body when " 275 "creating relay session: " << result; 276 // Retry creating session. 277 TryCreateRelaySession(); 278 return; 279 } 280 281 // Resize the buffer in case we've read less than was requested. 282 CHECK_LE(result, kReadSize); 283 CHECK_GE(static_cast<int>(relay_response_body_.size()), kReadSize); 284 relay_response_body_.resize(relay_response_body_.size() - kReadSize + result); 285 286 if (result == 0) { 287 relay_response_received_ = true; 288 ReceiveSessionResponse(std::string(relay_response_body_.begin(), 289 relay_response_body_.end())); 290 return; 291 } 292 293 ReadResponseBody(); 294} 295 296} // namespace 297 298// static 299scoped_ptr<PepperPortAllocator> PepperPortAllocator::Create( 300 const pp::InstanceHandle& instance) { 301 scoped_ptr<talk_base::NetworkManager> network_manager( 302 new PepperNetworkManager(instance)); 303 scoped_ptr<talk_base::PacketSocketFactory> socket_factory( 304 new PepperPacketSocketFactory(instance)); 305 scoped_ptr<PepperPortAllocator> result(new PepperPortAllocator( 306 instance, network_manager.Pass(), socket_factory.Pass())); 307 return result.Pass(); 308} 309 310PepperPortAllocator::PepperPortAllocator( 311 const pp::InstanceHandle& instance, 312 scoped_ptr<talk_base::NetworkManager> network_manager, 313 scoped_ptr<talk_base::PacketSocketFactory> socket_factory) 314 : HttpPortAllocatorBase(network_manager.get(), 315 socket_factory.get(), 316 std::string()), 317 instance_(instance), 318 network_manager_(network_manager.Pass()), 319 socket_factory_(socket_factory.Pass()) { 320 // TCP transport is disabled becase PseudoTCP works poorly over 321 // it. ENABLE_SHARED_UFRAG flag is specified so that the same 322 // username fragment is shared between all candidates for this 323 // channel. 324 set_flags(cricket::PORTALLOCATOR_DISABLE_TCP | 325 cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG); 326} 327 328PepperPortAllocator::~PepperPortAllocator() { 329} 330 331cricket::PortAllocatorSession* PepperPortAllocator::CreateSessionInternal( 332 const std::string& content_name, 333 int component, 334 const std::string& ice_username_fragment, 335 const std::string& ice_password) { 336 return new PepperPortAllocatorSession( 337 this, content_name, component, ice_username_fragment, ice_password, 338 stun_hosts(), relay_hosts(), relay_token(), instance_); 339} 340 341} // namespace remoting 342