10 #include <boost/asio.hpp>
11 #include <boost/bind.hpp>
12 #include <boost/assert.hpp>
13 #include <boost/lexical_cast.hpp>
14 #include <boost/filesystem/operations.hpp>
15 #include <boost/filesystem/fstream.hpp>
16 #include <boost/algorithm/string/case_conv.hpp>
17 #include <boost/exception/diagnostic_information.hpp>
19 #include "FileService.hpp"
20 #include <pion/error.hpp>
21 #include <pion/plugin.hpp>
22 #include <pion/algorithm.hpp>
23 #include <pion/http/response_writer.hpp>
33 const std::string FileService::DEFAULT_MIME_TYPE(
"application/octet-stream");
34 const unsigned int FileService::DEFAULT_CACHE_SETTING = 1;
35 const unsigned int FileService::DEFAULT_SCAN_SETTING = 0;
36 const unsigned long FileService::DEFAULT_MAX_CACHE_SIZE = 0;
37 const unsigned long FileService::DEFAULT_MAX_CHUNK_SIZE = 0;
38 boost::once_flag FileService::m_mime_types_init_flag = BOOST_ONCE_INIT;
44 FileService::FileService(
void)
45 : m_logger(PION_GET_LOGGER(
"pion.FileService")),
46 m_cache_setting(DEFAULT_CACHE_SETTING),
47 m_scan_setting(DEFAULT_SCAN_SETTING),
48 m_max_cache_size(DEFAULT_MAX_CACHE_SIZE),
49 m_max_chunk_size(DEFAULT_MAX_CHUNK_SIZE),
55 if (name ==
"directory") {
57 m_directory.normalize();
60 if (! boost::filesystem::exists(m_directory) || ! boost::filesystem::is_directory(m_directory)) {
61 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
62 const std::string dir_name = m_directory.string();
64 const std::string dir_name = m_directory.directory_string();
68 }
else if (name ==
"file") {
72 if (! boost::filesystem::exists(m_file) || boost::filesystem::is_directory(m_file)) {
73 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
74 const std::string file_name = m_file.string();
76 const std::string file_name = m_file.file_string();
80 }
else if (name ==
"cache") {
83 }
else if (value ==
"1") {
85 }
else if (value ==
"2") {
88 BOOST_THROW_EXCEPTION(
error::bad_arg() << error::errinfo_arg_name(name) );
90 }
else if (name ==
"scan") {
93 }
else if (value ==
"1") {
95 }
else if (value ==
"2") {
97 }
else if (value ==
"3") {
100 BOOST_THROW_EXCEPTION(
error::bad_arg() << error::errinfo_arg_name(name) );
102 }
else if (name ==
"max_chunk_size") {
103 m_max_chunk_size = boost::lexical_cast<
unsigned long>(value);
104 }
else if (name ==
"writable") {
105 if (value ==
"true") {
107 }
else if (value ==
"false") {
110 BOOST_THROW_EXCEPTION(
error::bad_arg() << error::errinfo_arg_name(name) );
113 BOOST_THROW_EXCEPTION(
error::bad_arg() << error::errinfo_arg_name(name) );
123 boost::filesystem::path file_path;
124 if (relative_path.empty()) {
127 if (m_file.empty()) {
129 PION_LOG_WARN(
m_logger,
"No file option defined ("
131 sendNotFoundResponse(http_request_ptr, tcp_conn);
139 if (m_directory.empty()) {
141 PION_LOG_WARN(
m_logger,
"No directory option defined ("
143 sendNotFoundResponse(http_request_ptr, tcp_conn);
146 file_path = m_directory / relative_path;
151 file_path.normalize();
152 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
153 std::string file_string = file_path.string();
154 if (file_string.find(m_directory.string()) != 0) {
156 std::string file_string = file_path.file_string();
157 if (file_string.find(m_directory.directory_string()) != 0) {
159 PION_LOG_WARN(
m_logger,
"Request for file outside of directory ("
161 static const std::string FORBIDDEN_HTML_START =
163 "<title>403 Forbidden</title>\n"
165 "<h1>Forbidden</h1>\n"
166 "<p>The requested URL ";
167 static const std::string FORBIDDEN_HTML_FINISH =
168 " is not in the configured directory.</p>\n"
172 writer->get_response().set_status_code(http::types::RESPONSE_CODE_FORBIDDEN);
173 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_FORBIDDEN);
174 if (http_request_ptr->get_method() != http::types::REQUEST_METHOD_HEAD) {
175 writer->write_no_copy(FORBIDDEN_HTML_START);
177 writer->write_no_copy(FORBIDDEN_HTML_FINISH);
184 if (boost::filesystem::is_directory(file_path)) {
185 PION_LOG_WARN(
m_logger,
"Request for directory ("
187 static const std::string FORBIDDEN_HTML_START =
189 "<title>403 Forbidden</title>\n"
191 "<h1>Forbidden</h1>\n"
192 "<p>The requested URL ";
193 static const std::string FORBIDDEN_HTML_FINISH =
194 " is a directory.</p>\n"
198 writer->get_response().set_status_code(http::types::RESPONSE_CODE_FORBIDDEN);
199 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_FORBIDDEN);
200 if (http_request_ptr->get_method() != http::types::REQUEST_METHOD_HEAD) {
201 writer->write_no_copy(FORBIDDEN_HTML_START);
203 writer->write_no_copy(FORBIDDEN_HTML_FINISH);
209 if (http_request_ptr->get_method() == http::types::REQUEST_METHOD_GET
210 || http_request_ptr->get_method() == http::types::REQUEST_METHOD_HEAD)
218 RESPONSE_NOT_MODIFIED
219 } response_type = RESPONSE_UNDEFINED;
225 const std::string if_modified_since(http_request_ptr->get_header(http::types::HEADER_IF_MODIFIED_SINCE));
229 if (m_cache_setting > 0 || m_scan_setting > 0) {
232 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
233 CacheMap::iterator cache_itr = m_cache_map.find(relative_path);
235 if (cache_itr == m_cache_map.end()) {
238 if (m_scan_setting == 1 || m_scan_setting == 3) {
242 PION_LOG_WARN(
m_logger,
"Request for unknown file ("
244 response_type = RESPONSE_NOT_FOUND;
246 PION_LOG_DEBUG(
m_logger,
"No cache entry for request ("
253 PION_LOG_DEBUG(
m_logger,
"Found cache entry for request ("
256 if (m_cache_setting == 0) {
260 response_file.
setFilePath(cache_itr->second.getFilePath());
261 response_file.
setMimeType(cache_itr->second.getMimeType());
269 response_type = RESPONSE_NOT_MODIFIED;
271 if (http_request_ptr->get_method() == http::types::REQUEST_METHOD_HEAD) {
272 response_type = RESPONSE_HEAD_OK;
274 response_type = RESPONSE_OK;
275 PION_LOG_DEBUG(
m_logger,
"Cache disabled, reading file ("
284 bool cache_was_updated =
false;
286 if (cache_itr->second.getLastModified() == 0) {
289 cache_was_updated =
true;
290 cache_itr->second.update();
291 if (m_max_cache_size==0 || cache_itr->second.getFileSize() <= m_max_cache_size) {
293 cache_itr->second.read();
295 cache_itr->second.resetFileContent();
298 }
else if (m_cache_setting == 1) {
301 cache_was_updated = cache_itr->second.checkUpdated();
306 if (cache_itr->second.getLastModifiedString() == if_modified_since) {
307 response_type = RESPONSE_NOT_MODIFIED;
308 }
else if (http_request_ptr->get_method() == http::types::REQUEST_METHOD_HEAD) {
309 response_type = RESPONSE_HEAD_OK;
311 response_type = RESPONSE_OK;
315 response_file = cache_itr->second;
318 if (cache_was_updated && m_max_cache_size > 0 && cache_itr->second.getFileSize() > m_max_cache_size) {
322 PION_LOG_DEBUG(
m_logger, (cache_was_updated ?
"Updated" :
"Using")
323 <<
" cache entry for request ("
329 if (response_type == RESPONSE_UNDEFINED) {
331 if (! boost::filesystem::exists(file_path)) {
332 PION_LOG_WARN(
m_logger,
"File not found ("
334 sendNotFoundResponse(http_request_ptr, tcp_conn);
340 PION_LOG_DEBUG(
m_logger,
"Found file for request ("
344 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
356 response_type = RESPONSE_NOT_MODIFIED;
357 }
else if (http_request_ptr->get_method() == http::types::REQUEST_METHOD_HEAD) {
358 response_type = RESPONSE_HEAD_OK;
360 response_type = RESPONSE_OK;
361 if (m_cache_setting != 0) {
362 if (m_max_cache_size==0 || response_file.
getFileSize() <= m_max_cache_size) {
364 response_file.
read();
367 PION_LOG_DEBUG(
m_logger,
"Adding cache entry for request ("
369 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
370 m_cache_map.insert( std::make_pair(relative_path, response_file) );
375 if (response_type == RESPONSE_OK) {
378 http_request_ptr, tcp_conn,
381 }
else if (response_type == RESPONSE_NOT_FOUND) {
382 sendNotFoundResponse(http_request_ptr, tcp_conn);
389 writer->get_response().set_content_type(response_file.
getMimeType());
392 writer->get_response().add_header(http::types::HEADER_LAST_MODIFIED,
395 switch(response_type) {
396 case RESPONSE_UNDEFINED:
397 case RESPONSE_NOT_FOUND:
402 case RESPONSE_NOT_MODIFIED:
404 writer->get_response().set_status_code(http::types::RESPONSE_CODE_NOT_MODIFIED);
405 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_NOT_MODIFIED);
407 case RESPONSE_HEAD_OK:
409 writer->get_response().set_status_code(http::types::RESPONSE_CODE_OK);
410 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_OK);
417 }
else if (http_request_ptr->get_method() == http::types::REQUEST_METHOD_POST
418 || http_request_ptr->get_method() == http::types::REQUEST_METHOD_PUT
419 || http_request_ptr->get_method() == http::types::REQUEST_METHOD_DELETE)
423 static const std::string NOT_ALLOWED_HTML_START =
425 "<title>405 Method Not Allowed</title>\n"
427 "<h1>Not Allowed</h1>\n"
428 "<p>The requested method ";
429 static const std::string NOT_ALLOWED_HTML_FINISH =
430 " is not allowed on this server.</p>\n"
434 writer->get_response().set_status_code(http::types::RESPONSE_CODE_METHOD_NOT_ALLOWED);
435 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED);
436 writer->write_no_copy(NOT_ALLOWED_HTML_START);
438 writer->write_no_copy(NOT_ALLOWED_HTML_FINISH);
439 writer->get_response().add_header(
"Allow",
"GET, HEAD");
444 if (http_request_ptr->get_method() == http::types::REQUEST_METHOD_POST
445 || http_request_ptr->get_method() == http::types::REQUEST_METHOD_PUT)
447 if (boost::filesystem::exists(file_path)) {
448 writer->get_response().set_status_code(http::types::RESPONSE_CODE_NO_CONTENT);
449 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_NO_CONTENT);
453 if (!boost::filesystem::exists(file_path.branch_path())) {
454 static const std::string NOT_FOUND_HTML_START =
456 "<title>404 Not Found</title>\n"
458 "<h1>Not Found</h1>\n"
459 "<p>The directory of the requested URL ";
460 static const std::string NOT_FOUND_HTML_FINISH =
461 " was not found on this server.</p>\n"
463 writer->get_response().set_status_code(http::types::RESPONSE_CODE_NOT_FOUND);
464 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_NOT_FOUND);
465 writer->write_no_copy(NOT_FOUND_HTML_START);
467 writer->write_no_copy(NOT_FOUND_HTML_FINISH);
471 static const std::string CREATED_HTML_START =
473 "<title>201 Created</title>\n"
477 static const std::string CREATED_HTML_FINISH =
480 writer->get_response().set_status_code(http::types::RESPONSE_CODE_CREATED);
481 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_CREATED);
482 writer->get_response().add_header(http::types::HEADER_LOCATION, http_request_ptr->get_resource());
483 writer->write_no_copy(CREATED_HTML_START);
485 writer->write_no_copy(CREATED_HTML_FINISH);
487 std::ios_base::openmode mode = http_request_ptr->get_method() == http::types::REQUEST_METHOD_POST?
488 std::ios::app : std::ios::out;
489 boost::filesystem::ofstream file_stream(file_path, mode);
490 file_stream.write(http_request_ptr->get_content(), http_request_ptr->get_content_length());
492 if (!boost::filesystem::exists(file_path)) {
493 static const std::string PUT_FAILED_HTML_START =
495 "<title>500 Server Error</title>\n"
497 "<h1>Server Error</h1>\n"
498 "<p>Error writing to ";
499 static const std::string PUT_FAILED_HTML_FINISH =
502 writer->get_response().set_status_code(http::types::RESPONSE_CODE_SERVER_ERROR);
503 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_SERVER_ERROR);
504 writer->write_no_copy(PUT_FAILED_HTML_START);
506 writer->write_no_copy(PUT_FAILED_HTML_FINISH);
509 }
else if (http_request_ptr->get_method() == http::types::REQUEST_METHOD_DELETE) {
510 if (!boost::filesystem::exists(file_path)) {
511 sendNotFoundResponse(http_request_ptr, tcp_conn);
514 boost::filesystem::remove(file_path);
515 writer->get_response().set_status_code(http::types::RESPONSE_CODE_NO_CONTENT);
516 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_NO_CONTENT);
518 }
catch (std::exception& e) {
519 static const std::string DELETE_FAILED_HTML_START =
521 "<title>500 Server Error</title>\n"
523 "<h1>Server Error</h1>\n"
524 "<p>Could not delete ";
525 static const std::string DELETE_FAILED_HTML_FINISH =
528 writer->get_response().set_status_code(http::types::RESPONSE_CODE_SERVER_ERROR);
529 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_SERVER_ERROR);
530 writer->write_no_copy(DELETE_FAILED_HTML_START);
533 << boost::diagnostic_information(e);
534 writer->write_no_copy(DELETE_FAILED_HTML_FINISH);
540 writer->get_response().set_status_code(http::types::RESPONSE_CODE_SERVER_ERROR);
541 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_SERVER_ERROR);
548 static const std::string NOT_IMPLEMENTED_HTML_START =
550 "<title>501 Not Implemented</title>\n"
552 "<h1>Not Implemented</h1>\n"
553 "<p>The requested method ";
554 static const std::string NOT_IMPLEMENTED_HTML_FINISH =
555 " is not implemented on this server.</p>\n"
559 writer->get_response().set_status_code(http::types::RESPONSE_CODE_NOT_IMPLEMENTED);
560 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_NOT_IMPLEMENTED);
561 writer->write_no_copy(NOT_IMPLEMENTED_HTML_START);
563 writer->write_no_copy(NOT_IMPLEMENTED_HTML_FINISH);
568 void FileService::sendNotFoundResponse(http::request_ptr& http_request_ptr,
569 tcp::connection_ptr& tcp_conn)
571 static const std::string NOT_FOUND_HTML_START =
573 "<title>404 Not Found</title>\n"
575 "<h1>Not Found</h1>\n"
576 "<p>The requested URL ";
577 static const std::string NOT_FOUND_HTML_FINISH =
578 " was not found on this server.</p>\n"
582 writer->get_response().set_status_code(http::types::RESPONSE_CODE_NOT_FOUND);
583 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_NOT_FOUND);
584 if (http_request_ptr->get_method() != http::types::REQUEST_METHOD_HEAD) {
585 writer->write_no_copy(NOT_FOUND_HTML_START);
587 writer->write_no_copy(NOT_FOUND_HTML_FINISH);
597 if (m_scan_setting != 0) {
599 if (m_cache_setting == 0 && m_scan_setting > 1)
602 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
605 if (! m_file.empty()) {
612 if (! m_directory.empty())
621 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
627 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
629 << dir_path.string());
632 << dir_path.directory_string());
636 boost::filesystem::directory_iterator end_itr;
637 for ( boost::filesystem::directory_iterator itr( dir_path );
638 itr != end_itr; ++itr )
640 if ( boost::filesystem::is_directory(*itr) ) {
650 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
651 std::string file_path_string( itr->path().string() );
652 std::string relative_path( file_path_string.substr(m_directory.string().size() + 1) );
654 std::string file_path_string( itr->path().file_string() );
655 std::string relative_path( file_path_string.substr(m_directory.directory_string().size() + 1) );
664 std::pair<FileService::CacheMap::iterator, bool>
666 const boost::filesystem::path& file_path,
667 const bool placeholder)
669 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
677 if (m_max_cache_size==0 || cache_entry.
getFileSize() <= m_max_cache_size) {
678 try { cache_entry.
read(); }
679 catch (std::exception&) {
680 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
681 PION_LOG_ERROR(
m_logger,
"Unable to add file to cache: "
682 << file_path.string());
684 PION_LOG_ERROR(
m_logger,
"Unable to add file to cache: "
685 << file_path.file_string());
687 return std::make_pair(m_cache_map.end(),
false);
692 std::pair<CacheMap::iterator, bool> add_entry_result
693 = m_cache_map.insert( std::make_pair(relative_path, cache_entry) );
695 if (add_entry_result.second) {
696 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
697 PION_LOG_DEBUG(
m_logger,
"Added file to cache: "
698 << file_path.string());
700 PION_LOG_DEBUG(
m_logger,
"Added file to cache: "
701 << file_path.file_string());
704 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
705 PION_LOG_ERROR(
m_logger,
"Unable to insert cache entry for file: "
706 << file_path.string());
708 PION_LOG_ERROR(
m_logger,
"Unable to insert cache entry for file: "
709 << file_path.file_string());
713 return add_entry_result;
718 boost::call_once(FileService::createMIMETypes, m_mime_types_init_flag);
721 std::string extension(file_name.substr(file_name.find_last_of(
'.') + 1));
722 boost::algorithm::to_lower(extension);
725 MIMETypeMap::iterator i = m_mime_types_ptr->find(extension);
726 return (i == m_mime_types_ptr->end() ? DEFAULT_MIME_TYPE : i->second);
729 void FileService::createMIMETypes(
void) {
734 mime_types[
"js"] =
"text/javascript";
735 mime_types[
"txt"] =
"text/plain";
736 mime_types[
"xml"] =
"text/xml";
737 mime_types[
"css"] =
"text/css";
738 mime_types[
"htm"] =
"text/html";
739 mime_types[
"html"] =
"text/html";
740 mime_types[
"xhtml"] =
"text/html";
741 mime_types[
"gif"] =
"image/gif";
742 mime_types[
"png"] =
"image/png";
743 mime_types[
"jpg"] =
"image/jpeg";
744 mime_types[
"jpeg"] =
"image/jpeg";
745 mime_types[
"svg"] =
"image/svg+xml";
746 mime_types[
"eof"] =
"application/vnd.ms-fontobject";
747 mime_types[
"otf"] =
"application/x-font-opentype";
748 mime_types[
"ttf"] =
"application/x-font-ttf";
749 mime_types[
"woff"] =
"application/font-woff";
753 m_mime_types_ptr = &mime_types;
762 m_file_size = boost::numeric_cast<std::streamsize>(boost::filesystem::file_size( m_file_path ));
763 m_last_modified = boost::filesystem::last_write_time( m_file_path );
770 m_file_content.reset(
new char[m_file_size]);
773 boost::filesystem::ifstream file_stream;
774 file_stream.open(m_file_path, std::ios::in | std::ios::binary);
777 if (!file_stream.is_open() || !file_stream.read(m_file_content.get(), m_file_size)) {
778 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
779 const std::string file_name = m_file_path.string();
781 const std::string file_name = m_file_path.file_string();
783 BOOST_THROW_EXCEPTION(
error::read_file() << error::errinfo_file_name(file_name) );
790 std::streamsize cur_size = boost::numeric_cast<std::streamsize>(boost::filesystem::file_size( m_file_path ));
791 time_t cur_modified = boost::filesystem::last_write_time( m_file_path );
794 if (cur_modified == m_last_modified && cur_size == m_file_size)
800 m_file_size = cur_size;
801 m_last_modified = cur_modified;
814 pion::tcp::connection_ptr& tcp_conn,
815 unsigned long max_chunk_size)
816 :
m_logger(PION_GET_LOGGER(
"pion.FileService.DiskFileSender")), m_disk_file(file),
817 m_writer(
pion::http::response_writer::create(tcp_conn, *http_request_ptr, boost::bind(&tcp::connection::finish, tcp_conn))),
818 m_max_chunk_size(max_chunk_size), m_file_bytes_to_send(0), m_bytes_sent(0)
820 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
821 PION_LOG_DEBUG(
m_logger,
"Preparing to send file"
825 PION_LOG_DEBUG(
m_logger,
"Preparing to send file"
831 m_writer->get_response().set_content_type(m_disk_file.
getMimeType());
834 m_writer->get_response().add_header(http::types::HEADER_LAST_MODIFIED,
838 m_writer->get_response().set_status_code(http::types::RESPONSE_CODE_OK);
839 m_writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_OK);
851 m_file_bytes_to_send = m_disk_file.
getFileSize() - m_bytes_sent;
852 if (m_max_chunk_size > 0 && m_file_bytes_to_send > m_max_chunk_size)
853 m_file_bytes_to_send = m_max_chunk_size;
856 char *file_content_ptr;
867 if (! m_file_stream.is_open()) {
869 m_file_stream.open(m_disk_file.
getFilePath(), std::ios::in | std::ios::binary);
870 if (! m_file_stream.is_open()) {
871 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
872 PION_LOG_ERROR(
m_logger,
"Unable to open file: "
875 PION_LOG_ERROR(
m_logger,
"Unable to open file: "
883 if (! m_content_buf) {
885 m_content_buf.reset(
new char[m_file_bytes_to_send]);
887 file_content_ptr = m_content_buf.get();
890 if (! m_file_stream.read(m_content_buf.get(), m_file_bytes_to_send)) {
891 if (m_file_stream.gcount() > 0) {
892 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
893 PION_LOG_ERROR(
m_logger,
"File size inconsistency: "
896 PION_LOG_ERROR(
m_logger,
"File size inconsistency: "
900 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
901 PION_LOG_ERROR(
m_logger,
"Unable to read file: "
904 PION_LOG_ERROR(
m_logger,
"Unable to read file: "
913 m_writer->write_no_copy(file_content_ptr, m_file_bytes_to_send);
915 if (m_bytes_sent + m_file_bytes_to_send >= m_disk_file.
getFileSize()) {
917 if (m_bytes_sent > 0) {
921 boost::asio::placeholders::error,
922 boost::asio::placeholders::bytes_transferred));
927 boost::asio::placeholders::error,
928 boost::asio::placeholders::bytes_transferred));
934 boost::asio::placeholders::error,
935 boost::asio::placeholders::bytes_transferred));
940 std::size_t bytes_written)
942 bool finished_sending =
true;
946 m_writer->get_connection()->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);
947 PION_LOG_WARN(
m_logger,
"Error sending file (" << write_error.message() <<
')');
953 m_bytes_sent += m_file_bytes_to_send;
958 << (m_file_bytes_to_send < m_disk_file.
getFileSize() ?
"file chunk" :
"complete file")
959 <<
" of " << m_file_bytes_to_send <<
" bytes (finished"
960 << (m_writer->get_connection()->get_keep_alive() ?
", keeping alive)" :
", closing)") );
963 PION_LOG_DEBUG(
m_logger,
"Sent file chunk of " << m_file_bytes_to_send <<
" bytes");
964 finished_sending =
false;
969 if (finished_sending) {
973 m_writer->get_connection()->finish();
virtual void stop(void)
called when the web service's server is stopping
void scanDirectory(const boost::filesystem::path &dir_path)
const std::string & get_resource(void) const
returns the URI stem or resource that is bound to the web service
DiskFileSender(DiskFile &file, pion::http::request_ptr &http_request_ptr, pion::tcp::connection_ptr &tcp_conn, unsigned long max_chunk_size)
const boost::filesystem::path & getFilePath(void) const
return path to the cached file
std::pair< CacheMap::iterator, bool > addCacheEntry(const std::string &relative_path, const boost::filesystem::path &file_path, const bool placeholder)
void setFilePath(const boost::filesystem::path &p)
sets the path to the cached file
void setMimeType(const std::string &t)
sets the mime type for the cached file
void read(void)
reads content from disk into file_content buffer (may throw)
exception thrown if a required directory is not found
void update(void)
updates the file_size and last_modified timestamp to disk
virtual void set_option(const std::string &name, const std::string &value)
void handle_write(const boost::system::error_code &write_error, std::size_t bytes_written)
unsigned long getFileSize(void) const
returns size of the file's content
static std::string get_date_string(const time_t t)
converts time_t format into an HTTP-date string
static void check_cygwin_path(boost::filesystem::path &final_path, const std::string &path_string)
logger m_logger
primary logging interface used by this class
logger m_logger
primary logging interface used by this class
exception thrown if a file is not found
void resetFileContent(unsigned long n=0)
resets the size of the file content buffer
static std::string xml_encode(const std::string &str)
TODO: escapes XML/HTML-encoded strings (1 < 2)
virtual void start(void)
called when the web service's server is starting
exception thrown if we failed to read data from a file
exception thrown for an invalid configuration argument or option
const std::string & getMimeType(void) const
returns mime type for the cached file
std::string get_relative_resource(const std::string &resource_requested) const
returns the path to the resource requested, relative to the web service's location ...
const std::string & getLastModifiedString(void) const
returns timestamp that the cached file was last modified (string format)
char * getFileContent(void)
returns content of the cached file
virtual void operator()(pion::http::request_ptr &http_request_ptr, pion::tcp::connection_ptr &tcp_conn)
handles requests for FileService
static std::string findMIMEType(const std::string &file_name)
static boost::shared_ptr< DiskFileSender > create(DiskFile &file, pion::http::request_ptr &http_request_ptr, pion::tcp::connection_ptr &tcp_conn, unsigned long max_chunk_size=0)
static boost::shared_ptr< response_writer > create(tcp::connection_ptr &tcp_conn, http::response_ptr &http_response_ptr, finished_handler_t handler=finished_handler_t())
PION_HASH_MAP< std::string, std::string, PION_HASH_STRING > MIMETypeMap
data type for map of file extensions to MIME types
bool hasFileContent(void) const
returns true if there is cached file content