diff --git a/src/http_parser.cpp b/src/http_parser.cpp index 13224403e..93c12496e 100644 --- a/src/http_parser.cpp +++ b/src/http_parser.cpp @@ -78,29 +78,52 @@ namespace libtorrent if (location[0] == '/') { // it's an absolute path. replace the path component of - // referrer with location + // referrer with location. - // 8 is to skip the ur;l scheme://. We want the first slash - // that's part of the path. - std::size_t i = url.find_first_of('/', 8); + // first skip the url scheme of the referer + std::size_t i = url.find("://"); + + // if the referrer doesn't appear to have a proper URL scheme + // just return the location verbatim (and probably fail) if (i == std::string::npos) return location; - url.resize(i); + + // then skip the hostname and port, it's fine for this to fail, in + // case the referrer doesn't have a path component, it's just the + // url-scheme and hostname, in which case we just append the location + i = url.find_first_of('/', i + 3); + if (i != std::string::npos) + url.resize(i); + url += location; } else { // some web servers send out relative paths // in the location header. + // remove the leaf filename - std::size_t i = url.find_last_of('/'); - if (i == std::string::npos) + // first skip the url scheme of the referer + std::size_t start = url.find("://"); + + // the referrer is not a valid full URL + if (start == std::string::npos) return location; - url.resize(i); + std::size_t end = url.find_last_of('/'); + // if the / we find is part of the scheme, there is no / in the path + // component or hostname. + if (end <= start + 2) end = std::string::npos; - if ((url.empty() || url[url.size()-1] != '/') - && (location.empty() || location[0] != '/')) + // if this fails, the referrer is just url-scheme and hostname. We can + // just append the location to it. + if (end != std::string::npos) + url.resize(end); + + // however, we may still need to insert a '/' in case neither side + // has one. We know the location doesn't start with a / already. + // so, if the referrer doesn't end with one, add it. + if ((url.empty() || url[url.size()-1] != '/')) url += '/'; url += location; } diff --git a/test/test_http_parser.cpp b/test/test_http_parser.cpp index 17ef4c043..375d70d6d 100644 --- a/test/test_http_parser.cpp +++ b/test/test_http_parser.cpp @@ -63,10 +63,14 @@ tuple feed_bytes(http_parser& parser, char const* str) ret.get<1>() += protocol; ret.get<2>() |= error; // std::cerr << payload << ", " << protocol << ", " << chunk_size << std::endl; - TORRENT_ASSERT(payload + protocol == chunk_size); + TORRENT_ASSERT(payload + protocol == chunk_size || ret.get<2>()); } - TEST_CHECK(prev == make_tuple(0, 0, false) || ret == prev); - TEST_EQUAL(ret.get<0>() + ret.get<1>(), strlen(str)); + TEST_CHECK(prev == make_tuple(0, 0, false) || ret == prev || ret.get<2>()); + if (!ret.get<2>()) + { + TEST_EQUAL(ret.get<0>() + ret.get<1>(), strlen(str)); + } + prev = ret; } return ret; @@ -269,6 +273,48 @@ int test_main() parser.reset(); + // test invalid content range + char const* invalid_range_response = + "HTTP/1.1 206 OK\n" + "content-range: bytes 4-0\n" + "content-type: test/plain\n" + "\n" + "\ntest"; + + received = feed_bytes(parser, invalid_range_response); + + TEST_CHECK(received.get<2>() == true); + + parser.reset(); + + // test invalid status line + char const* invalid_status_response = + "HTTP/1.1 206\n" + "content-range: bytes 4-0\n" + "content-type: test/plain\n" + "\n" + "\ntest"; + + received = feed_bytes(parser, invalid_status_response); + + TEST_CHECK(received.get<2>() == true); + + parser.reset(); + + // test invalid status line 2 + char const* invalid_status_response2 = + "HTTP/1.1\n" + "content-range: bytes 4-0\n" + "content-type: test/plain\n" + "\n" + "\ntest"; + + received = feed_bytes(parser, invalid_status_response2); + + TEST_CHECK(received.get<2>() == true); + + parser.reset(); + // make sure we support content-range responses // and that we're case insensitive char const* one_hundred_response = @@ -391,6 +437,28 @@ int test_main() TEST_EQUAL(resolve_redirect_location("http://example.com/a/b", "http://test.com/d") , "http://test.com/d"); + TEST_EQUAL(resolve_redirect_location("my-custom-scheme://example.com/a/b", "http://test.com/d") + , "http://test.com/d"); + + TEST_EQUAL(resolve_redirect_location("http://example.com", "/d") + , "http://example.com/d"); + + TEST_EQUAL(resolve_redirect_location("http://example.com", "d") + , "http://example.com/d"); + + TEST_EQUAL(resolve_redirect_location("my-custom-scheme://example.com/a/b", "/d") + , "my-custom-scheme://example.com/d"); + + TEST_EQUAL(resolve_redirect_location("my-custom-scheme://example.com/a/b", "c/d") + , "my-custom-scheme://example.com/a/c/d"); + + // if the referrer is invalid, just respond the verbatim location + + TEST_EQUAL(resolve_redirect_location("example.com/a/b", "/c/d") + , "/c/d"); + + // is_ok_status + TEST_EQUAL(is_ok_status(200), true); TEST_EQUAL(is_ok_status(206), true); TEST_EQUAL(is_ok_status(299), false); @@ -399,6 +467,8 @@ int test_main() TEST_EQUAL(is_ok_status(400), false); TEST_EQUAL(is_ok_status(299), false); + // is_redirect + TEST_EQUAL(is_redirect(299), false); TEST_EQUAL(is_redirect(100), false); TEST_EQUAL(is_redirect(300), true); diff --git a/test/test_piece_picker.cpp b/test/test_piece_picker.cpp index 435a599bd..8f2452c01 100644 --- a/test/test_piece_picker.cpp +++ b/test/test_piece_picker.cpp @@ -769,56 +769,142 @@ int test_main() // ======================================================== + // sweep up, we_have() + print_title("test cursors. sweep up, we_have"); + p = setup_picker("7654321", " ", "", ""); + for (int i = 0; i < 7; ++i) + { + TEST_EQUAL(p->cursor(), i); + TEST_EQUAL(p->reverse_cursor(), 7); + p->we_have(i); + } + TEST_CHECK(p->is_finished()); + TEST_CHECK(p->is_seeding()); + TEST_EQUAL(p->cursor(), 7); + TEST_EQUAL(p->reverse_cursor(), 0); + + // sweep up, set_piece_priority() + print_title("test cursors. sweep up, set_piece_priority"); + p = setup_picker("7654321", " ", "", ""); + for (int i = 0; i < 7; ++i) + { + TEST_EQUAL(p->cursor(), i); + TEST_EQUAL(p->reverse_cursor(), 7); + p->set_piece_priority(i, 0); + } + TEST_CHECK(p->is_finished()); + TEST_CHECK(!p->is_seeding()); + TEST_EQUAL(p->cursor(), 7); + TEST_EQUAL(p->reverse_cursor(), 0); + + // sweep down, we_have() + print_title("test cursors. sweep down, we_have"); + p = setup_picker("7654321", " ", "", ""); + for (int i = 6; i >= 0; --i) + { + TEST_EQUAL(p->cursor(), 0); + TEST_EQUAL(p->reverse_cursor(), i + 1); + p->we_have(i); + } + TEST_CHECK(p->is_finished()); + TEST_CHECK(p->is_seeding()); + TEST_EQUAL(p->cursor(), 7); + TEST_EQUAL(p->reverse_cursor(), 0); + + // sweep down, set_piece_priority() + print_title("test cursors. sweep down, set_piece_priority"); + p = setup_picker("7654321", " ", "", ""); + for (int i = 6; i >= 0; --i) + { + TEST_EQUAL(p->cursor(), 0); + TEST_EQUAL(p->reverse_cursor(), i + 1); + p->set_piece_priority(i, 0); + } + TEST_CHECK(p->is_finished()); + TEST_CHECK(!p->is_seeding()); + TEST_EQUAL(p->cursor(), 7); + TEST_EQUAL(p->reverse_cursor(), 0); + + print_title("test cursors. sweep up, we_dont_have"); + p = setup_picker("7654321", "*******", "", ""); + TEST_CHECK(p->is_finished()); + TEST_CHECK(p->is_seeding()); + TEST_EQUAL(p->cursor(), 7); + TEST_EQUAL(p->reverse_cursor(), 0); + for (int i = 0; i < 7; ++i) + { + p->we_dont_have(i); + TEST_EQUAL(p->cursor(), 0); + TEST_EQUAL(p->reverse_cursor(), i + 1); + } + TEST_CHECK(!p->is_finished()); + TEST_CHECK(!p->is_seeding()); + TEST_EQUAL(p->cursor(), 0); + TEST_EQUAL(p->reverse_cursor(), 7); + + print_title("test cursors. sweep down, we_dont_have"); + p = setup_picker("7654321", "*******", "", ""); + TEST_CHECK(p->is_finished()); + TEST_CHECK(p->is_seeding()); + TEST_EQUAL(p->cursor(), 7); + TEST_EQUAL(p->reverse_cursor(), 0); + for (int i = 6; i >= 0; --i) + { + p->we_dont_have(i); + TEST_EQUAL(p->cursor(), i); + TEST_EQUAL(p->reverse_cursor(), 7); + } + TEST_CHECK(!p->is_finished()); + TEST_CHECK(!p->is_seeding()); + TEST_EQUAL(p->cursor(), 0); + TEST_EQUAL(p->reverse_cursor(), 7); + // test cursors print_title("test cursors"); p = setup_picker("7654321", " ", "", ""); - TEST_CHECK(p->cursor() == 0); - TEST_CHECK(p->reverse_cursor() == 7); + TEST_EQUAL(p->cursor(), 0); + TEST_EQUAL(p->reverse_cursor(), 7); p->we_have(1); - TEST_CHECK(p->cursor() == 0); - TEST_CHECK(p->reverse_cursor() == 7); + TEST_EQUAL(p->cursor(), 0); + TEST_EQUAL(p->reverse_cursor(), 7); p->we_have(0); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 7); + TEST_EQUAL(p->cursor(), 2); + TEST_EQUAL(p->reverse_cursor(), 7); p->we_have(5); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 7); + TEST_EQUAL(p->cursor(), 2); + TEST_EQUAL(p->reverse_cursor(), 7); p->we_have(6); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 5); + TEST_EQUAL(p->cursor(), 2); + TEST_EQUAL(p->reverse_cursor(), 5); p->we_have(4); p->we_have(3); p->we_have(2); - TEST_CHECK(p->cursor() == 7); - TEST_CHECK(p->reverse_cursor() == 0); - p->we_dont_have(3); - TEST_CHECK(p->cursor() == 3); - TEST_CHECK(p->reverse_cursor() == 4); + TEST_EQUAL(p->cursor(), 7); + TEST_EQUAL(p->reverse_cursor(), 0); p = setup_picker("7654321", " ", "", ""); - TEST_CHECK(p->cursor() == 0); - TEST_CHECK(p->reverse_cursor() == 7); + TEST_EQUAL(p->cursor() , 0); + TEST_EQUAL(p->reverse_cursor(), 7); p->set_piece_priority(1, 0); - TEST_CHECK(p->cursor() == 0); - TEST_CHECK(p->reverse_cursor() == 7); + TEST_EQUAL(p->cursor(), 0); + TEST_EQUAL(p->reverse_cursor(), 7); p->set_piece_priority(0, 0); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 7); + TEST_EQUAL(p->cursor(), 2); + TEST_EQUAL(p->reverse_cursor(), 7); p->set_piece_priority(5, 0); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 7); + TEST_EQUAL(p->cursor(), 2); + TEST_EQUAL(p->reverse_cursor(), 7); p->set_piece_priority(6, 0); - TEST_CHECK(p->cursor() == 2); - TEST_CHECK(p->reverse_cursor() == 5); + TEST_EQUAL(p->cursor(), 2); + TEST_EQUAL(p->reverse_cursor(), 5); p->set_piece_priority(4, 0); p->set_piece_priority(3, 0); p->set_piece_priority(2, 0); - TEST_CHECK(p->cursor() == 7); - TEST_CHECK(p->reverse_cursor() == 0); + TEST_EQUAL(p->cursor(), 7); + TEST_EQUAL(p->reverse_cursor(), 0); p->set_piece_priority(3, 1); - TEST_CHECK(p->cursor() == 3); - TEST_CHECK(p->reverse_cursor() == 4); - + TEST_EQUAL(p->cursor(), 3); + TEST_EQUAL(p->reverse_cursor(), 4); // ========================================================