rewrote most of the web seed downloader to report its progress accurately (to prevent inconsistencies in the core). Not well tested with multi file torrents yet

This commit is contained in:
Arvid Norberg 2006-12-18 01:23:30 +00:00
parent 2ebff76caa
commit 3a7eeb3966
5 changed files with 152 additions and 61 deletions

View File

@ -72,11 +72,13 @@ namespace libtorrent
std::string const& protocol() const { return m_protocol; }
int status_code() const { return m_status_code; }
std::string message() const { return m_server_message; }
buffer::const_interval get_body();
buffer::const_interval get_body() const;
bool header_finished() const { return m_state == read_body; }
bool finished() const { return m_finished; }
boost::tuple<int, int> incoming(buffer::const_interval recv_buffer);
int body_start() const { return m_body_start_pos; }
void reset();
private:
int m_recv_pos;
int m_status_code;

View File

@ -98,9 +98,13 @@ namespace libtorrent
boost::tuple<int, int> http_parser::incoming(buffer::const_interval recv_buffer)
{
m_recv_buffer = recv_buffer;
assert(recv_buffer.left() >= m_recv_buffer.left());
boost::tuple<int, int> ret(0, 0);
// early exit if there's nothing new in the receive buffer
if (recv_buffer.left() == m_recv_buffer.left()) return ret;
m_recv_buffer = recv_buffer;
char const* pos = recv_buffer.begin + m_recv_pos;
if (m_state == read_status)
{
@ -200,20 +204,29 @@ namespace libtorrent
return ret;
}
buffer::const_interval http_parser::get_body()
buffer::const_interval http_parser::get_body() const
{
assert(m_state == read_body);
if (m_content_length >= 0)
return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos
, m_recv_buffer.begin + std::min(m_recv_pos
, m_body_start_pos + m_content_length));
else
return buffer::const_interval(m_recv_buffer.begin + m_body_start_pos
, m_recv_buffer.begin + m_recv_pos);
}
void http_parser::reset()
{
char const* body_begin = m_recv_buffer.begin + m_body_start_pos;
char const* body_end = m_recv_buffer.begin + m_recv_pos;
m_recv_pos = 0;
m_body_start_pos = 0;
m_status_code = -1;
m_content_length = -1;
m_finished = false;
m_state = read_status;
m_recv_buffer.begin = 0;
m_recv_buffer.end = 0;
m_header.clear();
return buffer::const_interval(body_begin, body_end);
}
http_tracker_connection::http_tracker_connection(

View File

@ -1088,12 +1088,6 @@ namespace libtorrent
bool was_finished = picker.num_filtered() + t->num_pieces()
== t->torrent_file().num_pieces();
for (std::vector<piece_block>::iterator i = finished_blocks.begin()
, end(finished_blocks.end()); i != end; ++i)
{
}
// did we just finish the piece?
if (picker.is_piece_finished(p.piece))
{
@ -1134,30 +1128,7 @@ namespace libtorrent
t->completed();
}
#ifndef NDEBUG
size_type total_done, total_wanted;
boost::tie(total_done, total_wanted) = t->bytes_done();
if (t->is_seed())
assert(total_done == t->torrent_file().total_size());
else
assert(total_done != t->torrent_file().total_size());
t->check_invariant();
#endif
}
#ifndef NDEBUG
size_type total_done, total_wanted;
boost::tie(total_done, total_wanted) = t->bytes_done();
if (t->is_seed())
assert(total_done == t->torrent_file().total_size());
else
assert(total_done != t->torrent_file().total_size());
t->check_invariant();
#endif
}
// -----------------------------

View File

@ -765,8 +765,8 @@ namespace libtorrent
wanted_done += corr;
}
assert(total_done <= m_torrent_file.total_size());
assert(wanted_done <= m_torrent_file.total_size());
assert(total_done < m_torrent_file.total_size());
assert(wanted_done < m_torrent_file.total_size());
std::map<piece_block, int> downloading_piece;
for (const_peer_iterator i = begin(); i != end(); ++i)
@ -794,7 +794,15 @@ namespace libtorrent
{
downloading_piece[block] = p->bytes_downloaded;
}
#ifndef NDEBUG
assert(p->bytes_downloaded <= p->full_block_bytes);
int last_piece = m_torrent_file.num_pieces() - 1;
if (p->piece_index == last_piece
&& p->block_index == m_torrent_file.piece_size(last_piece) / block_size())
assert(p->full_block_bytes == m_torrent_file.piece_size(last_piece) % block_size());
else
assert(p->full_block_bytes == block_size());
#endif
}
}
for (std::map<piece_block, int>::iterator i = downloading_piece.begin();
@ -805,8 +813,43 @@ namespace libtorrent
wanted_done += i->second;
}
assert(total_done <= m_torrent_file.total_size());
assert(wanted_done <= m_torrent_file.total_size());
#ifndef NDEBUG
if (total_done >= m_torrent_file.total_size())
{
std::copy(m_have_pieces.begin(), m_have_pieces.end()
, std::ostream_iterator<bool>(std::cerr, " "));
std::cerr << std::endl;
std::cerr << "num_pieces: " << m_num_pieces << std::endl;
std::cerr << "unfinished:" << std::endl;
for (std::vector<piece_picker::downloading_piece>::const_iterator i =
dl_queue.begin(); i != dl_queue.end(); ++i)
{
std::cerr << " " << i->index << " ";
for (int j = 0; j < blocks_per_piece; ++j)
{
std::cerr << i->finished_blocks[j];
}
std::cerr << std::endl;
}
std::cerr << "downloading pieces:" << std::endl;
for (std::map<piece_block, int>::iterator i = downloading_piece.begin();
i != downloading_piece.end(); ++i)
{
std::cerr << " " << i->first.piece_index << ":" << i->first.block_index
<< " " << i->second << std::endl;
}
}
assert(total_done < m_torrent_file.total_size());
assert(wanted_done < m_torrent_file.total_size());
#endif
assert(total_done >= wanted_done);
return make_tuple(total_done, wanted_done);
@ -1726,8 +1769,8 @@ namespace libtorrent
assert(total_done != m_torrent_file.total_size());
// This check is very expensive.
// assert(m_num_pieces
// == std::count(m_have_pieces.begin(), m_have_pieces.end(), true));
assert(m_num_pieces
== std::count(m_have_pieces.begin(), m_have_pieces.end(), true));
assert(m_priority >= 0.f && m_priority < 1.f);
assert(!valid_metadata() || m_block_size > 0);
assert(!valid_metadata() || (m_torrent_file.piece_length() % m_block_size) == 0);

