diff --git a/CMakeLists.txt b/CMakeLists.txt index fca590b..5033270 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -825,6 +825,7 @@ add_test(http_timeouts_test_write "--test-write=true" "--test-exec=${PYTHON} ${CNF}/http_timeouts_test.py write") + add_test(file_server_test file_server_test "-c" "${CNF}/file_server_test.js" "--test-exec=${PYTHON} ${CNF}/file_server_test.py" @@ -844,6 +845,27 @@ add_test(file_server_with_listing_test "-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 disco_test "-c" "${CNF}/proto_test.js" "--service-api=http" "--service-port=8080" "--service-ip=127.0.0.1" diff --git a/private/internal_file_server.h b/private/internal_file_server.h index e33103d..1b22ecb 100644 --- a/private/internal_file_server.h +++ b/private/internal_file_server.h @@ -18,7 +18,7 @@ namespace impl { public: static void normalize_path(std::string &path); - file_server(cppcms::service &srv); + file_server(cppcms::service &srv,bool async=false); ~file_server(); virtual void main(std::string file_name); @@ -38,6 +38,7 @@ namespace impl { mime_type mime_; bool list_directories_; bool check_symlinks_; + bool async_; std::string index_file_; }; diff --git a/private/service_impl.h b/private/service_impl.h index b382a39..c2f4717 100644 --- a/private/service_impl.h +++ b/private/service_impl.h @@ -19,6 +19,7 @@ namespace cppcms { class service; class applications_pool; +class application; class thread_pool; class session_pool; @@ -67,7 +68,7 @@ namespace impl { std::vector args_; - + booster::intrusive_ptr async_file_server_; }; diff --git a/src/internal_file_server.cpp b/src/internal_file_server.cpp index 54e9de7..7bc3d45 100644 --- a/src/internal_file_server.cpp +++ b/src/internal_file_server.cpp @@ -15,9 +15,11 @@ #include +#include #include #include #include +#include #include "internal_file_server.h" #include #include @@ -41,7 +43,7 @@ namespace cppcms { 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_)) throw cppcms_error("Invalid document root"); @@ -406,16 +408,63 @@ void file_server::list_dir(std::string const &url,std::string const &path) out <<"\n"; } +namespace file_server_detail { + +class async_file_handler : public booster::callable +{ +public: + async_file_handler(std::string const &path,booster::shared_ptr c) : + f(path.c_str(),std::ios_base::binary), + ctx(c) + { + } + typedef booster::intrusive_ptr 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 ctx; +}; + +} // file_server_detail + void file_server::main(std::string file_name) { std::string path; - if(!check_in_document_root(file_name,path)) { show404(); return; } - + int s=file_mode(path); if((s & S_IFDIR)) { @@ -467,33 +516,28 @@ void file_server::main(std::string file_name) else response().content_type("application/octet-stream"); - if(!allow_deflate_) { + if(!allow_deflate_ && !async_) { 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()<\n" - "\n" - " \n" - " 404 Not Found\n" - " \n" - " \n" - "

404 Not Found

\n" - " \n" - "\n"< #include "service_impl.h" #include +#include #include #include #include @@ -235,7 +236,13 @@ void service::setup() impl_->cache_pool_.reset(new cppcms::cache_pool(settings())); impl_->session_pool_.reset(new cppcms::session_pool(*this)); if(settings().get("file_server.enable",false)) { - applications_pool().mount(applications_factory(),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(),mount_point("")); + } } } @@ -977,6 +984,7 @@ namespace impl { } service::~service() { + async_file_server_ = 0; acceptors_.clear(); thread_pool_.reset(); sig_.reset();