diff --git a/ChangeLog b/ChangeLog index 17fbe3339..4c7e966ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ + * added support for HTTP redirection support for web seeds. + * fixed race condition when accessing a torrent that was checking its + fast resume data. * fixed a bug in the DHT which could be triggered if the network was dropped or extremely rare cases. * if the download rate is limited, web seeds will now only use left-over diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 29afe0263..87ea0f7bf 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -860,7 +860,9 @@ int main(int ac, char* av[]) out << state_str[s.state] << " "; } - h.get_peer_info(peers); + if ((print_downloads && s.state != torrent_status::seeding) + || print_peers) + h.get_peer_info(peers); if (s.state != torrent_status::seeding) { diff --git a/examples/make_torrent.cpp b/examples/make_torrent.cpp index 5b5739cd0..cce38af5f 100755 --- a/examples/make_torrent.cpp +++ b/examples/make_torrent.cpp @@ -77,10 +77,11 @@ int main(int argc, char* argv[]) path::default_name_check(no_check); - if (argc != 4) + if (argc != 4 && argc != 5) { std::cerr << "usage: make_torrent " - " \n"; + " " + "[url-seed]\n"; return 1; } @@ -113,6 +114,9 @@ int main(int argc, char* argv[]) t.set_creator(creator_str); + if (argc == 5) + t.add_url_seed(argv[4]); + // create the torrent and print it to out entry e = t.create_torrent(); libtorrent::bencode(std::ostream_iterator(out), e); diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 130b60747..f4ece1d80 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -359,8 +359,6 @@ namespace libtorrent // open files by this session. file_pool m_files; - // does the actual disconnections - // that are queued up in m_disconnect_peer void second_tick(asio::error_code const& e); boost::posix_time::ptime m_last_tick; diff --git a/include/libtorrent/http_tracker_connection.hpp b/include/libtorrent/http_tracker_connection.hpp index db71237f9..02dd20927 100755 --- a/include/libtorrent/http_tracker_connection.hpp +++ b/include/libtorrent/http_tracker_connection.hpp @@ -77,6 +77,7 @@ namespace libtorrent bool finished() const { return m_finished; } boost::tuple incoming(buffer::const_interval recv_buffer); int body_start() const { return m_body_start_pos; } + int content_length() const { return m_content_length; } void reset(); private: diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 60182dadf..ec47ff050 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -117,7 +117,8 @@ namespace libtorrent aux::session_impl& ses , boost::weak_ptr t , boost::shared_ptr s - , tcp::endpoint const& remote); + , tcp::endpoint const& remote + , tcp::endpoint const& proxy); // with this constructor we have been contacted and we still don't // know which torrent the connection belongs to @@ -204,13 +205,11 @@ namespace libtorrent boost::shared_ptr get_socket() const { return m_socket; } tcp::endpoint const& remote() const { return m_remote; } + tcp::endpoint const& proxy() const { return m_remote_proxy; } std::vector const& get_bitfield() const; // this will cause this peer_connection to be disconnected. - // what it does is that it puts a reference to it in - // m_ses.m_disconnect_peer list, which will be scanned in the - // mainloop to disconnect peers. void disconnect(); bool is_disconnecting() const { return m_disconnecting; } @@ -382,7 +381,7 @@ namespace libtorrent bool packet_finished() const { assert(m_recv_pos <= m_packet_size); - return m_packet_size == m_recv_pos; + return m_packet_size <= m_recv_pos; } void setup_receive(); @@ -475,7 +474,13 @@ namespace libtorrent boost::posix_time::ptime m_last_sent; boost::shared_ptr m_socket; + // this is the peer we're actually talking to + // it may not necessarily be the peer we're + // connected to, in case we use a proxy tcp::endpoint m_remote; + + // if we use a proxy, this is the address to it + tcp::endpoint m_remote_proxy; // this is the torrent this connection is // associated with. If the connection is an diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index d1aa40632..aeed80291 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -365,6 +365,11 @@ namespace libtorrent // this is the asio callback that is called when a name // lookup for a web seed is completed. void on_name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , std::string url, tcp::endpoint proxy); + + // this is the asio callback that is called when a name + // lookup for a proxy for a web seed is completed. + void on_proxy_name_lookup(asio::error_code const& e, tcp::resolver::iterator i , std::string url); // this is called when the torrent has finished. i.e. diff --git a/include/libtorrent/web_peer_connection.hpp b/include/libtorrent/web_peer_connection.hpp index b1fcbbea2..a8ed22820 100755 --- a/include/libtorrent/web_peer_connection.hpp +++ b/include/libtorrent/web_peer_connection.hpp @@ -98,6 +98,7 @@ namespace libtorrent , boost::weak_ptr t , boost::shared_ptr s , tcp::endpoint const& remote + , tcp::endpoint const& proxy , std::string const& url); ~web_peer_connection(); diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index cf429cb0b..a053b7978 100755 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -79,7 +79,7 @@ namespace libtorrent , boost::weak_ptr tor , shared_ptr s , tcp::endpoint const& remote) - : peer_connection(ses, tor, s, remote) + : peer_connection(ses, tor, s, remote, tcp::endpoint()) , m_state(read_protocol_length) #ifndef TORRENT_DISABLE_EXTENSIONS , m_supports_extensions(false) @@ -310,7 +310,7 @@ namespace libtorrent buffer::const_interval recv_buffer = receive_buffer(); // are we currently receiving a 'piece' message? if (m_state != read_packet - || (recv_buffer.end - recv_buffer.begin) < 9 + || recv_buffer.left() < 9 || recv_buffer[0] != msg_piece) return boost::optional(); @@ -328,7 +328,7 @@ namespace libtorrent p.piece_index = r.piece; p.block_index = r.start / t->block_size(); - p.bytes_downloaded = recv_buffer.end - recv_buffer.begin - 9; + p.bytes_downloaded = recv_buffer.left() - 9; p.full_block_bytes = r.length; return boost::optional(p); diff --git a/src/http_tracker_connection.cpp b/src/http_tracker_connection.cpp index 5e6e6ecbe..347e8a774 100755 --- a/src/http_tracker_connection.cpp +++ b/src/http_tracker_connection.cpp @@ -198,6 +198,20 @@ namespace libtorrent } catch(boost::bad_lexical_cast&) {} } + else if (name == "content-range") + { + std::stringstream range_str(value); + char dummy; + std::string bytes; + size_type range_start, range_end; + range_str >> bytes >> range_start >> dummy >> range_end; + if (!range_str || range_end < range_start) + { + throw std::runtime_error("invalid content-range in HTTP response: " + range_str.str()); + } + // the http range is inclusive + m_content_length = range_end - range_start + 1; + } // TODO: make sure we don't step outside of the buffer ++pos; diff --git a/src/kademlia/rpc_manager.cpp b/src/kademlia/rpc_manager.cpp index d95710668..fc7143dc9 100644 --- a/src/kademlia/rpc_manager.cpp +++ b/src/kademlia/rpc_manager.cpp @@ -386,6 +386,9 @@ void rpc_manager::reply_with_ping(msg& m, msg const& reply_to) } catch (std::exception& e) { +#ifndef NDEBUG + std::cerr << e.what() << "\n"; +#endif assert(false); } } diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 7bf1d0b4b..de060693d 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -75,7 +75,8 @@ namespace libtorrent session_impl& ses , boost::weak_ptr tor , shared_ptr s - , tcp::endpoint const& remote) + , tcp::endpoint const& remote + , tcp::endpoint const& proxy) : #ifndef NDEBUG m_last_choke(boost::posix_time::second_clock::universal_time() @@ -94,6 +95,7 @@ namespace libtorrent , m_last_sent(second_clock::universal_time()) , m_socket(s) , m_remote(remote) + , m_remote_proxy(proxy) , m_torrent(tor) , m_active(true) , m_peer_interested(false) @@ -593,7 +595,7 @@ namespace libtorrent // clear the request queue if the client isn't interested m_requests.clear(); - setup_send(); +// setup_send(); #ifdef TORRENT_VERBOSE_LOGGING using namespace boost::posix_time; @@ -701,8 +703,9 @@ namespace libtorrent return; } - // build a vector of all pieces - std::vector piece_list; + // let the torrent know which pieces the + // peer has + bool interesting = false; for (int i = 0; i < (int)m_have_piece.size(); ++i) { bool have = bitfield[i]; @@ -710,7 +713,10 @@ namespace libtorrent { m_have_piece[i] = true; ++m_num_pieces; - piece_list.push_back(i); + t->peer_has(i); + if (!t->have_piece(i) + && !t->picker().is_filtered(i)) + interesting = true; } else if (!have && m_have_piece[i]) { @@ -721,23 +727,7 @@ namespace libtorrent } } - // let the torrent know which pieces the - // peer has, in a shuffled order - bool interesting = false; - if (!t->is_seed()) - { - for (std::vector::reverse_iterator i = piece_list.rbegin(); - i != piece_list.rend(); ++i) - { - int index = *i; - t->peer_has(index); - if (!t->have_piece(index) - && !t->picker().is_filtered(index)) - interesting = true; - } - } - - if (piece_list.size() == m_have_piece.size()) + if (m_num_pieces == int(m_have_piece.size())) { #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " *** THIS IS A SEED ***\n"; @@ -1018,6 +1008,7 @@ namespace libtorrent if (picker.is_finished(block_finished)) { t->received_redundant_data(t->block_size()); + pol.block_finished(*this, block_finished); send_block_requests(); return; } @@ -1190,6 +1181,9 @@ namespace libtorrent assert(it != m_request_queue.end()); if (it == m_request_queue.end()) return; m_request_queue.erase(it); + + m_policy->block_finished(*this, block); + send_block_requests(); // since we found it in the request queue, it means it hasn't been // sent yet, so we don't have to send a cancel. return; @@ -1199,8 +1193,6 @@ namespace libtorrent m_download_queue.erase(it); } - send_block_requests(); - int block_offset = block.block_index * t->block_size(); int block_size = std::min((int)t->torrent_file().piece_size(block.piece_index)-block_offset, @@ -1215,6 +1207,9 @@ namespace libtorrent write_cancel(r); + m_policy->block_finished(*this, block); + send_block_requests(); + #ifdef TORRENT_VERBOSE_LOGGING using namespace boost::posix_time; (*m_logger) << to_simple_string(second_clock::universal_time()) @@ -1481,7 +1476,7 @@ namespace libtorrent assert(packet_size > 0); assert((int)m_recv_buffer.size() >= size); - + // TODO: replace with memmov std::copy(m_recv_buffer.begin() + size, m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.begin()); assert(m_recv_pos >= size); @@ -1492,7 +1487,7 @@ namespace libtorrent #endif m_packet_size = packet_size; - m_recv_buffer.resize(m_packet_size); + if (m_packet_size >= m_recv_pos) m_recv_buffer.resize(m_packet_size); } void peer_connection::second_tick(float tick_interval) @@ -1762,7 +1757,8 @@ namespace libtorrent (*m_logger) << "req bandwidth [ " << upload_channel << " ]\n"; #endif - t->request_bandwidth(upload_channel, self(), m_non_prioritized); + // the upload queue should not have non-prioritized peers + t->request_bandwidth(upload_channel, self(), /*m_non_prioritized*/ false); m_writing = true; } return; @@ -1775,7 +1771,7 @@ namespace libtorrent int sending_buffer = (m_current_send_buffer + 1) & 1; if (m_send_buffer[sending_buffer].empty()) { - // thise means we have to swap buffer, because there's no + // this means we have to swap buffer, because there's no // previous buffer we're still waiting for. std::swap(m_current_send_buffer, sending_buffer); m_write_pos = 0; @@ -1846,6 +1842,11 @@ namespace libtorrent void peer_connection::reset_recv_buffer(int packet_size) { assert(packet_size > 0); + if (m_recv_pos > m_packet_size) + { + cut_receive_buffer(m_packet_size, packet_size); + return; + } m_recv_pos = 0; m_packet_size = packet_size; if (int(m_recv_buffer.size()) < m_packet_size) @@ -1914,14 +1915,7 @@ namespace libtorrent m_last_receive = second_clock::universal_time(); m_recv_pos += bytes_transferred; - - // this will reset the m_recv_pos to 0 if the - // entire packet was received - // it is important that this is done before - // setup_receive() is called. Therefore, fire() is - // called before setup_receive(). - assert(m_recv_pos <= m_packet_size); - set_to_zero reset(m_recv_pos, m_recv_pos == m_packet_size); + assert(m_recv_pos <= int(m_recv_buffer.size())); { INVARIANT_CHECK; @@ -1930,9 +1924,6 @@ namespace libtorrent assert(m_packet_size > 0); - // do the reset immediately - reset.fire(); - setup_receive(); } catch (file_error& e) @@ -2002,8 +1993,16 @@ namespace libtorrent assert(m_connecting); m_socket->open(asio::ip::tcp::v4()); m_socket->bind(t->get_interface()); - m_socket->async_connect(m_remote - , bind(&peer_connection::on_connection_complete, self(), _1)); + if (m_remote_proxy != tcp::endpoint()) + { + m_socket->async_connect(m_remote_proxy + , bind(&peer_connection::on_connection_complete, self(), _1)); + } + else + { + m_socket->async_connect(m_remote + , bind(&peer_connection::on_connection_complete, self(), _1)); + } if (t->alerts().should_post(alert::debug)) { @@ -2189,6 +2188,11 @@ namespace libtorrent // TODO: the timeout should be called by an event INVARIANT_CHECK; +#ifndef NDEBUG + // allow step debugging without timing out + return false; +#endif + using namespace boost::posix_time; ptime now(second_clock::universal_time()); diff --git a/src/policy.cpp b/src/policy.cpp index 094612fa0..4bb8774f1 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -79,6 +79,7 @@ namespace - (int)c.download_queue().size() - (int)c.request_queue().size(); + assert(c.desired_queue_size() > 0); // if our request queue is already full, we // don't have to make any new requests yet if (num_requests <= 0) return; @@ -99,7 +100,8 @@ namespace // the number of blocks we want, but it will try to make the picked // blocks be from whole pieces, possibly by returning more blocks // than we requested. - assert(c.remote() == c.get_socket()->remote_endpoint()); + assert((c.proxy() == tcp::endpoint() && c.remote() == c.get_socket()->remote_endpoint()) + || c.proxy() == c.get_socket()->remote_endpoint()); // picks the interesting pieces from this peer // the integer is the number of pieces that @@ -248,7 +250,8 @@ namespace // the one we interrupted may need to request a new piece. // make sure it doesn't take over a block from the peer - // that just took over its block + // that just took over its block (that would cause an + // infinite recursion) ignore.push_back(&c); request_a_block(t, *peer, ignore); num_requests--; @@ -891,9 +894,9 @@ namespace libtorrent // TODO: only allow _one_ connection to use this // override at a time -#ifndef NDEBUG - assert(c.remote() == c.get_socket()->remote_endpoint()); -#endif + assert((c.proxy() == tcp::endpoint() && c.remote() == c.get_socket()->remote_endpoint()) + || c.proxy() == c.get_socket()->remote_endpoint()); + if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given && c.remote().address() != m_torrent->current_tracker().address()) { @@ -954,9 +957,9 @@ namespace libtorrent // we don't have ny info about this peer. // add a new entry -#ifndef NDEBUG - assert(c.remote() == c.get_socket()->remote_endpoint()); -#endif + assert((c.proxy() == tcp::endpoint() && c.remote() == c.get_socket()->remote_endpoint()) + || c.proxy() == c.get_socket()->remote_endpoint()); + peer p(c.remote(), peer::not_connectable); m_peers.push_back(p); i = m_peers.end()-1; @@ -1334,9 +1337,9 @@ namespace libtorrent bool policy::has_connection(const peer_connection* c) { assert(c); -#ifndef NDEBUG - assert(c->remote() == c->get_socket()->remote_endpoint()); -#endif + assert((c->proxy() == tcp::endpoint() && c->remote() == c->get_socket()->remote_endpoint()) + || c->proxy() == c->get_socket()->remote_endpoint()); + return std::find_if( m_peers.begin() , m_peers.end() diff --git a/src/torrent.cpp b/src/torrent.cpp index ddadf0e49..bef1a9d0b 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1106,9 +1106,7 @@ namespace libtorrent m_event = tracker_request::none; req.url = m_trackers[m_currently_trying_tracker].url; assert(m_connections_quota.given > 0); - req.num_want = std::max( - (m_connections_quota.given - - m_policy->num_peers()), 10); + req.num_want = std::max(m_connections_quota.given - num_peers(), 10); // if we are aborting. we don't want any new peers if (req.event == tracker_request::stopped) req.num_want = 0; @@ -1173,19 +1171,20 @@ namespace libtorrent (*m_ses.m_logger) << now << " resolving: " << url << "\n"; #endif - std::string protocol; - std::string hostname; - int port; - std::string path; - boost::tie(protocol, hostname, port, path) - = parse_url_components(url); - m_resolving_web_seeds.insert(url); if (m_ses.settings().proxy_ip.empty()) { + std::string protocol; + std::string hostname; + int port; + std::string path; + boost::tie(protocol, hostname, port, path) + = parse_url_components(url); + tcp::resolver::query q(hostname, boost::lexical_cast(port)); m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( - bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url))); + bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url + , tcp::endpoint()))); } else { @@ -1193,11 +1192,162 @@ namespace libtorrent tcp::resolver::query q(m_ses.settings().proxy_ip , boost::lexical_cast(m_ses.settings().proxy_port)); m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( - bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url))); + bind(&torrent::on_proxy_name_lookup, shared_from_this(), _1, _2, url))); } } + void torrent::on_proxy_name_lookup(asio::error_code const& e, tcp::resolver::iterator host + , std::string url) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string now(to_simple_string(second_clock::universal_time())); + (*m_ses.m_logger) << now << " completed resolve proxy hostname for: " << url << "\n"; +#endif + + if (e || host == tcp::resolver::iterator()) + { + if (m_ses.m_alerts.should_post(alert::warning)) + { + std::stringstream msg; + msg << "HTTP seed proxy hostname lookup failed: " << e.message(); + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), url, msg.str())); + } + + // the name lookup failed for the http host. Don't try + // this host again + remove_url_seed(url); + return; + } + + if (m_ses.is_aborted()) return; + + tcp::endpoint a(host->endpoint()); + + if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { + // TODO: post alert: "proxy at " + a.address().to_string() + " (" + hostname + ") blocked by ip filter"); + return; + } + + std::string protocol; + std::string hostname; + int port; + std::string path; + boost::tie(protocol, hostname, port, path) + = parse_url_components(url); + + tcp::resolver::query q(hostname, boost::lexical_cast(port)); + m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( + bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url, a))); + } + catch (std::exception& exc) + { + assert(false); + }; + + void torrent::on_name_lookup(asio::error_code const& e, tcp::resolver::iterator host + , std::string url, tcp::endpoint proxy) try + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + INVARIANT_CHECK; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + std::string now(to_simple_string(second_clock::universal_time())); + (*m_ses.m_logger) << now << " completed resolve: " << url << "\n"; +#endif + + std::set::iterator i = m_resolving_web_seeds.find(url); + if (i != m_resolving_web_seeds.end()) m_resolving_web_seeds.erase(i); + + if (e || host == tcp::resolver::iterator()) + { + if (m_ses.m_alerts.should_post(alert::warning)) + { + std::stringstream msg; + msg << "HTTP seed hostname lookup failed: " << e.message(); + m_ses.m_alerts.post_alert( + url_seed_alert(get_handle(), url, msg.str())); + } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << url << "\n"; +#endif + + // the name lookup failed for the http host. Don't try + // this host again + remove_url_seed(url); + return; + } + + if (m_ses.is_aborted()) return; + + tcp::endpoint a(host->endpoint()); + + if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { + // TODO: post alert: "web seed at " + a.address().to_string() + " blocked by ip filter"); + return; + } + + peer_iterator conn = m_connections.find(a); + if (conn != m_connections.end()) + { + if (dynamic_cast(conn->second) == 0 + || conn->second->is_disconnecting()) conn->second->disconnect(); + else return; + } + + boost::shared_ptr s(new stream_socket(m_ses.m_io_service)); + boost::intrusive_ptr c(new web_peer_connection( + m_ses, shared_from_this(), s, a, proxy, url)); + +#ifndef NDEBUG + c->m_in_constructor = false; +#endif + + try + { + m_ses.m_connection_queue.push_back(c); + + assert(m_connections.find(a) == m_connections.end()); + +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + // add the newly connected peer to this torrent's peer list + m_connections.insert( + std::make_pair(a, boost::get_pointer(c))); + +#ifndef NDEBUG + m_policy->check_invariant(); +#endif + + m_ses.process_connection_queue(); + } + catch (std::exception& e) + { +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << e.what() << "\n"; +#endif + + // TODO: post an error alert! + std::map::iterator i = m_connections.find(a); + if (i != m_connections.end()) m_connections.erase(i); + m_ses.connection_failed(s, a, e.what()); + c->disconnect(); + } + } + catch (std::exception& exc) + { + assert(false); + }; + void torrent::resolve_peer_country(boost::intrusive_ptr const& p) const { if (m_resolving_country @@ -1512,95 +1662,6 @@ namespace libtorrent } } - void torrent::on_name_lookup(asio::error_code const& e, tcp::resolver::iterator host - , std::string url) try - { - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - INVARIANT_CHECK; - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - std::string now(to_simple_string(second_clock::universal_time())); - (*m_ses.m_logger) << now << " completed resolve: " << url << "\n"; -#endif - - std::set::iterator i = m_resolving_web_seeds.find(url); - if (i != m_resolving_web_seeds.end()) m_resolving_web_seeds.erase(i); - - if (e || host == tcp::resolver::iterator()) - { - if (m_ses.m_alerts.should_post(alert::warning)) - { - std::stringstream msg; - msg << "HTTP seed hostname lookup failed: " << e.message(); - m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), url, msg.str())); - } -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << url << "\n"; -#endif - - // the name lookup failed for the http host. Don't try - // this host again - remove_url_seed(url); - return; - } - - if (m_ses.is_aborted()) return; - - tcp::endpoint a(host->endpoint()); - - if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) - { - // TODO: post alert: "web seed at " + a.address().to_string() + " blocked by ip filter"); - return; - } - - boost::shared_ptr s(new stream_socket(m_ses.m_io_service)); - boost::intrusive_ptr c(new web_peer_connection( - m_ses, shared_from_this(), s, a, url)); - -#ifndef NDEBUG - c->m_in_constructor = false; -#endif - - try - { - m_ses.m_connection_queue.push_back(c); - - assert(m_connections.find(a) == m_connections.end()); - -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - // add the newly connected peer to this torrent's peer list - m_connections.insert( - std::make_pair(a, boost::get_pointer(c))); - -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - - m_ses.process_connection_queue(); - } - catch (std::exception& e) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << e.what() << "\n"; -#endif - - // TODO: post an error alert! - std::map::iterator i = m_connections.find(a); - if (i != m_connections.end()) m_connections.erase(i); - m_ses.connection_failed(s, a, e.what()); - c->disconnect(); - } - } - catch (std::exception& exc) - { - assert(false); - }; - peer_connection& torrent::connect_to_peer(const tcp::endpoint& a) { INVARIANT_CHECK; @@ -1745,9 +1806,8 @@ namespace libtorrent m_connections.erase(ci); throw; } -#ifndef NDEBUG - assert(p->remote() == p->get_socket()->remote_endpoint()); -#endif + assert((p->proxy() == tcp::endpoint() && p->remote() == p->get_socket()->remote_endpoint()) + || p->proxy() == p->get_socket()->remote_endpoint()); #ifndef NDEBUG m_policy->check_invariant(); @@ -2247,6 +2307,10 @@ namespace libtorrent // let the stats fade out to 0 m_stat.second_tick(tick_interval); m_web_stat.second_tick(tick_interval); + m_connections_quota.min = 0; + m_connections_quota.max = 0; + m_uploads_quota.min = 0; + m_uploads_quota.max = 0; return; } diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index 4d1b4c565..4d907387b 100755 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -60,8 +60,9 @@ namespace libtorrent , boost::weak_ptr t , boost::shared_ptr s , tcp::endpoint const& remote + , tcp::endpoint const& proxy , std::string const& url) - : peer_connection(ses, t, s, remote) + : peer_connection(ses, t, s, remote, proxy) , m_url(url) , m_first_request(true) { @@ -136,7 +137,7 @@ namespace libtorrent // it is always possible to request pieces incoming_unchoke(); - reset_recv_buffer(512*1024+1024); + reset_recv_buffer(t->torrent_file().piece_length() + 1024 * 2); } void web_peer_connection::write_request(peer_request const& r) @@ -291,15 +292,25 @@ namespace libtorrent for (;;) { buffer::const_interval recv_buffer = receive_buffer(); + int payload; int protocol; + bool header_finished = m_parser.header_finished(); boost::tie(payload, protocol) = m_parser.incoming(recv_buffer); m_statistics.received_bytes(payload, protocol); + + assert(recv_buffer.left() <= packet_size()); + assert (recv_buffer.left() < packet_size() + || m_parser.finished()); + + // this means the entire status line hasn't been received yet + if (m_parser.status_code() == -1) break; - // TODO: maybe all 200 - 299 should be considered successful - if (m_parser.status_code() != 206 - && m_parser.status_code() != 200 - && m_parser.status_code() != -1) + // if the status code is not one of the accepted ones, abort + if (m_parser.status_code() != 206 // partial content + && m_parser.status_code() != 200 // OK + && m_parser.status_code() < 300 // redirect + && m_parser.status_code() >= 400) { // we should not try this server again. t->remove_url_seed(m_url); @@ -309,18 +320,63 @@ namespace libtorrent if (!m_parser.header_finished()) break; - buffer::const_interval http_body = m_parser.get_body(); - - std::string server_version = m_parser.header("server"); - if (!server_version.empty()) + // we just completed reading the header + if (!header_finished) { - m_server_string = "URL seed @ "; - m_server_string += m_host; - m_server_string += " ("; - m_server_string += server_version; - m_server_string += ")"; + if (m_parser.status_code() >= 300 && m_parser.status_code() < 400) + { + // this means we got a redirection request + // look for the location header + std::string location = m_parser.header("location"); + + if (location.empty()) + { + // we should not try this server again. + t->remove_url_seed(m_url); + throw std::runtime_error("got HTTP redirection status without location header"); + } + + bool single_file_request = false; + if (!m_path.empty() && m_path[m_path.size() - 1] != '/') + single_file_request = true; + + // add the redirected url and remove the current one + if (!single_file_request) + { + assert(!m_file_requests.empty()); + int file_index = m_file_requests.front(); + + torrent_info const& info = t->torrent_file(); + std::string path = info.file_at(file_index).path.string(); + path = escape_path(path.c_str(), path.length()); + size_t i = location.rfind(path); + if (i == std::string::npos) + { + t->remove_url_seed(m_url); + throw std::runtime_error("got invalid HTTP redirection location (\"" + location + "\") " + "expected it to end with: " + path); + } + location.resize(i); + } + t->add_url_seed(location); + t->remove_url_seed(m_url); + throw std::runtime_error("redirecting to " + location); + } + + std::string server_version = m_parser.header("server"); + if (!server_version.empty()) + { + m_server_string = "URL seed @ "; + m_server_string += m_host; + m_server_string += " ("; + m_server_string += server_version; + m_server_string += ")"; + } + } + buffer::const_interval http_body = m_parser.get_body(); + size_type range_start; size_type range_end; if (m_parser.status_code() == 206) @@ -366,14 +422,14 @@ namespace libtorrent throw std::runtime_error("invalid range in HTTP response"); } + front_request = m_requests.front(); + // skip the http header and the blocks we've already read. The // http_body.begin is now in sync with the request at the front // of the request queue assert(in_range.start - int(m_piece.size()) <= front_request.start); http_body.begin += front_request.start - in_range.start + int(m_piece.size()); - front_request = m_requests.front(); - // the http response body consists of 3 parts // 1. the middle of a block or the ending of a block // 2. a number of whole blocks @@ -417,6 +473,10 @@ namespace libtorrent { peer_request r = m_requests.front(); m_requests.pop_front(); + assert(http_body.begin == recv_buffer.begin + m_parser.body_start() + + r.start - in_range.start); + assert(http_body.left() >= r.length); + incoming_piece(r, http_body.begin); http_body.begin += r.length; } @@ -442,7 +502,9 @@ namespace libtorrent m_file_requests.pop_front(); assert(http_body.left() == 0); m_parser.reset(); - cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024); + assert(recv_buffer.end == http_body.end || *http_body.end == 'H'); + cut_receive_buffer(http_body.end - recv_buffer.begin + , t->torrent_file().piece_length() + 1024 * 2); continue; } break;