00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <boost/asio.hpp>
00011 #include <boost/bind.hpp>
00012 #include <boost/lexical_cast.hpp>
00013 #include <boost/filesystem/operations.hpp>
00014 #include <boost/filesystem/fstream.hpp>
00015 #include <boost/algorithm/string/case_conv.hpp>
00016
00017 #include "FileService.hpp"
00018 #include <pion/PionPlugin.hpp>
00019 #include <pion/net/HTTPResponseWriter.hpp>
00020
00021 using namespace pion;
00022 using namespace pion::net;
00023
00024 namespace pion {
00025 namespace plugins {
00026
00027
00028
00029
00030 const std::string FileService::DEFAULT_MIME_TYPE("application/octet-stream");
00031 const unsigned int FileService::DEFAULT_CACHE_SETTING = 1;
00032 const unsigned int FileService::DEFAULT_SCAN_SETTING = 0;
00033 const unsigned long FileService::DEFAULT_MAX_CACHE_SIZE = 0;
00034 const unsigned long FileService::DEFAULT_MAX_CHUNK_SIZE = 0;
00035 boost::once_flag FileService::m_mime_types_init_flag = BOOST_ONCE_INIT;
00036 FileService::MIMETypeMap *FileService::m_mime_types_ptr = NULL;
00037
00038
00039
00040
00041 FileService::FileService(void)
00042 : m_logger(PION_GET_LOGGER("pion.FileService")),
00043 m_cache_setting(DEFAULT_CACHE_SETTING),
00044 m_scan_setting(DEFAULT_SCAN_SETTING),
00045 m_max_cache_size(DEFAULT_MAX_CACHE_SIZE),
00046 m_max_chunk_size(DEFAULT_MAX_CHUNK_SIZE),
00047 m_writable(false)
00048 {}
00049
00050 void FileService::setOption(const std::string& name, const std::string& value)
00051 {
00052 if (name == "directory") {
00053 m_directory = value;
00054 PionPlugin::checkCygwinPath(m_directory, value);
00055
00056 if (! boost::filesystem::exists(m_directory) )
00057 throw DirectoryNotFoundException(value);
00058 if (! boost::filesystem::is_directory(m_directory) )
00059 throw NotADirectoryException(value);
00060 } else if (name == "file") {
00061 m_file = value;
00062 PionPlugin::checkCygwinPath(m_file, value);
00063
00064 if (! boost::filesystem::exists(m_file) )
00065 throw FileNotFoundException(value);
00066 if (boost::filesystem::is_directory(m_file) )
00067 throw NotAFileException(value);
00068 } else if (name == "cache") {
00069 if (value == "0") {
00070 m_cache_setting = 0;
00071 } else if (value == "1") {
00072 m_cache_setting = 1;
00073 } else if (value == "2") {
00074 m_cache_setting = 2;
00075 } else {
00076 throw InvalidCacheException(value);
00077 }
00078 } else if (name == "scan") {
00079 if (value == "0") {
00080 m_scan_setting = 0;
00081 } else if (value == "1") {
00082 m_scan_setting = 1;
00083 } else if (value == "2") {
00084 m_scan_setting = 2;
00085 } else if (value == "3") {
00086 m_scan_setting = 3;
00087 } else {
00088 throw InvalidScanException(value);
00089 }
00090 } else if (name == "max_chunk_size") {
00091 m_max_chunk_size = boost::lexical_cast<unsigned long>(value);
00092 } else if (name == "writable") {
00093 if (value == "true") {
00094 m_writable = true;
00095 } else if (value == "false") {
00096 m_writable = false;
00097 } else {
00098 throw InvalidOptionValueException("writable", value);
00099 }
00100 } else {
00101 throw UnknownOptionException(name);
00102 }
00103 }
00104
00105 void FileService::operator()(HTTPRequestPtr& request, TCPConnectionPtr& tcp_conn)
00106 {
00107
00108 const std::string relative_path(getRelativeResource(request->getResource()));
00109
00110
00111 boost::filesystem::path file_path;
00112 if (relative_path.empty()) {
00113
00114
00115 if (m_file.empty()) {
00116
00117 PION_LOG_WARN(m_logger, "No file option defined ("
00118 << getResource() << ")");
00119 sendNotFoundResponse(request, tcp_conn);
00120 return;
00121 } else {
00122 file_path = m_file;
00123 }
00124 } else {
00125
00126
00127 if (m_directory.empty()) {
00128
00129 PION_LOG_WARN(m_logger, "No directory option defined ("
00130 << getResource() << "): " << relative_path);
00131 sendNotFoundResponse(request, tcp_conn);
00132 return;
00133 } else {
00134 file_path = m_directory / relative_path;
00135 }
00136 }
00137
00138
00139 file_path.normalize();
00140 std::string file_string = file_path.file_string();
00141 if (file_string.find(m_directory.directory_string()) != 0) {
00142 PION_LOG_WARN(m_logger, "Request for file outside of directory ("
00143 << getResource() << "): " << relative_path);
00144 static const std::string FORBIDDEN_HTML_START =
00145 "<html><head>\n"
00146 "<title>403 Forbidden</title>\n"
00147 "</head><body>\n"
00148 "<h1>Forbidden</h1>\n"
00149 "<p>The requested URL ";
00150 static const std::string FORBIDDEN_HTML_FINISH =
00151 " is not in the configured directory.</p>\n"
00152 "</body></html>\n";
00153 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
00154 boost::bind(&TCPConnection::finish, tcp_conn)));
00155 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_FORBIDDEN);
00156 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_FORBIDDEN);
00157 if (request->getMethod() != HTTPTypes::REQUEST_METHOD_HEAD) {
00158 writer->writeNoCopy(FORBIDDEN_HTML_START);
00159 writer << request->getResource();
00160 writer->writeNoCopy(FORBIDDEN_HTML_FINISH);
00161 }
00162 writer->send();
00163 return;
00164 }
00165
00166
00167 if (boost::filesystem::is_directory(file_path)) {
00168 PION_LOG_WARN(m_logger, "Request for directory ("
00169 << getResource() << "): " << relative_path);
00170 static const std::string FORBIDDEN_HTML_START =
00171 "<html><head>\n"
00172 "<title>403 Forbidden</title>\n"
00173 "</head><body>\n"
00174 "<h1>Forbidden</h1>\n"
00175 "<p>The requested URL ";
00176 static const std::string FORBIDDEN_HTML_FINISH =
00177 " is a directory.</p>\n"
00178 "</body></html>\n";
00179 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
00180 boost::bind(&TCPConnection::finish, tcp_conn)));
00181 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_FORBIDDEN);
00182 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_FORBIDDEN);
00183 if (request->getMethod() != HTTPTypes::REQUEST_METHOD_HEAD) {
00184 writer->writeNoCopy(FORBIDDEN_HTML_START);
00185 writer << request->getResource();
00186 writer->writeNoCopy(FORBIDDEN_HTML_FINISH);
00187 }
00188 writer->send();
00189 return;
00190 }
00191
00192 if (request->getMethod() == HTTPTypes::REQUEST_METHOD_GET
00193 || request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD)
00194 {
00195
00196 enum ResponseType {
00197 RESPONSE_UNDEFINED,
00198 RESPONSE_OK,
00199 RESPONSE_HEAD_OK,
00200 RESPONSE_NOT_FOUND,
00201 RESPONSE_NOT_MODIFIED
00202 } response_type = RESPONSE_UNDEFINED;
00203
00204
00205 DiskFile response_file;
00206
00207
00208 const std::string if_modified_since(request->getHeader(HTTPTypes::HEADER_IF_MODIFIED_SINCE));
00209
00210
00211
00212 if (m_cache_setting > 0 || m_scan_setting > 0) {
00213
00214
00215 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00216 CacheMap::iterator cache_itr = m_cache_map.find(relative_path);
00217
00218 if (cache_itr == m_cache_map.end()) {
00219
00220
00221 if (m_scan_setting == 1 || m_scan_setting == 3) {
00222
00223
00224
00225 PION_LOG_WARN(m_logger, "Request for unknown file ("
00226 << getResource() << "): " << relative_path);
00227 response_type = RESPONSE_NOT_FOUND;
00228 } else {
00229 PION_LOG_DEBUG(m_logger, "No cache entry for request ("
00230 << getResource() << "): " << relative_path);
00231 }
00232
00233 } else {
00234
00235
00236 PION_LOG_DEBUG(m_logger, "Found cache entry for request ("
00237 << getResource() << "): " << relative_path);
00238
00239 if (m_cache_setting == 0) {
00240
00241
00242
00243 response_file.setFilePath(cache_itr->second.getFilePath());
00244 response_file.setMimeType(cache_itr->second.getMimeType());
00245
00246
00247 response_file.update();
00248
00249
00250 if (response_file.getLastModifiedString() == if_modified_since) {
00251
00252 response_type = RESPONSE_NOT_MODIFIED;
00253 } else {
00254 if (request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD) {
00255 response_type = RESPONSE_HEAD_OK;
00256 } else {
00257 response_type = RESPONSE_OK;
00258 PION_LOG_DEBUG(m_logger, "Cache disabled, reading file ("
00259 << getResource() << "): " << relative_path);
00260 }
00261 }
00262
00263 } else {
00264
00265
00266
00267 bool cache_was_updated = false;
00268
00269 if (cache_itr->second.getLastModified() == 0) {
00270
00271
00272 cache_was_updated = true;
00273 cache_itr->second.update();
00274 if (m_max_cache_size==0 || cache_itr->second.getFileSize() <= m_max_cache_size) {
00275
00276 cache_itr->second.read();
00277 } else {
00278 cache_itr->second.resetFileContent();
00279 }
00280
00281 } else if (m_cache_setting == 1) {
00282
00283
00284 cache_was_updated = cache_itr->second.checkUpdated();
00285
00286 }
00287
00288
00289 if (cache_itr->second.getLastModifiedString() == if_modified_since) {
00290 response_type = RESPONSE_NOT_MODIFIED;
00291 } else if (request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD) {
00292 response_type = RESPONSE_HEAD_OK;
00293 } else {
00294 response_type = RESPONSE_OK;
00295 }
00296
00297
00298 response_file = cache_itr->second;
00299
00300 PION_LOG_DEBUG(m_logger, (cache_was_updated ? "Updated" : "Using")
00301 << " cache entry for request ("
00302 << getResource() << "): " << relative_path);
00303 }
00304 }
00305 }
00306
00307 if (response_type == RESPONSE_UNDEFINED) {
00308
00309 if (! boost::filesystem::exists(file_path)) {
00310 PION_LOG_WARN(m_logger, "File not found ("
00311 << getResource() << "): " << relative_path);
00312 sendNotFoundResponse(request, tcp_conn);
00313 return;
00314 }
00315
00316 response_file.setFilePath(file_path);
00317
00318 PION_LOG_DEBUG(m_logger, "Found file for request ("
00319 << getResource() << "): " << relative_path);
00320
00321
00322 response_file.setMimeType(findMIMEType( response_file.getFilePath().leaf() ));
00323
00324
00325 response_file.update();
00326
00327
00328 if (response_file.getLastModifiedString() == if_modified_since) {
00329
00330 response_type = RESPONSE_NOT_MODIFIED;
00331 } else if (request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD) {
00332 response_type = RESPONSE_HEAD_OK;
00333 } else {
00334 response_type = RESPONSE_OK;
00335 if (m_cache_setting != 0) {
00336 if (m_max_cache_size==0 || response_file.getFileSize() <= m_max_cache_size) {
00337
00338 response_file.read();
00339 }
00340
00341 PION_LOG_DEBUG(m_logger, "Adding cache entry for request ("
00342 << getResource() << "): " << relative_path);
00343 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00344 m_cache_map.insert( std::make_pair(relative_path, response_file) );
00345 }
00346 }
00347 }
00348
00349 if (response_type == RESPONSE_OK) {
00350
00351 DiskFileSenderPtr sender_ptr(DiskFileSender::create(response_file,
00352 request, tcp_conn,
00353 m_max_chunk_size));
00354 sender_ptr->send();
00355 } else if (response_type == RESPONSE_NOT_FOUND) {
00356 sendNotFoundResponse(request, tcp_conn);
00357 } else {
00358
00359
00360
00361 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
00362 boost::bind(&TCPConnection::finish, tcp_conn)));
00363 writer->getResponse().setContentType(response_file.getMimeType());
00364
00365
00366 writer->getResponse().addHeader(HTTPTypes::HEADER_LAST_MODIFIED,
00367 response_file.getLastModifiedString());
00368
00369 switch(response_type) {
00370 case RESPONSE_UNDEFINED:
00371 case RESPONSE_NOT_FOUND:
00372 case RESPONSE_OK:
00373
00374 throw UndefinedResponseException(request->getResource());
00375 break;
00376 case RESPONSE_NOT_MODIFIED:
00377
00378 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_MODIFIED);
00379 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_MODIFIED);
00380 break;
00381 case RESPONSE_HEAD_OK:
00382
00383 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_OK);
00384 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_OK);
00385 break;
00386 }
00387
00388
00389 writer->send();
00390 }
00391 } else if (request->getMethod() == HTTPTypes::REQUEST_METHOD_POST
00392 || request->getMethod() == HTTPTypes::REQUEST_METHOD_PUT
00393 || request->getMethod() == HTTPTypes::REQUEST_METHOD_DELETE)
00394 {
00395
00396 if (!m_writable) {
00397 static const std::string NOT_ALLOWED_HTML_START =
00398 "<html><head>\n"
00399 "<title>405 Method Not Allowed</title>\n"
00400 "</head><body>\n"
00401 "<h1>Not Allowed</h1>\n"
00402 "<p>The requested method ";
00403 static const std::string NOT_ALLOWED_HTML_FINISH =
00404 " is not allowed on this server.</p>\n"
00405 "</body></html>\n";
00406 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
00407 boost::bind(&TCPConnection::finish, tcp_conn)));
00408 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_METHOD_NOT_ALLOWED);
00409 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED);
00410 writer->writeNoCopy(NOT_ALLOWED_HTML_START);
00411 writer << request->getMethod();
00412 writer->writeNoCopy(NOT_ALLOWED_HTML_FINISH);
00413 writer->getResponse().addHeader("Allow", "GET, HEAD");
00414 writer->send();
00415 } else {
00416 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
00417 boost::bind(&TCPConnection::finish, tcp_conn)));
00418 if (request->getMethod() == HTTPTypes::REQUEST_METHOD_POST
00419 || request->getMethod() == HTTPTypes::REQUEST_METHOD_PUT)
00420 {
00421 if (boost::filesystem::exists(file_path)) {
00422 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NO_CONTENT);
00423 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NO_CONTENT);
00424 } else {
00425
00426
00427 if (!boost::filesystem::exists(file_path.branch_path())) {
00428 static const std::string NOT_FOUND_HTML_START =
00429 "<html><head>\n"
00430 "<title>404 Not Found</title>\n"
00431 "</head><body>\n"
00432 "<h1>Not Found</h1>\n"
00433 "<p>The directory of the requested URL ";
00434 static const std::string NOT_FOUND_HTML_FINISH =
00435 " was not found on this server.</p>\n"
00436 "</body></html>\n";
00437 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_FOUND);
00438 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_FOUND);
00439 writer->writeNoCopy(NOT_FOUND_HTML_START);
00440 writer << request->getResource();
00441 writer->writeNoCopy(NOT_FOUND_HTML_FINISH);
00442 writer->send();
00443 return;
00444 }
00445 static const std::string CREATED_HTML_START =
00446 "<html><head>\n"
00447 "<title>201 Created</title>\n"
00448 "</head><body>\n"
00449 "<h1>Created</h1>\n"
00450 "<p>";
00451 static const std::string CREATED_HTML_FINISH =
00452 "</p>\n"
00453 "</body></html>\n";
00454 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_CREATED);
00455 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_CREATED);
00456 writer->getResponse().addHeader(HTTPTypes::HEADER_LOCATION, request->getResource());
00457 writer->writeNoCopy(CREATED_HTML_START);
00458 writer << request->getResource();
00459 writer->writeNoCopy(CREATED_HTML_FINISH);
00460 }
00461 std::ios_base::openmode mode = request->getMethod() == HTTPTypes::REQUEST_METHOD_POST?
00462 std::ios::app : std::ios::out;
00463 boost::filesystem::ofstream file_stream(file_path, mode);
00464 file_stream.write(request->getContent(), request->getContentLength());
00465 file_stream.close();
00466 if (!boost::filesystem::exists(file_path)) {
00467 static const std::string PUT_FAILED_HTML_START =
00468 "<html><head>\n"
00469 "<title>500 Server Error</title>\n"
00470 "</head><body>\n"
00471 "<h1>Server Error</h1>\n"
00472 "<p>Error writing to ";
00473 static const std::string PUT_FAILED_HTML_FINISH =
00474 ".</p>\n"
00475 "</body></html>\n";
00476 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_SERVER_ERROR);
00477 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR);
00478 writer->writeNoCopy(PUT_FAILED_HTML_START);
00479 writer << request->getResource();
00480 writer->writeNoCopy(PUT_FAILED_HTML_FINISH);
00481 }
00482 writer->send();
00483 } else if (request->getMethod() == HTTPTypes::REQUEST_METHOD_DELETE) {
00484 if (!boost::filesystem::exists(file_path)) {
00485 sendNotFoundResponse(request, tcp_conn);
00486 } else {
00487 try {
00488 boost::filesystem::remove(file_path);
00489 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NO_CONTENT);
00490 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NO_CONTENT);
00491 writer->send();
00492 } catch (...) {
00493 static const std::string DELETE_FAILED_HTML_START =
00494 "<html><head>\n"
00495 "<title>500 Server Error</title>\n"
00496 "</head><body>\n"
00497 "<h1>Server Error</h1>\n"
00498 "<p>Could not delete ";
00499 static const std::string DELETE_FAILED_HTML_FINISH =
00500 ".</p>\n"
00501 "</body></html>\n";
00502 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_SERVER_ERROR);
00503 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR);
00504 writer->writeNoCopy(DELETE_FAILED_HTML_START);
00505 writer << request->getResource();
00506 writer->writeNoCopy(DELETE_FAILED_HTML_FINISH);
00507 writer->send();
00508 }
00509 }
00510 } else {
00511
00512 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_SERVER_ERROR);
00513 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR);
00514 writer->send();
00515 }
00516 }
00517 }
00518
00519 else {
00520 static const std::string NOT_IMPLEMENTED_HTML_START =
00521 "<html><head>\n"
00522 "<title>501 Not Implemented</title>\n"
00523 "</head><body>\n"
00524 "<h1>Not Implemented</h1>\n"
00525 "<p>The requested method ";
00526 static const std::string NOT_IMPLEMENTED_HTML_FINISH =
00527 " is not implemented on this server.</p>\n"
00528 "</body></html>\n";
00529 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *request,
00530 boost::bind(&TCPConnection::finish, tcp_conn)));
00531 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_IMPLEMENTED);
00532 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_IMPLEMENTED);
00533 writer->writeNoCopy(NOT_IMPLEMENTED_HTML_START);
00534 writer << request->getMethod();
00535 writer->writeNoCopy(NOT_IMPLEMENTED_HTML_FINISH);
00536 writer->send();
00537 }
00538 }
00539
00540 void FileService::sendNotFoundResponse(HTTPRequestPtr& http_request,
00541 TCPConnectionPtr& tcp_conn)
00542 {
00543 static const std::string NOT_FOUND_HTML_START =
00544 "<html><head>\n"
00545 "<title>404 Not Found</title>\n"
00546 "</head><body>\n"
00547 "<h1>Not Found</h1>\n"
00548 "<p>The requested URL ";
00549 static const std::string NOT_FOUND_HTML_FINISH =
00550 " was not found on this server.</p>\n"
00551 "</body></html>\n";
00552 HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
00553 boost::bind(&TCPConnection::finish, tcp_conn)));
00554 writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_FOUND);
00555 writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_FOUND);
00556 if (http_request->getMethod() != HTTPTypes::REQUEST_METHOD_HEAD) {
00557 writer->writeNoCopy(NOT_FOUND_HTML_START);
00558 writer << http_request->getResource();
00559 writer->writeNoCopy(NOT_FOUND_HTML_FINISH);
00560 }
00561 writer->send();
00562 }
00563
00564 void FileService::start(void)
00565 {
00566 PION_LOG_DEBUG(m_logger, "Starting up resource (" << getResource() << ')');
00567
00568
00569 if (m_scan_setting != 0) {
00570
00571 if (m_cache_setting == 0 && m_scan_setting > 1)
00572 m_cache_setting = 1;
00573
00574 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00575
00576
00577 if (! m_file.empty()) {
00578
00579
00580 addCacheEntry("", m_file, m_scan_setting == 1);
00581 }
00582
00583
00584 if (! m_directory.empty())
00585 scanDirectory(m_directory);
00586 }
00587 }
00588
00589 void FileService::stop(void)
00590 {
00591 PION_LOG_DEBUG(m_logger, "Shutting down resource (" << getResource() << ')');
00592
00593 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00594 m_cache_map.clear();
00595 }
00596
00597 void FileService::scanDirectory(const boost::filesystem::path& dir_path)
00598 {
00599 PION_LOG_DEBUG(m_logger, "Scanning directory (" << getResource() << "): "
00600 << dir_path.directory_string());
00601
00602
00603 boost::filesystem::directory_iterator end_itr;
00604 for ( boost::filesystem::directory_iterator itr( dir_path );
00605 itr != end_itr; ++itr )
00606 {
00607 if ( boost::filesystem::is_directory(*itr) ) {
00608
00609
00610
00611 scanDirectory(*itr);
00612
00613 } else {
00614
00615
00616
00617 std::string file_path_string( itr->path().file_string() );
00618 std::string relative_path( file_path_string.substr(m_directory.directory_string().size() + 1) );
00619
00620
00621 addCacheEntry(relative_path, *itr, m_scan_setting == 1);
00622 }
00623 }
00624 }
00625
00626 std::pair<FileService::CacheMap::iterator, bool>
00627 FileService::addCacheEntry(const std::string& relative_path,
00628 const boost::filesystem::path& file_path,
00629 const bool placeholder)
00630 {
00631 DiskFile cache_entry(file_path, NULL, 0, 0, findMIMEType(file_path.leaf()));
00632 if (! placeholder) {
00633 cache_entry.update();
00634
00635 if (m_max_cache_size==0 || cache_entry.getFileSize() <= m_max_cache_size) {
00636 try { cache_entry.read(); }
00637 catch (std::exception&) {
00638 PION_LOG_ERROR(m_logger, "Unable to add file to cache: "
00639 << file_path.file_string());
00640 return std::make_pair(m_cache_map.end(), false);
00641 }
00642 }
00643 }
00644
00645 std::pair<CacheMap::iterator, bool> add_entry_result
00646 = m_cache_map.insert( std::make_pair(relative_path, cache_entry) );
00647
00648 if (add_entry_result.second) {
00649 PION_LOG_DEBUG(m_logger, "Added file to cache: "
00650 << file_path.file_string());
00651 } else {
00652 PION_LOG_ERROR(m_logger, "Unable to insert cache entry for file: "
00653 << file_path.file_string());
00654 }
00655
00656 return add_entry_result;
00657 }
00658
00659 std::string FileService::findMIMEType(const std::string& file_name) {
00660
00661 boost::call_once(FileService::createMIMETypes, m_mime_types_init_flag);
00662
00663
00664 std::string extension(file_name.substr(file_name.find_last_of('.') + 1));
00665 boost::algorithm::to_lower(extension);
00666
00667
00668 MIMETypeMap::iterator i = m_mime_types_ptr->find(extension);
00669 return (i == m_mime_types_ptr->end() ? DEFAULT_MIME_TYPE : i->second);
00670 }
00671
00672 void FileService::createMIMETypes(void) {
00673
00674 static MIMETypeMap mime_types;
00675
00676
00677 mime_types["js"] = "text/javascript";
00678 mime_types["txt"] = "text/plain";
00679 mime_types["xml"] = "text/xml";
00680 mime_types["css"] = "text/css";
00681 mime_types["htm"] = "text/html";
00682 mime_types["html"] = "text/html";
00683 mime_types["xhtml"] = "text/html";
00684 mime_types["gif"] = "image/gif";
00685 mime_types["png"] = "image/png";
00686 mime_types["jpg"] = "image/jpeg";
00687 mime_types["jpeg"] = "image/jpeg";
00688
00689
00690
00691 m_mime_types_ptr = &mime_types;
00692 }
00693
00694
00695
00696
00697 void DiskFile::update(void)
00698 {
00699
00700 m_file_size = boost::numeric_cast<std::streamsize>(boost::filesystem::file_size( m_file_path ));
00701 m_last_modified = boost::filesystem::last_write_time( m_file_path );
00702 m_last_modified_string = HTTPTypes::get_date_string( m_last_modified );
00703 }
00704
00705 void DiskFile::read(void)
00706 {
00707
00708 m_file_content.reset(new char[m_file_size]);
00709
00710
00711 boost::filesystem::ifstream file_stream;
00712 file_stream.open(m_file_path, std::ios::in | std::ios::binary);
00713
00714
00715 if (!file_stream.is_open() || !file_stream.read(m_file_content.get(), m_file_size))
00716 throw FileService::FileReadException(m_file_path.file_string());
00717 }
00718
00719 bool DiskFile::checkUpdated(void)
00720 {
00721
00722 std::streamsize cur_size = boost::numeric_cast<std::streamsize>(boost::filesystem::file_size( m_file_path ));
00723 time_t cur_modified = boost::filesystem::last_write_time( m_file_path );
00724
00725
00726 if (cur_modified == m_last_modified && cur_size == m_file_size)
00727 return false;
00728
00729
00730
00731
00732 m_file_size = cur_size;
00733 m_last_modified = cur_modified;
00734 m_last_modified_string = HTTPTypes::get_date_string( m_last_modified );
00735
00736
00737 read();
00738
00739 return true;
00740 }
00741
00742
00743
00744
00745 DiskFileSender::DiskFileSender(DiskFile& file, pion::net::HTTPRequestPtr& request,
00746 pion::net::TCPConnectionPtr& tcp_conn,
00747 unsigned long max_chunk_size)
00748 : m_logger(PION_GET_LOGGER("pion.FileService.DiskFileSender")), m_disk_file(file),
00749 m_writer(pion::net::HTTPResponseWriter::create(tcp_conn, *request, boost::bind(&TCPConnection::finish, tcp_conn))),
00750 m_max_chunk_size(max_chunk_size), m_file_bytes_to_send(0), m_bytes_sent(0)
00751 {
00752 PION_LOG_DEBUG(m_logger, "Preparing to send file"
00753 << (m_disk_file.hasFileContent() ? " (cached): " : ": ")
00754 << m_disk_file.getFilePath().file_string());
00755
00756
00757 m_writer->getResponse().setContentType(m_disk_file.getMimeType());
00758
00759
00760 m_writer->getResponse().addHeader(HTTPTypes::HEADER_LAST_MODIFIED,
00761 m_disk_file.getLastModifiedString());
00762
00763
00764 m_writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_OK);
00765 m_writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_OK);
00766 }
00767
00768 void DiskFileSender::send(void)
00769 {
00770
00771 if (m_disk_file.getFileSize() <= m_bytes_sent) {
00772 m_writer->send();
00773 return;
00774 }
00775
00776
00777 m_file_bytes_to_send = m_disk_file.getFileSize() - m_bytes_sent;
00778 if (m_max_chunk_size > 0 && m_file_bytes_to_send > m_max_chunk_size)
00779 m_file_bytes_to_send = m_max_chunk_size;
00780
00781
00782 char *file_content_ptr;
00783
00784 if (m_disk_file.hasFileContent()) {
00785
00786
00787 file_content_ptr = m_disk_file.getFileContent() + m_bytes_sent;
00788
00789 } else {
00790
00791
00792
00793 if (! m_file_stream.is_open()) {
00794
00795 m_file_stream.open(m_disk_file.getFilePath(), std::ios::in | std::ios::binary);
00796 if (! m_file_stream.is_open()) {
00797 PION_LOG_ERROR(m_logger, "Unable to open file: "
00798 << m_disk_file.getFilePath().file_string());
00799 return;
00800 }
00801 }
00802
00803
00804 if (! m_content_buf) {
00805
00806 m_content_buf.reset(new char[m_file_bytes_to_send]);
00807 }
00808 file_content_ptr = m_content_buf.get();
00809
00810
00811 if (! m_file_stream.read(m_content_buf.get(), m_file_bytes_to_send)) {
00812 if (m_file_stream.gcount() > 0) {
00813 PION_LOG_ERROR(m_logger, "File size inconsistency: "
00814 << m_disk_file.getFilePath().file_string());
00815 } else {
00816 PION_LOG_ERROR(m_logger, "Unable to read file: "
00817 << m_disk_file.getFilePath().file_string());
00818 }
00819 return;
00820 }
00821 }
00822
00823
00824 m_writer->writeNoCopy(file_content_ptr, m_file_bytes_to_send);
00825
00826 if (m_bytes_sent + m_file_bytes_to_send >= m_disk_file.getFileSize()) {
00827
00828 if (m_bytes_sent > 0) {
00829
00830 m_writer->sendFinalChunk(boost::bind(&DiskFileSender::handleWrite,
00831 shared_from_this(),
00832 boost::asio::placeholders::error,
00833 boost::asio::placeholders::bytes_transferred));
00834 } else {
00835
00836 m_writer->send(boost::bind(&DiskFileSender::handleWrite,
00837 shared_from_this(),
00838 boost::asio::placeholders::error,
00839 boost::asio::placeholders::bytes_transferred));
00840 }
00841 } else {
00842
00843 m_writer->sendChunk(boost::bind(&DiskFileSender::handleWrite,
00844 shared_from_this(),
00845 boost::asio::placeholders::error,
00846 boost::asio::placeholders::bytes_transferred));
00847 }
00848 }
00849
00850 void DiskFileSender::handleWrite(const boost::system::error_code& write_error,
00851 std::size_t bytes_written)
00852 {
00853 bool finished_sending = true;
00854
00855 if (write_error) {
00856
00857 m_writer->getTCPConnection()->setLifecycle(TCPConnection::LIFECYCLE_CLOSE);
00858 PION_LOG_WARN(m_logger, "Error sending file (" << write_error.message() << ')');
00859 } else {
00860
00861
00862
00863
00864 m_bytes_sent += m_file_bytes_to_send;
00865
00866 if (m_bytes_sent >= m_disk_file.getFileSize()) {
00867
00868 PION_LOG_DEBUG(m_logger, "Sent "
00869 << (m_file_bytes_to_send < m_disk_file.getFileSize() ? "file chunk" : "complete file")
00870 << " of " << m_file_bytes_to_send << " bytes (finished"
00871 << (m_writer->getTCPConnection()->getKeepAlive() ? ", keeping alive)" : ", closing)") );
00872 } else {
00873
00874 PION_LOG_DEBUG(m_logger, "Sent file chunk of " << m_file_bytes_to_send << " bytes");
00875 finished_sending = false;
00876 m_writer->clear();
00877 }
00878 }
00879
00880 if (finished_sending) {
00881
00882
00883
00884 m_writer->getTCPConnection()->finish();
00885 } else {
00886 send();
00887 }
00888 }
00889
00890
00891 }
00892 }
00893
00894
00896 extern "C" PION_SERVICE_API pion::plugins::FileService *pion_create_FileService(void)
00897 {
00898 return new pion::plugins::FileService();
00899 }
00900
00902 extern "C" PION_SERVICE_API void pion_destroy_FileService(pion::plugins::FileService *service_ptr)
00903 {
00904 delete service_ptr;
00905 }