24#include <boost/network/protocol/http/server.hpp> 
   36namespace http = boost::network::http;
 
   46    enum class RawPostAccess {
 
   52    auto sendServerError = [](HttpServer::connection_ptr& httpConn) {
 
   53        httpConn->set_status(HttpServer::connection::internal_server_error);
 
   54        httpConn->set_headers(unordered_multimap<string, string>({{
"content-type", 
"text/html; charset=utf-8"}}));
 
   58    inline string getListenAddr(shared_ptr<Config const> 
const& configPtr) {
 
   59        return (*configPtr)[{
"http", 
"listen"}].empty() ? 
"127.0.0.1" : (*configPtr)[{
"http", 
"listen"}];
 
   62    inline string getListenPort(shared_ptr<Config const> 
const& configPtr) {
 
   63        return (*configPtr)[{
"http", 
"port"}].empty() ? 
"8080" : (*configPtr)[{
"http", 
"port"}];
 
   68struct InputConsumingHttpHandler : 
public enable_shared_from_this<InputConsumingHttpHandler> {
 
   74    RawPostAccess rawPostAccess;
 
   77                              ssize_t maxPostSize, 
size_t expectedSize, RawPostAccess rawPostAccess)
 
   78        : requestHandler(requestHandler), connectionInit(std::move(connectionInit)), maxPostSize(maxPostSize),
 
   79          expectedSize(expectedSize), rawPostAccess(rawPostAccess) {}
 
   81    void operator()(HttpServer::connection::input_range input, boost::system::error_code ec,
 
   82                    size_t bytesTransferred, HttpServer::connection_ptr httpConn) {
 
   83        if (ec == boost::asio::error::eof) {
 
   84            NLOG_ERROR(logger, 
"Request with POST data could not be handled.")
 
   85            NLOG_DEBUG(logger, "Debug info: boost::asio::error::eof in cpp-netlib while processing POST data")
 
   86            sendServerError(httpConn);
 
   91        if (postBody.size() + bytesTransferred > maxPostSize) {
 
   92            sendServerError(httpConn);
 
   97        postBody.insert(postBody.end(), boost::begin(input), boost::end(input));
 
  100        if (postBody.size() < expectedSize) {
 
  101            auto self = this->shared_from_this();
 
  102            httpConn->read([
self](HttpServer::connection::input_range input,
 
  103                                  boost::system::error_code ec, 
size_t bytes_transferred,
 
  104                                  HttpServer::connection_ptr httpConn) {
 
  105                (*self)(input, ec, bytes_transferred, httpConn);
 
  110        string const multipartContentType = 
"multipart/form-data";
 
  111        string const plainTextContentType = 
"text/plain";
 
  115        if (rawPostAccess == RawPostAccess::ALWAYS) {
 
  116            requestInit.
rawPost = make_shared<string>(postBody);
 
  119        if (postContentType == 
"application/x-www-form-urlencoded") {
 
  120            requestInit.postContentType = postContentType;
 
  122        } 
else if (postContentType.substr(0, multipartContentType.length()) == multipartContentType) {
 
  125                for (
auto const& p : postData.parts()) {
 
  127                    if (!p.filename().empty() || (!p.contentType().empty() &&
 
  128                                                  p.contentType().substr(0, plainTextContentType.length()) !=
 
  129                                                          plainTextContentType)) {
 
  131                        requestInit.postFiles.insert({p.partName(), std::move(pf)});
 
  133                        requestInit.postVars.insert({p.partName(), p.content()});
 
  137        } 
else if (rawPostAccess == RawPostAccess::NONSTANDARD) {
 
  138            requestInit.rawPost = make_shared<string>(std::move(postBody));
 
  144        connection.flushResponse();
 
  151    void operator()(HttpServer::request 
const& request, HttpServer::connection_ptr httpConn) {
 
  152        auto configPtr = requestHandler->
getConfig();
 
  156                {
"REMOTE_ADDR", request.source.substr(0, request.source.find_first_of(
':'))},
 
  157                {
"REQUEST_URI", request.destination},
 
  158                {
"REMOTE_PORT", to_string(request.source_port)},
 
  159                {
"REQUEST_METHOD", request.method},
 
  160                {
"SERVER_ADDR", getListenAddr(configPtr)},
 
  161                {
"SERVER_PORT", getListenPort(configPtr)},
 
  162                {
"SERVER_SOFTWARE", 
"NAWA Development Web Server"},
 
  166        for (
auto const& h : request.headers) {
 
  174            stringstream baseUrl;
 
  177            baseUrl << 
"http://" << requestInit.
environment[
"host"];
 
  179            auto baseUrlStr = baseUrl.str();
 
  183            requestInit.
environment[
"FULL_URL_WITH_QS"] = baseUrlStr + request.destination;
 
  186            baseUrl << request.destination.substr(0, request.destination.find_first_of(
'?'));
 
  187            requestInit.
environment[
"FULL_URL_WITHOUT_QS"] = baseUrl.str();
 
  190        if (request.destination.find_first_of(
'?') != string::npos) {
 
  196        connectionInit.
requestInit = std::move(requestInit);
 
  197        connectionInit.
config = (*configPtr);
 
  200            if (!flushInfo.flushedBefore) {
 
  201                httpConn->set_status(HttpServer::connection::status_t(flushInfo.status));
 
  202                httpConn->set_headers(flushInfo.headers);
 
  204            httpConn->write(flushInfo.body);
 
  210                string rawPostStr = (*configPtr)[{
"post", 
"raw_access"}];
 
  211                auto rawPostAccess = (rawPostStr == 
"never")
 
  212                                             ? RawPostAccess::NEVER
 
  213                                             : ((rawPostStr == 
"always") ? RawPostAccess::ALWAYS
 
  214                                                                         : RawPostAccess::NONSTANDARD);
 
  217                ssize_t maxPostSize = stol((*configPtr)[{
"post", 
"max_size"}]) * 1024;
 
  219                if (contentLength > maxPostSize) {
 
  220                    sendServerError(httpConn);
 
  224                auto inputConsumingHandler = make_shared<InputConsumingHttpHandler>(requestHandler,
 
  225                                                                                    std::move(connectionInit), maxPostSize,
 
  226                                                                                    contentLength, rawPostAccess);
 
  227                httpConn->read([inputConsumingHandler](HttpServer::connection::input_range input,
 
  228                                                       boost::system::error_code ec, 
size_t bytesTransferred,
 
  229                                                       HttpServer::connection_ptr httpConn) {
 
  230                    (*inputConsumingHandler)(input, ec, bytesTransferred, httpConn);
 
  232            } 
catch (invalid_argument 
const&) {
 
  233            } 
catch (out_of_range 
const&) {}
 
  239        connection.flushResponse();
 
  243struct HttpRequestHandler::Data {
 
  244    unique_ptr<HttpHandler> handler;
 
  245    unique_ptr<HttpServer> server;
 
  247    vector<thread> threadPool;
 
  248    bool requestHandlingActive = 
false;
 
  252HttpRequestHandler::HttpRequestHandler(std::shared_ptr<HandleRequestFunctionWrapper> handleRequestFunction,
 
  255    data = make_unique<Data>();
 
  257    setAppRequestHandler(std::move(handleRequestFunction));
 
  258    setConfig(std::move(config));
 
  259    auto configPtr = getConfig();
 
  261    logger.setAppname(
"HttpRequestHandler");
 
  263    data->handler = make_unique<HttpHandler>();
 
  264    data->handler->requestHandler = 
this;
 
  265    HttpServer::options httpServerOptions(*data->handler);
 
  268    string listenAddr = getListenAddr(configPtr);
 
  269    string listenPort = getListenPort(configPtr);
 
  270    bool reuseAddr = (*configPtr)[{
"http", 
"reuseaddr"}] != 
"off";
 
  271    data->server = make_unique<HttpServer>(
 
  272            httpServerOptions.address(listenAddr).port(listenPort).reuse_address(reuseAddr));
 
  274    if (concurrency > 0) {
 
  275        data->concurrency = concurrency;
 
  279        data->server->listen();
 
  280    } 
catch (exception 
const& e) {
 
  282                        "Could not listen to host/port.", e.what());
 
  286HttpRequestHandler::~HttpRequestHandler() {
 
  287    if (data->requestHandlingActive && !data->joined) {
 
  288        data->server->stop();
 
  291        for (
auto& t : data->threadPool) {
 
  294        data->threadPool.clear();
 
  298void HttpRequestHandler::start() {
 
  299    if (data->requestHandlingActive) {
 
  303        throw Exception(__PRETTY_FUNCTION__, 10, 
"HttpRequestHandler was already joined.");
 
  307            for (
int i = 0; i < data->concurrency; ++i) {
 
  308                data->threadPool.emplace_back([
this] { data->server->run(); });
 
  310            data->requestHandlingActive = 
true;
 
  311        } 
catch (exception 
const& e) {
 
  313                            string(
"An error occurred during start of request handling."),
 
  317        throw Exception(__PRETTY_FUNCTION__, 2, 
"HTTP handler is not available.");
 
  321void HttpRequestHandler::stop() noexcept {
 
  326        data->server->stop();
 
  330void HttpRequestHandler::terminate() noexcept {
 
  335        data->server->stop();
 
  339void HttpRequestHandler::join() noexcept {
 
  343    for (
auto& t : data->threadPool) {
 
  347    data->threadPool.clear();
 
Container used by request handlers to initiate the nawa::Connection object.
 
Response object to be passed back to NAWA and accessor to the request.
 
Exception class that can be used by apps to catch errors resulting from nawa function calls.
 
http::server< HttpHandler > HttpServer
 
A request handler which creates a development web server.
 
Simple class for (not (yet) thread-safe) logging to stderr or to any other output stream.
 
#define NLOG_DEBUG(Logger, Message)
 
#define NLOG_ERROR(Logger, Message)
 
Parser for MIME multipart, especially in POST form data.
 
Handles and serves incoming requests via the NAWA app.
 
std::string & contentType() noexcept
 
std::shared_ptr< Config const > getConfig() const noexcept
 
void handleRequest(Connection &connection)
 
std::string toLowercase(std::string s)
 
std::string generateErrorPage(unsigned int httpStatus)
 
std::unordered_multimap< std::string, std::string > splitQueryString(std::string const &queryString)
 
std::unordered_multimap< std::string, std::string > parseCookies(std::string const &rawCookies)
 
std::unordered_multimap< std::string, std::string > getVars
 
std::unordered_map< std::string, std::string > environment
 
std::unordered_multimap< std::string, std::string > cookieVars
 
RequestInitContainer requestInit
 
FlushCallbackFunction flushCallback
 
std::shared_ptr< std::string > rawPost
 
Contains useful functions that improve the readability and facilitate maintenance of the NAWA code.