Generate HTML
This commit is contained in:
parent
167d87b41e
commit
419c5e573c
|
@ -23,7 +23,8 @@ list(APPEND FLAGS -Werror -Wall -Wextra -Wshadow -Wpedantic -Wno-gnu-anonymous-s
|
|||
add_link_options(${FLAGS})
|
||||
|
||||
|
||||
add_executable(${PROJECT_NAME} main.cpp misc.cpp config.cpp routes/home.cpp)
|
||||
add_executable(${PROJECT_NAME} main.cpp misc.cpp config.cpp servehelper.cpp blankie/serializer.cpp blankie/escape.cpp
|
||||
routes/home.cpp)
|
||||
set_target_properties(${PROJECT_NAME}
|
||||
PROPERTIES
|
||||
CXX_STANDARD 20
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#include <stdexcept>
|
||||
|
||||
#include "escape.h"
|
||||
|
||||
static inline const char* get_replacement(char c);
|
||||
|
||||
namespace blankie {
|
||||
namespace html {
|
||||
|
||||
std::string escape(const std::string& in) {
|
||||
std::string out;
|
||||
size_t pos = 0;
|
||||
size_t last_pos = 0;
|
||||
|
||||
out.reserve(in.size());
|
||||
while ((pos = in.find_first_of("&<>\"'", pos)) != std::string::npos) {
|
||||
out.append(in, last_pos, pos - last_pos);
|
||||
out.append(get_replacement(in[pos]));
|
||||
pos++;
|
||||
last_pos = pos;
|
||||
}
|
||||
|
||||
if (in.size() > last_pos) {
|
||||
out.append(in, last_pos);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}; // namespace html
|
||||
}; // namespace blankie
|
||||
|
||||
static inline const char* get_replacement(char c) {
|
||||
switch (c) {
|
||||
case '&': return "&";
|
||||
case '<': return "<";
|
||||
case '>': return ">";
|
||||
case '"': return """;
|
||||
case '\'': return "'";
|
||||
default: throw std::runtime_error("Encountered unknown replacement character");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace blankie {
|
||||
namespace html {
|
||||
|
||||
std::string escape(const std::string& in);
|
||||
|
||||
}; // namespace html
|
||||
}; // namespace blankie
|
|
@ -0,0 +1,54 @@
|
|||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "escape.h"
|
||||
#include "serializer.h"
|
||||
|
||||
static inline bool should_not_close_tag(const char* tag);
|
||||
|
||||
namespace blankie {
|
||||
namespace html {
|
||||
|
||||
std::string Element::serialize() const {
|
||||
std::string out;
|
||||
|
||||
out += '<';
|
||||
out += this->tag;
|
||||
for (const auto &[key, value] : this->attributes) {
|
||||
out += ' ';
|
||||
out += key;
|
||||
if (!value.empty()) {
|
||||
out += "=\"";
|
||||
out += escape(value);
|
||||
out += '"';
|
||||
}
|
||||
}
|
||||
out += '>';
|
||||
|
||||
for (const Node& node : this->nodes) {
|
||||
if (const Element* element = std::get_if<Element>(&node)) {
|
||||
out += element->serialize();
|
||||
} else if (const char* const* text = std::get_if<const char*>(&node)) {
|
||||
out += escape(*text);
|
||||
} else if (const std::string* str = std::get_if<std::string>(&node)) {
|
||||
out += escape(*str);
|
||||
} else {
|
||||
throw std::runtime_error("Encountered unknown node");
|
||||
}
|
||||
}
|
||||
|
||||
if (!should_not_close_tag(this->tag)) {
|
||||
out += "</";
|
||||
out += this->tag;
|
||||
out += '>';
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
}; // namespace html
|
||||
}; // namespace blankie
|
||||
|
||||
static inline bool should_not_close_tag(const char* tag) {
|
||||
return !strncmp(tag, "link", 5);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <utility>
|
||||
|
||||
namespace blankie {
|
||||
namespace html {
|
||||
|
||||
struct Element;
|
||||
|
||||
typedef std::pair<const char*, std::string> Attribute;
|
||||
typedef std::variant<Element, const char*, std::string> Node;
|
||||
|
||||
struct Element {
|
||||
const char* tag;
|
||||
std::vector<Attribute> attributes;
|
||||
std::vector<Node> nodes;
|
||||
|
||||
Element(const char* tag_) : tag(tag_) {}
|
||||
Element(const char* tag_, std::vector<Node> nodes_)
|
||||
: tag(tag_), nodes(std::move(nodes_)) {}
|
||||
Element(const char* tag_, std::vector<Attribute> attributes_, std::vector<Node> nodes_)
|
||||
: tag(tag_), attributes(std::move(attributes_)), nodes(std::move(nodes_)) {}
|
||||
std::string serialize() const;
|
||||
};
|
||||
|
||||
}; // namespace html
|
||||
}; // namespace blankie
|
4
main.cpp
4
main.cpp
|
@ -20,7 +20,9 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
httplib::Server server;
|
||||
server.Get("/", home_route);
|
||||
server.Get("/", [&](const httplib::Request& req, httplib::Response& res) {
|
||||
home_route(req, res, config);
|
||||
});
|
||||
|
||||
if (config.bind_port != 0) {
|
||||
if (!server.bind_to_port(config.bind_host, config.bind_port)) {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "home.h"
|
||||
#include "../servehelper.h"
|
||||
|
||||
void home_route(const httplib::Request& req, httplib::Response& res) {
|
||||
res.set_content("awoo", "text/plain");
|
||||
void home_route(const httplib::Request& req, httplib::Response& res, const Config& config) {
|
||||
Element body("body");
|
||||
body.nodes.push_back(Element("h1", {"awoo"}));
|
||||
serve(req, res, config, "Pixwhile", std::move(body));
|
||||
}
|
||||
|
|
|
@ -2,4 +2,6 @@
|
|||
|
||||
#include <httplib/httplib.h>
|
||||
|
||||
void home_route(const httplib::Request& req, httplib::Response& res);
|
||||
struct Config; // forward declaration from config.h
|
||||
|
||||
void home_route(const httplib::Request& req, httplib::Response& res, const Config& config);
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#include "config.h"
|
||||
#include "servehelper.h"
|
||||
|
||||
static inline std::string get_origin(const httplib::Request& req, const Config& config);
|
||||
|
||||
void serve(const httplib::Request& req, httplib::Response& res, const Config& config, std::string title, Element element) {
|
||||
using namespace std::string_literals;
|
||||
|
||||
std::string origin = get_origin(req, config);
|
||||
std::string css_url = origin + "/style.css";
|
||||
res.set_header("Content-Security-Policy", "default-src 'none'; img-src 'self'; style-src "s + css_url);
|
||||
|
||||
Element html("html", {
|
||||
Element("head", {
|
||||
Element("title", {std::move(title)}),
|
||||
Element("link", {{"rel", "stylesheet"}, {"href", std::move(css_url)}}, {})
|
||||
}),
|
||||
std::move(element)
|
||||
});
|
||||
res.set_content("<!DOCTYPE html>"s + html.serialize(), "text/html");
|
||||
}
|
||||
|
||||
static inline std::string get_origin(const httplib::Request& req, const Config& config) {
|
||||
if (req.has_header("X-Canonical-Origin")) {
|
||||
return req.get_header_value("X-Canonical-Origin");
|
||||
}
|
||||
|
||||
std::string origin = "http://";
|
||||
if (req.has_header("Host")) {
|
||||
origin += req.get_header_value("Host");
|
||||
} else {
|
||||
origin += config.bind_host;
|
||||
if (config.bind_port != 80) {
|
||||
origin += ':' + std::to_string(config.bind_port);
|
||||
}
|
||||
}
|
||||
return origin;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <httplib/httplib.h>
|
||||
#include "blankie/serializer.h"
|
||||
|
||||
struct Config; // forward declaration from config.h
|
||||
using Element = blankie::html::Element;
|
||||
|
||||
void serve(const httplib::Request& req, httplib::Response& res, const Config& config, std::string title, Element element);
|
Loading…
Reference in New Issue