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.
5 #include "remoting/client/plugin/pepper_port_allocator.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"
23 // Read buffer we allocate per read when reading response from
24 // URLLoader. Normally the response from URL loader is smaller than 1kB.
25 const int kReadSize
= 1024;
27 class PepperPortAllocatorSession
28 : public cricket::HttpPortAllocatorSessionBase
{
30 PepperPortAllocatorSession(
31 cricket::HttpPortAllocatorBase
* allocator
,
32 const std::string
& content_name
,
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 ~PepperPortAllocatorSession() override
;
42 // cricket::HttpPortAllocatorBase overrides.
43 void ConfigReady(cricket::PortConfiguration
* config
) override
;
44 void GetPortConfigurations() override
;
45 void SendSessionRequest(const std::string
& host
, int port
) override
;
48 void OnUrlOpened(int32_t result
);
49 void ReadResponseBody();
50 void OnResponseBodyRead(int32_t result
);
52 pp::InstanceHandle instance_
;
54 cricket::ServerAddresses stun_hosts_
;
56 scoped_ptr
<pp::URLLoader
> relay_url_loader_
;
57 std::vector
<char> relay_response_body_
;
58 bool relay_response_received_
;
60 pp::CompletionCallbackFactory
<PepperPortAllocatorSession
> callback_factory_
;
62 DISALLOW_COPY_AND_ASSIGN(PepperPortAllocatorSession
);
65 PepperPortAllocatorSession::PepperPortAllocatorSession(
66 cricket::HttpPortAllocatorBase
* allocator
,
67 const std::string
& content_name
,
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
,
78 ice_username_fragment
,
85 stun_hosts_(stun_hosts
.begin(), stun_hosts
.end()),
86 relay_response_received_(false),
87 callback_factory_(this) {
90 PepperPortAllocatorSession::~PepperPortAllocatorSession() {
93 void 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
);
105 relay
->ports
= filtered_ports
;
107 cricket::BasicPortAllocatorSession::ConfigReady(config
);
110 void 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()));
116 TryCreateRelaySession();
119 void PepperPortAllocatorSession::SendSessionRequest(
120 const std::string
& host
,
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());
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
);
140 void PepperPortAllocatorSession::OnUrlOpened(int32_t result
) {
141 if (result
== PP_ERROR_ABORTED
) {
146 LOG(WARNING
) << "URLLoader failed: " << result
;
147 // Retry creating session.
148 TryCreateRelaySession();
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();
161 relay_response_body_
.clear();
165 void 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
],
173 DCHECK_EQ(result
, PP_OK_COMPLETIONPENDING
);
176 void PepperPortAllocatorSession::OnResponseBodyRead(int32_t result
) {
177 if (result
== PP_ERROR_ABORTED
) {
182 LOG(WARNING
) << "Failed to read HTTP response body when "
183 "creating relay session: " << result
;
184 // Retry creating session.
185 TryCreateRelaySession();
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
);
195 relay_response_received_
= true;
196 ReceiveSessionResponse(std::string(relay_response_body_
.begin(),
197 relay_response_body_
.end()));
207 scoped_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();
218 PepperPortAllocator::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(),
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
232 set_flags(cricket::PORTALLOCATOR_DISABLE_TCP
|
233 cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG
|
234 cricket::PORTALLOCATOR_ENABLE_IPV6
);
237 PepperPortAllocator::~PepperPortAllocator() {
240 cricket::PortAllocatorSession
* PepperPortAllocator::CreateSessionInternal(
241 const std::string
& content_name
,
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_
);
250 } // namespace remoting