added support for chunked encoding for web seeds
This commit is contained in:
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user