Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* fix bug in parsing chunked encoding
* fix incorrect reporting of active_duration when entering graceful-pause
* fix python binding for functions taking string_view
* fix python binding for torrent_info constructor overloads
Expand Down
10 changes: 7 additions & 3 deletions src/http_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ namespace libtorrent {

span<char const> http_parser::get_body() const
{
TORRENT_ASSERT(m_state == read_body);
if (m_state != read_body) return {};
std::int64_t const received = m_recv_pos - m_body_start_pos;

std::int64_t const body_length = m_chunked_encoding && !m_chunked_ranges.empty()
Expand Down Expand Up @@ -612,8 +612,12 @@ namespace libtorrent {
{
auto const chunk_start = i.first;
auto const chunk_end = i.second;
TORRENT_ASSERT(i.second - i.first < std::numeric_limits<int>::max());
TORRENT_ASSERT(chunk_end - offset <= buffer.size());
if (chunk_end - offset > buffer.size()
|| (i.second - i.first) >= std::numeric_limits<int>::max())
{
// invalid chunk header. Return the body we've parsed out so far
return buffer.first(write_ptr - buffer.data());
}
span<char> chunk = buffer.subspan(
aux::numeric_cast<std::ptrdiff_t>(chunk_start - offset)
, aux::numeric_cast<std::ptrdiff_t>(chunk_end - chunk_start));
Expand Down
89 changes: 73 additions & 16 deletions test/test_http_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,30 +536,87 @@ TORRENT_TEST(http_parser)
TEST_EQUAL(is_redirect(400), false);
}

TORRENT_TEST(chunked_encoding)
std::string test_collapse_chunks(std::string chunked_input, bool const expect_error = false)
{
char const chunked_input[] =
"HTTP/1.1 200 OK\r\n"
http_parser parser;

std::string input = "HTTP/1.1 200 OK\r\n"
"Transfer-Encoding: chunked\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"4\r\ntest\r\n4\r\n1234\r\n10\r\n0123456789abcdef\r\n"
"0\r\n\r\n";
"\r\n";

http_parser parser;
std::tuple<int, int, bool> const received
= feed_bytes(parser, chunked_input);
input += chunked_input;

TEST_EQUAL(strlen(chunked_input), 24 + 94);
TEST_CHECK(received == std::make_tuple(24, 94, false));
TEST_CHECK(parser.finished());
bool error = false;
int payload = 0;
int protocol = 0;
std::tie(payload, protocol) = parser.incoming(input, error);

char mutable_buffer[100];
TEST_CHECK(protocol > 0);
TEST_CHECK(payload > 0);
if (expect_error)
{
TEST_CHECK(std::size_t(protocol + payload) <= input.size());
}
else
{
TEST_EQUAL(std::size_t(protocol + payload), input.size());
TEST_CHECK(parser.finished());
}

std::vector<char> mutable_buffer;
span<char const> body = parser.get_body();
std::copy(body.begin(), body.end(), mutable_buffer);
body = parser.collapse_chunk_headers({mutable_buffer, body.size()});
std::copy(body.begin(), body.end(), std::back_inserter(mutable_buffer));
body = parser.collapse_chunk_headers(mutable_buffer);
return std::string(body.data(), body.size());
}

TEST_CHECK(body == span<char const>("test12340123456789abcdef", 24));
TORRENT_TEST(chunked_encoding)
{
auto const collapsed = test_collapse_chunks(
"4\r\ntest\r\n4\r\n1234\r\n10\r\n0123456789abcdef\r\n"
"0\r\n\r\n");
TEST_EQUAL(collapsed, "test12340123456789abcdef");
}

TORRENT_TEST(chunked_encoding_beyond_end)
{
auto const collapsed = test_collapse_chunks(
"4\r\ntest\r\n4\r\n1234\r\n20\r\n0123456789abcdef\r\n"
"0\r\n\r\n", true);
TEST_EQUAL(collapsed, "test1234");
}

TORRENT_TEST(chunked_encoding_end_of_buffer)
{
auto const collapsed = test_collapse_chunks(
"4\r\ntest\r\n4\r\n1234\r\n17\r\n0123456789abcdef\r\n"
"0\r\n\r\n", true);
TEST_EQUAL(collapsed, "test12340123456789abcdef\r\n0\r\n\r\n");
}

TORRENT_TEST(chunked_encoding_past_end)
{
auto const collapsed = test_collapse_chunks(
"4\r\ntest\r\n4\r\n1234\r\n18\r\n0123456789abcdef\r\n"
"0\r\n\r\n", true);
TEST_EQUAL(collapsed, "test1234");
}

TORRENT_TEST(chunked_encoding_negative)
{
auto const collapsed = test_collapse_chunks(
"4\r\ntest\r\n4\r\n1234\r\n-10\r\n0123456789abcdef\r\n"
"0\r\n\r\n", true);
TEST_EQUAL(collapsed, "");
}

TORRENT_TEST(chunked_encoding_end)
{
auto const collapsed = test_collapse_chunks(
"4\r\ntest\r\n4\r\n1234\r\n11\r\n0123456789abcdef\r\n"
"0\r\n\r\n", true);
TEST_EQUAL(collapsed, "test12340123456789abcdef\r");
}

TORRENT_TEST(chunked_encoding_overflow)
Expand Down