📄 filemodule.cpp
字号:
// -----------------------------------------------------------------// libpion: a C++ framework for building lightweight HTTP interfaces// -----------------------------------------------------------------// Copyright (C) 2007 Atomic Labs, Inc. (http://www.atomiclabs.com)//// Distributed under the Boost Software License, Version 1.0.// See accompanying file COPYING or copy at http://www.boost.org/LICENSE_1_0.txt//#include "FileModule.hpp"#include <libpion/PionPlugin.hpp>#include <libpion/HTTPResponse.hpp>#include <boost/scoped_array.hpp>#include <boost/filesystem/operations.hpp>#include <boost/filesystem/fstream.hpp>#include <algorithm>using namespace pion;// static members of FileModuleconst std::string FileModule::DEFAULT_MIME_TYPE("application/octet-stream");const unsigned int FileModule::DEFAULT_CACHE_SETTING = 1;const unsigned int FileModule::DEFAULT_SCAN_SETTING = 0;boost::once_flag FileModule::m_mime_types_init_flag = BOOST_ONCE_INIT;FileModule::MIMETypeMap * FileModule::m_mime_types_ptr = NULL;// FileModule member functionsFileModule::FileModule(void) : m_logger(PION_GET_LOGGER("FileModule")), m_cache_setting(DEFAULT_CACHE_SETTING), m_scan_setting(DEFAULT_SCAN_SETTING){ PION_LOG_SETLEVEL_WARN(m_logger);}void FileModule::setOption(const std::string& name, const std::string& value){ if (name == "directory") { m_directory = value; PionPlugin::checkCygwinPath(m_directory, value); // make sure that the directory exists if (! boost::filesystem::exists(m_directory) ) throw DirectoryNotFoundException(value); if (! boost::filesystem::is_directory(m_directory) ) throw NotADirectoryException(value); } else if (name == "file") { m_file = value; PionPlugin::checkCygwinPath(m_file, value); // make sure that the directory exists if (! boost::filesystem::exists(m_file) ) throw FileNotFoundException(value); if (boost::filesystem::is_directory(m_file) ) throw NotAFileException(value); } else if (name == "cache") { if (value == "0") { m_cache_setting = 0; } else if (value == "1") { m_cache_setting = 1; } else if (value == "2") { m_cache_setting = 2; } else { throw InvalidCacheException(value); } } else if (name == "scan") { if (value == "0") { m_scan_setting = 0; } else if (value == "1") { m_scan_setting = 1; } else if (value == "2") { m_scan_setting = 2; } else if (value == "3") { m_scan_setting = 3; } else { throw InvalidScanException(value); } } else { throw UnknownOptionException(name); }}bool FileModule::handleRequest(HTTPRequestPtr& request, TCPConnectionPtr& tcp_conn){ // the type of response we will send enum ResponseType { RESPONSE_UNDEFINED, // initial state until we know how to respond RESPONSE_OK, // normal response that includes the file's content RESPONSE_HEAD_OK, // response to HEAD request (would send file's content) RESPONSE_NOT_MODIFIED // Not Modified (304) response to If-Modified-Since } response_type = RESPONSE_UNDEFINED; // used to hold our response information DiskFile response_file; // get the If-Modified-Since request header const std::string if_modified_since(request->getHeader(HTTPTypes::HEADER_IF_MODIFIED_SINCE)); // get the relative resource path for the request const std::string relative_path(getRelativeResource(request->getResource())); // check the cache for a corresponding entry (if enabled) // note that m_cache_setting may equal 0 if m_scan_setting == 1 if (m_cache_setting > 0 || m_scan_setting > 0) { // search for a matching cache entry boost::mutex::scoped_lock cache_lock(m_cache_mutex); CacheMap::iterator cache_itr = m_cache_map.find(relative_path); if (cache_itr == m_cache_map.end()) { // no existing cache entries found if (m_scan_setting == 1 || m_scan_setting == 3) { // do not allow files to be added; // all requests must correspond with existing cache entries // since no match was found, just return file not found PION_LOG_WARN(m_logger, "Request for unknown file (" << getResource() << "): " << relative_path); return false; } else { PION_LOG_DEBUG(m_logger, "No cache entry for request (" << getResource() << "): " << relative_path); } } else { // found an existing cache entry PION_LOG_DEBUG(m_logger, "Found cache entry for request (" << getResource() << "): " << relative_path); if (m_cache_setting == 0) { // cache is disabled // copy & re-use file_path and mime_type response_file.file_path = cache_itr->second.file_path; response_file.mime_type = cache_itr->second.mime_type; // get the file_size and last_modified timestamp response_file.update(); // just compare strings for simplicity (parsing this date format sucks!) if (response_file.last_modified_string == if_modified_since) { // no need to read the file; the modified times match! response_type = RESPONSE_NOT_MODIFIED; } else { if (request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD) { response_type = RESPONSE_HEAD_OK; } else { response_type = RESPONSE_OK; // read the file (may throw exception) response_file.read(); PION_LOG_DEBUG(m_logger, "Cache disabled, reading file (" << getResource() << "): " << relative_path); } } } else { // cache is enabled // true if the entry was updated (used for log message) bool cache_was_updated = false; if (cache_itr->second.last_modified == 0) { // cache file for the first time cache_was_updated = true; cache_itr->second.update(); // read the file (may throw exception) cache_itr->second.read(); } else if (m_cache_setting == 1) { // check if file has been updated (may throw exception) cache_was_updated = cache_itr->second.checkUpdated(); } // else cache_setting == 2 (use existing values) // get the response type if (cache_itr->second.last_modified_string == if_modified_since) { response_type = RESPONSE_NOT_MODIFIED; } else if (request->getMethod() == HTTPTypes::REQUEST_METHOD_HEAD) { response_type = RESPONSE_HEAD_OK; } else { response_type = RESPONSE_OK; } // copy cache contents so that we can release the mutex response_file = cache_itr->second; PION_LOG_DEBUG(m_logger, (cache_was_updated ? "Updated" : "Using") << " cache entry for request (" << getResource() << "): " << relative_path); } } } if (response_type == RESPONSE_UNDEFINED) { // determine the path to the file if (relative_path.empty()) { // request matches resource exactly // return false if no file defined // module's directory is not valid if (m_file.empty()) { PION_LOG_WARN(m_logger, "No file option defined (" << getResource() << "): " << relative_path); return false; } // use file to service directory request response_file.file_path = m_file; } else if (! m_directory.empty()) { // request does not match resource, and a directory is defined // calculate the location of the file being requested response_file.file_path = m_directory; response_file.file_path /= relative_path; // make sure that the file exists if (! boost::filesystem::exists(response_file.file_path) ) { PION_LOG_WARN(m_logger, "File not found (" << getResource() << "): " << relative_path); return false; } // make sure that it is not a directory if ( boost::filesystem::is_directory(response_file.file_path) ) { PION_LOG_WARN(m_logger, "Request for directory (" << getResource() << "): " << relative_path); return false; } // make sure that the file is within the configured directory std::string file_string = response_file.file_path.file_string(); if (file_string.find(m_directory.directory_string()) != 0) { PION_LOG_WARN(m_logger, "Request for file outside of directory (" << getResource() << "): " << relative_path); return false; } } else { // request does not match resource, and no directory is defined return false; } PION_LOG_DEBUG(m_logger, "Found file for request (" << getResource() << "): " << relative_path);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -