|
|
- // this file is derived from a boost::beast sample
-
- #include <boost/beast/core.hpp>
- #include <boost/filesystem/path.hpp>
- #include <cstdlib>
- #include <iostream>
- #include <memory>
- #include <string>
- #include <thread>
-
- namespace fs = boost::filesystem;
-
- #include "webserver.hh"
-
- namespace pEp {
- Webserver::Webserver(net::ip::address addr, unsigned short port, std::string doc_root)
- : _ioc(1), _acceptor(_ioc, {addr, port}), _doc_root(doc_root), _running(false) { }
-
- static beast::string_view mime_type(beast::string_view path)
- {
- using beast::iequals;
- auto const ext = [&path]
- {
- auto const pos = path.rfind(".");
- if(pos == beast::string_view::npos)
- return beast::string_view{};
- return path.substr(pos);
- }();
- if(iequals(ext, ".htm")) return "text/html";
- if(iequals(ext, ".html")) return "text/html";
- if(iequals(ext, ".css")) return "text/css";
- if(iequals(ext, ".txt")) return "text/plain";
- if(iequals(ext, ".js")) return "application/javascript";
- if(iequals(ext, ".json")) return "application/json";
- if(iequals(ext, ".xml")) return "application/xml";
- if(iequals(ext, ".png")) return "image/png";
- if(iequals(ext, ".jpeg")) return "image/jpeg";
- if(iequals(ext, ".jpg")) return "image/jpeg";
- if(iequals(ext, ".gif")) return "image/gif";
- if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
- if(iequals(ext, ".tiff")) return "image/tiff";
- if(iequals(ext, ".tif")) return "image/tiff";
- if(iequals(ext, ".svg")) return "image/svg+xml";
- if(iequals(ext, ".svgz")) return "image/svg+xml";
- return "application/octet-stream";
- }
-
- static void fail(beast::error_code ec, char const* what)
- {
- std::cerr << what << ": " << ec.message() << "\n";
- ;}
-
- void Webserver::add_url_handler(std::string url_regex, handler_t handler)
- {
- std::lock_guard< std::mutex > lock(_mtx);
- _urls.emplace(std::pair< std::string, Handling >(url_regex, {boost::regex(url_regex), handler}));
- }
-
- void Webserver::remove_url_handler(std::string url_regex) {
- std::lock_guard< std::mutex > lock(_mtx);
- _urls.erase(url_regex);
- }
-
- void Webserver::deliver_status(tcp::socket *socket, Webserver::request req, http::status status)
- {
- http::response< http::string_body > res{status, req.version()};
- res.set(http::field::content_type, "text/html");
- res.keep_alive(req.keep_alive());
- res.prepare_payload();
- if (status != http::status::internal_server_error)
- res.keep_alive(req.keep_alive());
- beast::error_code ec;
- http::write(*socket, res, ec);
- }
-
- void Webserver::deliver_file(tcp::socket *socket, Webserver::request req)
- {
- static boost::regex file{"/([\\w\\d]{1-100}\\.[\\w\\d]{1-4})"};
- boost::cmatch m;
- if (boost::regex_match(req.target().data(), m, file)) {
- fs::path p{_doc_root};
- p += m[1];
-
- beast::error_code ec;
- http::file_body::value_type body;
- body.open(p.c_str(), beast::file_mode::scan, ec);
- if (ec == beast::errc::no_such_file_or_directory) {
- deliver_status(socket, req, http::status::not_found);
- }
- else if (ec) {
- deliver_status(socket, req, http::status::internal_server_error);
- }
- else {
- auto const size = body.size();
- http::response<http::file_body> res{
- std::piecewise_construct,
- std::make_tuple(std::move(body)),
- std::make_tuple(http::status::ok, req.version())};
- res.set(http::field::content_type, mime_type(p.c_str()));
- res.content_length(size);
- res.keep_alive(req.keep_alive());
- http::write(*socket, res, ec);
- }
- }
- else {
- deliver_status(socket, req, http::status::not_found);
- }
- }
-
- Webserver::handler_t Webserver::find_handler(request& r, boost::cmatch& m)
- {
- std::lock_guard< std::mutex > lock(_mtx);
-
- for (auto it=_urls.begin(); it!=_urls.end(); ++it) {
- if (boost::regex_match(r.target().data(), m, it->second.regex))
- return it->second.handler;
- }
-
- return nullptr;
- }
-
- void Webserver::do_session(tcp::socket *socket)
- {
- beast::error_code ec;
- beast::flat_buffer buffer;
-
- while (_running)
- {
- http::request<http::string_body> req;
- http::read(*socket, buffer, req, ec);
- if (ec == http::error::end_of_stream)
- break;
- if (ec) {
- delete socket;
- return fail(ec, "reading from stream");
- }
-
- switch (req.method()) {
- case http::verb::post: {
- boost::cmatch m;
- Webserver::handler_t handler = find_handler(req, m);
-
- if (handler) {
- Webserver::response *res = handler(m, req);
- if (!res) {
- deliver_status(socket, req, http::status::not_found);
- }
- else {
- http::write(*socket, *res, ec);
- delete res;
- }
- }
- else {
- deliver_status(socket, req, http::status::not_found);
- }
- }
- break;
-
- case http::verb::get:
- deliver_file(socket, req);
- break;
-
- default:
- deliver_status(socket, req, http::status::method_not_allowed);
- };
-
- if (ec) {
- delete socket;
- return fail(ec, "writing to stream");
- }
- }
-
- socket->shutdown(tcp::socket::shutdown_send, ec);
- delete socket;
- }
-
- void Webserver::run()
- {
- _running = true;
- while (_running)
- {
- tcp::socket* socket = new tcp::socket{_ioc};
- _acceptor.accept(*socket);
-
- std::function< void() > tf = [&](){do_session(socket);};
- std::thread{tf}.detach();
- }
- }
-
- void Webserver::shutdown()
- {
- _running = false;
- }
-
- };
-
|