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