10 #include <boost/asio.hpp>
11 #include <boost/bind.hpp>
12 #include <boost/lexical_cast.hpp>
13 #include <boost/filesystem/operations.hpp>
14 #include <boost/filesystem/fstream.hpp>
15 #include <boost/algorithm/string/case_conv.hpp>
17 #include "FileService.hpp"
18 #include <pion/PionPlugin.hpp>
19 #include <pion/net/HTTPResponseWriter.hpp>
22 using namespace pion::net;
30 const std::string FileService::DEFAULT_MIME_TYPE(
"application/octet-stream");
31 const unsigned int FileService::DEFAULT_CACHE_SETTING = 1;
32 const unsigned int FileService::DEFAULT_SCAN_SETTING = 0;
33 const unsigned long FileService::DEFAULT_MAX_CACHE_SIZE = 0;
34 const unsigned long FileService::DEFAULT_MAX_CHUNK_SIZE = 0;
35 boost::once_flag FileService::m_mime_types_init_flag = BOOST_ONCE_INIT;
36 FileService::MIMETypeMap *FileService::m_mime_types_ptr = NULL;
41 FileService::FileService(
void)
42 : m_logger(PION_GET_LOGGER(
"pion.FileService")),
43 m_cache_setting(DEFAULT_CACHE_SETTING),
44 m_scan_setting(DEFAULT_SCAN_SETTING),
45 m_max_cache_size(DEFAULT_MAX_CACHE_SIZE),
46 m_max_chunk_size(DEFAULT_MAX_CHUNK_SIZE),
52 if (name ==
"directory") {
56 if (! boost::filesystem::exists(m_directory) )
58 if (! boost::filesystem::is_directory(m_directory) )
60 }
else if (name ==
"file") {
64 if (! boost::filesystem::exists(m_file) )
66 if (boost::filesystem::is_directory(m_file) )
68 }
else if (name ==
"cache") {
71 }
else if (value ==
"1") {
73 }
else if (value ==
"2") {
78 }
else if (name ==
"scan") {
81 }
else if (value ==
"1") {
83 }
else if (value ==
"2") {
85 }
else if (value ==
"3") {
90 }
else if (name ==
"max_chunk_size") {
91 m_max_chunk_size = boost::lexical_cast<
unsigned long>(value);
92 }
else if (name ==
"writable") {
93 if (value ==
"true") {
95 }
else if (value ==
"false") {
111 boost::filesystem::path file_path;
112 if (relative_path.empty()) {
115 if (m_file.empty()) {
117 PION_LOG_WARN(
m_logger,
"No file option defined ("
119 sendNotFoundResponse(request, tcp_conn);
127 if (m_directory.empty()) {
129 PION_LOG_WARN(
m_logger,
"No directory option defined ("
131 sendNotFoundResponse(request, tcp_conn);
134 file_path = m_directory / relative_path;
139 file_path.normalize();
140 std::string file_string = file_path.string();
141 if (file_string.find(m_directory.string()) != 0) {
142 PION_LOG_WARN(
m_logger,
"Request for file outside of directory ("
144 static const std::string FORBIDDEN_HTML_START =
146 "<title>403 Forbidden</title>\n"
148 "<h1>Forbidden</h1>\n"
149 "<p>The requested URL ";
150 static const std::string FORBIDDEN_HTML_FINISH =
151 " is not in the configured directory.</p>\n"
153 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
154 boost::bind(&TCPConnection::finish, tcp_conn)));
155 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_FORBIDDEN);
156 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_FORBIDDEN);
157 if (request->getMethod() != HTTPTypes::REQUEST_METHOD_HEAD) {
158 writer->writeNoCopy(FORBIDDEN_HTML_START);
159 writer << request->getResource();
160 writer->writeNoCopy(FORBIDDEN_HTML_FINISH);
167 if (boost::filesystem::is_directory(file_path)) {
168 PION_LOG_WARN(
m_logger,
"Request for directory ("
170 static const std::string FORBIDDEN_HTML_START =
172 "<title>403 Forbidden</title>\n"
174 "<h1>Forbidden</h1>\n"
175 "<p>The requested URL ";
176 static const std::string FORBIDDEN_HTML_FINISH =
177 " is a directory.</p>\n"
179 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
180 boost::bind(&TCPConnection::finish, tcp_conn)));
181 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_FORBIDDEN);
182 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_FORBIDDEN);
183 if (request->getMethod() != HTTPTypes::REQUEST_METHOD_HEAD) {
184 writer->writeNoCopy(FORBIDDEN_HTML_START);
185 writer << request->getResource();
186 writer->writeNoCopy(FORBIDDEN_HTML_FINISH);
192 if (request->getMethod() == HTTPTypes::REQUEST_METHOD_GET
193 || request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD)
201 RESPONSE_NOT_MODIFIED
202 } response_type = RESPONSE_UNDEFINED;
208 const std::string if_modified_since(request->getHeader(HTTPTypes::HEADER_IF_MODIFIED_SINCE));
212 if (m_cache_setting > 0 || m_scan_setting > 0) {
215 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
216 CacheMap::iterator cache_itr = m_cache_map.find(relative_path);
218 if (cache_itr == m_cache_map.end()) {
221 if (m_scan_setting == 1 || m_scan_setting == 3) {
225 PION_LOG_WARN(
m_logger,
"Request for unknown file ("
227 response_type = RESPONSE_NOT_FOUND;
229 PION_LOG_DEBUG(
m_logger,
"No cache entry for request ("
236 PION_LOG_DEBUG(
m_logger,
"Found cache entry for request ("
239 if (m_cache_setting == 0) {
243 response_file.
setFilePath(cache_itr->second.getFilePath());
244 response_file.
setMimeType(cache_itr->second.getMimeType());
252 response_type = RESPONSE_NOT_MODIFIED;
254 if (request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD) {
255 response_type = RESPONSE_HEAD_OK;
257 response_type = RESPONSE_OK;
258 PION_LOG_DEBUG(
m_logger,
"Cache disabled, reading file ("
267 bool cache_was_updated =
false;
269 if (cache_itr->second.getLastModified() == 0) {
272 cache_was_updated =
true;
273 cache_itr->second.update();
274 if (m_max_cache_size==0 || cache_itr->second.getFileSize() <= m_max_cache_size) {
276 cache_itr->second.read();
278 cache_itr->second.resetFileContent();
281 }
else if (m_cache_setting == 1) {
284 cache_was_updated = cache_itr->second.checkUpdated();
289 if (cache_itr->second.getLastModifiedString() == if_modified_since) {
290 response_type = RESPONSE_NOT_MODIFIED;
291 }
else if (request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD) {
292 response_type = RESPONSE_HEAD_OK;
294 response_type = RESPONSE_OK;
298 response_file = cache_itr->second;
300 PION_LOG_DEBUG(
m_logger, (cache_was_updated ?
"Updated" :
"Using")
301 <<
" cache entry for request ("
307 if (response_type == RESPONSE_UNDEFINED) {
309 if (! boost::filesystem::exists(file_path)) {
310 PION_LOG_WARN(
m_logger,
"File not found ("
312 sendNotFoundResponse(request, tcp_conn);
318 PION_LOG_DEBUG(
m_logger,
"Found file for request ("
330 response_type = RESPONSE_NOT_MODIFIED;
331 }
else if (request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD) {
332 response_type = RESPONSE_HEAD_OK;
334 response_type = RESPONSE_OK;
335 if (m_cache_setting != 0) {
336 if (m_max_cache_size==0 || response_file.
getFileSize() <= m_max_cache_size) {
338 response_file.
read();
341 PION_LOG_DEBUG(
m_logger,
"Adding cache entry for request ("
343 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
344 m_cache_map.insert( std::make_pair(relative_path, response_file) );
349 if (response_type == RESPONSE_OK) {
355 }
else if (response_type == RESPONSE_NOT_FOUND) {
356 sendNotFoundResponse(request, tcp_conn);
361 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
362 boost::bind(&TCPConnection::finish, tcp_conn)));
363 writer->getResponse().setContentType(response_file.
getMimeType());
366 writer->getResponse().addHeader(HTTPTypes::HEADER_LAST_MODIFIED,
369 switch(response_type) {
370 case RESPONSE_UNDEFINED:
371 case RESPONSE_NOT_FOUND:
376 case RESPONSE_NOT_MODIFIED:
378 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_MODIFIED);
379 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_MODIFIED);
381 case RESPONSE_HEAD_OK:
383 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_OK);
384 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_OK);
391 }
else if (request->getMethod() == HTTPTypes::REQUEST_METHOD_POST
392 || request->getMethod() == HTTPTypes::REQUEST_METHOD_PUT
393 || request->getMethod() == HTTPTypes::REQUEST_METHOD_DELETE)
397 static const std::string NOT_ALLOWED_HTML_START =
399 "<title>405 Method Not Allowed</title>\n"
401 "<h1>Not Allowed</h1>\n"
402 "<p>The requested method ";
403 static const std::string NOT_ALLOWED_HTML_FINISH =
404 " is not allowed on this server.</p>\n"
406 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
407 boost::bind(&TCPConnection::finish, tcp_conn)));
408 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_METHOD_NOT_ALLOWED);
409 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED);
410 writer->writeNoCopy(NOT_ALLOWED_HTML_START);
411 writer << request->getMethod();
412 writer->writeNoCopy(NOT_ALLOWED_HTML_FINISH);
413 writer->getResponse().addHeader(
"Allow",
"GET, HEAD");
416 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
417 boost::bind(&TCPConnection::finish, tcp_conn)));
418 if (request->getMethod() == HTTPTypes::REQUEST_METHOD_POST
419 || request->getMethod() == HTTPTypes::REQUEST_METHOD_PUT)
421 if (boost::filesystem::exists(file_path)) {
422 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NO_CONTENT);
423 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NO_CONTENT);
427 if (!boost::filesystem::exists(file_path.branch_path())) {
428 static const std::string NOT_FOUND_HTML_START =
430 "<title>404 Not Found</title>\n"
432 "<h1>Not Found</h1>\n"
433 "<p>The directory of the requested URL ";
434 static const std::string NOT_FOUND_HTML_FINISH =
435 " was not found on this server.</p>\n"
437 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_FOUND);
438 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_FOUND);
439 writer->writeNoCopy(NOT_FOUND_HTML_START);
440 writer << request->getResource();
441 writer->writeNoCopy(NOT_FOUND_HTML_FINISH);
445 static const std::string CREATED_HTML_START =
447 "<title>201 Created</title>\n"
451 static const std::string CREATED_HTML_FINISH =
454 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_CREATED);
455 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_CREATED);
456 writer->getResponse().addHeader(HTTPTypes::HEADER_LOCATION, request->getResource());
457 writer->writeNoCopy(CREATED_HTML_START);
458 writer << request->getResource();
459 writer->writeNoCopy(CREATED_HTML_FINISH);
461 std::ios_base::openmode mode = request->getMethod() == HTTPTypes::REQUEST_METHOD_POST?
462 std::ios::app : std::ios::out;
463 boost::filesystem::ofstream file_stream(file_path, mode);
464 file_stream.write(request->getContent(), request->getContentLength());
466 if (!boost::filesystem::exists(file_path)) {
467 static const std::string PUT_FAILED_HTML_START =
469 "<title>500 Server Error</title>\n"
471 "<h1>Server Error</h1>\n"
472 "<p>Error writing to ";
473 static const std::string PUT_FAILED_HTML_FINISH =
476 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_SERVER_ERROR);
477 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR);
478 writer->writeNoCopy(PUT_FAILED_HTML_START);
479 writer << request->getResource();
480 writer->writeNoCopy(PUT_FAILED_HTML_FINISH);
483 }
else if (request->getMethod() == HTTPTypes::REQUEST_METHOD_DELETE) {
484 if (!boost::filesystem::exists(file_path)) {
485 sendNotFoundResponse(request, tcp_conn);
488 boost::filesystem::remove(file_path);
489 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NO_CONTENT);
490 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NO_CONTENT);
493 static const std::string DELETE_FAILED_HTML_START =
495 "<title>500 Server Error</title>\n"
497 "<h1>Server Error</h1>\n"
498 "<p>Could not delete ";
499 static const std::string DELETE_FAILED_HTML_FINISH =
502 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_SERVER_ERROR);
503 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR);
504 writer->writeNoCopy(DELETE_FAILED_HTML_START);
505 writer << request->getResource();
506 writer->writeNoCopy(DELETE_FAILED_HTML_FINISH);
512 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_SERVER_ERROR);
513 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR);
520 static const std::string NOT_IMPLEMENTED_HTML_START =
522 "<title>501 Not Implemented</title>\n"
524 "<h1>Not Implemented</h1>\n"
525 "<p>The requested method ";
526 static const std::string NOT_IMPLEMENTED_HTML_FINISH =
527 " is not implemented on this server.</p>\n"
529 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
530 boost::bind(&TCPConnection::finish, tcp_conn)));
531 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_IMPLEMENTED);
532 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_IMPLEMENTED);
533 writer->writeNoCopy(NOT_IMPLEMENTED_HTML_START);
534 writer << request->getMethod();
535 writer->writeNoCopy(NOT_IMPLEMENTED_HTML_FINISH);
540 void FileService::sendNotFoundResponse(HTTPRequestPtr& http_request,
541 TCPConnectionPtr& tcp_conn)
543 static const std::string NOT_FOUND_HTML_START =
545 "<title>404 Not Found</title>\n"
547 "<h1>Not Found</h1>\n"
548 "<p>The requested URL ";
549 static const std::string NOT_FOUND_HTML_FINISH =
550 " was not found on this server.</p>\n"
552 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
553 boost::bind(&TCPConnection::finish, tcp_conn)));
554 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_FOUND);
555 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_FOUND);
556 if (http_request->getMethod() != HTTPTypes::REQUEST_METHOD_HEAD) {
557 writer->writeNoCopy(NOT_FOUND_HTML_START);
558 writer << http_request->getResource();
559 writer->writeNoCopy(NOT_FOUND_HTML_FINISH);
569 if (m_scan_setting != 0) {
571 if (m_cache_setting == 0 && m_scan_setting > 1)
574 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
577 if (! m_file.empty()) {
584 if (! m_directory.empty())
593 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
600 << dir_path.string());
603 boost::filesystem::directory_iterator end_itr;
604 for ( boost::filesystem::directory_iterator itr( dir_path );
605 itr != end_itr; ++itr )
607 if ( boost::filesystem::is_directory(*itr) ) {
617 std::string file_path_string( itr->path().string() );
618 std::string relative_path( file_path_string.substr(m_directory.string().size() + 1) );
626 std::pair<FileService::CacheMap::iterator, bool>
628 const boost::filesystem::path& file_path,
629 const bool placeholder)
635 if (m_max_cache_size==0 || cache_entry.
getFileSize() <= m_max_cache_size) {
636 try { cache_entry.
read(); }
637 catch (std::exception&) {
638 PION_LOG_ERROR(
m_logger,
"Unable to add file to cache: "
639 << file_path.string());
640 return std::make_pair(m_cache_map.end(),
false);
645 std::pair<CacheMap::iterator, bool> add_entry_result
646 = m_cache_map.insert( std::make_pair(relative_path, cache_entry) );
648 if (add_entry_result.second) {
649 PION_LOG_DEBUG(
m_logger,
"Added file to cache: "
650 << file_path.string());
652 PION_LOG_ERROR(
m_logger,
"Unable to insert cache entry for file: "
653 << file_path.string());
656 return add_entry_result;
661 boost::call_once(FileService::createMIMETypes, m_mime_types_init_flag);
664 std::string extension(file_name.substr(file_name.find_last_of(
'.') + 1));
665 boost::algorithm::to_lower(extension);
668 MIMETypeMap::iterator i = m_mime_types_ptr->find(extension);
669 return (i == m_mime_types_ptr->end() ? DEFAULT_MIME_TYPE : i->second);
672 void FileService::createMIMETypes(
void) {
677 mime_types[
"js"] =
"text/javascript";
678 mime_types[
"txt"] =
"text/plain";
679 mime_types[
"xml"] =
"text/xml";
680 mime_types[
"css"] =
"text/css";
681 mime_types[
"htm"] =
"text/html";
682 mime_types[
"html"] =
"text/html";
683 mime_types[
"xhtml"] =
"text/html";
684 mime_types[
"gif"] =
"image/gif";
685 mime_types[
"png"] =
"image/png";
686 mime_types[
"jpg"] =
"image/jpeg";
687 mime_types[
"jpeg"] =
"image/jpeg";
691 m_mime_types_ptr = &mime_types;
711 boost::filesystem::ifstream file_stream;
712 file_stream.open(
m_file_path, std::ios::in | std::ios::binary);
722 std::streamsize cur_size = boost::numeric_cast<std::streamsize>(boost::filesystem::file_size(
m_file_path ));
723 time_t cur_modified = boost::filesystem::last_write_time(
m_file_path );
746 pion::net::TCPConnectionPtr& tcp_conn,
747 unsigned long max_chunk_size)
748 : m_logger(PION_GET_LOGGER(
"pion.FileService.DiskFileSender")), m_disk_file(file),
750 m_max_chunk_size(max_chunk_size), m_file_bytes_to_send(0), m_bytes_sent(0)
752 PION_LOG_DEBUG(
m_logger,
"Preparing to send file"
757 m_writer->getResponse().setContentType(m_disk_file.
getMimeType());
760 m_writer->getResponse().addHeader(HTTPTypes::HEADER_LAST_MODIFIED,
764 m_writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_OK);
765 m_writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_OK);
777 m_file_bytes_to_send = m_disk_file.
getFileSize() - m_bytes_sent;
778 if (m_max_chunk_size > 0 && m_file_bytes_to_send > m_max_chunk_size)
779 m_file_bytes_to_send = m_max_chunk_size;
782 char *file_content_ptr;
793 if (! m_file_stream.is_open()) {
795 m_file_stream.open(m_disk_file.
getFilePath(), std::ios::in | std::ios::binary);
796 if (! m_file_stream.is_open()) {
797 PION_LOG_ERROR(
m_logger,
"Unable to open file: "
804 if (! m_content_buf) {
806 m_content_buf.reset(
new char[m_file_bytes_to_send]);
808 file_content_ptr = m_content_buf.get();
811 if (! m_file_stream.read(m_content_buf.get(), m_file_bytes_to_send)) {
812 if (m_file_stream.gcount() > 0) {
813 PION_LOG_ERROR(
m_logger,
"File size inconsistency: "
816 PION_LOG_ERROR(
m_logger,
"Unable to read file: "
824 m_writer->writeNoCopy(file_content_ptr, m_file_bytes_to_send);
826 if (m_bytes_sent + m_file_bytes_to_send >= m_disk_file.
getFileSize()) {
828 if (m_bytes_sent > 0) {
832 boost::asio::placeholders::error,
833 boost::asio::placeholders::bytes_transferred));
838 boost::asio::placeholders::error,
839 boost::asio::placeholders::bytes_transferred));
845 boost::asio::placeholders::error,
846 boost::asio::placeholders::bytes_transferred));
851 std::size_t bytes_written)
853 bool finished_sending =
true;
857 m_writer->getTCPConnection()->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
858 PION_LOG_WARN(
m_logger,
"Error sending file (" << write_error.message() <<
')');
864 m_bytes_sent += m_file_bytes_to_send;
869 << (m_file_bytes_to_send < m_disk_file.
getFileSize() ?
"file chunk" :
"complete file")
870 <<
" of " << m_file_bytes_to_send <<
" bytes (finished"
871 << (m_writer->getTCPConnection()->getKeepAlive() ?
", keeping alive)" :
", closing)") );
874 PION_LOG_DEBUG(
m_logger,
"Sent file chunk of " << m_file_bytes_to_send <<
" bytes");
875 finished_sending =
false;
880 if (finished_sending) {
884 m_writer->getTCPConnection()->finish();
virtual void stop(void)
called when the web service's server is stopping
exception thrown if the cache option is set to an invalid value
std::string getRelativeResource(const std::string &resource_requested) const
returns the path to the resource requested, relative to the web service's location ...
void scanDirectory(const boost::filesystem::path &dir_path)
std::streamsize m_file_size
size of the file's content
exception thrown if an option is set to an invalid value
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
exception thrown if the directory configured is not found
static void checkCygwinPath(boost::filesystem::path &final_path, const std::string &path_string)
void setMimeType(const std::string &t)
sets the mime type for the cached file
std::string m_last_modified_string
timestamp that the cached file was last modified (string format)
void read(void)
reads content from disk into file_content buffer (may throw)
void update(void)
updates the file_size and last_modified timestamp to disk
exception thrown if the file configured is not found
PionLogger m_logger
primary logging interface used by this class
exception thrown if we are unable to read a file from disk
unsigned long getFileSize(void) const
returns size of the file's content
PionLogger m_logger
primary logging interface used by this class
virtual void setOption(const std::string &name, const std::string &value)
exception thrown if we do not know how to respond (should never happen)
const std::string & getResource(void) const
returns the URI stem or resource that is bound to the web service
virtual void operator()(pion::net::HTTPRequestPtr &request, pion::net::TCPConnectionPtr &tcp_conn)
handles requests for FileService
static boost::shared_ptr< DiskFileSender > create(DiskFile &file, pion::net::HTTPRequestPtr &request, pion::net::TCPConnectionPtr &tcp_conn, unsigned long max_chunk_size=0)
exception thrown if the service does not recognize a configuration option
exception thrown if the file configuration option is not a file
boost::filesystem::path m_file_path
path to the cached file
virtual void start(void)
called when the web service's server is starting
std::time_t m_last_modified
timestamp that the cached file was last modified (0 = cache disabled)
exception thrown if the directory configuration option is not a directory
void handleWrite(const boost::system::error_code &write_error, std::size_t bytes_written)
const std::string & getMimeType(void) const
returns mime type for the cached file
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
static std::string findMIMEType(const std::string &file_name)
boost::shared_array< char > m_file_content
content of the cached file
exception thrown if the scan option is set to an invalid value
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
DiskFileSender(DiskFile &file, pion::net::HTTPRequestPtr &request, pion::net::TCPConnectionPtr &tcp_conn, unsigned long max_chunk_size)