1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
#if defined(_MSC_VER) && _MSC_VER < 1300
#pragma warning(disable:4786)
#endif
#include "talk/base/asynchttprequest.h"
#include "talk/base/basicdefs.h"
#include "talk/base/common.h"
#include "talk/base/helpers.h"
#include "talk/base/logging.h"
#include "talk/base/signalthread.h"
#include "talk/p2p/client/httpportallocator.h"
#include <cassert>
#include <ctime>
namespace {
// Records the port on the hosts that will receive HTTP requests.
const uint16 kHostPort = 80;
// Records the URL that we will GET in order to create a session.
const std::string kCreateSessionURL = "/create_session";
// The number of HTTP requests we should attempt before giving up.
const size_t kNumRetries = 5;
// The delay before we give up on an HTTP request;
const int TIMEOUT = 5 * 1000; // 5 seconds
const uint32 MSG_TIMEOUT = 100; // must not conflict with BasicPortAllocator.cpp
// Helper routine to remove whitespace from the ends of a string.
void Trim(std::string& str) {
size_t first = str.find_first_not_of(" \t\r\n");
if (first == std::string::npos) {
str.clear();
return;
}
size_t last = str.find_last_not_of(" \t\r\n");
ASSERT(last != std::string::npos);
}
// Parses the lines in the result of the HTTP request that are of the form
// 'a=b' and returns them in a map.
typedef std::map<std::string,std::string> StringMap;
void ParseMap(const std::string& string, StringMap& map) {
size_t start_of_line = 0;
size_t end_of_line = 0;
for (;;) { // for each line
start_of_line = string.find_first_not_of("\r\n", end_of_line);
if (start_of_line == std::string::npos)
break;
end_of_line = string.find_first_of("\r\n", start_of_line);
if (end_of_line == std::string::npos) {
end_of_line = string.length();
}
size_t equals = string.find('=', start_of_line);
if ((equals >= end_of_line) || (equals == std::string::npos))
continue;
std::string key(string, start_of_line, equals - start_of_line);
std::string value(string, equals + 1, end_of_line - equals - 1);
Trim(key);
Trim(value);
if ((key.size() > 0) && (value.size() > 0))
map[key] = value;
}
}
}
namespace cricket {
// HttpPortAllocator
HttpPortAllocator::HttpPortAllocator(talk_base::NetworkManager* network_manager, const std::string &user_agent)
: BasicPortAllocator(network_manager), agent_(user_agent) {
relay_hosts_.push_back("relay.l.google.com");
stun_hosts_.push_back(talk_base::SocketAddress("stun.l.google.com",19302));
}
HttpPortAllocator::~HttpPortAllocator() {
}
PortAllocatorSession *HttpPortAllocator::CreateSession(const std::string &name, const std::string &session_type) {
return new HttpPortAllocatorSession(this, name, session_type, stun_hosts_, relay_hosts_, relay_token_, agent_);
}
// HttpPortAllocatorSession
HttpPortAllocatorSession::HttpPortAllocatorSession(HttpPortAllocator* allocator, const std::string &name,
const std::string &session_type,
const std::vector<talk_base::SocketAddress> &stun_hosts,
const std::vector<std::string> &relay_hosts,
const std::string &relay_token,
const std::string &user_agent)
: BasicPortAllocatorSession(allocator, name, session_type),
attempts_(0), relay_hosts_(relay_hosts), stun_hosts_(stun_hosts), relay_token_(relay_token), agent_(user_agent) {
}
void HttpPortAllocatorSession::GetPortConfigurations() {
if (attempts_ == kNumRetries) {
LOG(WARNING) << "HttpPortAllocator: maximum number of requests reached";
return;
}
if (relay_hosts_.size() <= 0) {
LOG(WARNING) << "HttpPortAllocator: no relay hosts found";
return;
}
// Choose the next host to try.
std::string host = relay_hosts_[attempts_ % relay_hosts_.size()];
attempts_++;
LOG(INFO) << "HTTPPortAllocator: sending to host " << host;
// Initiate an HTTP request to create a session through the chosen host.
talk_base::AsyncHttpRequest* request = new talk_base::AsyncHttpRequest(agent_);
request->SignalWorkDone.connect(this, &HttpPortAllocatorSession::OnRequestDone);
request->set_proxy(allocator()->proxy());
request->response().document.reset(new talk_base::MemoryStream);
request->request().verb = talk_base::HV_GET;
request->request().path = kCreateSessionURL;
request->request().addHeader("X-Talk-Google-Relay-Auth", relay_token_, true);
request->request().addHeader("X-Google-Relay-Auth", relay_token_, true);
request->request().addHeader("X-Session-Type", session_type(), true);
request->set_host(host);
request->set_port(kHostPort);
request->Start();
request->Release();
}
void HttpPortAllocatorSession::OnRequestDone(talk_base::SignalThread* data) {
talk_base::AsyncHttpRequest *request =
static_cast<talk_base::AsyncHttpRequest*> (data);
if (request->response().scode != 200) {
LOG(WARNING) << "HTTPPortAllocator: request "
<< " received error " << request->response().scode;
GetPortConfigurations();
return;
}
LOG(INFO) << "HTTPPortAllocator: request succeeded";
StringMap map;
talk_base::MemoryStream *stream = static_cast<talk_base::MemoryStream*>(request->response().document.get());
stream->Rewind();
size_t length;
stream->GetSize(&length);
std::string resp = std::string(stream->GetBuffer(), length);
ParseMap(resp, map);
std::string username = map["username"];
std::string password = map["password"];
std::string magic_cookie = map["magic_cookie"];
std::string relay_ip = map["relay.ip"];
std::string relay_udp_port = map["relay.udp_port"];
std::string relay_tcp_port = map["relay.tcp_port"];
std::string relay_ssltcp_port = map["relay.ssltcp_port"];
PortConfiguration* config = new PortConfiguration(stun_hosts_[0],
username,
password,
magic_cookie);
PortConfiguration::PortList ports;
if (!relay_udp_port.empty()) {
talk_base::SocketAddress address(relay_ip, atoi(relay_udp_port.c_str()));
ports.push_back(ProtocolAddress(address, PROTO_UDP));
}
if (!relay_tcp_port.empty()) {
talk_base::SocketAddress address(relay_ip, atoi(relay_tcp_port.c_str()));
ports.push_back(ProtocolAddress(address, PROTO_TCP));
}
if (!relay_ssltcp_port.empty()) {
talk_base::SocketAddress address(relay_ip, atoi(relay_ssltcp_port.c_str()));
ports.push_back(ProtocolAddress(address, PROTO_SSLTCP));
}
config->AddRelay(ports, 0.0f);
ConfigReady(config);
}
} // namespace cricket
|