Advertisement
Guest User

Untitled

a guest
Mar 31st, 2015
213
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.61 KB | None | 0 0
  1. // Copyright 2010 Dean Michael Berris.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5.  
  6. #include <boost/network/include/http/server.hpp>
  7. #include <boost/thread.hpp>
  8. #include <boost/lexical_cast.hpp>
  9. #include <boost/filesystem.hpp>
  10. #include <sys/mman.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <sys/fcntl.h>
  14. #include <unistd.h>
  15. #include <iostream>
  16.  
  17. namespace http = boost::network::http;
  18. namespace utils = boost::network::utils;
  19.  
  20. struct file_server;
  21. struct dir_browser;
  22. typedef http::async_server<file_server> server;
  23.  
  24. struct file_cache
  25. {
  26.     typedef std::map<std::string, std::pair<void *, std::size_t> > region_map;
  27.     typedef std::map<std::string, std::vector<server::response_header> > meta_map;
  28.  
  29.     std::string doc_root_;
  30.     region_map regions;
  31.     meta_map file_headers;
  32.     boost::shared_mutex cache_mutex;
  33.  
  34.     explicit file_cache(std::string const &doc_root) : doc_root_(doc_root) {}
  35.  
  36.     ~file_cache() throw() {
  37.         BOOST_FOREACH(region_map::value_type const & region, regions) {
  38.             munmap(region.second.first, region.second.second);
  39.         }
  40.     }
  41.  
  42.     bool has(std::string const &path)
  43.     {
  44.         boost::shared_lock<boost::shared_mutex> lock(cache_mutex);
  45.         return regions.find(doc_root_ + path) != regions.end();
  46.     }
  47.  
  48.     bool add(std::string const &path)
  49.     {
  50.         boost::upgrade_lock<boost::shared_mutex> lock(cache_mutex);
  51.  
  52.         std::string real_filename = doc_root_ + path;
  53.  
  54.         if (regions.find(real_filename) != regions.end()) return true;
  55.  
  56.         #ifdef O_NOATIME
  57.         int fd = open(real_filename.c_str(), O_RDONLY | O_NOATIME | O_NONBLOCK);
  58.         #else
  59.         int fd = open(real_filename.c_str(), O_RDONLY | O_NONBLOCK);
  60.         #endif
  61.  
  62.         if (fd == -1) return false;
  63.  
  64.         std::size_t size = lseek(fd, 0, SEEK_END);
  65.         void *region = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
  66.         if (region == MAP_FAILED) {
  67.             close(fd);
  68.             return false;
  69.         }
  70.  
  71.         boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock);
  72.  
  73.         regions.insert(std::make_pair(real_filename, std::make_pair(region, size)));
  74.  
  75.         static server::response_header common_headers[] = {
  76.             {"Connection", "close"},
  77.             {"Content-Type", "x-application/octet-stream"},
  78.             {"Content-Length", "0"}
  79.         };
  80.  
  81.         std::vector<server::response_header> headers(common_headers, common_headers + 3);
  82.         headers[2].value = boost::lexical_cast<std::string>(size);
  83.  
  84.         file_headers.insert(std::make_pair(real_filename, headers));
  85.         return true;
  86.     }
  87.  
  88.     std::pair<void *, std::size_t> get(std::string const &path)
  89.     {
  90.         boost::shared_lock<boost::shared_mutex> lock(cache_mutex);
  91.         region_map::const_iterator region = regions.find(doc_root_ + path);
  92.         if (region != regions.end())
  93.             return region->second;
  94.         else
  95.             return std::pair<void *, std::size_t>(0, 0);
  96.     }
  97.  
  98.     boost::iterator_range<std::vector<server::response_header>::iterator> meta(std::string const &path)
  99.     {
  100.         boost::shared_lock<boost::shared_mutex> lock(cache_mutex);
  101.  
  102.         static std::vector<server::response_header> empty_vector;
  103.  
  104.         meta_map::iterator headers = file_headers.find(doc_root_ + path);
  105.  
  106.         if (headers != file_headers.end()) {
  107.             std::vector<server::response_header>::iterator begin = headers->second.begin();
  108.             std::vector<server::response_header>::iterator end = headers->second.end();
  109.  
  110.             return boost::make_iterator_range(begin, end);
  111.         } else
  112.             return boost::make_iterator_range(empty_vector);
  113.         }
  114. };
  115.  
  116.  
  117. struct dir_browser
  118. {
  119.     std::string root_;
  120.  
  121.     explicit dir_browser (const std::string& doc_root) : doc_root_(doc_root) {}
  122.  
  123.     bool is_directory(const std::string& path_) const {
  124.         boost::filesystem::path path(path_);
  125.  
  126.         return is_directory(path_);
  127.     }
  128.  
  129.     boost::iterator_range<std::vector<server::response_header>::iterator> meta(std::string const &path)
  130.     {
  131.  
  132.         static std::vector<server::response_header> empty_vector;
  133.  
  134.         /*meta_map::iterator headers = file_headers.find(doc_root_ + path);
  135.  
  136.         if (headers != file_headers.end()) {
  137.             std::vector<server::response_header>::iterator begin = headers->second.begin();
  138.             std::vector<server::response_header>::iterator end = headers->second.end();
  139.  
  140.             return boost::make_iterator_range(begin, end);
  141.         } else*/
  142.  
  143.         return boost::make_iterator_range(empty_vector);
  144.     }
  145.  
  146.     std::string doc_root_;
  147. };
  148.  
  149.  
  150. struct connection_handler : boost::enable_shared_from_this<connection_handler>
  151. {
  152.     explicit connection_handler(file_cache &cache, dir_browser& browser) : file_cache_(cache), dir_browser_(browser) {}
  153.  
  154.     void operator()(std::string const &path, server::connection_ptr connection, bool serve_body)
  155.     {
  156.         bool ok = file_cache_.has(path);
  157.         if (!ok) ok = file_cache_.add(path);
  158.         if (ok) {
  159.             send_headers(file_cache_.meta(path), connection);
  160.             if (serve_body) send_file(file_cache_.get(path), 0, connection);
  161.         } else {
  162.             not_found(path, connection);
  163.         }
  164.     }
  165.  
  166.     void not_found(std::string const &path, server::connection_ptr connection)
  167.     {
  168.         static server::response_header headers[] = {{"Connection", "close"},
  169.                                                     {"Content-Type", "text/plain"}};
  170.         connection->set_status(server::connection::not_found);
  171.         connection->set_headers(boost::make_iterator_range(headers, headers + 2));
  172.         connection->write("File Not Found!");
  173.     }
  174.  
  175.     template <class Range>
  176.     void send_headers(Range const &headers, server::connection_ptr connection)
  177.     {
  178.         connection->set_status(server::connection::ok);
  179.         connection->set_headers(headers);
  180.     }
  181.  
  182.     void send_file(std::pair<void *, std::size_t> mmaped_region, off_t offset, server::connection_ptr connection)
  183.     {
  184.         // chunk it up page by page
  185.         std::size_t adjusted_offset = offset + 4096;
  186.         off_t rightmost_bound = std::min(mmaped_region.second, adjusted_offset);
  187.         connection->write(  boost::asio::const_buffers_1(static_cast<char const *>(mmaped_region.first) + offset,
  188.                                                          rightmost_bound),
  189.                             boost::bind(&connection_handler::handle_chunk,
  190.                                         connection_handler::shared_from_this(),
  191.                                         mmaped_region,
  192.                                         rightmost_bound,
  193.                                         connection,
  194.                                         _1)
  195.                          ); // write
  196.     }
  197.  
  198.     void handle_chunk(std::pair<void *, std::size_t> mmaped_region, off_t offset, server::connection_ptr connection, boost::system::error_code const &ec)
  199.     {
  200.         if (!ec && offset < mmaped_region.second)
  201.             send_file(mmaped_region, offset, connection);
  202.     }
  203.  
  204.     file_cache &file_cache_;
  205.     dir_browser& dir_browser_;
  206. };
  207.  
  208. struct file_server
  209. {
  210.     explicit file_server(file_cache &cache, dir_browser& browser) : cache_(cache), browser_(browser) {}
  211.  
  212.     void operator()(server::request const &request,
  213.                     server::connection_ptr connection)
  214.     {
  215.         if (request.method == "HEAD") {
  216.             boost::shared_ptr<connection_handler> h(new connection_handler(cache_, browser_));
  217.             (*h)(request.destination, connection, false);
  218.         } else if (request.method == "GET") {
  219.             boost::shared_ptr<connection_handler> h(new connection_handler(cache_, browser_));
  220.             (*h)(request.destination, connection, true);
  221.         } else {
  222.             static server::response_header error_headers[] = {{"Connection", "close"}};
  223.             connection->set_status(server::connection::not_supported);
  224.             connection->set_headers(boost::make_iterator_range(error_headers, error_headers + 1));
  225.             connection->write("Method not supported.");
  226.         }
  227.     }
  228.  
  229.     file_cache &cache_;
  230.     dir_browser& browser_;
  231. };
  232.  
  233. int main(int argc, char *argv[]) {
  234.     file_cache cache(".");
  235.     dir_browser browser(".");
  236.  
  237.     file_server handler(cache, browser);
  238.  
  239.  
  240.     server::options options(handler);
  241.     server instance(options.thread_pool(boost::make_shared<utils::thread_pool>(4))
  242.                     .address("0.0.0.0")
  243.                     .port("8000")
  244.                     .reuse_address(true));
  245.     instance.run();
  246.     return 0;
  247. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement