// this file is derived from a boost::beast sample
|
|
|
|
#include <boost/beast/core.hpp>
|
|
#include <boost/beast/http.hpp>
|
|
#include <boost/beast/version.hpp>
|
|
#include <boost/asio/ip/tcp.hpp>
|
|
#include <boost/config.hpp>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <thread>
|
|
|
|
namespace beast = boost::beast; // from <boost/beast.hpp>
|
|
namespace http = beast::http; // from <boost/beast/http.hpp>
|
|
namespace net = boost::asio; // from <boost/asio.hpp>
|
|
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
|
|
|
|
#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_file(boost::string_view target)
|
|
{
|
|
|
|
}
|
|
|
|
Webserver::handler_t Webserver::find_handler(request& r, boost::cmatch& m)
|
|
{
|
|
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)
|
|
return fail(ec, "reading from stream");
|
|
|
|
boost::cmatch m;
|
|
Webserver::response *resp = nullptr;
|
|
Webserver::handler_t handler = find_handler(req, m);
|
|
|
|
if (handler) {
|
|
resp = handler(m, req);
|
|
}
|
|
else {
|
|
deliver_file(req.target());
|
|
}
|
|
delete resp;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
};
|
|
|