From 7a8ffc2f2d51574df007585d3dd11588e4670833 Mon Sep 17 00:00:00 2001 From: Andrei Kurushin Date: Thu, 23 Feb 2017 06:46:33 +0300 Subject: [PATCH] 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 --- simulation/libsimulator | 2 +- simulation/test_web_seed.cpp | 69 ++++++++++++++++++++++++++++++++++-- src/web_peer_connection.cpp | 51 +++++++++++++++----------- test/main.cpp | 19 ++++------ 4 files changed, 106 insertions(+), 35 deletions(-) diff --git a/simulation/libsimulator b/simulation/libsimulator index 36b46fc2c..60d786b8f 160000 --- a/simulation/libsimulator +++ b/simulation/libsimulator @@ -1 +1 @@ -Subproject commit 36b46fc2c316d34714315c04c87cf74de6efae90 +Subproject commit 60d786b8fa6ddaacdc98bdf691220660bc194494 diff --git a/simulation/test_web_seed.cpp b/simulation/test_web_seed.cpp index fd3845399..c525635fb 100644 --- a/simulation/test_web_seed.cpp +++ b/simulation/test_web_seed.cpp @@ -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 @@ -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(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( + [¶ms](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(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) diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index 9cd1a9da0..c1a8fae79 100644 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -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 files = info.orig_files().map_block(req.piece, req.start , req.length); - for (std::vector::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(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 const& headers = m_parser.headers(); - for (std::multimap::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 diff --git a/test/main.cpp b/test/main.cpp index 641be329b..fe7b79a52 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -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)