fix web_seed redirects (#1651)

fix web_seed redirects, make all redirects ephemeral (to avoid resume serialization). remove dup slash for redirects in proxy mode. consolidate {convert_path_to_posix,escape_path} to escape_file_path
This commit is contained in:
Andrei Kurushin 2017-02-23 06:46:33 +03:00 committed by Arvid Norberg
parent 7204082088
commit 7a8ffc2f2d
4 changed files with 106 additions and 35 deletions

@ -1 +1 @@
Subproject commit 36b46fc2c316d34714315c04c87cf74de6efae90
Subproject commit 60d786b8fa6ddaacdc98bdf691220660bc194494

View File

@ -36,11 +36,13 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/alert_types.hpp"
#include "simulator/http_server.hpp"
#include "simulator/http_proxy.hpp"
#include "settings.hpp"
#include "libtorrent/create_torrent.hpp"
#include "simulator/simulator.hpp"
#include "setup_swarm.hpp"
#include "utils.hpp"
#include "make_proxy_settings.hpp"
#include "simulator/utils.hpp"
#include <iostream>
@ -49,9 +51,10 @@ using namespace libtorrent;
namespace lt = libtorrent;
int const piece_size = 0x4000;
add_torrent_params create_torrent(file_storage& fs, bool const pad_files = false)
{
int const piece_size = 0x4000;
libtorrent::create_torrent t(fs, piece_size
, pad_files ? piece_size : -1
, pad_files ? create_torrent::optimize_alignment : 0);
@ -221,7 +224,7 @@ std::string generate_content(lt::file_storage const& fs, file_index_t file
, std::int64_t offset, std::int64_t len)
{
std::string ret;
ret.reserve(len);
ret.reserve(lt::aux::numeric_cast<std::size_t>(len));
std::int64_t const file_offset = fs.file_offset(file);
int const piece_size = fs.piece_length();
for (std::int64_t i = offset + file_offset; i < offset + file_offset + len; ++i)
@ -383,6 +386,68 @@ TORRENT_TEST(multi_file_redirect)
TEST_EQUAL(seeding, true);
}
// test web_seed redirect through proxy
TORRENT_TEST(multi_file_redirect_through_proxy)
{
using namespace libtorrent;
file_storage fs;
fs.add_file(combine_path("foo", "1"), 0xc000);
fs.add_file(combine_path("foo", "2"), 0xc030);
lt::add_torrent_params params = ::create_torrent(fs);
params.url_seeds.push_back("http://2.2.2.2:8080/");
bool seeding = false;
run_test(
[&params](lt::session& ses)
{
settings_pack pack;
pack.set_int(settings_pack::proxy_type, settings_pack::http);
pack.set_str(settings_pack::proxy_hostname, "50.50.50.50");
pack.set_str(settings_pack::proxy_username, "testuser");
pack.set_str(settings_pack::proxy_password, "testpass");
pack.set_int(settings_pack::proxy_port, 4445);
pack.set_bool(settings_pack::proxy_hostnames, true);
ses.apply_settings(pack);
ses.async_add_torrent(params);
},
[&](lt::session& ses, lt::alert const* alert) {
if (lt::alert_cast<lt::torrent_finished_alert>(alert)) {
seeding = true;
}
},
[&fs](sim::simulation& sim, lt::session& ses)
{
sim::asio::io_service proxy_ios(sim, address_v4::from_string("50.50.50.50"));
sim::http_proxy http_p(proxy_ios, 4445);
// http1 is the root web server that will just redirect requests to
// other servers
sim::asio::io_service web_server1(sim, address_v4::from_string("2.2.2.2"));
sim::http_server http1(web_server1, 8080);
// redirect file 1 and file 2 to different servers
http1.register_redirect("/foo/1", "http://3.3.3.3:4444/bla/file1");
http1.register_redirect("/foo/2", "http://4.4.4.4:9999/bar/file2");
// server for file 1
sim::asio::io_service web_server2(sim, address_v4::from_string("3.3.3.3"));
sim::http_server http2(web_server2, 4444);
serve_content_for(http2, "/bla/file1", fs, file_index_t(0));
// server for file 2
sim::asio::io_service web_server3(sim, address_v4::from_string("4.4.4.4"));
sim::http_server http3(web_server3, 9999);
serve_content_for(http3, "/bar/file2", fs, file_index_t(1));
sim.run();
}
);
TEST_EQUAL(seeding, true);
}
// this is expected to fail, since the files are not aligned and redirected to
// separate servers, without pad files
TORRENT_TEST(multi_file_unaligned_redirect)

View File

@ -57,6 +57,8 @@ namespace libtorrent
{
constexpr int request_size_overhead = 5000;
std::string escape_file_path(file_storage const& storage, file_index_t index);
web_peer_connection::web_peer_connection(peer_connection_args const& pack
, web_seed_t& web)
: web_connection_base(pack, web)
@ -107,11 +109,7 @@ web_peer_connection::web_peer_connection(peer_connection_args const& pack
if (!m_url.empty() && m_url[m_url.size() - 1] == '/')
{
std::string tmp = t->torrent_file().files().file_path(file_index_t(0));
#ifdef TORRENT_WINDOWS
convert_path_to_posix(tmp);
#endif
m_url += escape_path(tmp);
m_url += escape_file_path(t->torrent_file().files(), file_index_t(0));
}
}
@ -126,6 +124,15 @@ web_peer_connection::web_peer_connection(peer_connection_args const& pack
#endif
}
std::string escape_file_path(file_storage const& storage, file_index_t index)
{
std::string new_path { storage.file_path(index) };
#ifdef TORRENT_WINDOWS
convert_path_to_posix(new_path);
#endif
return escape_path(new_path);
}
void web_peer_connection::on_connected()
{
if (m_web->have_files.empty())
@ -380,11 +387,8 @@ void web_peer_connection::write_request(peer_request const& r)
std::vector<file_slice> files = info.orig_files().map_block(req.piece, req.start
, req.length);
for (std::vector<file_slice>::iterator i = files.begin();
i != files.end(); ++i)
for (auto const &f : files)
{
file_slice const& f = *i;
file_request_t file_req;
file_req.file_index = f.file_index;
file_req.start = f.offset;
@ -411,7 +415,10 @@ void web_peer_connection::write_request(peer_request const& r)
auto redirection = m_web->redirects.find(f.file_index);
if (redirection != m_web->redirects.end())
{
request += redirection->second;
auto const& redirect = redirection->second;
// in case of http proxy "request" already contains m_url with trailing slash, so let's skip dup slash
bool const trailing_slash = using_proxy && !redirect.empty() && redirect[0] == '/';
request.append(redirect, trailing_slash, std::string::npos);
}
else
{
@ -422,11 +429,7 @@ void web_peer_connection::write_request(peer_request const& r)
request += m_path;
}
std::string path = info.orig_files().file_path(f.file_index);
#ifdef TORRENT_WINDOWS
convert_path_to_posix(path);
#endif
request += escape_path(path);
request += escape_file_path(info.orig_files(), f.file_index);
}
request += " HTTP/1.1\r\n";
add_headers(request, m_settings, using_proxy);
@ -640,7 +643,15 @@ void web_peer_connection::handle_redirect(int const bytes_left)
// add_web_seed won't add duplicates. If we have already added an entry
// with this URL, we'll get back the existing entry
web_seed_t* web = t->add_web_seed(redirect_base, web_seed_entry::url_seed, m_external_auth, m_extra_headers);
// "ephemeral" flag should be set to avoid "web_seed_t" saving in resume data.
// E.g. original "web_seed_t" request url points to "http://example1.com/file1" and
// web server responses with redirect location "http://example2.com/subpath/file2".
// "handle_redirect" process this location to create new "web_seed_t"
// with base url=="http://example2.com/" and redirects[0]=="/subpath/file2").
// If we try to load resume with such "web_seed_t" then "web_peer_connection" will send
// request with wrong path "http://example2.com/file1" (cause "redirects" map is not serialized in resume)
web_seed_t* web = t->add_web_seed(redirect_base, web_seed_entry::url_seed, m_external_auth, m_extra_headers, true);
web->have_files.resize(t->torrent_file().num_files(), false);
// the new web seed we're adding only has this file for now
@ -649,6 +660,7 @@ void web_peer_connection::handle_redirect(int const bytes_left)
if (web->have_files.get_bit(file_index) == false)
{
web->have_files.set_bit(file_index);
if (web->peer_info.connection != nullptr)
{
peer_connection* pc = static_cast<peer_connection*>(web->peer_info.connection);
@ -677,7 +689,7 @@ void web_peer_connection::handle_redirect(int const bytes_left)
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "LOCATION", "%s", location.c_str());
#endif
t->add_web_seed(location, web_seed_entry::url_seed, m_external_auth, m_extra_headers);
t->add_web_seed(location, web_seed_entry::url_seed, m_external_auth, m_extra_headers, true);
// this web seed doesn't have any files. Don't try to request from it
// again this session
@ -780,9 +792,8 @@ void web_peer_connection::on_receive(error_code const& error
peer_log(peer_log_alert::info, "STATUS"
, "%d %s", m_parser.status_code(), m_parser.message().c_str());
std::multimap<std::string, std::string> const& headers = m_parser.headers();
for (std::multimap<std::string, std::string>::const_iterator i = headers.begin()
, end(headers.end()); i != end; ++i)
peer_log(peer_log_alert::info, "STATUS", " %s: %s", i->first.c_str(), i->second.c_str());
for (auto const &i : headers)
peer_log(peer_log_alert::info, "STATUS", " %s: %s", i.first.c_str(), i.second.c_str());
}
#endif

View File

@ -225,16 +225,16 @@ void change_directory(std::string const& f, error_code& ec)
#ifdef TORRENT_WINDOWS
#if TORRENT_USE_WSTRING
#define SetCurrentDirectory_ SetCurrentDirectoryW
std::wstring n = convert_to_wstring(f);
native_path_string const n = convert_to_wstring(f);
#else
#define SetCurrentDirectory_ SetCurrentDirectoryA
std::string const& n = convert_to_native(f);
native_path_string const n = convert_to_native(f);
#endif // TORRENT_USE_WSTRING
if (SetCurrentDirectory_(n.c_str()) == 0)
ec.assign(GetLastError(), system_category());
#undef SetCurrentDirectory_
#else
std::string n = convert_to_native(f);
native_path_string const n = convert_to_native_path_string(f);
int ret = ::chdir(n.c_str());
if (ret != 0)
ec.assign(errno, system_category());
@ -361,10 +361,8 @@ EXPORT int main(int argc, char const* argv[])
#else
process_id = getpid();
#endif
std::string root_dir = current_working_directory();
char dir[40];
snprintf(dir, sizeof(dir), "test_tmp_%u", process_id);
std::string unit_dir_prefix = combine_path(root_dir, dir);
std::string const root_dir = current_working_directory();
std::string const unit_dir_prefix = combine_path(root_dir, "test_tmp_" + std::to_string(process_id) + "_");
std::printf("test: %s\ncwd_prefix = \"%s\"\nrnd = %x\n"
, executable, unit_dir_prefix.c_str(), libtorrent::random(0xffffffff));
@ -383,10 +381,7 @@ EXPORT int main(int argc, char const* argv[])
if (filter && tests_to_run.count(_g_unit_tests[i].name) == 0)
continue;
std::string unit_dir = unit_dir_prefix;
char i_str[40];
snprintf(i_str, sizeof(i_str), "%u", i);
unit_dir.append(i_str);
std::string const unit_dir = unit_dir_prefix + std::to_string(i);
error_code ec;
create_directory(unit_dir, ec);
if (ec)