# net/services/FileService.hpp

    00001 // ------------------------------------------------------------------
    00002 // pion-net: a C++ framework for building lightweight HTTP interfaces
    00003 // ------------------------------------------------------------------
    00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
    00005 //
    00006 // Distributed under the Boost Software License, Version 1.0.
    00007 // See http://www.boost.org/LICENSE_1_0.txt
    00008 //
    00009
    00010 #ifndef __PION_FILESERVICE_HEADER__
    00011 #define __PION_FILESERVICE_HEADER__
    00012
    00013 #include <boost/shared_ptr.hpp>
    00014 #include <boost/functional/hash.hpp>
    00015 #include <boost/filesystem/path.hpp>
    00016 #include <boost/thread/once.hpp>
    00017 #include <boost/thread/mutex.hpp>
    00018 #include <boost/shared_array.hpp>
    00019 #include <pion/PionLogger.hpp>
    00020 #include <pion/PionException.hpp>
    00021 #include <pion/PionHashMap.hpp>
    00022 #include <pion/net/WebService.hpp>
    00023 #include <pion/net/HTTPRequest.hpp>
    00024 #include <pion/net/HTTPResponseWriter.hpp>
    00025 #include <pion/net/HTTPServer.hpp>
    00026 #include <string>
    00027 #include <map>
    00028
    00029
    00030 namespace pion {        // begin namespace pion
    00031 namespace plugins {     // begin namespace plugins
    00032
    00033
    00037 class DiskFile {
    00038 public:
    00040     DiskFile(void)
    00041         : m_file_size(0), m_last_modified(0) {}
    00042
    00044     DiskFile(const boost::filesystem::path& path,
    00045              char *content, unsigned long size,
    00046              std::time_t modified, const std::string& mime)
    00047         : m_file_path(path), m_file_content(content), m_file_size(size),
    00048         m_last_modified(modified), m_mime_type(mime)
    00049     {}
    00050
    00052     DiskFile(const DiskFile& f)
    00053         : m_file_path(f.m_file_path), m_file_content(f.m_file_content),
    00054         m_file_size(f.m_file_size), m_last_modified(f.m_last_modified),
    00055         m_last_modified_string(f.m_last_modified_string), m_mime_type(f.m_mime_type)
    00056     {}
    00057
    00059     void update(void);
    00060
    00062     void read(void);
    00063
    00069     bool checkUpdated(void);
    00070
    00072     inline const boost::filesystem::path& getFilePath(void) const { return m_file_path; }
    00073
    00075     inline char *getFileContent(void) { return m_file_content.get(); }
    00076
    00078     inline bool hasFileContent(void) const { return m_file_content; }
    00079
    00081     inline unsigned long getFileSize(void) const { return m_file_size; }
    00082
    00084     inline std::time_t getLastModified(void) const { return m_last_modified; }
    00085
    00087     inline const std::string& getLastModifiedString(void) const { return m_last_modified_string; }
    00088
    00090     inline const std::string& getMimeType(void) const { return m_mime_type; }
    00091
    00093     inline void setFilePath(const boost::filesystem::path& p) { m_file_path = p; }
    00094
    00096     inline void appendFilePath(const std::string& p) { m_file_path /= p; }
    00097
    00099     inline void setMimeType(const std::string& t) { m_mime_type = t; }
    00100
    00102     inline void resetFileContent(unsigned long n = 0) {
    00103         if (n == 0) m_file_content.reset();
    00104         else m_file_content.reset(new char[n]);
    00105     }
    00106
    00107
    00108 protected:
    00109
    00111     boost::filesystem::path     m_file_path;
    00112
    00114     boost::shared_array<char>   m_file_content;
    00115
    00117     std::streamsize             m_file_size;
    00118
    00120     std::time_t                 m_last_modified;
    00121
    00123     std::string                 m_last_modified_string;
    00124
    00126     std::string                 m_mime_type;
    00127 };
    00128
    00129
    00133 class DiskFileSender :
    00134     public boost::enable_shared_from_this<DiskFileSender>,
    00135     private boost::noncopyable
    00136 {
    00137 public:
    00146     static inline boost::shared_ptr<DiskFileSender>
    00147         create(DiskFile& file,
    00148                pion::net::HTTPRequestPtr& request,
    00149                pion::net::TCPConnectionPtr& tcp_conn,
    00150                unsigned long max_chunk_size = 0)
    00151     {
    00152         return boost::shared_ptr<DiskFileSender>(new DiskFileSender(file, request,
    00153                                                                     tcp_conn, max_chunk_size));
    00154     }
    00155
    00157     virtual ~DiskFileSender() {}
    00158
    00162     void send(void);
    00163
    00165     inline void setLogger(PionLogger log_ptr) { m_logger = log_ptr; }
    00166
    00168     inline PionLogger getLogger(void) { return m_logger; }
    00169
    00170
    00171 protected:
    00172
    00181     DiskFileSender(DiskFile& file,
    00182                    pion::net::HTTPRequestPtr& request,
    00183                    pion::net::TCPConnectionPtr& tcp_conn,
    00184                    unsigned long max_chunk_size);
    00185
    00192     void handleWrite(const boost::system::error_code& write_error,
    00193                      std::size_t bytes_written);
    00194
    00195
    00197     PionLogger                              m_logger;
    00198
    00199
    00200 private:
    00201
    00203     DiskFile                                m_disk_file;
    00204
    00206     pion::net::HTTPResponseWriterPtr        m_writer;
    00207
    00209     boost::filesystem::ifstream             m_file_stream;
    00210
    00212     boost::shared_array<char>               m_content_buf;
    00213
    00219     unsigned long                           m_max_chunk_size;
    00220
    00222     unsigned long                           m_file_bytes_to_send;
    00223
    00225     unsigned long                           m_bytes_sent;
    00226 };
    00227
    00229 typedef boost::shared_ptr<DiskFileSender>       DiskFileSenderPtr;
    00230
    00231
    00235 class FileService :
    00236     public pion::net::WebService
    00237 {
    00238 public:
    00239
    00241     class DirectoryNotFoundException : public PionException {
    00242     public:
    00243         DirectoryNotFoundException(const std::string& dir)
    00244             : PionException("FileService directory not found: ", dir) {}
    00245     };
    00246
    00248     class NotADirectoryException : public PionException {
    00249     public:
    00250         NotADirectoryException(const std::string& dir)
    00251             : PionException("FileService option is not a directory: ", dir) {}
    00252     };
    00253
    00255     class FileNotFoundException : public PionException {
    00256     public:
    00257         FileNotFoundException(const std::string& file)
    00258             : PionException("FileService file not found: ", file) {}
    00259     };
    00260
    00262     class NotAFileException : public PionException {
    00263     public:
    00264         NotAFileException(const std::string& file)
    00265             : PionException("FileService option is not a file: ", file) {}
    00266     };
    00267
    00269     class InvalidCacheException : public PionException {
    00270     public:
    00271         InvalidCacheException(const std::string& value)
    00272             : PionException("FileService invalid value for cache option: ", value) {}
    00273     };
    00274
    00276     class InvalidScanException : public PionException {
    00277     public:
    00278         InvalidScanException(const std::string& value)
    00279             : PionException("FileService invalid value for scan option: ", value) {}
    00280     };
    00281
    00283     class InvalidOptionValueException : public PionException {
    00284     public:
    00285         InvalidOptionValueException(const std::string& option, const std::string& value)
    00286             : PionException("FileService invalid value for " + option + " option: ", value) {}
    00287     };
    00288
    00290     class FileReadException : public PionException {
    00291     public:
    00292         FileReadException(const std::string& value)
    00293             : PionException("FileService unable to read file: ", value) {}
    00294     };
    00295
    00297     class UndefinedResponseException : public PionException {
    00298     public:
    00299         UndefinedResponseException(const std::string& value)
    00300             : PionException("FileService has an undefined response: ", value) {}
    00301     };
    00302
    00303
    00304     // default constructor and destructor
    00305     FileService(void);
    00306     virtual ~FileService() {}
    00307
    00318     virtual void setOption(const std::string& name, const std::string& value);
    00319
    00321     virtual void operator()(pion::net::HTTPRequestPtr& request,
    00322                             pion::net::TCPConnectionPtr& tcp_conn);
    00323
    00325     virtual void start(void);
    00326
    00328     virtual void stop(void);
    00329
    00331     inline void setLogger(PionLogger log_ptr) { m_logger = log_ptr; }
    00332
    00334     inline PionLogger getLogger(void) { return m_logger; }
    00335
    00336
    00337 protected:
    00338
    00340     typedef PION_HASH_MAP<std::string, DiskFile, PION_HASH_STRING >     CacheMap;
    00341
    00343     typedef PION_HASH_MAP<std::string, std::string, PION_HASH_STRING >  MIMETypeMap;
    00344
    00350     void scanDirectory(const boost::filesystem::path& dir_path);
    00351
    00362     std::pair<CacheMap::iterator, bool>
    00363         addCacheEntry(const std::string& relative_path,
    00364                       const boost::filesystem::path& file_path,
    00365                       const bool placeholder);
    00366
    00373     static std::string findMIMEType(const std::string& file_name);
    00374
    00375     void sendNotFoundResponse(pion::net::HTTPRequestPtr& http_request,
    00376                               pion::net::TCPConnectionPtr& tcp_conn);
    00377
    00379     PionLogger                  m_logger;
    00380
    00381
    00382 private:
    00383
    00385     static void createMIMETypes(void);
    00386
    00387
    00389     static const std::string    DEFAULT_MIME_TYPE;
    00390
    00392     static const unsigned int   DEFAULT_CACHE_SETTING;
    00393
    00395     static const unsigned int   DEFAULT_SCAN_SETTING;
    00396
    00398     static const unsigned long  DEFAULT_MAX_CACHE_SIZE;
    00399
    00401     static const unsigned long  DEFAULT_MAX_CHUNK_SIZE;
    00402
    00404     static boost::once_flag     m_mime_types_init_flag;
    00405
    00407     static MIMETypeMap *        m_mime_types_ptr;
    00408
    00409
    00411     boost::filesystem::path     m_directory;
    00412
    00414     boost::filesystem::path     m_file;
    00415
    00417     CacheMap                    m_cache_map;
    00418
    00420     boost::mutex                m_cache_mutex;
    00421
    00428     unsigned int                m_cache_setting;
    00429
    00437     unsigned int                m_scan_setting;
    00438
    00443     unsigned long               m_max_cache_size;
    00444
    00450     unsigned long               m_max_chunk_size;
    00451
    00455     bool                        m_writable;
    00456 };
    00457
    00458
    00459 }   // end namespace plugins
    00460 }   // end namespace pion
    00461
    00462 #endif