added support for chunked encoding for web seeds

This commit is contained in:
Arvid Norberg
2010-10-27 06:39:18 +00:00
parent 4968192654
commit d737dd051d
12 changed files with 419 additions and 54 deletions

View File

@@ -511,9 +511,9 @@ void stop_web_server()
}
}
void web_server_thread(int* port, bool ssl);
void web_server_thread(int* port, bool ssl, bool chunked);
int start_web_server(bool ssl)
int start_web_server(bool ssl, bool chunked_encoding)
{
stop_web_server();
@@ -537,7 +537,8 @@ int start_web_server(bool ssl)
int port = 0;
web_server.reset(new libtorrent::thread(boost::bind(&web_server_thread, &port, ssl)));
web_server.reset(new libtorrent::thread(boost::bind(
&web_server_thread, &port, ssl, chunked_encoding)));
{
libtorrent::mutex::scoped_lock l(web_lock);
@@ -553,16 +554,22 @@ int start_web_server(bool ssl)
}
void send_response(socket_type& s, error_code& ec
, int code, char const* status_message, char const* extra_header
, int code, char const* status_message, char const** extra_header
, int len)
{
char msg[400];
char msg[600];
int pkt_len = snprintf(msg, sizeof(msg), "HTTP/1.0 %d %s\r\n"
"content-length: %d\r\n"
"%s"
"%s"
"%s"
"%s"
"\r\n"
, code, status_message, len
, extra_header ? extra_header : "");
, extra_header[0]
, extra_header[1]
, extra_header[2]
, extra_header[3]);
// fprintf(stderr, ">> %s\n", msg);
write(s, boost::asio::buffer(msg, pkt_len), boost::asio::transfer_all(), ec);
}
@@ -583,7 +590,42 @@ void on_accept(error_code const& ec)
}
}
void web_server_thread(int* port, bool ssl)
void send_content(socket_type& s, char const* file, int size, bool chunked)
{
error_code ec;
if (chunked)
{
int chunk_size = 13;
char head[20];
std::vector<boost::asio::const_buffer> bufs(3);
bufs[2] = asio::const_buffer("\r\n", 2);
while (chunk_size > 0)
{
chunk_size = std::min(chunk_size, size);
int len = snprintf(head, sizeof(head), "%x\r\n", chunk_size);
bufs[0] = asio::const_buffer(head, len);
if (chunk_size == 0)
{
// terminate
bufs.erase(bufs.begin()+1);
}
else
{
bufs[1] = asio::const_buffer(file, chunk_size);
}
write(s, bufs, boost::asio::transfer_all(), ec);
size -= chunk_size;
file += chunk_size;
chunk_size *= 2;
}
}
else
{
write(s, boost::asio::buffer(file, size), boost::asio::transfer_all(), ec);
}
}
void web_server_thread(int* port, bool ssl, bool chunked)
{
io_service ios;
socket_acceptor acceptor(ios);
@@ -699,6 +741,8 @@ void web_server_thread(int* port, bool ssl)
p.incoming(buffer::const_interval(buf + offset, buf + len), error);
char const* extra_header[4] = {"","","",""};
TEST_CHECK(error == false);
if (error)
{
@@ -766,19 +810,22 @@ void web_server_thread(int* port, bool ssl)
if (path == "/redirect")
{
send_response(s, ec, 301, "Moved Permanently", "Location: /test_file\r\n", 0);
extra_header[0] = "Location: /test_file\r\n";
send_response(s, ec, 301, "Moved Permanently", extra_header, 0);
break;
}
if (path == "/infinite_redirect")
{
send_response(s, ec, 301, "Moved Permanently", "Location: /infinite_redirect\r\n", 0);
extra_header[0] = "Location: /infinite_redirext\r\n";
send_response(s, ec, 301, "Moved Permanently", extra_header, 0);
break;
}
if (path == "/relative/redirect")
{
send_response(s, ec, 301, "Moved Permanently", "Location: ../test_file\r\n", 0);
extra_header[0] = "Location: ../test_file\r\n";
send_response(s, ec, 301, "Moved Permanently", extra_header, 0);
break;
}
@@ -792,7 +839,7 @@ void web_server_thread(int* port, bool ssl)
std::vector<char> buf;
bencode(std::back_inserter(buf), announce);
send_response(s, ec, 200, "OK", 0, buf.size());
send_response(s, ec, 200, "OK", extra_header, buf.size());
write(s, boost::asio::buffer(&buf[0], buf.size()), boost::asio::transfer_all(), ec);
}
@@ -834,10 +881,10 @@ void web_server_thread(int* port, bool ssl)
error_code ec;
if (res == -1 || file_buf.empty())
{
send_response(s, ec, 404, "Not Found", 0, 0);
send_response(s, ec, 404, "Not Found", extra_header, 0);
continue;
}
send_response(s, ec, 200, "OK", 0, size);
send_response(s, ec, 200, "OK", extra_header, size);
// fprintf(stderr, "sending %d bytes of payload [%d, %d)\n"
// , size, int(off), int(off + size));
write(s, boost::asio::buffer(&file_buf[0] + off, size)
@@ -856,24 +903,27 @@ void web_server_thread(int* port, bool ssl)
int res = load_file(path, file_buf);
if (res == -1)
{
send_response(s, ec, 404, "Not Found", 0, 0);
send_response(s, ec, 404, "Not Found", extra_header, 0);
continue;
}
if (res != 0)
{
// this means the file was either too big or couldn't be read
send_response(s, ec, 503, "Internal Error", 0, 0);
send_response(s, ec, 503, "Internal Error", extra_header, 0);
continue;
}
// serve file
char const* extra_header = 0;
if (extension(path) == ".gz")
{
extra_header = "Content-Encoding: gzip\r\n";
extra_header[0] = "Content-Encoding: gzip\r\n";
}
if (chunked)
{
extra_header[2] = "Transfer-Encoding: chunked\r\n";
}
if (!p.header("range").empty())
@@ -881,14 +931,13 @@ void web_server_thread(int* port, bool ssl)
std::string range = p.header("range");
int start, end;
sscanf(range.c_str(), "bytes=%d-%d", &start, &end);
char eh[200];
snprintf(eh, sizeof(eh), "%sContent-Range: bytes %d-%d\r\n"
, extra_header ? extra_header : "", start, end);
send_response(s, ec, 206, "Partial", eh, end - start + 1);
char eh[400];
snprintf(eh, sizeof(eh), "Content-Range: bytes %d-%d\r\n", start, end);
extra_header[1] = eh;
send_response(s, ec, 206, "Partial", extra_header, end - start + 1);
if (!file_buf.empty())
{
write(s, boost::asio::buffer(&file_buf[0] + start, end - start + 1)
, boost::asio::transfer_all(), ec);
send_content(s, &file_buf[0] + start, end - start + 1, chunked);
}
// fprintf(stderr, "send %d bytes of payload\n", end - start + 1);
}
@@ -896,7 +945,7 @@ void web_server_thread(int* port, bool ssl)
{
send_response(s, ec, 200, "OK", extra_header, file_buf.size());
if (!file_buf.empty())
write(s, boost::asio::buffer(&file_buf[0], file_buf.size()), boost::asio::transfer_all(), ec);
send_content(s, &file_buf[0], file_buf.size(), chunked);
}
// fprintf(stderr, "%d bytes left in receive buffer. offset: %d\n", len - offset, offset);
memmove(buf, buf + offset, len - offset);

View File

@@ -62,7 +62,7 @@ setup_transfer(libtorrent::session* ses1, libtorrent::session* ses2
, boost::intrusive_ptr<libtorrent::torrent_info>* torrent = 0, bool super_seeding = false
, libtorrent::add_torrent_params const* p = 0);
int start_web_server(bool ssl = false);
int start_web_server(bool ssl = false, bool chunked = false);
void stop_web_server();
void start_proxy(int port, int type);
void stop_proxy(int port);

View File

@@ -815,7 +815,6 @@ int test_main()
std::cerr << unescape_string(escape_string(test_string, strlen(test_string)), ec) << std::endl;
// verify_encoding
test = "\b?filename=4";
TEST_CHECK(!verify_encoding(test));
#ifdef TORRENT_WINDOWS
@@ -827,8 +826,8 @@ int test_main()
test = "filename=4";
TEST_CHECK(verify_encoding(test));
TEST_CHECK(test == "filename=4");
// HTTP request parser
// HTTP request parser
http_parser parser;
boost::tuple<int, int, bool> received;
@@ -939,8 +938,38 @@ int test_main()
TEST_CHECK(received == make_tuple(5, int(strlen(web_seed_response) - 5), false));
TEST_CHECK(parser.content_range() == (std::pair<size_type, size_type>(0, 4)));
TEST_CHECK(parser.content_length() == 5);
// test xml parser
{
// test chunked encoding parser
char const chunk_header1[] = "f;this is a comment\r\n";
size_type chunk_size;
int header_size;
bool ret = parser.parse_chunk_header(buffer::const_interval(chunk_header1, chunk_header1 + 10)
, &chunk_size, &header_size);
TEST_EQUAL(ret, false);
ret = parser.parse_chunk_header(buffer::const_interval(chunk_header1, chunk_header1 + sizeof(chunk_header1))
, &chunk_size, &header_size);
TEST_EQUAL(ret, true);
TEST_EQUAL(chunk_size, 15);
TEST_EQUAL(header_size, sizeof(chunk_header1) - 1);
char const chunk_header2[] =
"0;this is a comment\r\n"
"test1: foo\r\n"
"test2: bar\r\n"
"\r\n";
ret = parser.parse_chunk_header(buffer::const_interval(chunk_header2, chunk_header2 + sizeof(chunk_header2))
, &chunk_size, &header_size);
TEST_EQUAL(ret, true);
TEST_EQUAL(chunk_size, 0);
TEST_EQUAL(header_size, sizeof(chunk_header2) - 1);
TEST_EQUAL(parser.headers().find("test1")->second, "foo");
TEST_EQUAL(parser.headers().find("test2")->second, "bar");
}
// test xml parser
char xml1[] = "<a>foo<b/>bar</a>";
std::string out1;

View File

@@ -48,7 +48,7 @@ using namespace libtorrent;
// proxy: 0=none, 1=socks4, 2=socks5, 3=socks5_pw 4=http 5=http_pw
void test_transfer(boost::intrusive_ptr<torrent_info> torrent_file
, int proxy, int port, char const* protocol, bool url_seed)
, int proxy, int port, char const* protocol, bool url_seed, bool chunked_encoding)
{
using namespace libtorrent;
@@ -63,8 +63,8 @@ void test_transfer(boost::intrusive_ptr<torrent_info> torrent_file
char const* test_name[] = {"no", "SOCKS4", "SOCKS5", "SOCKS5 password", "HTTP", "HTTP password"};
fprintf(stderr, "\n\n ==== TESTING %s proxy ==== %s ==== %s ===\n\n\n"
, test_name[proxy], protocol, url_seed ? "URL seed" : "HTTP seed");
fprintf(stderr, "\n\n ==== TESTING === proxy: %s ==== protocol: %s ==== seed: %s === transfer-encoding: %s\n\n\n"
, test_name[proxy], protocol, url_seed ? "URL seed" : "HTTP seed", chunked_encoding ? "chunked": "none");
if (proxy)
{
@@ -187,7 +187,7 @@ sha1_hash file_hash(std::string const& name)
}
// test_url_seed determines whether to use url-seed or http-seed
int run_suite(char const* protocol, bool test_url_seed)
int run_suite(char const* protocol, bool test_url_seed, bool chunked_encoding)
{
using namespace libtorrent;
@@ -221,7 +221,7 @@ int run_suite(char const* protocol, bool test_url_seed)
fs.add_file("seed", sizeof(random_data));
}
int port = start_web_server(strcmp(protocol, "https") == 0);
int port = start_web_server(strcmp(protocol, "https") == 0, chunked_encoding);
libtorrent::create_torrent t(fs, 16, 0, libtorrent::create_torrent::calculate_file_hashes);
char tmp[512];
@@ -262,12 +262,12 @@ int run_suite(char const* protocol, bool test_url_seed)
}
for (int i = 0; i < 6; ++i)
test_transfer(torrent_file, i, port, protocol, test_url_seed);
test_transfer(torrent_file, i, port, protocol, test_url_seed, chunked_encoding);
if (test_url_seed)
{
torrent_file->rename_file(0, "./tmp2_web_seed/test_torrent_dir/renamed_test1");
test_transfer(torrent_file, 0, port, protocol, test_url_seed);
test_transfer(torrent_file, 0, port, protocol, test_url_seed, chunked_encoding);
}
stop_web_server();
@@ -280,10 +280,16 @@ int test_main()
int ret = 0;
for (int i = 0; i < 2; ++i)
{
// we only support chunked encoding for
// URL seeds (not HTTP seeds).
// that's why the variable limit on this loop
for (int j = 0; j < (i==0?2:1); ++j)
{
#ifdef TORRENT_USE_OPENSSL
run_suite("https", i);
run_suite("https", i, j);
#endif
run_suite("http", i);
run_suite("http", i, j);
}
}
return ret;
}