If you want all the code in a buildable format from the last 10 or so posts. You can find it here:
The PathMatcher object allows you to register paths with named sections of the path. But there are other vaiables available in the reuest. I want to make all the variables available to the lambda in a logic way.
So I have a wrapper class that Implements the PyntHttp interface and uses the PathMatcher to check if a request has a handler. If it does then we combine the header parameters query parameters and path match parameters into the variables()
object to be used by the lambda:
HTTPHandler.h
#ifndef THORSANVIL_NISSE_NISSEHTTP_HTTP_HANDLER_H
#define THORSANVIL_NISSE_NISSEHTTP_HTTP_HANDLER_H
#include "NisseHTTPConfig.h"
#include "PyntHTTP.h"
#include "PathMatcher.h"
#include "Util.h"
#include <string_view>
namespace ThorsAnvil::Nisse::NisseHTTP
{
class HeaderRequest;
class HTTPHandler: public PyntHTTP
{
using HTTPAction = std::function<void(Request& request, Response& response)>;
PathMatcher pathMatcher;
std::vector<HTTPAction> actions;
public:
virtual void processRequest(Request& request, Response& response);
void addPath(std::string const& path, HTTPAction&& action);
private:
std::string normalize(std::string_view path);
void addHeaders(RequestVariables& var, HeaderRequest const& headers);
void addQueryParam(RequestVariables& var, std::string_view query);
void addPathMatch(RequestVariables& var, Match const& matches);
};
}
#endif
HTTPHandler.cpp
#include "HTTPHandler.h"
#include "Request.h"
#include "Response.h"
using namespace ThorsAnvil::Nisse::NisseHTTP;
std::string HTTPHandler::normalize(std::string_view path)
{
return std::string(path);
}
void HTTPHandler::processRequest(Request& request, Response& response)
{
std::string path = normalize(request.getUrl().pathname());
if (!pathMatcher.findMatch(path, request, response)) {
response.setStatus(404);
}
}
void HTTPHandler::addHeaders(RequestVariables& var, HeaderRequest const& headers)
{
for (auto const& head: headers) {
var[head.first] = head.second.back();
}
}
void HTTPHandler::addQueryParam(RequestVariables& var, std::string_view query)
{
// Add URL paramets to the variables object.
if (query.size() == 0) {
return;
}
// Remove the first ? or &
query.remove_prefix(1);
std::smatch queryParamMatch;
std::regex queryParamExpr{"([^=]*)=([^&]*)&"};
std::string queryStr(query);
queryStr += "&";
while (std::regex_search(queryStr, queryParamMatch, queryParamExpr))
{
var[queryParamMatch[1].str()] = queryParamMatch[2].str();
queryStr = queryParamMatch.suffix().str();
}
}
void HTTPHandler::addPathMatch(RequestVariables& var, Match const& matches)
{
for (auto const& match: matches) {
var[match.first] = match.second;
}
}
void HTTPHandler::addPath(std::string const& path, HTTPAction&& action)
{
actions.emplace_back(std::move(action));
pathMatcher.addPath(path, [&, actionId = actions.size() - 1](Match const& matches, Request& request, Response& response)
{
// Get the variable object
RequestVariables& var = request.variables();
addHeaders(var, request.headers());
addQueryParam(var, request.getUrl().query());
addPathMatch(var, matches);
actions[actionId](request, response);
});
}
Hello World
We can now simplify the Hello World handler to:
// Processes HTTP connection on port.
ThorsAnvil::Nisse::NisseHTTP::HTTPHandler http;
http.addPath("/HW-Length{Who}.html", [](ThorsAnvil::Nisse::NisseHTTP::Request& request, ThorsAnvil::Nisse::NisseHTTP::Response& response)
{
std::string who = request.variables()["Who"];
std::string page = R"(
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head><title>Nisse server 1.1</title></head>
<body>Hello world: )" + who + R"(</body>
</html>
)";
ThorsAnvil::Nisse::NisseHTTP::HeaderResponse header;
response.addHeaders(header, page.size()) << page;
});
http.addPath("/HW-Chunked{Who}.html", [](ThorsAnvil::Nisse::NisseHTTP::Request& request, ThorsAnvil::Nisse::NisseHTTP::Response& response)
{
ThorsAnvil::Nisse::NisseHTTP::HeaderResponse header;
response.addHeaders(header, ThorsAnvil::Nisse::NisseHTTP::Encoding::Chunked) << R"(
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head><title>Nisse server 1.1</title></head>
<body>Hello world: )" << request.variables()["Who"] << R"(</body>
</html>
)";
});