3
Copyright (c) 2007, Arvid Norberg
6
Redistribution and use in source and binary forms, with or without
7
modification, are permitted provided that the following conditions
10
* Redistributions of source code must retain the above copyright
11
notice, this list of conditions and the following disclaimer.
12
* Redistributions in binary form must reproduce the above copyright
13
notice, this list of conditions and the following disclaimer in
14
the documentation and/or other materials provided with the distribution.
15
* Neither the name of the author nor the names of its
16
contributors may be used to endorse or promote products derived
17
from this software without specific prior written permission.
19
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
POSSIBILITY OF SUCH DAMAGE.
33
#include "libtorrent/http_connection.hpp"
35
#include <boost/bind.hpp>
36
#include <boost/lexical_cast.hpp>
37
#include <asio/ip/tcp.hpp>
45
enum { max_bottled_buffer = 1024 * 1024 };
47
void http_connection::get(std::string const& url, time_duration timeout
48
, int handle_redirects)
55
boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url);
56
std::stringstream headers;
57
headers << "GET " << path << " HTTP/1.0\r\n"
58
"Host:" << hostname <<
59
"\r\nConnection: close\r\n";
61
headers << "Authorization: Basic " << base64encode(auth) << "\r\n";
63
sendbuffer = headers.str();
65
start(hostname, boost::lexical_cast<std::string>(port), timeout, handle_redirects);
68
void http_connection::start(std::string const& hostname, std::string const& port
69
, time_duration timeout, int handle_redirects)
71
m_redirects = handle_redirects;
73
m_timer.expires_from_now(m_timeout);
74
m_timer.async_wait(bind(&http_connection::on_timeout
75
, boost::weak_ptr<http_connection>(shared_from_this()), _1));
80
if (m_sock.is_open() && m_hostname == hostname && m_port == port)
82
asio::async_write(m_sock, asio::buffer(sendbuffer)
83
, bind(&http_connection::on_write, shared_from_this(), _1));
88
tcp::resolver::query query(hostname, port);
89
m_resolver.async_resolve(query, bind(&http_connection::on_resolve
90
, shared_from_this(), _1, _2));
91
m_hostname = hostname;
96
void http_connection::on_connect_timeout()
98
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
99
m_connection_ticket = -1;
101
callback(asio::error::timed_out);
105
void http_connection::on_timeout(boost::weak_ptr<http_connection> p
106
, asio::error_code const& e)
108
boost::shared_ptr<http_connection> c = p.lock();
110
if (c->m_connection_ticket > -1) c->m_cc.done(c->m_connection_ticket);
111
c->m_connection_ticket = -1;
113
if (e == asio::error::operation_aborted) return;
115
if (c->m_last_receive + c->m_timeout < time_now())
117
c->callback(asio::error::timed_out);
122
if (!c->m_sock.is_open()) return;
124
c->m_timer.expires_at(c->m_last_receive + c->m_timeout);
125
c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
128
void http_connection::close()
131
m_limiter_timer.cancel();
136
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
137
m_connection_ticket = -1;
142
void http_connection::on_resolve(asio::error_code const& e
143
, tcp::resolver::iterator i)
151
TORRENT_ASSERT(i != tcp::resolver::iterator());
152
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
153
, bind(&http_connection::on_connect_timeout, shared_from_this())
157
void http_connection::connect(int ticket, tcp::endpoint target_address)
159
m_connection_ticket = ticket;
160
m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect
161
, shared_from_this(), _1/*, ++i*/));
164
void http_connection::on_connect(asio::error_code const& e
165
/*, tcp::resolver::iterator i*/)
169
m_last_receive = time_now();
170
if (m_connect_handler) m_connect_handler(*this);
171
asio::async_write(m_sock, asio::buffer(sendbuffer)
172
, bind(&http_connection::on_write, shared_from_this(), _1));
174
/* else if (i != tcp::resolver::iterator())
176
// The connection failed. Try the next endpoint in the list.
178
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i)
179
, bind(&http_connection::on_connect_timeout, shared_from_this())
189
void http_connection::callback(asio::error_code const& e, char const* data, int size)
191
if (!m_bottled || !m_called)
194
if (m_handler) m_handler(e, m_parser, data, size, *this);
198
void http_connection::on_write(asio::error_code const& e)
207
std::string().swap(sendbuffer);
208
m_recvbuffer.resize(4096);
210
int amount_to_read = m_recvbuffer.size() - m_read_pos;
211
if (m_rate_limit > 0 && amount_to_read > m_download_quota)
213
amount_to_read = m_download_quota;
214
if (m_download_quota == 0)
216
if (!m_limiter_timer_active)
217
on_assign_bandwidth(asio::error_code());
221
m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
223
, bind(&http_connection::on_read
224
, shared_from_this(), _1, _2));
227
void http_connection::on_read(asio::error_code const& e
228
, std::size_t bytes_transferred)
232
m_download_quota -= bytes_transferred;
233
TORRENT_ASSERT(m_download_quota >= 0);
236
if (e == asio::error::eof)
238
TORRENT_ASSERT(bytes_transferred == 0);
239
char const* data = 0;
240
std::size_t size = 0;
241
if (m_bottled && m_parser.header_finished())
243
data = m_parser.get_body().begin;
244
size = m_parser.get_body().left();
246
callback(e, data, size);
253
TORRENT_ASSERT(bytes_transferred == 0);
259
m_read_pos += bytes_transferred;
260
TORRENT_ASSERT(m_read_pos <= int(m_recvbuffer.size()));
262
if (m_bottled || !m_parser.header_finished())
264
libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
265
, &m_recvbuffer[0] + m_read_pos);
268
m_parser.incoming(rcv_buf);
270
catch (std::exception&)
273
callback(asio::error::fault, 0, 0);
278
// having a nonempty path means we should handle redirects
279
if (m_redirects && m_parser.header_finished())
281
int code = m_parser.status_code();
283
if (code >= 300 && code < 400)
285
// attempt a redirect
286
std::string const& location = m_parser.header("location");
287
if (location.empty())
289
// missing location header
290
callback(asio::error::fault);
299
get(location, m_timeout, m_redirects - 1);
302
catch (std::exception& e)
304
// some broken web servers send out relative paths
305
// in the location header.
306
std::string url = m_url;
307
// remove the leaf filename
308
std::size_t i = url.find_last_of('/');
309
if (i == std::string::npos)
321
get(url, m_timeout, m_redirects - 1);
323
catch (std::exception& e)
325
// location header is invalid
326
callback(asio::error::fault);
336
if (!m_bottled && m_parser.header_finished())
338
if (m_read_pos > m_parser.body_start())
339
callback(e, &m_recvbuffer[0] + m_parser.body_start()
340
, m_read_pos - m_parser.body_start());
342
m_last_receive = time_now();
344
else if (m_bottled && m_parser.finished())
347
callback(e, m_parser.get_body().begin, m_parser.get_body().left());
352
TORRENT_ASSERT(!m_bottled);
353
callback(e, &m_recvbuffer[0], m_read_pos);
355
m_last_receive = time_now();
358
if (int(m_recvbuffer.size()) == m_read_pos)
359
m_recvbuffer.resize((std::min)(m_read_pos + 2048, int(max_bottled_buffer)));
360
if (m_read_pos == max_bottled_buffer)
362
callback(asio::error::eof);
366
int amount_to_read = m_recvbuffer.size() - m_read_pos;
367
if (m_rate_limit > 0 && amount_to_read > m_download_quota)
369
amount_to_read = m_download_quota;
370
if (m_download_quota == 0)
372
if (!m_limiter_timer_active)
373
on_assign_bandwidth(asio::error_code());
377
m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
379
, bind(&http_connection::on_read
380
, shared_from_this(), _1, _2));
383
void http_connection::on_assign_bandwidth(asio::error_code const& e)
385
if ((e == asio::error::operation_aborted
386
&& m_limiter_timer_active)
387
|| !m_sock.is_open())
389
callback(asio::error::eof);
392
m_limiter_timer_active = false;
395
if (m_download_quota > 0) return;
397
m_download_quota = m_rate_limit / 4;
399
int amount_to_read = m_recvbuffer.size() - m_read_pos;
400
if (amount_to_read > m_download_quota)
401
amount_to_read = m_download_quota;
403
if (!m_sock.is_open()) return;
405
m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
407
, bind(&http_connection::on_read
408
, shared_from_this(), _1, _2));
410
m_limiter_timer_active = true;
411
m_limiter_timer.expires_from_now(milliseconds(250));
412
m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
413
, shared_from_this(), _1));
416
void http_connection::rate_limit(int limit)
418
if (!m_sock.is_open()) return;
420
if (!m_limiter_timer_active)
422
m_limiter_timer_active = true;
423
m_limiter_timer.expires_from_now(milliseconds(250));
424
m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
425
, shared_from_this(), _1));
427
m_rate_limit = limit;