2007-03-27 09:04:31 +02:00
|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (c) 2007, Arvid Norberg
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2007-04-05 00:27:36 +02:00
|
|
|
#include "libtorrent/http_connection.hpp"
|
2007-12-02 19:34:37 +01:00
|
|
|
#include "libtorrent/escape_string.hpp"
|
2008-01-27 23:39:50 +01:00
|
|
|
#include "libtorrent/instantiate_connection.hpp"
|
2008-01-30 19:32:13 +01:00
|
|
|
#include "libtorrent/gzip.hpp"
|
2008-05-17 16:19:34 +02:00
|
|
|
#include "libtorrent/parse_url.hpp"
|
2008-05-03 18:05:42 +02:00
|
|
|
#include "libtorrent/socket.hpp"
|
2008-05-17 16:19:34 +02:00
|
|
|
#include "libtorrent/connection_queue.hpp"
|
2007-04-05 00:27:36 +02:00
|
|
|
|
2007-04-03 22:00:47 +02:00
|
|
|
#include <boost/bind.hpp>
|
2007-04-10 11:11:32 +02:00
|
|
|
#include <string>
|
2008-05-19 00:14:55 +02:00
|
|
|
#include <algorithm>
|
2007-03-27 09:04:31 +02:00
|
|
|
|
2007-04-03 22:00:47 +02:00
|
|
|
using boost::bind;
|
2007-04-02 20:51:35 +02:00
|
|
|
|
2008-01-30 19:32:13 +01:00
|
|
|
namespace libtorrent {
|
|
|
|
|
|
|
|
enum { max_bottled_buffer = 1024 * 1024 };
|
2007-03-27 09:04:31 +02:00
|
|
|
|
2007-09-11 19:45:20 +02:00
|
|
|
|
2008-03-12 08:44:27 +01:00
|
|
|
void http_connection::get(std::string const& url, time_duration timeout, int prio
|
2008-01-31 09:24:01 +01:00
|
|
|
, proxy_settings const* ps, int handle_redirects, std::string const& user_agent
|
2009-08-20 05:19:12 +02:00
|
|
|
, address const& bind_addr
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
, i2p_connection* i2p_conn
|
|
|
|
#endif
|
|
|
|
)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
|
|
|
std::string protocol;
|
2007-05-22 22:44:18 +02:00
|
|
|
std::string auth;
|
2007-03-27 09:04:31 +02:00
|
|
|
std::string hostname;
|
|
|
|
std::string path;
|
2009-06-12 18:40:38 +02:00
|
|
|
error_code ec;
|
2007-03-27 09:04:31 +02:00
|
|
|
int port;
|
2008-05-17 16:19:34 +02:00
|
|
|
|
2009-06-12 18:40:38 +02:00
|
|
|
boost::tie(protocol, auth, hostname, port, path)
|
|
|
|
= parse_url_components(url, ec);
|
2008-05-17 16:19:34 +02:00
|
|
|
|
2009-02-27 10:03:05 +01:00
|
|
|
int default_port = protocol == "https" ? 443 : 80;
|
|
|
|
|
2009-06-30 05:31:58 +02:00
|
|
|
// keep ourselves alive even if the callback function
|
|
|
|
// deletes this object
|
|
|
|
boost::shared_ptr<http_connection> me(shared_from_this());
|
|
|
|
|
2009-05-30 20:50:38 +02:00
|
|
|
if (protocol != "http"
|
2009-06-09 17:43:56 +02:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
2009-05-30 20:50:38 +02:00
|
|
|
&& protocol != "https"
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
|
|
|
error_code ec(errors::unsupported_url_protocol, libtorrent_category);
|
|
|
|
m_resolver.get_io_service().post(boost::bind(&http_connection::callback
|
|
|
|
, this, ec, (char*)0, 0));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-06-12 18:40:38 +02:00
|
|
|
if (ec)
|
2008-05-17 16:19:34 +02:00
|
|
|
{
|
2009-05-30 20:50:38 +02:00
|
|
|
m_resolver.get_io_service().post(boost::bind(&http_connection::callback
|
|
|
|
, this, ec, (char*)0, 0));
|
2008-05-17 16:19:34 +02:00
|
|
|
return;
|
|
|
|
}
|
2008-01-27 23:39:50 +01:00
|
|
|
|
2008-03-14 11:17:27 +01:00
|
|
|
TORRENT_ASSERT(prio >= 0 && prio < 2);
|
|
|
|
|
2008-01-27 23:39:50 +01:00
|
|
|
bool ssl = false;
|
|
|
|
if (protocol == "https") ssl = true;
|
|
|
|
|
2009-04-12 11:00:16 +02:00
|
|
|
char request[2048];
|
|
|
|
char* end = request + sizeof(request);
|
|
|
|
char* ptr = request;
|
|
|
|
|
|
|
|
#define APPEND_FMT(fmt) ptr += snprintf(ptr, end - ptr, fmt)
|
|
|
|
#define APPEND_FMT1(fmt, arg) ptr += snprintf(ptr, end - ptr, fmt, arg)
|
|
|
|
#define APPEND_FMT2(fmt, arg1, arg2) ptr += snprintf(ptr, end - ptr, fmt, arg1, arg2)
|
|
|
|
|
2008-01-27 23:39:50 +01:00
|
|
|
if (ps && (ps->type == proxy_settings::http
|
|
|
|
|| ps->type == proxy_settings::http_pw)
|
|
|
|
&& !ssl)
|
|
|
|
{
|
|
|
|
// if we're using an http proxy and not an ssl
|
|
|
|
// connection, just do a regular http proxy request
|
2009-04-12 11:00:16 +02:00
|
|
|
APPEND_FMT1("GET %s HTTP/1.0\r\n", url.c_str());
|
2008-01-27 23:39:50 +01:00
|
|
|
if (ps->type == proxy_settings::http_pw)
|
2009-04-12 11:00:16 +02:00
|
|
|
APPEND_FMT1("Proxy-Authorization: Basic %s\r\n", base64encode(
|
|
|
|
ps->username + ":" + ps->password).c_str());
|
2008-01-27 23:39:50 +01:00
|
|
|
hostname = ps->hostname;
|
|
|
|
port = ps->port;
|
|
|
|
ps = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-04-12 11:00:16 +02:00
|
|
|
APPEND_FMT2("GET %s HTTP/1.0\r\n"
|
|
|
|
"Host: %s", path.c_str(), hostname.c_str());
|
|
|
|
if (port != default_port) APPEND_FMT1(":%d\r\n", port);
|
|
|
|
else APPEND_FMT("\r\n");
|
2008-01-27 23:39:50 +01:00
|
|
|
}
|
|
|
|
|
2007-05-22 22:44:18 +02:00
|
|
|
if (!auth.empty())
|
2009-04-12 11:00:16 +02:00
|
|
|
APPEND_FMT1("Authorization: Basic %s\r\n", base64encode(auth).c_str());
|
2008-01-30 19:32:13 +01:00
|
|
|
|
|
|
|
if (!user_agent.empty())
|
2009-04-12 11:00:16 +02:00
|
|
|
APPEND_FMT1("User-Agent: %s\r\n", user_agent.c_str());
|
2008-01-30 19:32:13 +01:00
|
|
|
|
2008-12-29 08:31:33 +01:00
|
|
|
if (m_bottled)
|
2009-04-12 11:00:16 +02:00
|
|
|
APPEND_FMT("Accept-Encoding: gzip\r\n");
|
2008-12-29 08:31:33 +01:00
|
|
|
|
2009-04-12 11:00:16 +02:00
|
|
|
APPEND_FMT("Connection: close\r\n\r\n");
|
2008-01-30 19:32:13 +01:00
|
|
|
|
2009-04-12 11:00:16 +02:00
|
|
|
sendbuffer.assign(request);
|
2008-05-18 06:48:06 +02:00
|
|
|
m_url = url;
|
2009-01-27 07:17:55 +01:00
|
|
|
start(hostname, to_string(port).elems, timeout, prio
|
2009-08-20 05:19:12 +02:00
|
|
|
, ps, ssl, handle_redirects, bind_addr
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
, i2p_conn
|
|
|
|
#endif
|
|
|
|
);
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void http_connection::start(std::string const& hostname, std::string const& port
|
2008-03-12 08:44:27 +01:00
|
|
|
, time_duration timeout, int prio, proxy_settings const* ps, bool ssl, int handle_redirects
|
2009-08-20 05:19:12 +02:00
|
|
|
, address const& bind_addr
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
, i2p_connection* i2p_conn
|
|
|
|
#endif
|
|
|
|
)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
2008-03-14 11:17:27 +01:00
|
|
|
TORRENT_ASSERT(prio >= 0 && prio < 2);
|
|
|
|
|
2008-01-12 02:41:56 +01:00
|
|
|
m_redirects = handle_redirects;
|
2008-01-27 23:39:50 +01:00
|
|
|
if (ps) m_proxy = *ps;
|
|
|
|
|
2007-03-27 09:04:31 +02:00
|
|
|
m_timeout = timeout;
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2007-12-30 00:37:50 +01:00
|
|
|
m_timer.expires_from_now(m_timeout, ec);
|
2007-04-01 17:39:08 +02:00
|
|
|
m_timer.async_wait(bind(&http_connection::on_timeout
|
|
|
|
, boost::weak_ptr<http_connection>(shared_from_this()), _1));
|
|
|
|
m_called = false;
|
2008-01-12 02:41:56 +01:00
|
|
|
m_parser.reset();
|
|
|
|
m_recvbuffer.clear();
|
|
|
|
m_read_pos = 0;
|
2008-03-12 08:44:27 +01:00
|
|
|
m_priority = prio;
|
2008-01-27 23:39:50 +01:00
|
|
|
|
2009-06-30 05:31:58 +02:00
|
|
|
// keep ourselves alive even if the callback function
|
|
|
|
// deletes this object
|
|
|
|
boost::shared_ptr<http_connection> me(shared_from_this());
|
|
|
|
|
2008-01-31 09:24:01 +01:00
|
|
|
if (ec)
|
|
|
|
{
|
2009-05-30 20:50:38 +02:00
|
|
|
m_resolver.get_io_service().post(boost::bind(&http_connection::callback
|
|
|
|
, this, ec, (char*)0, 0));
|
2008-01-31 09:24:01 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_sock.is_open() && m_hostname == hostname && m_port == port
|
|
|
|
&& m_ssl == ssl && m_bind_addr == bind_addr)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
2008-05-03 18:05:42 +02:00
|
|
|
async_write(m_sock, asio::buffer(sendbuffer)
|
2007-03-27 09:04:31 +02:00
|
|
|
, bind(&http_connection::on_write, shared_from_this(), _1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-01-31 09:24:01 +01:00
|
|
|
m_ssl = ssl;
|
|
|
|
m_bind_addr = bind_addr;
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2007-12-30 00:37:50 +01:00
|
|
|
m_sock.close(ec);
|
2008-01-27 23:39:50 +01:00
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
bool is_i2p = false;
|
|
|
|
char const* top_domain = strrchr(hostname.c_str(), '.');
|
|
|
|
if (top_domain && strcmp(top_domain, ".i2p") == 0 && i2p_conn)
|
|
|
|
{
|
|
|
|
// this is an i2p name, we need to use the sam connection
|
|
|
|
// to do the name lookup
|
|
|
|
is_i2p = true;
|
|
|
|
m_i2p_conn = i2p_conn;
|
|
|
|
// quadruple the timeout for i2p destinations
|
|
|
|
// because i2p is sloooooow
|
|
|
|
m_timeout *= 4;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
if (is_i2p && i2p_conn->proxy().type != proxy_settings::i2p_proxy)
|
|
|
|
{
|
|
|
|
m_resolver.get_io_service().post(boost::bind(&http_connection::callback
|
|
|
|
, this, error_code(errors::no_i2p_router, libtorrent_category), (char*)0, 0));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-01-27 23:39:50 +01:00
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
|
|
|
if (m_ssl)
|
|
|
|
{
|
|
|
|
m_sock.instantiate<ssl_stream<socket_type> >(m_resolver.get_io_service());
|
2008-10-18 09:12:04 +02:00
|
|
|
ssl_stream<socket_type>* s = m_sock.get<ssl_stream<socket_type> >();
|
|
|
|
TORRENT_ASSERT(s);
|
2009-08-20 05:19:12 +02:00
|
|
|
bool ret = false;
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
if (is_i2p)
|
|
|
|
{
|
|
|
|
ret = instantiate_connection(m_resolver.get_io_service(), i2p_conn->proxy()
|
|
|
|
, s->next_layer());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
ret = instantiate_connection(m_resolver.get_io_service(), m_proxy, s->next_layer());
|
|
|
|
}
|
|
|
|
|
2008-01-27 23:39:50 +01:00
|
|
|
TORRENT_ASSERT(ret);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_sock.instantiate<socket_type>(m_resolver.get_io_service());
|
2009-08-20 05:19:12 +02:00
|
|
|
bool ret = false;
|
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
if (is_i2p)
|
|
|
|
{
|
|
|
|
ret = instantiate_connection(m_resolver.get_io_service(), i2p_conn->proxy()
|
|
|
|
, *m_sock.get<socket_type>());
|
|
|
|
TORRENT_ASSERT(m_sock.get<socket_type>());
|
|
|
|
TORRENT_ASSERT(m_sock.get<socket_type>()->get<i2p_stream>());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
ret = instantiate_connection(m_resolver.get_io_service()
|
|
|
|
, m_proxy, *m_sock.get<socket_type>());
|
|
|
|
}
|
2008-01-27 23:39:50 +01:00
|
|
|
TORRENT_ASSERT(ret);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
bool ret = instantiate_connection(m_resolver.get_io_service(), m_proxy, m_sock);
|
|
|
|
TORRENT_ASSERT(ret);
|
|
|
|
#endif
|
2008-01-31 09:24:01 +01:00
|
|
|
if (m_bind_addr != address_v4::any())
|
|
|
|
{
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2009-01-19 23:17:48 +01:00
|
|
|
m_sock.open(m_bind_addr.is_v4()?tcp::v4():tcp::v6(), ec);
|
2008-01-31 09:24:01 +01:00
|
|
|
m_sock.bind(tcp::endpoint(m_bind_addr, 0), ec);
|
|
|
|
if (ec)
|
|
|
|
{
|
2009-05-30 20:50:38 +02:00
|
|
|
m_resolver.get_io_service().post(boost::bind(&http_connection::callback
|
|
|
|
, this, ec, (char*)0, 0));
|
2008-01-31 09:24:01 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2008-01-27 23:39:50 +01:00
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
if (is_i2p)
|
|
|
|
{
|
|
|
|
i2p_conn->async_name_lookup(hostname.c_str(), bind(&http_connection::on_i2p_resolve
|
|
|
|
, shared_from_this(), _1, _2));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
tcp::resolver::query query(hostname, port);
|
|
|
|
m_resolver.async_resolve(query, bind(&http_connection::on_resolve
|
|
|
|
, shared_from_this(), _1, _2));
|
|
|
|
}
|
2007-03-27 09:04:31 +02:00
|
|
|
m_hostname = hostname;
|
|
|
|
m_port = port;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-05 02:29:33 +02:00
|
|
|
void http_connection::on_connect_timeout()
|
|
|
|
{
|
|
|
|
if (m_connection_ticket > -1) m_cc.done(m_connection_ticket);
|
|
|
|
m_connection_ticket = -1;
|
|
|
|
|
2009-06-30 05:31:58 +02:00
|
|
|
// keep ourselves alive even if the callback function
|
|
|
|
// deletes this object
|
|
|
|
boost::shared_ptr<http_connection> me(shared_from_this());
|
|
|
|
|
2008-05-19 00:14:55 +02:00
|
|
|
if (!m_endpoints.empty())
|
|
|
|
{
|
2008-10-21 10:45:42 +02:00
|
|
|
error_code ec;
|
|
|
|
m_sock.close(ec);
|
2008-05-19 00:14:55 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
callback(asio::error::timed_out);
|
|
|
|
close();
|
|
|
|
}
|
2007-05-05 02:29:33 +02:00
|
|
|
}
|
|
|
|
|
2007-04-01 17:39:08 +02:00
|
|
|
void http_connection::on_timeout(boost::weak_ptr<http_connection> p
|
2008-05-03 18:05:42 +02:00
|
|
|
, error_code const& e)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
2007-04-01 17:39:08 +02:00
|
|
|
boost::shared_ptr<http_connection> c = p.lock();
|
|
|
|
if (!c) return;
|
2007-05-05 02:29:33 +02:00
|
|
|
|
|
|
|
if (e == asio::error::operation_aborted) return;
|
|
|
|
|
2009-07-26 04:17:21 +02:00
|
|
|
if (c->m_last_receive + c->m_timeout < time_now_hires())
|
2007-04-02 20:51:35 +02:00
|
|
|
{
|
2008-05-19 00:14:55 +02:00
|
|
|
if (c->m_connection_ticket > -1 && !c->m_endpoints.empty())
|
|
|
|
{
|
|
|
|
error_code ec;
|
2008-10-21 10:45:42 +02:00
|
|
|
c->m_sock.close(ec);
|
2008-05-19 00:14:55 +02:00
|
|
|
c->m_timer.expires_at(c->m_last_receive + c->m_timeout, ec);
|
|
|
|
c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
c->callback(asio::error::timed_out);
|
|
|
|
c->close();
|
|
|
|
}
|
2007-04-02 20:51:35 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-11-16 00:55:30 +01:00
|
|
|
if (!c->m_sock.is_open()) return;
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2007-12-30 00:37:50 +01:00
|
|
|
c->m_timer.expires_at(c->m_last_receive + c->m_timeout, ec);
|
2007-04-02 20:51:35 +02:00
|
|
|
c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1));
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void http_connection::close()
|
|
|
|
{
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2007-12-30 00:37:50 +01:00
|
|
|
m_timer.cancel(ec);
|
2008-12-20 19:27:09 +01:00
|
|
|
m_resolver.cancel();
|
2007-12-30 00:37:50 +01:00
|
|
|
m_limiter_timer.cancel(ec);
|
|
|
|
m_sock.close(ec);
|
2007-03-27 09:04:31 +02:00
|
|
|
m_hostname.clear();
|
|
|
|
m_port.clear();
|
2007-11-18 05:12:35 +01:00
|
|
|
m_handler.clear();
|
2008-05-19 00:14:55 +02:00
|
|
|
m_abort = true;
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
|
2009-08-20 05:19:12 +02:00
|
|
|
#if TORRENT_USE_I2P
|
|
|
|
void http_connection::on_i2p_resolve(error_code const& e
|
|
|
|
, char const* destination)
|
|
|
|
{
|
|
|
|
if (e)
|
|
|
|
{
|
|
|
|
callback(e);
|
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef TORRENT_USE_OPENSSL
|
|
|
|
TORRENT_ASSERT(m_ssl == false);
|
|
|
|
TORRENT_ASSERT(m_sock.get<socket_type>());
|
|
|
|
TORRENT_ASSERT(m_sock.get<socket_type>()->get<i2p_stream>());
|
|
|
|
m_sock.get<socket_type>()->get<i2p_stream>()->set_destination(destination);
|
|
|
|
m_sock.get<socket_type>()->get<i2p_stream>()->set_command(i2p_stream::cmd_connect);
|
|
|
|
m_sock.get<socket_type>()->get<i2p_stream>()->set_session_id(m_i2p_conn->session_id());
|
|
|
|
#else
|
|
|
|
m_sock.get<i2p_stream>()->set_destination(destination);
|
|
|
|
m_sock.get<i2p_stream>()->set_command(i2p_stream::cmd_connect);
|
|
|
|
m_sock.get<i2p_stream>()->set_session_id(m_i2p_conn->session_id());
|
|
|
|
#endif
|
|
|
|
m_sock.async_connect(tcp::endpoint(), boost::bind(&http_connection::on_connect
|
|
|
|
, shared_from_this(), _1));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
void http_connection::on_resolve(error_code const& e
|
2008-01-31 09:24:01 +01:00
|
|
|
, tcp::resolver::iterator i)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
|
|
|
if (e)
|
|
|
|
{
|
2009-06-30 05:31:58 +02:00
|
|
|
boost::shared_ptr<http_connection> me(shared_from_this());
|
|
|
|
|
2007-11-18 05:12:35 +01:00
|
|
|
callback(e);
|
2007-03-27 09:04:31 +02:00
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(i != tcp::resolver::iterator());
|
2008-01-31 09:24:01 +01:00
|
|
|
|
2008-05-19 00:14:55 +02:00
|
|
|
std::transform(i, tcp::resolver::iterator(), std::back_inserter(m_endpoints)
|
|
|
|
, boost::bind(&tcp::resolver::iterator::value_type::endpoint, _1));
|
2008-01-31 09:24:01 +01:00
|
|
|
|
2008-10-22 21:40:32 +02:00
|
|
|
if (m_filter_handler) m_filter_handler(*this, m_endpoints);
|
|
|
|
if (m_endpoints.empty())
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-07-03 12:05:19 +02:00
|
|
|
// The following statement causes msvc to crash (ICE). Since it's not
|
|
|
|
// necessary in the vast majority of cases, just ignore the endpoint
|
|
|
|
// order for windows
|
|
|
|
#if !defined _MSC_VER || _MSC_VER > 1310
|
2008-05-19 00:14:55 +02:00
|
|
|
// sort the endpoints so that the ones with the same IP version as our
|
|
|
|
// bound listen socket are first. So that when contacting a tracker,
|
|
|
|
// we'll talk to it from the same IP that we're listening on
|
2009-05-15 23:23:41 +02:00
|
|
|
if (m_bind_addr != address_v4::any())
|
|
|
|
std::partition(m_endpoints.begin(), m_endpoints.end()
|
|
|
|
, boost::bind(&address::is_v4, boost::bind(&tcp::endpoint::address, _1))
|
|
|
|
== m_bind_addr.is_v4());
|
2008-07-03 12:05:19 +02:00
|
|
|
#endif
|
2008-05-19 00:14:55 +02:00
|
|
|
|
|
|
|
queue_connect();
|
|
|
|
}
|
|
|
|
|
|
|
|
void http_connection::queue_connect()
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(!m_endpoints.empty());
|
|
|
|
tcp::endpoint target = m_endpoints.front();
|
|
|
|
m_endpoints.pop_front();
|
|
|
|
|
|
|
|
m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, target)
|
2007-05-05 02:29:33 +02:00
|
|
|
, bind(&http_connection::on_connect_timeout, shared_from_this())
|
2008-03-12 08:44:27 +01:00
|
|
|
, m_timeout, m_priority);
|
2007-05-05 02:29:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void http_connection::connect(int ticket, tcp::endpoint target_address)
|
|
|
|
{
|
|
|
|
m_connection_ticket = ticket;
|
|
|
|
m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect
|
2008-05-19 00:14:55 +02:00
|
|
|
, shared_from_this(), _1));
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
|
2008-05-19 00:14:55 +02:00
|
|
|
void http_connection::on_connect(error_code const& e)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
2008-06-07 19:47:05 +02:00
|
|
|
if (m_connection_ticket >= 0)
|
|
|
|
{
|
|
|
|
m_cc.done(m_connection_ticket);
|
|
|
|
m_connection_ticket = -1;
|
|
|
|
}
|
2008-05-19 00:14:55 +02:00
|
|
|
|
2009-07-26 04:17:21 +02:00
|
|
|
m_last_receive = time_now_hires();
|
2007-03-27 09:04:31 +02:00
|
|
|
if (!e)
|
|
|
|
{
|
2007-09-10 01:52:34 +02:00
|
|
|
if (m_connect_handler) m_connect_handler(*this);
|
2008-05-03 18:05:42 +02:00
|
|
|
async_write(m_sock, asio::buffer(sendbuffer)
|
2007-03-27 09:04:31 +02:00
|
|
|
, bind(&http_connection::on_write, shared_from_this(), _1));
|
|
|
|
}
|
2008-05-19 00:14:55 +02:00
|
|
|
else if (!m_endpoints.empty() && !m_abort)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
|
|
|
// The connection failed. Try the next endpoint in the list.
|
2008-10-21 10:45:42 +02:00
|
|
|
error_code ec;
|
|
|
|
m_sock.close(ec);
|
2008-05-19 00:14:55 +02:00
|
|
|
queue_connect();
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
2008-05-19 00:14:55 +02:00
|
|
|
else
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
2009-06-30 05:31:58 +02:00
|
|
|
boost::shared_ptr<http_connection> me(shared_from_this());
|
2007-11-18 05:12:35 +01:00
|
|
|
callback(e);
|
2007-03-27 09:04:31 +02:00
|
|
|
close();
|
2007-11-18 05:12:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
void http_connection::callback(error_code const& e, char const* data, int size)
|
2007-11-18 05:12:35 +01:00
|
|
|
{
|
2009-02-11 09:16:14 +01:00
|
|
|
if (m_bottled && m_called) return;
|
|
|
|
|
|
|
|
std::vector<char> buf;
|
|
|
|
if (m_bottled && m_parser.header_finished())
|
2007-11-18 05:12:35 +01:00
|
|
|
{
|
2009-02-11 09:16:14 +01:00
|
|
|
std::string const& encoding = m_parser.header("content-encoding");
|
|
|
|
if (encoding == "gzip" || encoding == "x-gzip")
|
2008-01-30 19:32:13 +01:00
|
|
|
{
|
2009-02-11 09:16:14 +01:00
|
|
|
std::string error;
|
|
|
|
if (inflate_gzip(data, size, buf, max_bottled_buffer, error))
|
2008-01-30 19:32:13 +01:00
|
|
|
{
|
2009-02-11 09:16:14 +01:00
|
|
|
if (m_handler) m_handler(asio::error::fault, m_parser, data, size, *this);
|
|
|
|
close();
|
|
|
|
return;
|
2008-01-30 19:32:13 +01:00
|
|
|
}
|
2009-02-11 09:16:14 +01:00
|
|
|
size = int(buf.size());
|
2009-09-18 21:55:41 +02:00
|
|
|
data = size == 0 ? 0 : &buf[0];
|
2008-01-30 19:32:13 +01:00
|
|
|
}
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
2009-02-11 09:16:14 +01:00
|
|
|
m_called = true;
|
|
|
|
error_code ec;
|
|
|
|
m_timer.cancel(ec);
|
|
|
|
if (m_handler) m_handler(e, m_parser, data, size, *this);
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
void http_connection::on_write(error_code const& e)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
|
|
|
if (e)
|
|
|
|
{
|
2009-06-30 05:31:58 +02:00
|
|
|
boost::shared_ptr<http_connection> me(shared_from_this());
|
2007-11-18 05:12:35 +01:00
|
|
|
callback(e);
|
2007-03-27 09:04:31 +02:00
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string().swap(sendbuffer);
|
|
|
|
m_recvbuffer.resize(4096);
|
2007-04-03 22:00:47 +02:00
|
|
|
|
|
|
|
int amount_to_read = m_recvbuffer.size() - m_read_pos;
|
|
|
|
if (m_rate_limit > 0 && amount_to_read > m_download_quota)
|
|
|
|
{
|
|
|
|
amount_to_read = m_download_quota;
|
|
|
|
if (m_download_quota == 0)
|
|
|
|
{
|
|
|
|
if (!m_limiter_timer_active)
|
2008-05-03 18:05:42 +02:00
|
|
|
on_assign_bandwidth(error_code());
|
2007-04-03 22:00:47 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2007-03-27 09:04:31 +02:00
|
|
|
m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
|
2007-04-03 22:00:47 +02:00
|
|
|
, amount_to_read)
|
|
|
|
, bind(&http_connection::on_read
|
|
|
|
, shared_from_this(), _1, _2));
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
void http_connection::on_read(error_code const& e
|
2007-03-27 09:04:31 +02:00
|
|
|
, std::size_t bytes_transferred)
|
|
|
|
{
|
2007-04-03 22:00:47 +02:00
|
|
|
if (m_rate_limit)
|
|
|
|
{
|
|
|
|
m_download_quota -= bytes_transferred;
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_download_quota >= 0);
|
2007-04-03 22:00:47 +02:00
|
|
|
}
|
|
|
|
|
2009-06-30 05:31:58 +02:00
|
|
|
// keep ourselves alive even if the callback function
|
|
|
|
// deletes this object
|
|
|
|
boost::shared_ptr<http_connection> me(shared_from_this());
|
|
|
|
|
2009-02-11 08:54:16 +01:00
|
|
|
// when using the asio SSL wrapper, it seems like
|
|
|
|
// we get the shut_down error instead of EOF
|
|
|
|
if (e == asio::error::eof || e == asio::error::shut_down)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
2009-02-11 08:54:16 +01:00
|
|
|
error_code ec = asio::error::eof;
|
2008-01-12 02:41:56 +01:00
|
|
|
TORRENT_ASSERT(bytes_transferred == 0);
|
2007-05-02 00:44:54 +02:00
|
|
|
char const* data = 0;
|
|
|
|
std::size_t size = 0;
|
2007-10-04 00:23:18 +02:00
|
|
|
if (m_bottled && m_parser.header_finished())
|
2007-05-02 00:44:54 +02:00
|
|
|
{
|
|
|
|
data = m_parser.get_body().begin;
|
|
|
|
size = m_parser.get_body().left();
|
|
|
|
}
|
2009-02-11 08:54:16 +01:00
|
|
|
callback(ec, data, size);
|
2007-11-18 05:12:35 +01:00
|
|
|
close();
|
2007-03-27 09:04:31 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e)
|
|
|
|
{
|
2008-01-12 02:41:56 +01:00
|
|
|
TORRENT_ASSERT(bytes_transferred == 0);
|
2007-11-18 05:12:35 +01:00
|
|
|
callback(e);
|
2007-03-27 09:04:31 +02:00
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_read_pos += bytes_transferred;
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(m_read_pos <= int(m_recvbuffer.size()));
|
2007-03-27 09:04:31 +02:00
|
|
|
|
|
|
|
if (m_bottled || !m_parser.header_finished())
|
|
|
|
{
|
|
|
|
libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
|
|
|
|
, &m_recvbuffer[0] + m_read_pos);
|
2007-12-29 19:24:50 +01:00
|
|
|
bool error = false;
|
|
|
|
m_parser.incoming(rcv_buf, error);
|
|
|
|
if (error)
|
2007-12-12 20:29:19 +01:00
|
|
|
{
|
2007-12-29 19:24:50 +01:00
|
|
|
// HTTP parse error
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec = asio::error::fault;
|
2007-12-29 19:24:50 +01:00
|
|
|
callback(ec, 0, 0);
|
2007-12-12 20:29:19 +01:00
|
|
|
return;
|
|
|
|
}
|
2008-01-12 02:41:56 +01:00
|
|
|
|
|
|
|
// having a nonempty path means we should handle redirects
|
|
|
|
if (m_redirects && m_parser.header_finished())
|
|
|
|
{
|
|
|
|
int code = m_parser.status_code();
|
|
|
|
|
|
|
|
if (code >= 300 && code < 400)
|
|
|
|
{
|
|
|
|
// attempt a redirect
|
2008-05-18 06:48:06 +02:00
|
|
|
std::string const& location = m_parser.header("location");
|
|
|
|
if (location.empty())
|
2008-01-12 02:41:56 +01:00
|
|
|
{
|
|
|
|
// missing location header
|
2008-05-18 06:48:06 +02:00
|
|
|
callback(asio::error::fault);
|
|
|
|
close();
|
2008-01-12 02:41:56 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2008-01-12 02:41:56 +01:00
|
|
|
m_sock.close(ec);
|
2008-05-18 06:48:06 +02:00
|
|
|
using boost::tuples::ignore;
|
2009-06-12 18:40:38 +02:00
|
|
|
boost::tie(ignore, ignore, ignore, ignore, ignore)
|
|
|
|
= parse_url_components(location, ec);
|
|
|
|
if (!ec)
|
2008-05-18 06:48:06 +02:00
|
|
|
{
|
|
|
|
get(location, m_timeout, m_priority, &m_proxy, m_redirects - 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// some broken web servers send out relative paths
|
|
|
|
// in the location header.
|
|
|
|
std::string url = m_url;
|
|
|
|
// remove the leaf filename
|
|
|
|
std::size_t i = url.find_last_of('/');
|
|
|
|
if (i == std::string::npos)
|
|
|
|
{
|
|
|
|
url += '/';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
url.resize(i + 1);
|
|
|
|
}
|
|
|
|
url += location;
|
|
|
|
|
|
|
|
get(url, m_timeout, m_priority, &m_proxy, m_redirects - 1);
|
|
|
|
}
|
2008-01-12 02:41:56 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_redirects = 0;
|
|
|
|
}
|
|
|
|
|
2007-03-27 09:04:31 +02:00
|
|
|
if (!m_bottled && m_parser.header_finished())
|
|
|
|
{
|
|
|
|
if (m_read_pos > m_parser.body_start())
|
2007-11-18 05:12:35 +01:00
|
|
|
callback(e, &m_recvbuffer[0] + m_parser.body_start()
|
2007-03-27 09:04:31 +02:00
|
|
|
, m_read_pos - m_parser.body_start());
|
|
|
|
m_read_pos = 0;
|
2009-07-26 04:17:21 +02:00
|
|
|
m_last_receive = time_now_hires();
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
else if (m_bottled && m_parser.finished())
|
|
|
|
{
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2007-12-30 00:37:50 +01:00
|
|
|
m_timer.cancel(ec);
|
2007-11-18 05:12:35 +01:00
|
|
|
callback(e, m_parser.get_body().begin, m_parser.get_body().left());
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-10-05 02:30:00 +02:00
|
|
|
TORRENT_ASSERT(!m_bottled);
|
2007-11-18 05:12:35 +01:00
|
|
|
callback(e, &m_recvbuffer[0], m_read_pos);
|
2007-03-27 09:04:31 +02:00
|
|
|
m_read_pos = 0;
|
2009-07-26 04:17:21 +02:00
|
|
|
m_last_receive = time_now_hires();
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (int(m_recvbuffer.size()) == m_read_pos)
|
2007-09-11 19:45:20 +02:00
|
|
|
m_recvbuffer.resize((std::min)(m_read_pos + 2048, int(max_bottled_buffer)));
|
|
|
|
if (m_read_pos == max_bottled_buffer)
|
2007-03-27 09:04:31 +02:00
|
|
|
{
|
2007-11-18 05:12:35 +01:00
|
|
|
callback(asio::error::eof);
|
2007-03-27 09:04:31 +02:00
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
2007-04-03 22:00:47 +02:00
|
|
|
int amount_to_read = m_recvbuffer.size() - m_read_pos;
|
|
|
|
if (m_rate_limit > 0 && amount_to_read > m_download_quota)
|
|
|
|
{
|
|
|
|
amount_to_read = m_download_quota;
|
|
|
|
if (m_download_quota == 0)
|
|
|
|
{
|
|
|
|
if (!m_limiter_timer_active)
|
2008-05-03 18:05:42 +02:00
|
|
|
on_assign_bandwidth(error_code());
|
2007-04-03 22:00:47 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
|
|
|
|
, amount_to_read)
|
|
|
|
, bind(&http_connection::on_read
|
2009-06-30 05:31:58 +02:00
|
|
|
, me, _1, _2));
|
2007-04-03 22:00:47 +02:00
|
|
|
}
|
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
void http_connection::on_assign_bandwidth(error_code const& e)
|
2007-04-03 22:00:47 +02:00
|
|
|
{
|
2007-04-10 20:53:12 +02:00
|
|
|
if ((e == asio::error::operation_aborted
|
2007-04-07 01:01:27 +02:00
|
|
|
&& m_limiter_timer_active)
|
2007-04-10 20:53:12 +02:00
|
|
|
|| !m_sock.is_open())
|
2007-04-07 01:01:27 +02:00
|
|
|
{
|
2007-11-18 05:12:35 +01:00
|
|
|
callback(asio::error::eof);
|
2007-04-10 21:22:08 +02:00
|
|
|
return;
|
2007-04-07 01:01:27 +02:00
|
|
|
}
|
2007-04-03 22:00:47 +02:00
|
|
|
m_limiter_timer_active = false;
|
|
|
|
if (e) return;
|
|
|
|
|
|
|
|
if (m_download_quota > 0) return;
|
|
|
|
|
|
|
|
m_download_quota = m_rate_limit / 4;
|
|
|
|
|
|
|
|
int amount_to_read = m_recvbuffer.size() - m_read_pos;
|
|
|
|
if (amount_to_read > m_download_quota)
|
|
|
|
amount_to_read = m_download_quota;
|
|
|
|
|
2007-11-16 00:55:30 +01:00
|
|
|
if (!m_sock.is_open()) return;
|
|
|
|
|
2007-03-27 09:04:31 +02:00
|
|
|
m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
|
2007-04-03 22:00:47 +02:00
|
|
|
, amount_to_read)
|
2007-03-27 09:04:31 +02:00
|
|
|
, bind(&http_connection::on_read
|
|
|
|
, shared_from_this(), _1, _2));
|
2007-04-03 22:00:47 +02:00
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2007-04-03 22:00:47 +02:00
|
|
|
m_limiter_timer_active = true;
|
2007-12-30 00:37:50 +01:00
|
|
|
m_limiter_timer.expires_from_now(milliseconds(250), ec);
|
2007-04-03 22:00:47 +02:00
|
|
|
m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
|
|
|
|
, shared_from_this(), _1));
|
|
|
|
}
|
|
|
|
|
|
|
|
void http_connection::rate_limit(int limit)
|
|
|
|
{
|
2007-11-16 00:55:30 +01:00
|
|
|
if (!m_sock.is_open()) return;
|
|
|
|
|
2007-04-03 22:00:47 +02:00
|
|
|
if (!m_limiter_timer_active)
|
|
|
|
{
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2007-04-03 22:00:47 +02:00
|
|
|
m_limiter_timer_active = true;
|
2007-12-30 00:37:50 +01:00
|
|
|
m_limiter_timer.expires_from_now(milliseconds(250), ec);
|
2007-04-03 22:00:47 +02:00
|
|
|
m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth
|
|
|
|
, shared_from_this(), _1));
|
|
|
|
}
|
|
|
|
m_rate_limit = limit;
|
2007-03-27 09:04:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|