move web_server for unit tests over to python
This commit is contained in:
@@ -116,7 +116,7 @@ namespace libtorrent
|
|||||||
int receive_buffer_size = receive_buffer().left() - m_parser.body_start();
|
int receive_buffer_size = receive_buffer().left() - m_parser.body_start();
|
||||||
// TODO: 1 in chunked encoding mode, this assert won't hold.
|
// TODO: 1 in chunked encoding mode, this assert won't hold.
|
||||||
// the chunk headers should be subtracted from the receive_buffer_size
|
// the chunk headers should be subtracted from the receive_buffer_size
|
||||||
TORRENT_ASSERT(receive_buffer_size <= t->block_size());
|
TORRENT_ASSERT_VAL(receive_buffer_size <= t->block_size(), receive_buffer_size);
|
||||||
ret.bytes_downloaded = t->block_size() - receive_buffer_size;
|
ret.bytes_downloaded = t->block_size() - receive_buffer_size;
|
||||||
}
|
}
|
||||||
// this is used to make sure that the block_index stays within
|
// this is used to make sure that the block_index stays within
|
||||||
|
@@ -151,12 +151,12 @@ class ConnectionHandler:
|
|||||||
self.client.send(HTTPVER+' 200 Connection established\n'+
|
self.client.send(HTTPVER+' 200 Connection established\n'+
|
||||||
'Proxy-agent: %s\n\n'%VERSION)
|
'Proxy-agent: %s\n\n'%VERSION)
|
||||||
self.client_buffer = ''
|
self.client_buffer = ''
|
||||||
self._read_write()
|
self._read_write()
|
||||||
|
|
||||||
def method_others(self):
|
def method_others(self):
|
||||||
self.path = self.path[7:]
|
self.path = self.path[7:]
|
||||||
i = self.path.find('/')
|
i = self.path.find('/')
|
||||||
host = self.path[:i]
|
host = self.path[:i]
|
||||||
path = self.path[i:]
|
path = self.path[i:]
|
||||||
self._connect_target(host)
|
self._connect_target(host)
|
||||||
self.target.send('%s %s %s\n'%(self.method, path, self.protocol)+
|
self.target.send('%s %s %s\n'%(self.method, path, self.protocol)+
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright (c) 2008, Arvid Norberg
|
Copyright (c) 2008, Arvid Norberg
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
@@ -85,11 +85,6 @@ void report_failure(char const* err, char const* file, int line)
|
|||||||
|
|
||||||
int print_failures()
|
int print_failures()
|
||||||
{
|
{
|
||||||
for (std::vector<std::string>::iterator i = failure_strings.begin()
|
|
||||||
, end(failure_strings.end()); i != end; ++i)
|
|
||||||
{
|
|
||||||
fputs(i->c_str(), stderr);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "\n\n\x1b[41m == %d TEST(S) FAILED ==\x1b[0m\n\n\n", tests_failure);
|
fprintf(stderr, "\n\n\x1b[41m == %d TEST(S) FAILED ==\x1b[0m\n\n\n", tests_failure);
|
||||||
return tests_failure;
|
return tests_failure;
|
||||||
}
|
}
|
||||||
@@ -380,20 +375,25 @@ pid_type async_run(char const* cmdline)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void stop_process(pid_type p)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE proc = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, p);
|
||||||
|
TerminateProcess(proc, 138);
|
||||||
|
CloseHandle(proc);
|
||||||
|
#else
|
||||||
|
printf("killing pid: %d\n", p);
|
||||||
|
kill(p, SIGKILL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void stop_all_proxies()
|
void stop_all_proxies()
|
||||||
{
|
{
|
||||||
std::map<int, proxy_t> proxies = running_proxies;
|
std::map<int, proxy_t> proxies = running_proxies;
|
||||||
for (std::map<int, proxy_t>::iterator i = proxies.begin()
|
for (std::map<int, proxy_t>::iterator i = proxies.begin()
|
||||||
, end(proxies.end()); i != end; ++i)
|
, end(proxies.end()); i != end; ++i)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
stop_process(i->second.pid);
|
||||||
HANDLE proc = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, i->second.pid);
|
|
||||||
TerminateProcess(proc, 138);
|
|
||||||
CloseHandle(proc);
|
|
||||||
#else
|
|
||||||
printf("killing pid: %d\n", i->second.pid);
|
|
||||||
kill(i->second.pid, SIGKILL);
|
|
||||||
#endif
|
|
||||||
running_proxies.erase(i->second.pid);
|
running_proxies.erase(i->second.pid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,7 +449,7 @@ int start_proxy(int proxy_type)
|
|||||||
|
|
||||||
fprintf(stderr, "%s starting proxy on port %d (%s %s)...\n", time_now_string(), port, type, auth);
|
fprintf(stderr, "%s starting proxy on port %d (%s %s)...\n", time_now_string(), port, type, auth);
|
||||||
fprintf(stderr, "%s\n", buf);
|
fprintf(stderr, "%s\n", buf);
|
||||||
int r = async_run(buf);
|
pid_type r = async_run(buf);
|
||||||
if (r == 0) exit(1);
|
if (r == 0) exit(1);
|
||||||
proxy_t t = { r, proxy_type };
|
proxy_t t = { r, proxy_type };
|
||||||
running_proxies.insert(std::make_pair(port, t));
|
running_proxies.insert(std::make_pair(port, t));
|
||||||
@@ -790,7 +790,6 @@ int start_tracker()
|
|||||||
}
|
}
|
||||||
|
|
||||||
boost::detail::atomic_count g_udp_tracker_requests(0);
|
boost::detail::atomic_count g_udp_tracker_requests(0);
|
||||||
boost::detail::atomic_count g_http_tracker_requests(0);
|
|
||||||
|
|
||||||
void on_udp_receive(error_code const& ec, size_t bytes_transferred, udp::endpoint const* from, char* buffer, udp::socket* sock)
|
void on_udp_receive(error_code const& ec, size_t bytes_transferred, udp::endpoint const* from, char* buffer, udp::socket* sock)
|
||||||
{
|
{
|
||||||
@@ -909,584 +908,34 @@ void udp_tracker_thread(int* port)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::asio::io_service* web_ios = 0;
|
pid_type web_server_pid = 0;
|
||||||
boost::shared_ptr<libtorrent::thread> web_server;
|
|
||||||
libtorrent::mutex web_lock;
|
|
||||||
libtorrent::event web_initialized;
|
|
||||||
bool stop_thread = false;
|
|
||||||
|
|
||||||
static void terminate_web_thread()
|
int start_web_server(bool ssl, bool chunked_encoding)
|
||||||
{
|
{
|
||||||
stop_thread = true;
|
unsigned int seed = total_microseconds(time_now_hires() - min_time());
|
||||||
web_ios->stop();
|
printf("random seed: %u\n", seed);
|
||||||
web_ios = 0;
|
std::srand(seed);
|
||||||
|
int port = 5000 + (rand() % 55000);
|
||||||
|
|
||||||
|
char buf[200];
|
||||||
|
snprintf(buf, sizeof(buf), "python ../web_server.py %d %d %d"
|
||||||
|
, port, chunked_encoding , ssl);
|
||||||
|
|
||||||
|
fprintf(stderr, "%s starting web_server on port %d...\n", time_now_string(), port);
|
||||||
|
|
||||||
|
fprintf(stderr, "%s\n", buf);
|
||||||
|
pid_type r = async_run(buf);
|
||||||
|
if (r == 0) exit(1);
|
||||||
|
web_server_pid = r;
|
||||||
|
fprintf(stderr, "%s launched\n", time_now_string());
|
||||||
|
test_sleep(500);
|
||||||
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop_web_server()
|
void stop_web_server()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: stop_web_server()\n", time_now_string());
|
if (web_server_pid == 0) return;
|
||||||
if (web_server && web_ios)
|
stop_process(web_server_pid);
|
||||||
{
|
web_server_pid = 0;
|
||||||
fprintf(stderr, "%s: stopping web server thread\n", time_now_string());
|
|
||||||
web_ios->post(&terminate_web_thread);
|
|
||||||
web_server->join();
|
|
||||||
web_server.reset();
|
|
||||||
}
|
|
||||||
remove("server.pem");
|
|
||||||
fprintf(stderr, "%s: stop_web_server() done\n", time_now_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void web_server_thread(int* port, bool ssl, bool chunked);
|
|
||||||
|
|
||||||
int start_web_server(bool ssl, bool chunked_encoding)
|
|
||||||
{
|
|
||||||
stop_web_server();
|
|
||||||
|
|
||||||
stop_thread = false;
|
|
||||||
|
|
||||||
{
|
|
||||||
libtorrent::mutex::scoped_lock l(web_lock);
|
|
||||||
web_initialized.clear(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
int port = 0;
|
|
||||||
|
|
||||||
web_server.reset(new libtorrent::thread(boost::bind(
|
|
||||||
&web_server_thread, &port, ssl, chunked_encoding)));
|
|
||||||
|
|
||||||
{
|
|
||||||
libtorrent::mutex::scoped_lock l(web_lock);
|
|
||||||
web_initialized.wait(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create this directory so that the path
|
|
||||||
// "relative/../test_file" can resolve
|
|
||||||
error_code ec;
|
|
||||||
create_directory("relative", ec);
|
|
||||||
// test_sleep(100);
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
void send_response(socket_type& s, error_code& ec
|
|
||||||
, int code, char const* status_message, char const** extra_header
|
|
||||||
, int len)
|
|
||||||
{
|
|
||||||
char msg[600];
|
|
||||||
int pkt_len = snprintf(msg, sizeof(msg), "HTTP/1.1 %d %s\r\n"
|
|
||||||
"content-length: %d\r\n"
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"\r\n"
|
|
||||||
, code, status_message, len
|
|
||||||
, extra_header[0]
|
|
||||||
, extra_header[1]
|
|
||||||
, extra_header[2]
|
|
||||||
, extra_header[3]);
|
|
||||||
DLOG(stderr, ">> %s\n", msg);
|
|
||||||
write(s, boost::asio::buffer(msg, pkt_len), boost::asio::transfer_all(), ec);
|
|
||||||
if (ec) fprintf(stderr, "*** send failed: %s\n", ec.message().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_accept(error_code& accept_ec, error_code const& ec, bool* done)
|
|
||||||
{
|
|
||||||
accept_ec = ec;
|
|
||||||
*done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
if (ec) fprintf(stderr, "*** send failed: %s\n", ec.message().c_str());
|
|
||||||
size -= chunk_size;
|
|
||||||
file += chunk_size;
|
|
||||||
chunk_size *= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
write(s, boost::asio::buffer(file, size), boost::asio::transfer_all(), ec);
|
|
||||||
// DLOG(stderr, " >> %s\n", std::string(file, size).c_str());
|
|
||||||
if (ec) fprintf(stderr, "*** send failed: %s\n", ec.message().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_read(error_code const& ec, size_t bytes_transferred, size_t* bt, error_code* e, bool* done)
|
|
||||||
{
|
|
||||||
DLOG(stderr, "on_read %d [ ec: %s ]\n", int(bytes_transferred), ec.message().c_str());
|
|
||||||
*bt = bytes_transferred;
|
|
||||||
*e = ec;
|
|
||||||
*done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_read_timeout(error_code const& ec, bool* timed_out)
|
|
||||||
{
|
|
||||||
if (ec) return;
|
|
||||||
fprintf(stderr, "read timed out\n");
|
|
||||||
*timed_out = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void web_server_thread(int* port, bool ssl, bool chunked)
|
|
||||||
{
|
|
||||||
io_service ios;
|
|
||||||
socket_acceptor acceptor(ios);
|
|
||||||
error_code ec;
|
|
||||||
acceptor.open(tcp::v4(), ec);
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error opening listen socket: %s\n", ec.message().c_str());
|
|
||||||
libtorrent::mutex::scoped_lock l(web_lock);
|
|
||||||
web_initialized.signal(l);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
acceptor.set_option(socket_acceptor::reuse_address(true), ec);
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error setting listen socket to reuse addr: %s\n", ec.message().c_str());
|
|
||||||
libtorrent::mutex::scoped_lock l(web_lock);
|
|
||||||
web_initialized.signal(l);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
acceptor.bind(tcp::endpoint(address_v4::any(), 0), ec);
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error binding listen socket to port 0: %s\n", ec.message().c_str());
|
|
||||||
libtorrent::mutex::scoped_lock l(web_lock);
|
|
||||||
web_initialized.signal(l);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*port = acceptor.local_endpoint().port();
|
|
||||||
acceptor.listen(10, ec);
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error listening on socket: %s\n", ec.message().c_str());
|
|
||||||
libtorrent::mutex::scoped_lock l(web_lock);
|
|
||||||
web_initialized.signal(l);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
web_ios = &ios;
|
|
||||||
|
|
||||||
char buf[10000];
|
|
||||||
int len = 0;
|
|
||||||
int offset = 0;
|
|
||||||
bool connection_close = false;
|
|
||||||
socket_type s(ios);
|
|
||||||
void* ctx = 0;
|
|
||||||
#ifdef TORRENT_USE_OPENSSL
|
|
||||||
boost::asio::ssl::context ssl_ctx(ios, boost::asio::ssl::context::sslv23_server);
|
|
||||||
if (ssl)
|
|
||||||
{
|
|
||||||
ssl_ctx.use_certificate_chain_file("../ssl/server.pem");
|
|
||||||
ssl_ctx.use_private_key_file("../ssl/server.pem", asio::ssl::context::pem);
|
|
||||||
ssl_ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
|
|
||||||
|
|
||||||
ctx = &ssl_ctx;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
proxy_settings p;
|
|
||||||
instantiate_connection(ios, p, s, ctx);
|
|
||||||
|
|
||||||
fprintf(stderr, "web server initialized on port %d%s\n", *port, ssl ? " [SSL]" : "");
|
|
||||||
|
|
||||||
{
|
|
||||||
libtorrent::mutex::scoped_lock l(web_lock);
|
|
||||||
web_initialized.signal(l);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (connection_close)
|
|
||||||
{
|
|
||||||
error_code ec;
|
|
||||||
#ifdef TORRENT_USE_OPENSSL
|
|
||||||
if (ssl)
|
|
||||||
{
|
|
||||||
DLOG(stderr, "shutting down SSL connection\n");
|
|
||||||
s.get<ssl_stream<stream_socket> >()->shutdown(ec);
|
|
||||||
if (ec) fprintf(stderr, "SSL shutdown failed: %s\n", ec.message().c_str());
|
|
||||||
ec.clear();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
DLOG(stderr, "closing connection\n");
|
|
||||||
s.close(ec);
|
|
||||||
if (ec) fprintf(stderr, "close failed: %s\n", ec.message().c_str());
|
|
||||||
connection_close = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!s.is_open())
|
|
||||||
{
|
|
||||||
len = 0;
|
|
||||||
offset = 0;
|
|
||||||
|
|
||||||
error_code ec;
|
|
||||||
instantiate_connection(ios, p, s, ctx);
|
|
||||||
stream_socket* sock;
|
|
||||||
#ifdef TORRENT_USE_OPENSSL
|
|
||||||
if (ssl) sock = &s.get<ssl_stream<stream_socket> >()->next_layer();
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
sock = s.get<stream_socket>();
|
|
||||||
|
|
||||||
bool accept_done = false;
|
|
||||||
fprintf(stderr, "HTTP waiting for incoming connection\n");
|
|
||||||
acceptor.async_accept(*sock, boost::bind(&on_accept, boost::ref(ec), _1, &accept_done));
|
|
||||||
while (!accept_done)
|
|
||||||
{
|
|
||||||
error_code e;
|
|
||||||
ios.reset();
|
|
||||||
if (stop_thread || ios.run_one(e) == 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: io_service stopped: %s\n", time_now_string(), e.message().c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stop_thread) break;
|
|
||||||
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: accept failed: %s\n", time_now_string(), ec.message().c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fprintf(stderr, "%s: accepting incoming connection\n", time_now_string());
|
|
||||||
if (!s.is_open())
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: incoming connection closed\n", time_now_string());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TORRENT_USE_OPENSSL
|
|
||||||
if (ssl)
|
|
||||||
{
|
|
||||||
DLOG(stderr, "%s: SSL handshake\n", time_now_string());
|
|
||||||
s.get<ssl_stream<stream_socket> >()->accept_handshake(ec);
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "SSL handshake failed: %s\n", ec.message().c_str());
|
|
||||||
connection_close = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
http_parser p;
|
|
||||||
bool failed = false;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
p.reset();
|
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
p.incoming(buffer::const_interval(buf + offset, buf + len), error);
|
|
||||||
|
|
||||||
char const* extra_header[4] = {"","","",""};
|
|
||||||
|
|
||||||
TEST_CHECK(error == false);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "HTTP parse failed\n");
|
|
||||||
failed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!p.finished())
|
|
||||||
{
|
|
||||||
TORRENT_ASSERT(len <= int(sizeof(buf)));
|
|
||||||
size_t received = 0;
|
|
||||||
bool done = false;
|
|
||||||
bool timed_out = false;
|
|
||||||
DLOG(stderr, "async_read_some %d bytes [ len: %d ]\n", int(sizeof(buf) - len), len);
|
|
||||||
s.async_read_some(boost::asio::buffer(&buf[len]
|
|
||||||
, sizeof(buf) - len), boost::bind(&on_read, _1, _2, &received, &ec, &done));
|
|
||||||
deadline_timer timer(ios);
|
|
||||||
timer.expires_at(time_now_hires() + seconds(2));
|
|
||||||
timer.async_wait(boost::bind(&on_read_timeout, _1, &timed_out));
|
|
||||||
|
|
||||||
while (!done && !timed_out)
|
|
||||||
{
|
|
||||||
error_code e;
|
|
||||||
ios.reset();
|
|
||||||
if (stop_thread || ios.run_one(e) == 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "HTTP io_service stopped: %s\n", e.message().c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (timed_out)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "HTTP read timed out, closing connection\n");
|
|
||||||
failed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// fprintf(stderr, "read: %d\n", int(received));
|
|
||||||
|
|
||||||
if (ec || received <= 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "HTTP read failed: \"%s\" (%s) received: %d\n"
|
|
||||||
, ec.message().c_str(), ec.category().name(), int(received));
|
|
||||||
failed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
timer.cancel(ec);
|
|
||||||
if (ec)
|
|
||||||
fprintf(stderr, "HTTP timer.cancel failed: %s\n", ec.message().c_str());
|
|
||||||
|
|
||||||
len += received;
|
|
||||||
|
|
||||||
p.incoming(buffer::const_interval(buf + offset, buf + len), error);
|
|
||||||
|
|
||||||
TEST_CHECK(error == false);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "HTTP parse failed\n");
|
|
||||||
failed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string connection = p.header("connection");
|
|
||||||
std::string via = p.header("via");
|
|
||||||
|
|
||||||
if (p.protocol() == "HTTP/1.0")
|
|
||||||
{
|
|
||||||
DLOG(stderr, "*** HTTP/1.0, closing connection when done\n");
|
|
||||||
connection_close = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DLOG(stderr, "REQ: %s", std::string(buf + offset, p.body_start()).c_str());
|
|
||||||
|
|
||||||
if (failed)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "*** connection failed\n");
|
|
||||||
connection_close = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += int(p.body_start() + p.content_length());
|
|
||||||
// fprintf(stderr, "offset: %d len: %d\n", offset, len);
|
|
||||||
|
|
||||||
if (p.method() != "get" && p.method() != "post")
|
|
||||||
{
|
|
||||||
fprintf(stderr, "*** incorrect method: %s\n", p.method().c_str());
|
|
||||||
connection_close = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string path = p.path();
|
|
||||||
|
|
||||||
std::vector<char> file_buf;
|
|
||||||
if (path.substr(0, 4) == "http")
|
|
||||||
{
|
|
||||||
// remove the http://hostname and the first / of the path
|
|
||||||
path = path.substr(path.find("://")+3);
|
|
||||||
path = path.substr(path.find_first_of('/')+1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// remove the / from the path
|
|
||||||
path = path.substr(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fprintf(stderr, "%s: [HTTP] %s\n", time_now_string(), path.c_str());
|
|
||||||
|
|
||||||
if (path == "redirect")
|
|
||||||
{
|
|
||||||
extra_header[0] = "Location: /test_file\r\n";
|
|
||||||
send_response(s, ec, 301, "Moved Permanently", extra_header, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path == "infinite_redirect")
|
|
||||||
{
|
|
||||||
extra_header[0] = "Location: /infinite_redirect\r\n";
|
|
||||||
send_response(s, ec, 301, "Moved Permanently", extra_header, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path == "relative/redirect")
|
|
||||||
{
|
|
||||||
extra_header[0] = "Location: ../test_file\r\n";
|
|
||||||
send_response(s, ec, 301, "Moved Permanently", extra_header, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.substr(0, 8) == "announce")
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s\n", path.c_str());
|
|
||||||
entry announce;
|
|
||||||
announce["interval"] = 1800;
|
|
||||||
announce["complete"] = 1;
|
|
||||||
announce["incomplete"] = 1;
|
|
||||||
announce["peers"].string();
|
|
||||||
std::vector<char> buf;
|
|
||||||
bencode(std::back_inserter(buf), announce);
|
|
||||||
++g_http_tracker_requests;
|
|
||||||
fprintf(stderr, "[HTTP]: announce [%d]\n", int(g_http_tracker_requests));
|
|
||||||
|
|
||||||
send_response(s, ec, 200, "OK", extra_header, buf.size());
|
|
||||||
write(s, boost::asio::buffer(&buf[0], buf.size()), boost::asio::transfer_all(), ec);
|
|
||||||
if (ec)
|
|
||||||
fprintf(stderr, "[HTTP] *** send response failed: %s\n", ec.message().c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filename(path).substr(0, 5) == "seed?")
|
|
||||||
{
|
|
||||||
char const* piece = strstr(path.c_str(), "&piece=");
|
|
||||||
if (piece == 0) piece = strstr(path.c_str(), "?piece=");
|
|
||||||
if (piece == 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "invalid web seed request: %s\n", path.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
boost::uint64_t idx = atoi(piece + 7);
|
|
||||||
char const* range = strstr(path.c_str(), "&ranges=");
|
|
||||||
if (range == 0) range = strstr(path.c_str(), "?ranges=");
|
|
||||||
int range_end = 0;
|
|
||||||
int range_start = 0;
|
|
||||||
if (range)
|
|
||||||
{
|
|
||||||
range_start = atoi(range + 8);
|
|
||||||
range = strchr(range, '-');
|
|
||||||
if (range == 0)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "invalid web seed request: %s\n", path.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
range_end = atoi(range + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
range_start = 0;
|
|
||||||
// assume piece size of 64kiB
|
|
||||||
range_end = 64*1024+1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = range_end - range_start + 1;
|
|
||||||
boost::uint64_t off = idx * 64 * 1024 + range_start;
|
|
||||||
std::vector<char> file_buf;
|
|
||||||
error_code ec;
|
|
||||||
std::string file_path = path.substr(0, path.find_first_of('?'));
|
|
||||||
int res = load_file(file_path.c_str(), file_buf, ec);
|
|
||||||
|
|
||||||
if (res == -1 || file_buf.empty())
|
|
||||||
{
|
|
||||||
fprintf(stderr, "file not found: %s\n", file_path.c_str());
|
|
||||||
send_response(s, ec, 404, "Not Found", extra_header, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
send_response(s, ec, 200, "OK", extra_header, size);
|
|
||||||
DLOG(stderr, "sending %d bytes of payload [%d, %d) piece: %d\n"
|
|
||||||
, size, int(off), int(off + size), int(idx));
|
|
||||||
write(s, boost::asio::buffer(&file_buf[0] + off, size)
|
|
||||||
, boost::asio::transfer_all(), ec);
|
|
||||||
if (ec)
|
|
||||||
fprintf(stderr, "*** send failed: %s\n", ec.message().c_str());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DLOG(stderr, "*** done\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
memmove(buf, buf + offset, len - offset);
|
|
||||||
len -= offset;
|
|
||||||
offset = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
DLOG(stderr, ">> serving file %s\n", path.c_str());
|
|
||||||
error_code ec;
|
|
||||||
int res = load_file(path, file_buf, ec, 8000000);
|
|
||||||
if (res == -1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, ">> file not found: %s\n", path.c_str());
|
|
||||||
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
|
|
||||||
fprintf(stderr, ">> file too big: %s\n", path.c_str());
|
|
||||||
send_response(s, ec, 503, "Internal Error", extra_header, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// serve file
|
|
||||||
|
|
||||||
if (extension(path) == ".gz")
|
|
||||||
{
|
|
||||||
extra_header[0] = "Content-Encoding: gzip\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chunked)
|
|
||||||
{
|
|
||||||
extra_header[2] = "Transfer-Encoding: chunked\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p.header("range").empty())
|
|
||||||
{
|
|
||||||
std::string range = p.header("range");
|
|
||||||
int start, end;
|
|
||||||
sscanf(range.c_str(), "bytes=%d-%d", &start, &end);
|
|
||||||
char eh[400];
|
|
||||||
snprintf(eh, sizeof(eh), "Content-Range: bytes %d-%d\r\n", start, end);
|
|
||||||
extra_header[1] = eh;
|
|
||||||
if (end - start + 1 >= 1000)
|
|
||||||
{
|
|
||||||
DLOG(stderr, "request size: %.2f kB\n", int(end - start + 1)/1000.f);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DLOG(stderr, "request size: %d Bytes\n", int(end - start + 1));
|
|
||||||
}
|
|
||||||
send_response(s, ec, 206, "Partial", extra_header, end - start + 1);
|
|
||||||
if (!file_buf.empty())
|
|
||||||
{
|
|
||||||
send_content(s, &file_buf[0] + start, end - start + 1, chunked);
|
|
||||||
}
|
|
||||||
DLOG(stderr, "send %d bytes of payload\n", end - start + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
send_response(s, ec, 200, "OK", extra_header, file_buf.size());
|
|
||||||
if (!file_buf.empty())
|
|
||||||
send_content(s, &file_buf[0], file_buf.size(), chunked);
|
|
||||||
}
|
|
||||||
DLOG(stderr, "%d bytes left in receive buffer. offset: %d\n", len - offset, offset);
|
|
||||||
memmove(buf, buf + offset, len - offset);
|
|
||||||
len -= offset;
|
|
||||||
offset = 0;
|
|
||||||
} while (offset < len);
|
|
||||||
}
|
|
||||||
|
|
||||||
web_ios = 0;
|
|
||||||
fprintf(stderr, "%s: exiting web server thread\n", time_now_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -69,7 +69,6 @@ void EXPORT wait_for_listen(libtorrent::session& ses, char const* name);
|
|||||||
void EXPORT test_sleep(int millisec);
|
void EXPORT test_sleep(int millisec);
|
||||||
|
|
||||||
extern EXPORT boost::detail::atomic_count g_udp_tracker_requests;
|
extern EXPORT boost::detail::atomic_count g_udp_tracker_requests;
|
||||||
extern EXPORT boost::detail::atomic_count g_http_tracker_requests;
|
|
||||||
|
|
||||||
void EXPORT create_random_files(std::string const& path, const int file_sizes[], int num_files);
|
void EXPORT create_random_files(std::string const& path, const int file_sizes[], int num_files);
|
||||||
|
|
||||||
|
@@ -186,7 +186,11 @@ class SocksHandler(StreamRequestHandler):
|
|||||||
outbound_sock = socket.socket(socket.AF_INET6)
|
outbound_sock = socket.socket(socket.AF_INET6)
|
||||||
else:
|
else:
|
||||||
outbound_sock = socket.socket(socket.AF_INET)
|
outbound_sock = socket.socket(socket.AF_INET)
|
||||||
out_address = socket.getaddrinfo(dest_address,dest_port)[0][4]
|
try:
|
||||||
|
out_address = socket.getaddrinfo(dest_address,dest_port)[0][4]
|
||||||
|
except Exception, e:
|
||||||
|
print e
|
||||||
|
return
|
||||||
debug("Creating forwarder connection to %r", out_address)
|
debug("Creating forwarder connection to %r", out_address)
|
||||||
outbound_sock.connect(out_address)
|
outbound_sock.connect(out_address)
|
||||||
|
|
||||||
|
@@ -159,7 +159,7 @@ void run_suite(std::string const& protocol, proxy_settings ps, int port)
|
|||||||
|
|
||||||
run_test(url_base + "relative/redirect", 3216, 200, 2, error_code(), ps);
|
run_test(url_base + "relative/redirect", 3216, 200, 2, error_code(), ps);
|
||||||
run_test(url_base + "redirect", 3216, 200, 2, error_code(), ps);
|
run_test(url_base + "redirect", 3216, 200, 2, error_code(), ps);
|
||||||
run_test(url_base + "infinite_redirect", 0, 301, 6, error_code(), ps);
|
run_test(url_base + "infinite_redirect", 0, 301, 6, error_code(asio::error::eof), ps);
|
||||||
run_test(url_base + "test_file", 3216, 200, 1, error_code(), ps);
|
run_test(url_base + "test_file", 3216, 200, 1, error_code(), ps);
|
||||||
run_test(url_base + "test_file.gz", 3216, 200, 1, error_code(), ps);
|
run_test(url_base + "test_file.gz", 3216, 200, 1, error_code(), ps);
|
||||||
run_test(url_base + "non-existing-file", -1, 404, 1, err(), ps);
|
run_test(url_base + "non-existing-file", -1, 404, 1, err(), ps);
|
||||||
|
@@ -88,7 +88,6 @@ session_proxy test_proxy(proxy_settings::proxy_type proxy_type, int flags)
|
|||||||
int peer_port = start_peer();
|
int peer_port = start_peer();
|
||||||
|
|
||||||
int prev_udp_announces = g_udp_tracker_requests;
|
int prev_udp_announces = g_udp_tracker_requests;
|
||||||
int prev_http_announces = g_http_tracker_requests;
|
|
||||||
|
|
||||||
int const alert_mask = alert::all_categories
|
int const alert_mask = alert::all_categories
|
||||||
& ~alert::progress_notification
|
& ~alert::progress_notification
|
||||||
@@ -161,14 +160,12 @@ session_proxy test_proxy(proxy_settings::proxy_type proxy_type, int flags)
|
|||||||
test_sleep(100);
|
test_sleep(100);
|
||||||
|
|
||||||
if (g_udp_tracker_requests >= prev_udp_announces + 1
|
if (g_udp_tracker_requests >= prev_udp_announces + 1
|
||||||
&& g_http_tracker_requests >= prev_http_announces + 1
|
|
||||||
&& num_peer_hits() > 0)
|
&& num_peer_hits() > 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we should have announced to the tracker by now
|
// we should have announced to the tracker by now
|
||||||
TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + bool(flags & expect_udp_connection));
|
TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + bool(flags & expect_udp_connection));
|
||||||
TEST_EQUAL(g_http_tracker_requests, prev_http_announces + bool(flags & expect_http_connection));
|
|
||||||
if (flags & expect_dht_msg)
|
if (flags & expect_dht_msg)
|
||||||
{
|
{
|
||||||
TEST_CHECK(num_dht_hits() > 0);
|
TEST_CHECK(num_dht_hits() > 0);
|
||||||
|
@@ -46,7 +46,6 @@ int test_main()
|
|||||||
int udp_port = start_tracker();
|
int udp_port = start_tracker();
|
||||||
|
|
||||||
int prev_udp_announces = g_udp_tracker_requests;
|
int prev_udp_announces = g_udp_tracker_requests;
|
||||||
int prev_http_announces = g_http_tracker_requests;
|
|
||||||
|
|
||||||
int const alert_mask = alert::all_categories
|
int const alert_mask = alert::all_categories
|
||||||
& ~alert::progress_notification
|
& ~alert::progress_notification
|
||||||
@@ -84,15 +83,12 @@ int test_main()
|
|||||||
{
|
{
|
||||||
print_alerts(*s, "s");
|
print_alerts(*s, "s");
|
||||||
test_sleep(100);
|
test_sleep(100);
|
||||||
// fprintf(stderr, "udp_announces: %d http_announces: %d\n", int(g_udp_tracker_requests), int(g_http_tracker_requests));
|
if (g_udp_tracker_requests == prev_udp_announces + 1)
|
||||||
if (g_udp_tracker_requests == prev_udp_announces + 1
|
|
||||||
&& g_http_tracker_requests == prev_http_announces + 1)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we should have announced to the tracker by now
|
// we should have announced to the tracker by now
|
||||||
TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
|
TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
|
||||||
TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 1);
|
|
||||||
|
|
||||||
fprintf(stderr, "destructing session\n");
|
fprintf(stderr, "destructing session\n");
|
||||||
delete s;
|
delete s;
|
||||||
@@ -100,7 +96,6 @@ int test_main()
|
|||||||
|
|
||||||
// we should have announced the stopped event now
|
// we should have announced the stopped event now
|
||||||
TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 2);
|
TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 2);
|
||||||
TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 2);
|
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// test that we move on to try the next tier if the first one fails
|
// test that we move on to try the next tier if the first one fails
|
||||||
@@ -139,7 +134,6 @@ int test_main()
|
|||||||
t->add_tracker(tracker_url, 3);
|
t->add_tracker(tracker_url, 3);
|
||||||
|
|
||||||
prev_udp_announces = g_udp_tracker_requests;
|
prev_udp_announces = g_udp_tracker_requests;
|
||||||
prev_http_announces = g_http_tracker_requests;
|
|
||||||
|
|
||||||
addp.flags &= ~add_torrent_params::flag_paused;
|
addp.flags &= ~add_torrent_params::flag_paused;
|
||||||
addp.flags &= ~add_torrent_params::flag_auto_managed;
|
addp.flags &= ~add_torrent_params::flag_auto_managed;
|
||||||
@@ -157,7 +151,6 @@ int test_main()
|
|||||||
test_sleep(1000);
|
test_sleep(1000);
|
||||||
|
|
||||||
TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
|
TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
|
||||||
TEST_EQUAL(g_http_tracker_requests, prev_http_announces);
|
|
||||||
|
|
||||||
fprintf(stderr, "destructing session\n");
|
fprintf(stderr, "destructing session\n");
|
||||||
delete s;
|
delete s;
|
||||||
|
@@ -175,6 +175,7 @@ static void test_transfer(session& ses, boost::intrusive_ptr<torrent_info> torre
|
|||||||
{
|
{
|
||||||
TEST_EQUAL(cs.cache_size, 0);
|
TEST_EQUAL(cs.cache_size, 0);
|
||||||
TEST_EQUAL(cs.total_used_buffers, 0);
|
TEST_EQUAL(cs.total_used_buffers, 0);
|
||||||
|
TEST_EQUAL(th.status().is_seeding, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cerr << "total_size: " << total_size
|
std::cerr << "total_size: " << total_size
|
||||||
@@ -213,11 +214,11 @@ int EXPORT run_http_suite(int proxy, char const* protocol, bool test_url_seed, b
|
|||||||
{
|
{
|
||||||
using namespace libtorrent;
|
using namespace libtorrent;
|
||||||
|
|
||||||
std::string save_path = "tmp1_web_seed";
|
std::string save_path = "web_seed";
|
||||||
save_path += proxy_name[proxy];
|
save_path += proxy_name[proxy];
|
||||||
|
|
||||||
error_code ec;
|
error_code ec;
|
||||||
create_directories(combine_path(save_path, "test_torrent_dir"), ec);
|
create_directories(combine_path(save_path, "torrent_dir"), ec);
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "FAILED TO CREATE DIRECTORY: (%d) %s\n"
|
fprintf(stderr, "FAILED TO CREATE DIRECTORY: (%d) %s\n"
|
||||||
@@ -235,15 +236,16 @@ int EXPORT run_http_suite(int proxy, char const* protocol, bool test_url_seed, b
|
|||||||
|
|
||||||
if (test_url_seed)
|
if (test_url_seed)
|
||||||
{
|
{
|
||||||
create_random_files(combine_path(save_path, "test_torrent_dir"), file_sizes, sizeof(file_sizes)/sizeof(file_sizes[0]));
|
create_random_files(combine_path(save_path, "torrent_dir"), file_sizes, sizeof(file_sizes)/sizeof(file_sizes[0]));
|
||||||
add_files(fs, combine_path(save_path, "test_torrent_dir"));
|
add_files(fs, combine_path(save_path, "torrent_dir"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
piece_size = 64 * 1024;
|
piece_size = 64 * 1024;
|
||||||
char* random_data = (char*)malloc(64 * 1024 * num_pieces);
|
char* random_data = (char*)malloc(64 * 1024 * num_pieces);
|
||||||
std::generate(random_data, random_data + 64 * 1024 * num_pieces, &std::rand);
|
std::generate(random_data, random_data + 64 * 1024 * num_pieces, &std::rand);
|
||||||
save_file(combine_path(save_path, "seed").c_str(), random_data, 64 * 1024 * num_pieces);
|
std::string seed_filename = combine_path(save_path, "seed");
|
||||||
|
save_file(seed_filename.c_str(), random_data, 64 * 1024 * num_pieces);
|
||||||
fs.add_file("seed", 64 * 1024 * num_pieces);
|
fs.add_file("seed", 64 * 1024 * num_pieces);
|
||||||
free(random_data);
|
free(random_data);
|
||||||
}
|
}
|
||||||
@@ -290,7 +292,7 @@ int EXPORT run_http_suite(int proxy, char const* protocol, bool test_url_seed, b
|
|||||||
// corrupt the files now, so that the web seed will be banned
|
// corrupt the files now, so that the web seed will be banned
|
||||||
if (test_url_seed)
|
if (test_url_seed)
|
||||||
{
|
{
|
||||||
create_random_files(combine_path(save_path, "test_torrent_dir"), file_sizes, sizeof(file_sizes)/sizeof(file_sizes[0]));
|
create_random_files(combine_path(save_path, "torrent_dir"), file_sizes, sizeof(file_sizes)/sizeof(file_sizes[0]));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -336,7 +338,7 @@ int EXPORT run_http_suite(int proxy, char const* protocol, bool test_url_seed, b
|
|||||||
|
|
||||||
if (test_url_seed)
|
if (test_url_seed)
|
||||||
{
|
{
|
||||||
torrent_file->rename_file(0, combine_path(save_path, "test_torrent_dir/renamed_test1"));
|
torrent_file->rename_file(0, combine_path(save_path, combine_path("torrent_dir", "renamed_test1")));
|
||||||
test_transfer(ses, torrent_file, 0, port, protocol, test_url_seed, chunked_encoding, test_ban);
|
test_transfer(ses, torrent_file, 0, port, protocol, test_url_seed, chunked_encoding, test_ban);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,5 +31,6 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||||||
*/
|
*/
|
||||||
#include "test.hpp"
|
#include "test.hpp"
|
||||||
|
|
||||||
int EXPORT run_http_suite(int proxy, char const* protocol, bool test_url_seed, bool chunked_encoding, bool test_ban);
|
int EXPORT run_http_suite(int proxy, char const* protocol
|
||||||
|
, bool test_url_seed, bool chunked_encoding, bool test_ban);
|
||||||
|
|
||||||
|
145
test/web_server.py
Normal file
145
test/web_server.py
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import BaseHTTPServer
|
||||||
|
import SimpleHTTPServer
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import ssl
|
||||||
|
|
||||||
|
chunked_encoding = False
|
||||||
|
|
||||||
|
class http_handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||||
|
|
||||||
|
def do_GET(s):
|
||||||
|
|
||||||
|
#print s.requestline
|
||||||
|
global chunked_encoding
|
||||||
|
|
||||||
|
# if the request contains the hostname and port. strip it
|
||||||
|
if s.path.startswith('http://') or s.path.startswith('https://'):
|
||||||
|
s.path = s.path[8:]
|
||||||
|
s.path = s.path[s.path.find('/'):]
|
||||||
|
|
||||||
|
s.path = os.path.normpath(s.path)
|
||||||
|
|
||||||
|
if s.path == '/redirect':
|
||||||
|
s.send_response(301)
|
||||||
|
s.send_header("Location", "/test_file")
|
||||||
|
s.send_header("Connection", "close")
|
||||||
|
s.end_headers()
|
||||||
|
s.finish()
|
||||||
|
elif s.path == '/infinite_redirect':
|
||||||
|
s.send_response(301)
|
||||||
|
s.send_header("Location", "/infinite_redirect")
|
||||||
|
s.send_header("Connection", "close")
|
||||||
|
s.end_headers()
|
||||||
|
s.finish()
|
||||||
|
elif s.path == '/relative/redirect':
|
||||||
|
s.send_response(301)
|
||||||
|
s.send_header("Location", "../test_file")
|
||||||
|
s.send_header("Connection", "close")
|
||||||
|
s.end_headers()
|
||||||
|
s.finish()
|
||||||
|
elif s.path.startswith('/announce'):
|
||||||
|
s.send_response(200)
|
||||||
|
response = 'd8:intervali1800e8:completei1e10:incompletei1e5:peers0:e'
|
||||||
|
s.send_header("Content-Length", "%d" % len(response))
|
||||||
|
s.send_header("Connection", "close")
|
||||||
|
s.end_headers()
|
||||||
|
s.wfile.write(response)
|
||||||
|
s.finish()
|
||||||
|
elif os.path.split(s.path)[1].startswith('seed?'):
|
||||||
|
query = s.path[6:]
|
||||||
|
args_raw = query.split('&')
|
||||||
|
args = {}
|
||||||
|
for a in args_raw:
|
||||||
|
kvp = a.split('=')
|
||||||
|
args[kvp[0]] = kvp[1]
|
||||||
|
piece = int(args['piece'])
|
||||||
|
ranges = args['ranges'].split('-')
|
||||||
|
|
||||||
|
try:
|
||||||
|
filename = s.path[1:s.path.find('seed?') + 4]
|
||||||
|
#print 'filename = %s' % filename
|
||||||
|
f = open(filename, 'rb')
|
||||||
|
f.seek(piece * 64 * 1024 + int(ranges[0]))
|
||||||
|
data = f.read(int(ranges[1]) - int(ranges[0]) + 1)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
s.send_response(200)
|
||||||
|
print 'sending %d bytes' % len(data)
|
||||||
|
s.send_header("Content-Length", "%d" % len(data))
|
||||||
|
s.end_headers()
|
||||||
|
s.wfile.write(data);
|
||||||
|
except Exception, e:
|
||||||
|
print 'FILE NOT FOUND: ', e
|
||||||
|
s.send_response(404)
|
||||||
|
s.send_header("Content-Length", "0")
|
||||||
|
s.end_headers()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
filename = s.path[1:]
|
||||||
|
# serve file by invoking default handler
|
||||||
|
f = open(filename)
|
||||||
|
size = int(os.stat(filename).st_size)
|
||||||
|
start_range = 0
|
||||||
|
end_range = size
|
||||||
|
if 'Range' in s.headers:
|
||||||
|
s.send_response(206)
|
||||||
|
s.send_header('Content-Range', 'bytes ' + str(start_range) + '-' + str(end_range - 1) + '/' + str(size))
|
||||||
|
st, e = s.headers['range'][6:].split('-', 1)
|
||||||
|
sl = len(st)
|
||||||
|
el = len(e)
|
||||||
|
if sl > 0:
|
||||||
|
start_range = int(st)
|
||||||
|
if el > 0:
|
||||||
|
end_range = int(e) + 1
|
||||||
|
elif el > 0:
|
||||||
|
ei = int(e)
|
||||||
|
if ei < size:
|
||||||
|
start_range = size - ei
|
||||||
|
else:
|
||||||
|
s.send_response(200)
|
||||||
|
s.send_header('Accept-Ranges', 'bytes')
|
||||||
|
if chunked_encoding:
|
||||||
|
s.send_header('Transfer-Encoding', 'chunked')
|
||||||
|
s.send_header('Content-Length', end_range - start_range)
|
||||||
|
if filename.endswith('.gz'):
|
||||||
|
s.send_header('Content-Encoding', 'gzip')
|
||||||
|
s.end_headers()
|
||||||
|
|
||||||
|
f.seek(start_range)
|
||||||
|
length = end_range - start_range
|
||||||
|
while length > 0:
|
||||||
|
to_send = min(length, 0x900)
|
||||||
|
if chunked_encoding:
|
||||||
|
s.wfile.write('%x\r\n' % to_send)
|
||||||
|
data = f.read(to_send)
|
||||||
|
s.wfile.write(data)
|
||||||
|
if chunked_encoding:
|
||||||
|
s.wfile.write('\r\n')
|
||||||
|
length -= to_send
|
||||||
|
if chunked_encoding:
|
||||||
|
s.wfile.write('0\r\n\r\n')
|
||||||
|
except Exception, e:
|
||||||
|
print 'FILE NOT FOUND: ', e
|
||||||
|
s.send_response(404)
|
||||||
|
s.send_header("Content-Length", "0")
|
||||||
|
s.end_headers()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
port = int(sys.argv[1])
|
||||||
|
chunked_encoding = sys.argv[2] != '0'
|
||||||
|
ssl = sys.argv[3] != '0'
|
||||||
|
|
||||||
|
# TODO: SSL support
|
||||||
|
http_handler.protocol_version = 'HTTP/1.1'
|
||||||
|
httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', port), http_handler)
|
||||||
|
if ssl:
|
||||||
|
httpd.socket = ssl.wrap_socket(httpd.socket, certfile='../ssl/server.pem', server_side=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
httpd.serve_forever()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
httpd.server_close()
|
||||||
|
|
Reference in New Issue
Block a user