Upstream version 11.40.271.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / p2p / client / httpportallocator.cc
1 /*
2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10
11 #include "webrtc/p2p/client/httpportallocator.h"
12
13 #include <algorithm>
14 #include <map>
15
16 #include "webrtc/base/asynchttprequest.h"
17 #include "webrtc/base/basicdefs.h"
18 #include "webrtc/base/common.h"
19 #include "webrtc/base/helpers.h"
20 #include "webrtc/base/logging.h"
21 #include "webrtc/base/nethelpers.h"
22 #include "webrtc/base/signalthread.h"
23 #include "webrtc/base/stringencode.h"
24
25 namespace {
26
27 // Helper routine to remove whitespace from the ends of a string.
28 void Trim(std::string& str) {
29   size_t first = str.find_first_not_of(" \t\r\n");
30   if (first == std::string::npos) {
31     str.clear();
32     return;
33   }
34
35   ASSERT(str.find_last_not_of(" \t\r\n") != std::string::npos);
36 }
37
38 // Parses the lines in the result of the HTTP request that are of the form
39 // 'a=b' and returns them in a map.
40 typedef std::map<std::string, std::string> StringMap;
41 void ParseMap(const std::string& string, StringMap& map) {
42   size_t start_of_line = 0;
43   size_t end_of_line = 0;
44
45   for (;;) {  // for each line
46     start_of_line = string.find_first_not_of("\r\n", end_of_line);
47     if (start_of_line == std::string::npos)
48       break;
49
50     end_of_line = string.find_first_of("\r\n", start_of_line);
51     if (end_of_line == std::string::npos) {
52       end_of_line = string.length();
53     }
54
55     size_t equals = string.find('=', start_of_line);
56     if ((equals >= end_of_line) || (equals == std::string::npos))
57       continue;
58
59     std::string key(string, start_of_line, equals - start_of_line);
60     std::string value(string, equals + 1, end_of_line - equals - 1);
61
62     Trim(key);
63     Trim(value);
64
65     if ((key.size() > 0) && (value.size() > 0))
66       map[key] = value;
67   }
68 }
69
70 }  // namespace
71
72 namespace cricket {
73
74 // HttpPortAllocatorBase
75
76 const int HttpPortAllocatorBase::kNumRetries = 5;
77
78 const char HttpPortAllocatorBase::kCreateSessionURL[] = "/create_session";
79
80 HttpPortAllocatorBase::HttpPortAllocatorBase(
81     rtc::NetworkManager* network_manager,
82     rtc::PacketSocketFactory* socket_factory,
83     const std::string &user_agent)
84     : BasicPortAllocator(network_manager, socket_factory), agent_(user_agent) {
85   relay_hosts_.push_back("relay.google.com");
86   stun_hosts_.push_back(
87       rtc::SocketAddress("stun.l.google.com", 19302));
88 }
89
90 HttpPortAllocatorBase::HttpPortAllocatorBase(
91     rtc::NetworkManager* network_manager,
92     const std::string &user_agent)
93     : BasicPortAllocator(network_manager), agent_(user_agent) {
94   relay_hosts_.push_back("relay.google.com");
95   stun_hosts_.push_back(
96       rtc::SocketAddress("stun.l.google.com", 19302));
97 }
98
99 HttpPortAllocatorBase::~HttpPortAllocatorBase() {
100 }
101
102 // HttpPortAllocatorSessionBase
103
104 HttpPortAllocatorSessionBase::HttpPortAllocatorSessionBase(
105     HttpPortAllocatorBase* allocator,
106     const std::string& content_name,
107     int component,
108     const std::string& ice_ufrag,
109     const std::string& ice_pwd,
110     const std::vector<rtc::SocketAddress>& stun_hosts,
111     const std::vector<std::string>& relay_hosts,
112     const std::string& relay_token,
113     const std::string& user_agent)
114     : BasicPortAllocatorSession(allocator, content_name, component,
115                                 ice_ufrag, ice_pwd),
116       relay_hosts_(relay_hosts), stun_hosts_(stun_hosts),
117       relay_token_(relay_token), agent_(user_agent), attempts_(0) {
118 }
119
120 HttpPortAllocatorSessionBase::~HttpPortAllocatorSessionBase() {}
121
122 void HttpPortAllocatorSessionBase::GetPortConfigurations() {
123   // Creating relay sessions can take time and is done asynchronously.
124   // Creating stun sessions could also take time and could be done aysnc also,
125   // but for now is done here and added to the initial config.  Note any later
126   // configs will have unresolved stun ips and will be discarded by the
127   // AllocationSequence.
128   ServerAddresses hosts;
129   for (std::vector<rtc::SocketAddress>::iterator it = stun_hosts_.begin();
130       it != stun_hosts_.end(); ++it) {
131     hosts.insert(*it);
132   }
133
134   PortConfiguration* config = new PortConfiguration(hosts,
135                                                     username(),
136                                                     password());
137   ConfigReady(config);
138   TryCreateRelaySession();
139 }
140
141 void HttpPortAllocatorSessionBase::TryCreateRelaySession() {
142   if (allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY) {
143     LOG(LS_VERBOSE) << "HttpPortAllocator: Relay ports disabled, skipping.";
144     return;
145   }
146
147   if (attempts_ == HttpPortAllocator::kNumRetries) {
148     LOG(LS_ERROR) << "HttpPortAllocator: maximum number of requests reached; "
149                   << "giving up on relay.";
150     return;
151   }
152
153   if (relay_hosts_.size() == 0) {
154     LOG(LS_ERROR) << "HttpPortAllocator: no relay hosts configured.";
155     return;
156   }
157
158   // Choose the next host to try.
159   std::string host = relay_hosts_[attempts_ % relay_hosts_.size()];
160   attempts_++;
161   LOG(LS_INFO) << "HTTPPortAllocator: sending to relay host " << host;
162   if (relay_token_.empty()) {
163     LOG(LS_WARNING) << "No relay auth token found.";
164   }
165
166   SendSessionRequest(host, rtc::HTTP_SECURE_PORT);
167 }
168
169 std::string HttpPortAllocatorSessionBase::GetSessionRequestUrl() {
170   std::string url = std::string(HttpPortAllocator::kCreateSessionURL);
171   if (allocator()->flags() & PORTALLOCATOR_ENABLE_SHARED_UFRAG) {
172     ASSERT(!username().empty());
173     ASSERT(!password().empty());
174     url = url + "?username=" + rtc::s_url_encode(username()) +
175         "&password=" + rtc::s_url_encode(password());
176   }
177   return url;
178 }
179
180 void HttpPortAllocatorSessionBase::ReceiveSessionResponse(
181     const std::string& response) {
182
183   StringMap map;
184   ParseMap(response, map);
185
186   if (!username().empty() && map["username"] != username()) {
187     LOG(LS_WARNING) << "Received unexpected username value from relay server.";
188   }
189   if (!password().empty() && map["password"] != password()) {
190     LOG(LS_WARNING) << "Received unexpected password value from relay server.";
191   }
192
193   std::string relay_ip = map["relay.ip"];
194   std::string relay_udp_port = map["relay.udp_port"];
195   std::string relay_tcp_port = map["relay.tcp_port"];
196   std::string relay_ssltcp_port = map["relay.ssltcp_port"];
197
198   ServerAddresses hosts;
199   for (std::vector<rtc::SocketAddress>::iterator it = stun_hosts_.begin();
200       it != stun_hosts_.end(); ++it) {
201     hosts.insert(*it);
202   }
203
204   PortConfiguration* config = new PortConfiguration(hosts,
205                                                     map["username"],
206                                                     map["password"]);
207
208   RelayServerConfig relay_config(RELAY_GTURN);
209   if (!relay_udp_port.empty()) {
210     rtc::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str()));
211     relay_config.ports.push_back(ProtocolAddress(address, PROTO_UDP));
212   }
213   if (!relay_tcp_port.empty()) {
214     rtc::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str()));
215     relay_config.ports.push_back(ProtocolAddress(address, PROTO_TCP));
216   }
217   if (!relay_ssltcp_port.empty()) {
218     rtc::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str()));
219     relay_config.ports.push_back(ProtocolAddress(address, PROTO_SSLTCP));
220   }
221   config->AddRelay(relay_config);
222   ConfigReady(config);
223 }
224
225 // HttpPortAllocator
226
227 HttpPortAllocator::HttpPortAllocator(
228     rtc::NetworkManager* network_manager,
229     rtc::PacketSocketFactory* socket_factory,
230     const std::string &user_agent)
231     : HttpPortAllocatorBase(network_manager, socket_factory, user_agent) {
232 }
233
234 HttpPortAllocator::HttpPortAllocator(
235     rtc::NetworkManager* network_manager,
236     const std::string &user_agent)
237     : HttpPortAllocatorBase(network_manager, user_agent) {
238 }
239 HttpPortAllocator::~HttpPortAllocator() {}
240
241 PortAllocatorSession* HttpPortAllocator::CreateSessionInternal(
242     const std::string& content_name,
243     int component,
244     const std::string& ice_ufrag, const std::string& ice_pwd) {
245   return new HttpPortAllocatorSession(this, content_name, component,
246                                       ice_ufrag, ice_pwd, stun_hosts(),
247                                       relay_hosts(), relay_token(),
248                                       user_agent());
249 }
250
251 // HttpPortAllocatorSession
252
253 HttpPortAllocatorSession::HttpPortAllocatorSession(
254     HttpPortAllocator* allocator,
255     const std::string& content_name,
256     int component,
257     const std::string& ice_ufrag,
258     const std::string& ice_pwd,
259     const std::vector<rtc::SocketAddress>& stun_hosts,
260     const std::vector<std::string>& relay_hosts,
261     const std::string& relay,
262     const std::string& agent)
263     : HttpPortAllocatorSessionBase(allocator, content_name, component,
264                                    ice_ufrag, ice_pwd, stun_hosts,
265                                    relay_hosts, relay, agent) {
266 }
267
268 HttpPortAllocatorSession::~HttpPortAllocatorSession() {
269   for (std::list<rtc::AsyncHttpRequest*>::iterator it = requests_.begin();
270        it != requests_.end(); ++it) {
271     (*it)->Destroy(true);
272   }
273 }
274
275 void HttpPortAllocatorSession::SendSessionRequest(const std::string& host,
276                                                   int port) {
277   // Initiate an HTTP request to create a session through the chosen host.
278   rtc::AsyncHttpRequest* request =
279       new rtc::AsyncHttpRequest(user_agent());
280   request->SignalWorkDone.connect(this,
281       &HttpPortAllocatorSession::OnRequestDone);
282
283   request->set_secure(port == rtc::HTTP_SECURE_PORT);
284   request->set_proxy(allocator()->proxy());
285   request->response().document.reset(new rtc::MemoryStream);
286   request->request().verb = rtc::HV_GET;
287   request->request().path = GetSessionRequestUrl();
288   request->request().addHeader("X-Talk-Google-Relay-Auth", relay_token(), true);
289   request->request().addHeader("X-Stream-Type", "video_rtp", true);
290   request->set_host(host);
291   request->set_port(port);
292   request->Start();
293   request->Release();
294
295   requests_.push_back(request);
296 }
297
298 void HttpPortAllocatorSession::OnRequestDone(rtc::SignalThread* data) {
299   rtc::AsyncHttpRequest* request =
300       static_cast<rtc::AsyncHttpRequest*>(data);
301
302   // Remove the request from the list of active requests.
303   std::list<rtc::AsyncHttpRequest*>::iterator it =
304       std::find(requests_.begin(), requests_.end(), request);
305   if (it != requests_.end()) {
306     requests_.erase(it);
307   }
308
309   if (request->response().scode != 200) {
310     LOG(LS_WARNING) << "HTTPPortAllocator: request "
311                     << " received error " << request->response().scode;
312     TryCreateRelaySession();
313     return;
314   }
315   LOG(LS_INFO) << "HTTPPortAllocator: request succeeded";
316
317   rtc::MemoryStream* stream =
318       static_cast<rtc::MemoryStream*>(request->response().document.get());
319   stream->Rewind();
320   size_t length;
321   stream->GetSize(&length);
322   std::string resp = std::string(stream->GetBuffer(), length);
323   ReceiveSessionResponse(resp);
324 }
325
326 }  // namespace cricket