View File

@ -107,15 +107,17 @@ namespace libtorrent
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
int body_start = m_parser.body_start();
buffer::const_interval recv_buffer = receive_buffer();
assert(body_start <= recv_buffer.left());
buffer::const_interval http_body = m_parser.get_body();
piece_block_progress ret;
ret.piece_index = m_requests.front().piece;
ret.block_index = m_requests.front().start / t->block_size();
ret.bytes_downloaded = recv_buffer.left() - body_start;
ret.full_block_bytes = m_requests.front().length;
ret.bytes_downloaded = http_body.left() % t->block_size();
ret.block_index = (m_requests.front().start + ret.bytes_downloaded) / t->block_size();
ret.full_block_bytes = t->block_size();
const int last_piece = t->torrent_file().num_pieces() - 1;
if (ret.piece_index == last_piece && ret.block_index
== t->torrent_file().piece_size(last_piece) / t->block_size())
ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size();
return ret;
}
@ -150,7 +152,16 @@ namespace libtorrent
std::string request;
m_requests.push_back(r);
int size = r.length;
const int block_size = t->block_size();
while (size > 0)
{
int request_size = std::min(block_size, size);
peer_request pr = {r.piece, r.start + r.length - size
, request_size};
m_requests.push_back(pr);
size -= request_size;
}
bool using_proxy = false;
if (!m_ses.settings().proxy_ip.empty())
@ -281,7 +292,9 @@ namespace libtorrent
throw std::runtime_error("HTTP server does not support byte range requests");
}
if (!m_parser.finished()) break;
if (!m_parser.header_finished()) break;
buffer::const_interval http_body = m_parser.get_body();
std::string server_version = m_parser.header<std::string>("server");
if (!server_version.empty())
@ -314,21 +327,69 @@ namespace libtorrent
throw std::runtime_error("unexpected HTTP response");
int file_index = m_file_requests.front();
m_file_requests.pop_front();
peer_request r = info.map_file(file_index, range_start
peer_request in_range = info.map_file(file_index, range_start
, range_end - range_start);
buffer::const_interval http_body = m_parser.get_body();
if (r == m_requests.front())
peer_request front_request = m_requests.front();
if (in_range.piece != front_request.piece
|| in_range.start > front_request.start)
{
m_requests.pop_front();
incoming_piece(r, http_body.begin);
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
return;
throw std::runtime_error("invalid range in HTTP response");
}
// 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 <= front_request.start);
http_body.begin += front_request.start - in_range.start;
if ((in_range.start + in_range.length - front_request.start
< front_request.length)
&& (http_body.left() > front_request.length - m_piece.size()))
{
// the start of the next block to receive is stored
// in m_piece. We need to append the rest of that
// block from the http receive buffer and then
// (if it completed) call incoming_piece() with
// m_piece as buffer.
m_piece.reserve(info.piece_length());
char const* start = http_body.begin;
int copy_size = std::min(front_request.length - int(m_piece.size())
, http_body.left());
std::copy(start, start + copy_size, std::back_inserter(m_piece));
http_body.begin += copy_size;
if (m_piece.size() == front_request.length)
{
m_requests.pop_front();
incoming_piece(front_request, &m_piece[0]);
m_piece.clear();
}
}
// report all received blocks to the bittorrent engine
if (m_piece.empty())
{
while (!m_requests.empty()
&& http_body.left() >= m_requests.front().length)
{
peer_request r = m_requests.front();
m_requests.pop_front();
incoming_piece(r, http_body.begin);
http_body.begin += r.length;
}
}
if (m_parser.finished())
{
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);
continue;
}
break;
/*
if (!m_piece.empty())
{
// this is not the first partial request we get
@ -368,6 +429,7 @@ namespace libtorrent
}
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
*/
}
}