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