diff --git a/include/libtorrent/http_parser.hpp b/include/libtorrent/http_parser.hpp index 7d308ca36..bb032922a 100644 --- a/include/libtorrent/http_parser.hpp +++ b/include/libtorrent/http_parser.hpp @@ -81,6 +81,8 @@ namespace libtorrent , bool& error); int body_start() const { return m_body_start_pos; } size_type content_length() const { return m_content_length; } + std::pair content_range() const + { return std::make_pair(m_range_start, m_range_end); } void reset(); @@ -95,6 +97,8 @@ namespace libtorrent std::string m_server_message; size_type m_content_length; + size_type m_range_start; + size_type m_range_end; enum { read_status, read_header, read_body, error_state } m_state; diff --git a/src/http_parser.cpp b/src/http_parser.cpp index 80b0c0c53..1f1b57477 100644 --- a/src/http_parser.cpp +++ b/src/http_parser.cpp @@ -49,6 +49,8 @@ namespace libtorrent : m_recv_pos(0) , m_status_code(-1) , m_content_length(-1) + , m_range_start(-1) + , m_range_end(-1) , m_state(read_status) , m_recv_buffer(0, 0) , m_body_start_pos(0) @@ -162,31 +164,37 @@ namespace libtorrent if (name == "content-length") { -#ifdef TORRENT_WINDOWS - m_content_length = _atoi64(value.c_str()); -#else - m_content_length = atoll(value.c_str()); -#endif + m_content_length = strtoll(value.c_str(), 0, 10); } else if (name == "content-range") { - std::stringstream range_str(value); - char dummy; - std::string bytes; - size_type range_start, range_end; + bool success = true; + char const* ptr = value.c_str(); + // apparently some web servers do not send the "bytes" - // in their content-range - if (value.find(' ') != std::string::npos) - range_str >> bytes; - range_str >> range_start >> dummy >> range_end; - if (!range_str || range_end < range_start) + // in their content-range. Don't treat it as an error + // if we can't find it, just assume the byte counters + // start immediately + if (string_begins_no_case("bytes ", ptr)) ptr += 6; + char* end; + m_range_start = strtoll(ptr, &end, 10); + if (end == ptr) success = false; + else if (*end != '-') success = false; + else + { + ptr = end + 1; + m_range_end = strtoll(ptr, &end, 10); + if (end == ptr) success = false; + } + + if (!success || m_range_end < m_range_start) { m_state = error_state; error = true; return ret; } // the http range is inclusive - m_content_length = range_end - range_start + 1; + m_content_length = m_range_end - m_range_start + 1; } TORRENT_ASSERT(m_recv_pos <= (int)recv_buffer.left()); @@ -233,6 +241,8 @@ namespace libtorrent m_body_start_pos = 0; m_status_code = -1; m_content_length = -1; + m_range_start = -1; + m_range_end = -1; m_finished = false; m_state = read_status; m_recv_buffer.begin = 0; diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index 0a8d53e88..9a63229e0 100644 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -480,31 +480,14 @@ namespace libtorrent size_type range_end; if (m_parser.status_code() == 206) { - std::string const& range_str = m_parser.header("content-range"); - - bool success = true; - char const* ptr = range_str.c_str(); - if (string_begins_no_case("bytes ", ptr)) ptr += 6; - else success = false; - char* end; - range_start = strtoll(ptr, &end, 10); - if (end == ptr) success = false; - else if (*end != '-') success = false; - else - { - ptr = end + 1; - range_end = strtoll(ptr, &end, 10); - if (end == ptr) success = false; - } - - if (!success) + boost::tie(range_start, range_end) = m_parser.content_range(); + if (range_start < 0 || range_end < range_start) { m_statistics.received_bytes(0, bytes_transferred); // we should not try this server again. t->remove_web_seed(m_url, web_seed_entry::url_seed); char msg[200]; - snprintf(msg, 200, "invalid range in HTTP response: %s" - , range_str.c_str()); + snprintf(msg, 200, "invalid range in HTTP response"); disconnect(msg, 2); return; } diff --git a/test/test_primitives.cpp b/test/test_primitives.cpp index b9894e89f..ecc38e2da 100644 --- a/test/test_primitives.cpp +++ b/test/test_primitives.cpp @@ -546,6 +546,22 @@ int test_main() TEST_CHECK(received == make_tuple(5, int(strlen(tracker_response) - 5), false)); TEST_CHECK(parser.get_body().left() == 5); + parser.reset(); + + // make sure we support content-range responses + // and that we're case insensitive + char const* web_seed_response = + "HTTP/1.1 206 OK\n" + "contEnt-rAngE: bYTes 0-4\n" + "conTent-TyPe: test/plain\n" + "\n" + "\ntest"; + + received = feed_bytes(parser, web_seed_response); + + TEST_CHECK(received == make_tuple(5, int(strlen(web_seed_response) - 5), false)); + TEST_CHECK(parser.content_range() == (std::pair(0, 4))); + TEST_CHECK(parser.content_length() == 5); // test xml parser char xml1[] = "foobar";