Home | History | Annotate | Download | only in client
      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/common.h"
     17 #include "webrtc/base/helpers.h"
     18 #include "webrtc/base/httpcommon.h"
     19 #include "webrtc/base/logging.h"
     20 #include "webrtc/base/nethelpers.h"
     21 #include "webrtc/base/signalthread.h"
     22 #include "webrtc/base/stringencode.h"
     23 
     24 namespace {
     25 
     26 // Helper routine to remove whitespace from the ends of a string.
     27 void Trim(std::string& str) {
     28   size_t first = str.find_first_not_of(" \t\r\n");
     29   if (first == std::string::npos) {
     30     str.clear();
     31     return;
     32   }
     33 
     34   ASSERT(str.find_last_not_of(" \t\r\n") != std::string::npos);
     35 }
     36 
     37 // Parses the lines in the result of the HTTP request that are of the form
     38 // 'a=b' and returns them in a map.
     39 typedef std::map<std::string, std::string> StringMap;
     40 void ParseMap(const std::string& string, StringMap& map) {
     41   size_t start_of_line = 0;
     42   size_t end_of_line = 0;
     43 
     44   for (;;) {  // for each line
     45     start_of_line = string.find_first_not_of("\r\n", end_of_line);
     46     if (start_of_line == std::string::npos)
     47       break;
     48 
     49     end_of_line = string.find_first_of("\r\n", start_of_line);
     50     if (end_of_line == std::string::npos) {
     51       end_of_line = string.length();
     52     }
     53 
     54     size_t equals = string.find('=', start_of_line);
     55     if ((equals >= end_of_line) || (equals == std::string::npos))
     56       continue;
     57 
     58     std::string key(string, start_of_line, equals - start_of_line);
     59     std::string value(string, equals + 1, end_of_line - equals - 1);
     60 
     61     Trim(key);
     62     Trim(value);
     63 
     64     if ((key.size() > 0) && (value.size() > 0))
     65       map[key] = value;
     66   }
     67 }
     68 
     69 }  // namespace
     70 
     71 namespace cricket {
     72 
     73 // HttpPortAllocatorBase
     74 
     75 const int HttpPortAllocatorBase::kNumRetries = 5;
     76 
     77 const char HttpPortAllocatorBase::kCreateSessionURL[] = "/create_session";
     78 
     79 HttpPortAllocatorBase::HttpPortAllocatorBase(
     80     rtc::NetworkManager* network_manager,
     81     rtc::PacketSocketFactory* socket_factory,
     82     const std::string &user_agent)
     83     : BasicPortAllocator(network_manager, socket_factory), agent_(user_agent) {
     84   relay_hosts_.push_back("relay.google.com");
     85   stun_hosts_.push_back(
     86       rtc::SocketAddress("stun.l.google.com", 19302));
     87 }
     88 
     89 HttpPortAllocatorBase::HttpPortAllocatorBase(
     90     rtc::NetworkManager* network_manager,
     91     const std::string &user_agent)
     92     : BasicPortAllocator(network_manager), agent_(user_agent) {
     93   relay_hosts_.push_back("relay.google.com");
     94   stun_hosts_.push_back(
     95       rtc::SocketAddress("stun.l.google.com", 19302));
     96 }
     97 
     98 HttpPortAllocatorBase::~HttpPortAllocatorBase() {
     99 }
    100 
    101 // HttpPortAllocatorSessionBase
    102 
    103 HttpPortAllocatorSessionBase::HttpPortAllocatorSessionBase(
    104     HttpPortAllocatorBase* allocator,
    105     const std::string& content_name,
    106     int component,
    107     const std::string& ice_ufrag,
    108     const std::string& ice_pwd,
    109     const std::vector<rtc::SocketAddress>& stun_hosts,
    110     const std::vector<std::string>& relay_hosts,
    111     const std::string& relay_token,
    112     const std::string& user_agent)
    113     : BasicPortAllocatorSession(allocator, content_name, component,
    114                                 ice_ufrag, ice_pwd),
    115       relay_hosts_(relay_hosts), stun_hosts_(stun_hosts),
    116       relay_token_(relay_token), agent_(user_agent), attempts_(0) {
    117 }
    118 
    119 HttpPortAllocatorSessionBase::~HttpPortAllocatorSessionBase() {}
    120 
    121 void HttpPortAllocatorSessionBase::GetPortConfigurations() {
    122   // Creating relay sessions can take time and is done asynchronously.
    123   // Creating stun sessions could also take time and could be done aysnc also,
    124   // but for now is done here and added to the initial config.  Note any later
    125   // configs will have unresolved stun ips and will be discarded by the
    126   // AllocationSequence.
    127   ServerAddresses hosts;
    128   for (std::vector<rtc::SocketAddress>::iterator it = stun_hosts_.begin();
    129       it != stun_hosts_.end(); ++it) {
    130     hosts.insert(*it);
    131   }
    132 
    133   PortConfiguration* config = new PortConfiguration(hosts,
    134                                                     username(),
    135                                                     password());
    136   ConfigReady(config);
    137   TryCreateRelaySession();
    138 }
    139 
    140 void HttpPortAllocatorSessionBase::TryCreateRelaySession() {
    141   if (allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY) {
    142     LOG(LS_VERBOSE) << "HttpPortAllocator: Relay ports disabled, skipping.";
    143     return;
    144   }
    145 
    146   if (attempts_ == HttpPortAllocatorBase::kNumRetries) {
    147     LOG(LS_ERROR) << "HttpPortAllocator: maximum number of requests reached; "
    148                   << "giving up on relay.";
    149     return;
    150   }
    151 
    152   if (relay_hosts_.size() == 0) {
    153     LOG(LS_ERROR) << "HttpPortAllocator: no relay hosts configured.";
    154     return;
    155   }
    156 
    157   // Choose the next host to try.
    158   std::string host = relay_hosts_[attempts_ % relay_hosts_.size()];
    159   attempts_++;
    160   LOG(LS_INFO) << "HTTPPortAllocator: sending to relay host " << host;
    161   if (relay_token_.empty()) {
    162     LOG(LS_WARNING) << "No relay auth token found.";
    163   }
    164 
    165   SendSessionRequest(host, rtc::HTTP_SECURE_PORT);
    166 }
    167 
    168 std::string HttpPortAllocatorSessionBase::GetSessionRequestUrl() {
    169   std::string url = std::string(HttpPortAllocatorBase::kCreateSessionURL);
    170   ASSERT(!username().empty());
    171   ASSERT(!password().empty());
    172   url = url + "?username=" + rtc::s_url_encode(username()) +
    173       "&password=" + rtc::s_url_encode(password());
    174   return url;
    175 }
    176 
    177 void HttpPortAllocatorSessionBase::ReceiveSessionResponse(
    178     const std::string& response) {
    179 
    180   StringMap map;
    181   ParseMap(response, map);
    182 
    183   if (!username().empty() && map["username"] != username()) {
    184     LOG(LS_WARNING) << "Received unexpected username value from relay server.";
    185   }
    186   if (!password().empty() && map["password"] != password()) {
    187     LOG(LS_WARNING) << "Received unexpected password value from relay server.";
    188   }
    189 
    190   std::string relay_ip = map["relay.ip"];
    191   std::string relay_udp_port = map["relay.udp_port"];
    192   std::string relay_tcp_port = map["relay.tcp_port"];
    193   std::string relay_ssltcp_port = map["relay.ssltcp_port"];
    194 
    195   ServerAddresses hosts;
    196   for (std::vector<rtc::SocketAddress>::iterator it = stun_hosts_.begin();
    197       it != stun_hosts_.end(); ++it) {
    198     hosts.insert(*it);
    199   }
    200 
    201   PortConfiguration* config = new PortConfiguration(hosts,
    202                                                     map["username"],
    203                                                     map["password"]);
    204 
    205   RelayServerConfig relay_config(RELAY_GTURN);
    206   if (!relay_udp_port.empty()) {
    207     rtc::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str()));
    208     relay_config.ports.push_back(ProtocolAddress(address, PROTO_UDP));
    209   }
    210   if (!relay_tcp_port.empty()) {
    211     rtc::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str()));
    212     relay_config.ports.push_back(ProtocolAddress(address, PROTO_TCP));
    213   }
    214   if (!relay_ssltcp_port.empty()) {
    215     rtc::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str()));
    216     relay_config.ports.push_back(ProtocolAddress(address, PROTO_SSLTCP));
    217   }
    218   config->AddRelay(relay_config);
    219   ConfigReady(config);
    220 }
    221 
    222 }  // namespace cricket
    223