@@ -825,6 +825,7 @@ add_test(http_timeouts_test_write | |||||
"--test-write=true" | "--test-write=true" | ||||
"--test-exec=${PYTHON} ${CNF}/http_timeouts_test.py write") | "--test-exec=${PYTHON} ${CNF}/http_timeouts_test.py write") | ||||
add_test(file_server_test | add_test(file_server_test | ||||
file_server_test "-c" "${CNF}/file_server_test.js" | file_server_test "-c" "${CNF}/file_server_test.js" | ||||
"--test-exec=${PYTHON} ${CNF}/file_server_test.py" | "--test-exec=${PYTHON} ${CNF}/file_server_test.py" | ||||
@@ -844,6 +845,27 @@ add_test(file_server_with_listing_test | |||||
"-U" "${CNF}" | "-U" "${CNF}" | ||||
) | ) | ||||
add_test(file_server_test_async | |||||
file_server_test "-c" "${CNF}/file_server_test.js" "--file_server-async=true" | |||||
"--test-exec=${PYTHON} ${CNF}/file_server_test.py" | |||||
"-U" "${CNF}" | |||||
) | |||||
add_test(file_server_test_no_links_async | |||||
file_server_test "-c" "${CNF}/file_server_test.js" "--file_server-check_symlink=false" "--file_server-async=true" | |||||
"--test-exec=${PYTHON} ${CNF}/file_server_test.py no_links" | |||||
"-U" "${CNF}" | |||||
) | |||||
add_test(file_server_with_listing_test_async | |||||
file_server_test "-c" "${CNF}/file_server_test.js" "--file_server-async=true" | |||||
"--file_server-listing=true" | |||||
"--test-exec=${PYTHON} ${CNF}/file_server_test.py listing" | |||||
"-U" "${CNF}" | |||||
) | |||||
add_test(disco_test_http | add_test(disco_test_http | ||||
disco_test "-c" "${CNF}/proto_test.js" | disco_test "-c" "${CNF}/proto_test.js" | ||||
"--service-api=http" "--service-port=8080" "--service-ip=127.0.0.1" | "--service-api=http" "--service-port=8080" "--service-ip=127.0.0.1" | ||||
@@ -18,7 +18,7 @@ namespace impl { | |||||
public: | public: | ||||
static void normalize_path(std::string &path); | static void normalize_path(std::string &path); | ||||
file_server(cppcms::service &srv); | |||||
file_server(cppcms::service &srv,bool async=false); | |||||
~file_server(); | ~file_server(); | ||||
virtual void main(std::string file_name); | virtual void main(std::string file_name); | ||||
@@ -38,6 +38,7 @@ namespace impl { | |||||
mime_type mime_; | mime_type mime_; | ||||
bool list_directories_; | bool list_directories_; | ||||
bool check_symlinks_; | bool check_symlinks_; | ||||
bool async_; | |||||
std::string index_file_; | std::string index_file_; | ||||
}; | }; | ||||
@@ -19,6 +19,7 @@ | |||||
namespace cppcms { | namespace cppcms { | ||||
class service; | class service; | ||||
class applications_pool; | class applications_pool; | ||||
class application; | |||||
class thread_pool; | class thread_pool; | ||||
class session_pool; | class session_pool; | ||||
@@ -67,7 +68,7 @@ namespace impl { | |||||
std::vector<std::string> args_; | std::vector<std::string> args_; | ||||
booster::intrusive_ptr<cppcms::application> async_file_server_; | |||||
}; | }; | ||||
@@ -15,9 +15,11 @@ | |||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <booster/callback.h> | |||||
#include <cppcms/application.h> | #include <cppcms/application.h> | ||||
#include <cppcms/service.h> | #include <cppcms/service.h> | ||||
#include <cppcms/http_response.h> | #include <cppcms/http_response.h> | ||||
#include <cppcms/http_context.h> | |||||
#include "internal_file_server.h" | #include "internal_file_server.h" | ||||
#include <cppcms/cppcms_error.h> | #include <cppcms/cppcms_error.h> | ||||
#include <cppcms/json.h> | #include <cppcms/json.h> | ||||
@@ -41,7 +43,7 @@ | |||||
namespace cppcms { | namespace cppcms { | ||||
namespace impl { | namespace impl { | ||||
file_server::file_server(cppcms::service &srv) : application(srv) | |||||
file_server::file_server(cppcms::service &srv,bool async) : application(srv), async_(async) | |||||
{ | { | ||||
if(!canonical(settings().get("file_server.document_root","."),document_root_)) | if(!canonical(settings().get("file_server.document_root","."),document_root_)) | ||||
throw cppcms_error("Invalid document root"); | throw cppcms_error("Invalid document root"); | ||||
@@ -406,16 +408,63 @@ void file_server::list_dir(std::string const &url,std::string const &path) | |||||
out <<"</body>\n"; | out <<"</body>\n"; | ||||
} | } | ||||
namespace file_server_detail { | |||||
class async_file_handler : public booster::callable<void(cppcms::http::context::completion_type)> | |||||
{ | |||||
public: | |||||
async_file_handler(std::string const &path,booster::shared_ptr<cppcms::http::context> c) : | |||||
f(path.c_str(),std::ios_base::binary), | |||||
ctx(c) | |||||
{ | |||||
} | |||||
typedef booster::intrusive_ptr<async_file_handler> pointer_type; | |||||
void go() | |||||
{ | |||||
if(!f) { | |||||
ctx->response().set_html_header(); | |||||
ctx->response().make_error_response(404); | |||||
ctx->async_complete_response(); | |||||
} | |||||
else { | |||||
ctx->response(); | |||||
(*this)(cppcms::http::context::operation_completed); | |||||
} | |||||
} | |||||
void operator()(cppcms::http::context::completion_type c) | |||||
{ | |||||
if(c!=cppcms::http::context::operation_completed) | |||||
return; | |||||
char buf[4096]; | |||||
size_t total = 0; | |||||
while(!f.eof() && total < 65536) { | |||||
f.read(buf,sizeof(buf)); | |||||
size_t n = f.gcount(); | |||||
total += n; | |||||
ctx->response().out().write(buf,n); | |||||
} | |||||
if(f.eof()) | |||||
ctx->async_complete_response(); | |||||
else | |||||
ctx->async_flush_output(pointer_type(this)); | |||||
} | |||||
private: | |||||
booster::nowide::ifstream f; | |||||
booster::shared_ptr<cppcms::http::context> ctx; | |||||
}; | |||||
} // file_server_detail | |||||
void file_server::main(std::string file_name) | void file_server::main(std::string file_name) | ||||
{ | { | ||||
std::string path; | std::string path; | ||||
if(!check_in_document_root(file_name,path)) { | if(!check_in_document_root(file_name,path)) { | ||||
show404(); | show404(); | ||||
return; | return; | ||||
} | } | ||||
int s=file_mode(path); | int s=file_mode(path); | ||||
if((s & S_IFDIR)) { | if((s & S_IFDIR)) { | ||||
@@ -467,33 +516,28 @@ void file_server::main(std::string file_name) | |||||
else | else | ||||
response().content_type("application/octet-stream"); | response().content_type("application/octet-stream"); | ||||
if(!allow_deflate_) { | |||||
if(!allow_deflate_ && !async_) { | |||||
response().io_mode(http::response::nogzip); | response().io_mode(http::response::nogzip); | ||||
} | } | ||||
booster::nowide::ifstream file(path.c_str(),std::ios_base::binary); | |||||
if(!file) { | |||||
show404(); | |||||
return; | |||||
if(async_) { | |||||
file_server_detail::async_file_handler::pointer_type p=new file_server_detail::async_file_handler(path,release_context()); | |||||
p->go(); | |||||
} | |||||
else { | |||||
booster::nowide::ifstream file(path.c_str(),std::ios_base::binary); | |||||
if(!file) { | |||||
show404(); | |||||
return; | |||||
} | |||||
response().out()<<file.rdbuf(); // write stream to stream | |||||
} | } | ||||
response().out()<<file.rdbuf(); // write stream to stream | |||||
} | } | ||||
void file_server::show404() | void file_server::show404() | ||||
{ | { | ||||
response().status(http::response::not_found); | |||||
response().set_html_header(); | response().set_html_header(); | ||||
response().out() << | |||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n" | |||||
" \"http://www.w3.org/TR/html4/loose.dtd\">\n" | |||||
"<html>\n" | |||||
" <head>\n" | |||||
" <title>404 Not Found</title>\n" | |||||
" </head>\n" | |||||
" <body>\n" | |||||
" <h1>404 Not Found</h1>\n" | |||||
" </body>\n" | |||||
"</html>\n"<<std::flush; | |||||
response().make_error_response(404); | |||||
} | } | ||||
@@ -26,6 +26,7 @@ | |||||
#include <cppcms/service.h> | #include <cppcms/service.h> | ||||
#include "service_impl.h" | #include "service_impl.h" | ||||
#include <cppcms/applications_pool.h> | #include <cppcms/applications_pool.h> | ||||
#include <cppcms/application.h> | |||||
#include <cppcms/thread_pool.h> | #include <cppcms/thread_pool.h> | ||||
#include <cppcms/cppcms_error.h> | #include <cppcms/cppcms_error.h> | ||||
#include <cppcms/mount_point.h> | #include <cppcms/mount_point.h> | ||||
@@ -235,7 +236,13 @@ void service::setup() | |||||
impl_->cache_pool_.reset(new cppcms::cache_pool(settings())); | impl_->cache_pool_.reset(new cppcms::cache_pool(settings())); | ||||
impl_->session_pool_.reset(new cppcms::session_pool(*this)); | impl_->session_pool_.reset(new cppcms::session_pool(*this)); | ||||
if(settings().get("file_server.enable",false)) { | if(settings().get("file_server.enable",false)) { | ||||
applications_pool().mount(applications_factory<cppcms::impl::file_server>(),mount_point("")); | |||||
if(settings().get("file_server.async",false)) { | |||||
impl_->async_file_server_ = new cppcms::impl::file_server(*this,true); | |||||
applications_pool().mount(impl_->async_file_server_,mount_point("")); | |||||
} | |||||
else { | |||||
applications_pool().mount(applications_factory<cppcms::impl::file_server>(),mount_point("")); | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -977,6 +984,7 @@ namespace impl { | |||||
} | } | ||||
service::~service() | service::~service() | ||||
{ | { | ||||
async_file_server_ = 0; | |||||
acceptors_.clear(); | acceptors_.clear(); | ||||
thread_pool_.reset(); | thread_pool_.reset(); | ||||
sig_.reset(); | sig_.reset(); | ||||