Merge pull request #239 from arvidn/http_connection-sim
start of an http_connection test under the simulator
This commit is contained in:
@@ -482,28 +482,11 @@ namespace libtorrent
|
||||
TORRENT_EXPORT boost::system::error_category& get_http_category();
|
||||
|
||||
using boost::system::error_code;
|
||||
|
||||
// hidden
|
||||
inline boost::system::error_category const& system_category()
|
||||
#if BOOST_VERSION < 104400
|
||||
{ return boost::system::get_system_category(); }
|
||||
#else
|
||||
{ return boost::system::system_category(); }
|
||||
#endif
|
||||
|
||||
// hidden
|
||||
inline boost::system::error_category const& get_posix_category()
|
||||
#if BOOST_VERSION < 103600
|
||||
{ return boost::system::get_posix_category(); }
|
||||
#elif BOOST_VERSION < 104400
|
||||
{ return boost::system::get_generic_category(); }
|
||||
#else
|
||||
{ return boost::system::generic_category(); }
|
||||
#endif // BOOST_VERSION < 103600
|
||||
using boost::system::error_condition;
|
||||
|
||||
// internal
|
||||
inline boost::system::error_category const& generic_category()
|
||||
{ return get_posix_category(); }
|
||||
using boost::system::generic_category;
|
||||
using boost::system::system_category;
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
struct TORRENT_EXPORT libtorrent_exception: std::exception
|
||||
|
@@ -146,7 +146,7 @@ private:
|
||||
, error_code const& e);
|
||||
void on_assign_bandwidth(error_code const& e);
|
||||
|
||||
void callback(error_code e, char* data = 0, int size = 0);
|
||||
void callback(error_code e, char* data = NULL, int size = 0);
|
||||
|
||||
std::vector<char> m_recvbuffer;
|
||||
|
||||
|
@@ -111,6 +111,17 @@ public:
|
||||
|
||||
void set_dst_name(std::string const& host)
|
||||
{
|
||||
// TODO: 3 enable this assert and fix remaining causes of it triggering
|
||||
/*
|
||||
#if TORRENT_USE_ASSERTS
|
||||
error_code ec;
|
||||
address::from_string(host, ec);
|
||||
// if this assert trips, set_dst_name() is called wth an IP address rather
|
||||
// than a hostname. Instead, resolve the IP into an address and pass it to
|
||||
// async_connect instead
|
||||
TORRENT_ASSERT(ec);
|
||||
#endif
|
||||
*/
|
||||
m_dst_name = host;
|
||||
if (m_dst_name.size() > 255)
|
||||
m_dst_name.resize(255);
|
||||
@@ -118,7 +129,6 @@ public:
|
||||
|
||||
void close(error_code& ec)
|
||||
{
|
||||
m_hostname.clear();
|
||||
m_dst_name.clear();
|
||||
proxy_base::close(ec);
|
||||
}
|
||||
@@ -126,7 +136,6 @@ public:
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
void close()
|
||||
{
|
||||
m_hostname.clear();
|
||||
m_dst_name.clear();
|
||||
proxy_base::close();
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ project
|
||||
;
|
||||
|
||||
alias libtorrent-sims :
|
||||
[ run test_http_connection.cpp ]
|
||||
[ run test_auto_manage.cpp ]
|
||||
[ run test_torrent_status.cpp ]
|
||||
[ run test_swarm.cpp ]
|
||||
|
Submodule simulation/libsimulator updated: a5479665ad...a4b10668ea
485
simulation/test_http_connection.cpp
Normal file
485
simulation/test_http_connection.cpp
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2010, 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.
|
||||
|
||||
*/
|
||||
|
||||
#include "test.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "setup_swarm.hpp"
|
||||
#include "swarm_config.hpp"
|
||||
#include "simulator/simulator.hpp"
|
||||
#include "simulator/http_server.hpp"
|
||||
#include "simulator/socks_server.hpp"
|
||||
#include "libtorrent/alert_types.hpp"
|
||||
#include "libtorrent/aux_/proxy_settings.hpp"
|
||||
#include "libtorrent/http_connection.hpp"
|
||||
#include "libtorrent/resolver.hpp"
|
||||
#include "libtorrent/io.hpp"
|
||||
|
||||
#include <boost/crc.hpp>
|
||||
|
||||
using namespace libtorrent;
|
||||
using namespace sim;
|
||||
namespace lt = libtorrent;
|
||||
namespace io = lt::detail;
|
||||
|
||||
using chrono::duration_cast;
|
||||
|
||||
struct sim_config : sim::default_config
|
||||
{
|
||||
chrono::high_resolution_clock::duration hostname_lookup(
|
||||
asio::ip::address const& requestor
|
||||
, std::string hostname
|
||||
, std::vector<asio::ip::address>& result
|
||||
, boost::system::error_code& ec)
|
||||
{
|
||||
if (hostname == "try-next.com")
|
||||
{
|
||||
result.push_back(address_v4::from_string("10.0.0.10"));
|
||||
result.push_back(address_v4::from_string("10.0.0.9"));
|
||||
result.push_back(address_v4::from_string("10.0.0.8"));
|
||||
result.push_back(address_v4::from_string("10.0.0.7"));
|
||||
result.push_back(address_v4::from_string("10.0.0.6"));
|
||||
result.push_back(address_v4::from_string("10.0.0.5"));
|
||||
result.push_back(address_v4::from_string("10.0.0.4"));
|
||||
result.push_back(address_v4::from_string("10.0.0.3"));
|
||||
|
||||
// this is the IP that works, all other should fail
|
||||
result.push_back(address_v4::from_string("10.0.0.2"));
|
||||
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
if (hostname == "test-hostname.com")
|
||||
{
|
||||
result.push_back(address_v4::from_string("10.0.0.2"));
|
||||
return duration_cast<chrono::high_resolution_clock::duration>(chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
return default_config::hostname_lookup(requestor, hostname, result, ec);
|
||||
}
|
||||
};
|
||||
|
||||
// takes a string of data and chunks it up using HTTP chunked encoding
|
||||
std::string chunk_string(std::string s)
|
||||
{
|
||||
size_t i = 10;
|
||||
std::string ret;
|
||||
while (!s.empty())
|
||||
{
|
||||
i = std::min(i, s.size());
|
||||
char header[50];
|
||||
snprintf(header, sizeof(header), "%x\r\n", int(i));
|
||||
ret += header;
|
||||
ret += s.substr(0, i);
|
||||
s.erase(s.begin(), s.begin() + i);
|
||||
i *= 2;
|
||||
}
|
||||
ret += "0\r\n\r\n";
|
||||
return ret;
|
||||
}
|
||||
|
||||
boost::shared_ptr<http_connection> test_request(io_service& ios
|
||||
, resolver& res
|
||||
, std::string const& url
|
||||
, char const* expected_data
|
||||
, int expected_size
|
||||
, int expected_status
|
||||
, error_condition expected_error
|
||||
, lt::aux::proxy_settings const& ps
|
||||
, int* connect_handler_called
|
||||
, int* handler_called
|
||||
, std::string const& auth = std::string())
|
||||
{
|
||||
fprintf(stderr, " ===== TESTING: %s =====\n", url.c_str());
|
||||
|
||||
auto h = boost::make_shared<http_connection>(ios
|
||||
, res
|
||||
, [=](error_code const& ec, http_parser const& parser
|
||||
, char const* data, const int size, http_connection& c)
|
||||
{
|
||||
printf("RESPONSE: %s\n", url.c_str());
|
||||
++*handler_called;
|
||||
|
||||
// this is pretty gross. Since boost.asio is a header-only library, when this test is
|
||||
// build against shared libraries of libtorrent and simulator, there will be multiple
|
||||
// (distinct) error categories in boost.asio. The traditional comparison of error_code
|
||||
// and error_condition may hence fail.
|
||||
const bool error_ok = ec == expected_error
|
||||
|| (strcmp(ec.category().name(), expected_error.category().name()) == 0
|
||||
&& ec.value() == expected_error.value());
|
||||
|
||||
if (!error_ok)
|
||||
{
|
||||
printf("ERROR: %s (expected: %s)\n"
|
||||
, ec.message().c_str()
|
||||
, expected_error.message().c_str());
|
||||
}
|
||||
|
||||
const int http_status = parser.status_code();
|
||||
if (expected_size != -1)
|
||||
{
|
||||
TEST_EQUAL(size, expected_size);
|
||||
}
|
||||
TEST_CHECK(error_ok);
|
||||
if (expected_status != -1)
|
||||
{
|
||||
TEST_EQUAL(http_status, expected_status);
|
||||
}
|
||||
if (http_status == 200)
|
||||
{
|
||||
TEST_CHECK(expected_data
|
||||
&& size == expected_size
|
||||
&& memcmp(expected_data, data, size) == 0);
|
||||
}
|
||||
}
|
||||
, true, 1024*1024
|
||||
, [=](http_connection& c)
|
||||
{
|
||||
++*connect_handler_called;
|
||||
TEST_CHECK(c.socket().is_open());
|
||||
printf("CONNECTED: %s\n", url.c_str());
|
||||
});
|
||||
|
||||
h->get(url, seconds(1), 0, &ps, 5, "test/user-agent", address_v4::any()
|
||||
, 0, auth);
|
||||
return h;
|
||||
}
|
||||
|
||||
void print_http_header(std::map<std::string, std::string> const& headers)
|
||||
{
|
||||
for (std::map<std::string, std::string>::const_iterator i
|
||||
= headers.begin(), end(headers.end()); i != end; ++i)
|
||||
{
|
||||
printf("%s: %s\n", i->first.c_str(), i->second.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void run_test(lt::aux::proxy_settings ps, std::string url, int expect_size, int expect_status
|
||||
, boost::system::error_condition expect_error, std::vector<int> expect_counters);
|
||||
|
||||
enum expect_counters
|
||||
{
|
||||
connect_handler = 0,
|
||||
handler = 1,
|
||||
test_file_req = 2,
|
||||
redirect_req = 3,
|
||||
rel_redirect_req = 4,
|
||||
inf_redirect_req = 5,
|
||||
chunked_req = 6,
|
||||
test_file_gz_req = 7,
|
||||
|
||||
num_counters
|
||||
};
|
||||
|
||||
void run_suite(lt::aux::proxy_settings ps)
|
||||
{
|
||||
std::string url_base = "http://10.0.0.2:8080";
|
||||
|
||||
run_test(ps, url_base + "/test_file", 1337, 200, error_condition(), { 1, 1, 1});
|
||||
|
||||
// positive test with a successful hostname
|
||||
run_test(ps, "http://test-hostname.com:8080/test_file", 1337, 200, error_condition(), { 1, 1, 1});
|
||||
|
||||
run_test(ps, url_base + "/non-existent", 0, 404, error_condition(), { 1, 1 });
|
||||
run_test(ps, url_base + "/redirect", 1337, 200, error_condition(), { 2, 1, 1, 1 });
|
||||
run_test(ps, url_base + "/relative/redirect", 1337, 200, error_condition(), {2, 1, 1, 0, 1});
|
||||
|
||||
run_test(ps, url_base + "/infinite/redirect", 0, 301
|
||||
, error_condition(asio::error::eof, asio::error::get_misc_category()), {6, 1, 0, 0, 0, 6});
|
||||
|
||||
run_test(ps, url_base + "/chunked_encoding", 1337, 200, error_condition(), { 1, 1, 0, 0, 0, 0, 1});
|
||||
|
||||
// we are on an IPv4 host, we can't connect to IPv6 addresses, make sure that
|
||||
// error is correctly propagated
|
||||
// with socks5 we would be able to do this, assuming the socks server
|
||||
// supported it, but the current socks implementation in libsimulator does
|
||||
// not support IPv6
|
||||
if (ps.type != settings_pack::socks5)
|
||||
{
|
||||
run_test(ps, "http://[ff::dead:beef]:8080/test_file", 0, -1
|
||||
, error_condition(boost::system::errc::address_family_not_supported, generic_category())
|
||||
, {0,1});
|
||||
}
|
||||
|
||||
// there is no node at 10.0.0.10, this should fail with connection refused
|
||||
run_test(ps, "http://10.0.0.10:8080/test_file", 0, -1,
|
||||
error_condition(boost::system::errc::connection_refused, generic_category())
|
||||
, {0,1});
|
||||
|
||||
// the try-next test in his case would test the socks proxy itself, whether
|
||||
// it has robust retry behavior (which the simple test proxy that comes with
|
||||
// libsimulator doesn't).
|
||||
if (ps.proxy_hostnames == false)
|
||||
{
|
||||
// this hostname will resolve to multiple IPs, all but one that we cannot
|
||||
// connect to and the second one where we'll get the test file response. Make
|
||||
// sure the http_connection correcly tries the second IP if the first one
|
||||
// fails.
|
||||
run_test(ps, "http://try-next.com:8080/test_file", 1337, 200
|
||||
, error_condition(), { 1, 1, 1});
|
||||
}
|
||||
|
||||
const error_condition expected_error = ps.proxy_hostnames
|
||||
? error_condition(boost::system::errc::host_unreachable, generic_category())
|
||||
: error_condition(asio::error::host_not_found, boost::asio::error::get_netdb_category());
|
||||
|
||||
// make sure hostname lookup failures are passed through correctly
|
||||
run_test(ps, "http://non-existent.com/test_file", 0, -1
|
||||
, expected_error, { 0, 1});
|
||||
|
||||
// make sure we handle gzipped content correctly
|
||||
run_test(ps, url_base + "/test_file.gz", 1337, 200, error_condition(), { 1, 1, 0, 0, 0, 0, 0, 1});
|
||||
|
||||
// TODO: 2 test basic-auth
|
||||
// TODO: 2 test https
|
||||
|
||||
}
|
||||
|
||||
lt::aux::proxy_settings make_proxy_settings(lt::settings_pack::proxy_type_t proxy_type)
|
||||
{
|
||||
lt::aux::proxy_settings ps;
|
||||
ps.type = proxy_type;
|
||||
ps.proxy_hostnames = false;
|
||||
if (proxy_type != settings_pack::none)
|
||||
{
|
||||
ps.hostname = "50.50.50.50";
|
||||
ps.port = 4444;
|
||||
ps.username = "testuser";
|
||||
ps.password = "testpass";
|
||||
}
|
||||
return ps;
|
||||
}
|
||||
|
||||
void run_test(lt::aux::proxy_settings ps, std::string url, int expect_size, int expect_status
|
||||
, boost::system::error_condition expect_error, std::vector<int> expect_counters)
|
||||
{
|
||||
using sim::asio::ip::address_v4;
|
||||
sim_config network_cfg;
|
||||
sim::simulation sim{network_cfg};
|
||||
|
||||
// allow sparse expected counters
|
||||
expect_counters.resize(num_counters, 0);
|
||||
|
||||
sim::asio::io_service web_server(sim, address_v4::from_string("10.0.0.2"));
|
||||
sim::asio::io_service ios(sim, address_v4::from_string("10.0.0.1"));
|
||||
sim::asio::io_service proxy_ios(sim, address_v4::from_string("50.50.50.50"));
|
||||
lt::resolver res(ios);
|
||||
|
||||
sim::http_server http(web_server, 8080);
|
||||
sim::socks_server socks(proxy_ios, 4444, ps.type == settings_pack::socks4 ? 4 : 5);
|
||||
|
||||
char data_buffer[4000];
|
||||
std::generate(data_buffer, data_buffer + sizeof(data_buffer), &std::rand);
|
||||
|
||||
std::vector<int> counters(num_counters, 0);
|
||||
|
||||
http.register_handler("/test_file"
|
||||
, [&data_buffer,&counters](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
{
|
||||
++counters[test_file_req];
|
||||
print_http_header(headers);
|
||||
TEST_EQUAL(method, "GET");
|
||||
return sim::send_response(200, "OK", 1337).append(data_buffer, 1337);
|
||||
});
|
||||
|
||||
http.register_handler("/chunked_encoding"
|
||||
, [&data_buffer,&counters](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
{
|
||||
++counters[chunked_req];
|
||||
print_http_header(headers);
|
||||
TEST_EQUAL(method, "GET");
|
||||
|
||||
// there's no content length with chunked encoding
|
||||
return "HTTP/1.1 200 OK\r\nTransfer-encoding: Chunked\r\n\r\n"
|
||||
+ chunk_string(std::string(data_buffer, 1337));
|
||||
});
|
||||
|
||||
http.register_handler("/test_file.gz"
|
||||
, [&data_buffer,&counters](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
{
|
||||
++counters[test_file_gz_req];
|
||||
print_http_header(headers);
|
||||
TEST_EQUAL(method, "GET");
|
||||
|
||||
char const* extra_headers[4] = {"Content-Encoding: gzip\r\n", "", "", ""};
|
||||
unsigned char const gzheader[] = {
|
||||
0x1f , 0x8b , 0x08 , 0x00 // ID, compression=deflate, flags=0
|
||||
, 0x00 , 0x00 , 0x00 , 0x00 // mtime=0
|
||||
, 0x00, 0x01 // extra headers, OS
|
||||
, 0x01 // last block, uncompressed
|
||||
, 0x39 , 0x05, 0xc6 , 0xfa // length = 1337 (little endian 16 bit and inverted)
|
||||
};
|
||||
unsigned char trailer[8] = { 0, 0, 0, 0, 0x39, 0x05, 0x00, 0x00 };
|
||||
boost::crc_32_type crc;
|
||||
crc.process_bytes(data_buffer, 1337);
|
||||
boost::uint32_t checksum = crc.checksum();
|
||||
trailer[0] = checksum >> 24;
|
||||
trailer[1] = (checksum >> 16) & 0xff;
|
||||
trailer[2] = (checksum >> 8) & 0xff;
|
||||
trailer[3] = (checksum) & 0xff;
|
||||
|
||||
std::string ret = sim::send_response(200, "OK", 1337 + sizeof(gzheader)
|
||||
+ sizeof(trailer), extra_headers);
|
||||
ret.append(std::string((char const*)gzheader, sizeof(gzheader)));
|
||||
ret.append(data_buffer, 1337);
|
||||
ret.append(std::string((char const*)trailer, sizeof(trailer)));
|
||||
return ret;
|
||||
});
|
||||
|
||||
http.register_handler("/redirect"
|
||||
, [&data_buffer,&counters](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
{
|
||||
++counters[redirect_req];
|
||||
TEST_EQUAL(method, "GET");
|
||||
return "HTTP/1.1 301 Moved Temporarily\r\n"
|
||||
"Location: /test_file\r\n"
|
||||
"\r\n";
|
||||
});
|
||||
|
||||
http.register_handler("/relative/redirect"
|
||||
, [&data_buffer,&counters](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
{
|
||||
++counters[rel_redirect_req];
|
||||
TEST_EQUAL(method, "GET");
|
||||
return "HTTP/1.1 301 Moved Temporarily\r\n"
|
||||
"Location: ../test_file\r\n"
|
||||
"\r\n";
|
||||
});
|
||||
|
||||
http.register_handler("/infinite/redirect"
|
||||
, [&data_buffer,&counters](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
{
|
||||
++counters[inf_redirect_req];
|
||||
TEST_EQUAL(method, "GET");
|
||||
return "HTTP/1.1 301 Moved Temporarily\r\n"
|
||||
"Location: /infinite/redirect\r\n"
|
||||
"\r\n";
|
||||
});
|
||||
|
||||
auto c = test_request(ios, res, url, data_buffer, expect_size
|
||||
, expect_status, expect_error, ps, &counters[connect_handler]
|
||||
, &counters[handler]);
|
||||
|
||||
error_code e;
|
||||
sim.run(e);
|
||||
|
||||
if (e) std::cerr << " run failed: " << e.message() << std::endl;
|
||||
TEST_EQUAL(e, error_code());
|
||||
|
||||
TEST_EQUAL(counters.size(), expect_counters.size());
|
||||
for (int i = 0; i < counters.size(); ++i)
|
||||
{
|
||||
if (counters[i] != expect_counters[i]) fprintf(stderr, "i=%d\n", i);
|
||||
TEST_EQUAL(counters[i], expect_counters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TORRENT_TEST(http_connection)
|
||||
{
|
||||
lt::aux::proxy_settings ps = make_proxy_settings(settings_pack::none);
|
||||
run_suite(ps);
|
||||
}
|
||||
|
||||
TORRENT_TEST(http_connection_socks4)
|
||||
{
|
||||
lt::aux::proxy_settings ps = make_proxy_settings(settings_pack::socks4);
|
||||
run_suite(ps);
|
||||
}
|
||||
|
||||
TORRENT_TEST(http_connection_socks5)
|
||||
{
|
||||
lt::aux::proxy_settings ps = make_proxy_settings(settings_pack::socks5);
|
||||
run_suite(ps);
|
||||
}
|
||||
|
||||
TORRENT_TEST(http_connection_socks5_proxy_names)
|
||||
{
|
||||
lt::aux::proxy_settings ps = make_proxy_settings(settings_pack::socks5);
|
||||
ps.proxy_hostnames = true;
|
||||
run_suite(ps);
|
||||
}
|
||||
|
||||
TORRENT_TEST(http_connection_socks_error)
|
||||
{
|
||||
// if we set up to user a proxy that does not exist, expect failure!
|
||||
using sim::asio::ip::address_v4;
|
||||
sim_config network_cfg;
|
||||
sim::simulation sim{network_cfg};
|
||||
|
||||
sim::asio::io_service web_server(sim, address_v4::from_string("10.0.0.2"));
|
||||
sim::asio::io_service ios(sim, address_v4::from_string("10.0.0.1"));
|
||||
lt::resolver res(ios);
|
||||
|
||||
sim::http_server http(web_server, 8080);
|
||||
|
||||
lt::aux::proxy_settings ps;
|
||||
ps.hostname = "50.50.50.50";
|
||||
ps.port = 4444;
|
||||
ps.username = "testuser";
|
||||
ps.password = "testpass";
|
||||
ps.type = settings_pack::socks5;
|
||||
|
||||
char data_buffer[4000];
|
||||
std::generate(data_buffer, data_buffer + sizeof(data_buffer), &std::rand);
|
||||
|
||||
http.register_handler("/test_file"
|
||||
, [&data_buffer](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
{
|
||||
print_http_header(headers);
|
||||
TEST_CHECK(false && "we're not supposed to get here!");
|
||||
return sim::send_response(200, "OK", 1337).append(data_buffer, 1337);
|
||||
});
|
||||
|
||||
int connect_counter = 0;
|
||||
int handler_counter = 0;
|
||||
auto c = test_request(ios, res, "http://10.0.0.2:8080/test_file"
|
||||
, data_buffer, -1, -1
|
||||
, error_condition(boost::system::errc::connection_refused, boost::system::generic_category())
|
||||
, ps, &connect_counter, &handler_counter);
|
||||
|
||||
error_code e;
|
||||
sim.run(e);
|
||||
|
||||
if (e) std::cerr << " run failed: " << e.message() << std::endl;
|
||||
TEST_EQUAL(e, error_code());
|
||||
}
|
||||
|
||||
// TODO: test http proxy
|
||||
// TODO: test socks5 with password
|
||||
// TODO: test SSL
|
||||
// TODO: test keepalive
|
||||
|
@@ -140,7 +140,7 @@ void test_interval(int interval)
|
||||
|
||||
http.register_handler("/announce"
|
||||
, [&announces,interval,start](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
, std::map<std::string, std::string>&)
|
||||
{
|
||||
boost::uint32_t seconds = chrono::duration_cast<lt::seconds>(
|
||||
lt::clock_type::now() - start).count();
|
||||
@@ -185,7 +185,7 @@ void test_completed()
|
||||
|
||||
http.register_handler("/announce"
|
||||
, [&announces,interval,start](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
, std::map<std::string, std::string>&)
|
||||
{
|
||||
TEST_EQUAL(method, "GET");
|
||||
announces.push_back(req);
|
||||
@@ -309,15 +309,12 @@ TORRENT_TEST(ipv6_support)
|
||||
sim::http_server http_v4(web_server_v4, 8080);
|
||||
sim::http_server http_v6(web_server_v6, 8080);
|
||||
|
||||
// the timestamps (in seconds) of all announces
|
||||
std::vector<std::string> announces;
|
||||
|
||||
int v4_announces = 0;
|
||||
int v6_announces = 0;
|
||||
|
||||
http_v4.register_handler("/announce"
|
||||
, [&v4_announces](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
, std::map<std::string, std::string>&)
|
||||
{
|
||||
++v4_announces;
|
||||
TEST_EQUAL(method, "GET");
|
||||
@@ -329,7 +326,7 @@ TORRENT_TEST(ipv6_support)
|
||||
|
||||
http_v6.register_handler("/announce"
|
||||
, [&v6_announces](std::string method, std::string req
|
||||
, std::map<std::string, std::string>& headers)
|
||||
, std::map<std::string, std::string>&)
|
||||
{
|
||||
++v6_announces;
|
||||
TEST_EQUAL(method, "GET");
|
||||
|
@@ -201,7 +201,7 @@ namespace libtorrent
|
||||
e.file_ptr = boost::make_shared<file>();
|
||||
if (!e.file_ptr)
|
||||
{
|
||||
ec = error_code(ENOMEM, get_posix_category());
|
||||
ec = error_code(boost::system::errc::not_enough_memory, generic_category());
|
||||
return e.file_ptr;
|
||||
}
|
||||
std::string full_path = fs.file_path(file_index, p);
|
||||
|
@@ -119,6 +119,8 @@ namespace libtorrent
|
||||
const unsigned char* buffer = reinterpret_cast<const unsigned char*>(buf);
|
||||
const int total_size = size;
|
||||
|
||||
// gzip is defined in https://tools.ietf.org/html/rfc1952
|
||||
|
||||
// The zip header cannot be shorter than 10 bytes
|
||||
if (size < 10 || buf == 0) return -1;
|
||||
|
||||
@@ -129,9 +131,14 @@ namespace libtorrent
|
||||
int flags = buffer[3];
|
||||
|
||||
// check for reserved flag and make sure it's compressed with the correct metod
|
||||
// we only support deflate
|
||||
if (method != 8 || (flags & FRESERVED) != 0) return -1;
|
||||
|
||||
// skip time, xflags, OS code
|
||||
// skip time, xflags, OS code. The first 10 bytes of the header:
|
||||
// +---+---+---+---+---+---+---+---+---+---+
|
||||
// |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->)
|
||||
// +---+---+---+---+---+---+---+---+---+---+
|
||||
|
||||
size -= 10;
|
||||
buffer += 10;
|
||||
|
||||
|
@@ -379,6 +379,9 @@ void http_connection::start(std::string const& hostname, int port
|
||||
return;
|
||||
}
|
||||
|
||||
m_endpoints.clear();
|
||||
m_next_ep = 0;
|
||||
|
||||
#if TORRENT_USE_I2P
|
||||
if (is_i2p)
|
||||
{
|
||||
@@ -409,8 +412,6 @@ void http_connection::start(std::string const& hostname, int port
|
||||
#if defined TORRENT_ASIO_DEBUGGING
|
||||
add_outstanding_async("http_connection::on_resolve");
|
||||
#endif
|
||||
m_endpoints.clear();
|
||||
m_next_ep = 0;
|
||||
m_resolver.async_resolve(hostname, m_resolve_flags
|
||||
, boost::bind(&http_connection::on_resolve
|
||||
, me, _1, _2));
|
||||
@@ -450,7 +451,6 @@ void http_connection::on_timeout(boost::weak_ptr<http_connection> p
|
||||
else
|
||||
{
|
||||
c->callback(boost::asio::error::timed_out);
|
||||
c->close(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -519,7 +519,6 @@ void http_connection::on_i2p_resolve(error_code const& e
|
||||
if (e)
|
||||
{
|
||||
callback(e);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
connect_i2p_tracker(destination);
|
||||
@@ -534,10 +533,7 @@ void http_connection::on_resolve(error_code const& e
|
||||
#endif
|
||||
if (e)
|
||||
{
|
||||
boost::shared_ptr<http_connection> me(shared_from_this());
|
||||
|
||||
callback(e);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
TORRENT_ASSERT(!addresses.empty());
|
||||
@@ -581,19 +577,32 @@ void http_connection::connect()
|
||||
&& (m_proxy.type == settings_pack::socks5
|
||||
|| m_proxy.type == settings_pack::socks5_pw))
|
||||
{
|
||||
// we're using a socks proxy and we're resolving
|
||||
// hostnames through it
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (m_ssl)
|
||||
// test to see if m_hostname really just is an IP (and not a hostname). If it
|
||||
// is, ec will be represent "success". If so, don't set it as the socks5
|
||||
// hostname, just connect to the IP
|
||||
error_code ec;
|
||||
address adr = address::from_string(m_hostname, ec);
|
||||
|
||||
if (ec)
|
||||
{
|
||||
TORRENT_ASSERT(m_sock.get<ssl_stream<socks5_stream> >());
|
||||
m_sock.get<ssl_stream<socks5_stream> >()->next_layer().set_dst_name(m_hostname);
|
||||
// we're using a socks proxy and we're resolving
|
||||
// hostnames through it
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
if (m_ssl)
|
||||
{
|
||||
TORRENT_ASSERT(m_sock.get<ssl_stream<socks5_stream> >());
|
||||
m_sock.get<ssl_stream<socks5_stream> >()->next_layer().set_dst_name(m_hostname);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
TORRENT_ASSERT(m_sock.get<socks5_stream>());
|
||||
m_sock.get<socks5_stream>()->set_dst_name(m_hostname);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
TORRENT_ASSERT(m_sock.get<socks5_stream>());
|
||||
m_sock.get<socks5_stream>()->set_dst_name(m_hostname);
|
||||
m_endpoints[0].address(adr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,9 +649,7 @@ void http_connection::on_connect(error_code const& e)
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::shared_ptr<http_connection> me(shared_from_this());
|
||||
callback(e);
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,7 +671,6 @@ void http_connection::callback(error_code e, char* data, int size)
|
||||
if (ec)
|
||||
{
|
||||
if (m_handler) m_handler(ec, m_parser, data, size, *this);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
size = int(buf.size());
|
||||
@@ -692,9 +698,7 @@ void http_connection::on_write(error_code const& e)
|
||||
|
||||
if (e)
|
||||
{
|
||||
boost::shared_ptr<http_connection> me(shared_from_this());
|
||||
callback(e);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -763,7 +767,6 @@ void http_connection::on_read(error_code const& e
|
||||
size = m_parser.get_body().left();
|
||||
}
|
||||
callback(ec, data, size);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -771,7 +774,6 @@ void http_connection::on_read(error_code const& e
|
||||
{
|
||||
TORRENT_ASSERT(bytes_transferred == 0);
|
||||
callback(e);
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -805,7 +807,6 @@ void http_connection::on_read(error_code const& e
|
||||
{
|
||||
// missing location header
|
||||
callback(error_code(errors::http_missing_location));
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -825,7 +826,7 @@ void http_connection::on_read(error_code const& e
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
m_redirects = 0;
|
||||
}
|
||||
|
||||
@@ -861,7 +862,6 @@ void http_connection::on_read(error_code const& e
|
||||
// if we've reached the size limit, terminate the connection and
|
||||
// report the error
|
||||
callback(error_code(boost::system::errc::file_too_large, generic_category()));
|
||||
close();
|
||||
return;
|
||||
}
|
||||
int amount_to_read = m_recvbuffer.size() - m_read_pos;
|
||||
|
@@ -141,8 +141,6 @@ namespace libtorrent
|
||||
else
|
||||
{
|
||||
(*h)(socks_error::unsupported_version);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,8 +175,6 @@ namespace libtorrent
|
||||
if (version < m_version)
|
||||
{
|
||||
(*h)(socks_error::unsupported_version);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,8 +187,6 @@ namespace libtorrent
|
||||
if (m_user.empty())
|
||||
{
|
||||
(*h)(socks_error::username_required);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -214,8 +208,6 @@ namespace libtorrent
|
||||
else
|
||||
{
|
||||
(*h)(socks_error::unsupported_authentication_method);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -253,16 +245,12 @@ namespace libtorrent
|
||||
if (version != 1)
|
||||
{
|
||||
(*h)(socks_error::unsupported_authentication_version);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
(*h)(socks_error::authentication_error);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -309,8 +297,6 @@ namespace libtorrent
|
||||
if (!m_remote_endpoint.address().is_v4())
|
||||
{
|
||||
(*h)(boost::asio::error::address_family_not_supported);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
m_buffer.resize(m_user.size() + 9);
|
||||
@@ -326,8 +312,6 @@ namespace libtorrent
|
||||
else
|
||||
{
|
||||
(*h)(socks_error::unsupported_version);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -376,8 +360,6 @@ namespace libtorrent
|
||||
if (version < m_version)
|
||||
{
|
||||
(*h)(socks_error::unsupported_version);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
if (response != 0)
|
||||
@@ -394,7 +376,6 @@ namespace libtorrent
|
||||
case 8: ec = boost::asio::error::address_family_not_supported; break;
|
||||
}
|
||||
(*h)(ec);
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
p += 1; // reserved
|
||||
@@ -437,8 +418,6 @@ namespace libtorrent
|
||||
else
|
||||
{
|
||||
(*h)(boost::asio::error::address_family_not_supported);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
m_buffer.resize(m_buffer.size() + extra_bytes);
|
||||
@@ -455,8 +434,6 @@ namespace libtorrent
|
||||
if (version != 0)
|
||||
{
|
||||
(*h)(socks_error::general_failure);
|
||||
error_code ec;
|
||||
close(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -487,16 +464,14 @@ namespace libtorrent
|
||||
return;
|
||||
}
|
||||
|
||||
int code = socks_error::general_failure;
|
||||
error_code ec(socks_error::general_failure, get_socks_category());
|
||||
switch (response)
|
||||
{
|
||||
case 91: code = socks_error::authentication_error; break;
|
||||
case 92: code = socks_error::no_identd; break;
|
||||
case 93: code = socks_error::identd_error; break;
|
||||
case 91: ec = boost::asio::error::connection_refused; break;
|
||||
case 92: ec = socks_error::no_identd; break;
|
||||
case 93: ec = socks_error::identd_error; break;
|
||||
}
|
||||
error_code ec(code, get_socks_category());
|
||||
(*h)(ec);
|
||||
close(ec);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -310,6 +310,10 @@ EXPORT int main(int argc, char const* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// get proper interleaving of stderr and stdout
|
||||
setbuf(stdout, NULL);
|
||||
setbuf(stderr, NULL);
|
||||
|
||||
_g_test_idx = i;
|
||||
current_test = &t;
|
||||
|
||||
|
@@ -219,26 +219,15 @@ void run_suite(std::string const& protocol
|
||||
stop_web_server();
|
||||
}
|
||||
|
||||
TORRENT_TEST(no_proxy) { run_suite("http", settings_pack::none); }
|
||||
TORRENT_TEST(socks4) { run_suite("http", settings_pack::socks4); }
|
||||
TORRENT_TEST(socks5) { run_suite("http", settings_pack::socks5); }
|
||||
TORRENT_TEST(socks5_pw) { run_suite("http", settings_pack::socks5_pw); }
|
||||
TORRENT_TEST(http) { run_suite("http", settings_pack::http); }
|
||||
TORRENT_TEST(http_pw) { run_suite("http", settings_pack::http_pw); }
|
||||
|
||||
#ifdef TORRENT_USE_OPENSSL
|
||||
TORRENT_TEST(no_proxy_ssl) { run_suite("https", settings_pack::none); }
|
||||
TORRENT_TEST(socks4_ssl) { run_suite("https", settings_pack::socks4); }
|
||||
TORRENT_TEST(socks5_ssl) { run_suite("https", settings_pack::socks5); }
|
||||
TORRENT_TEST(socks5_pw_ssl) { run_suite("https", settings_pack::socks5_pw); }
|
||||
TORRENT_TEST(http_ssl) { run_suite("https", settings_pack::http); }
|
||||
TORRENT_TEST(http_pw_ssl) { run_suite("https", settings_pack::http_pw); }
|
||||
#endif // USE_OPENSSL
|
||||
|
||||
TORRENT_TEST(chunked_encoding)
|
||||
{
|
||||
run_suite("http", settings_pack::none, flag_chunked_encoding | flag_keepalive);
|
||||
}
|
||||
TORRENT_TEST(no_keepalive)
|
||||
{
|
||||
run_suite("http", settings_pack::none, 0);
|
||||
|
@@ -104,7 +104,7 @@ struct test_storage : default_storage
|
||||
{
|
||||
std::cerr << "storage written: " << m_written << " limit: " << m_limit << std::endl;
|
||||
error_code ec;
|
||||
ec = error_code(boost::system::errc::no_space_on_device, get_posix_category());
|
||||
ec = error_code(boost::system::errc::no_space_on_device, generic_category());
|
||||
se.ec = ec;
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user