C++20 compiler, CMake 3.20+, and OpenSSL are required.
$ git clone https://github.com/dvdsvds/yNet.git
$ sudo cp yNet/cli/ynet /usr/local/bin/
$ ynet new myapp
$ cd myapp
$ ynet build
$ ynet run
Open http://localhost:8080 in your browser to see the Welcome page.
myapp/
├── CMakeLists.txt
├── src/
│ └── main.cpp
├── static/
└── templates/
└── index.html
Register routes using method chaining on the App class.
#include <ynet/app.h>
ynet::App app;
// GET request
app.get("/").handle([](ynet::Request& req, ynet::Response& res) {
res.html("<h1>Hello</h1>");
});
// POST request
app.post("/submit").handle([](ynet::Request& req, ynet::Response& res) {
res.json("{\"ok\":true}");
});
// PUT, DELETE
app.put("/update").handle(...);
app.del("/remove").handle(...);
res.html("<h1>HTML</h1>");
res.json("{\"key\":\"value\"}");
res.status(201).body("Created");
res.redirect("/other");
res.header("X-Custom", "value");
// GET /hello?name=dvd
app.get("/hello").handle([](ynet::Request& req, ynet::Response& res) {
auto name = req.getQueryParam("name");
res.html("Hello, " + name.value_or("World") + "!");
});
app.serveStatic("/static", "static");
// Serves files from static/ directory at /static/ path
app.onError(404).html("<h1>Not Found</h1>");
app.onError(500).html("<h1>Server Error</h1>");
Enable middleware via App class methods. They execute in order.
app.logger(); // Request logging
app.cors("*"); // CORS allow
app.rateLimit(100, 60); // 100 req per 60 sec
app.csrf(); // CSRF token validation
app.sanitizer(); // Input sanitization
app.secureHeaders(); // Security headers
app.session(); // Session management
// Default CSP
app.secureHeaders();
// Custom CSP
app.secureHeaders("default-src 'self'; style-src 'self' 'unsafe-inline'");
Supports Mustache/Handlebars-style syntax.
ynet::Cache cache(256);
ynet::TemplateEngine engine(cache);
ynet::Object vars;
vars["title"] = ynet::JsonValue{std::string("Hello")};
std::string html = engine.render("templates/index.html", ynet::JsonValue{vars});
res.html(html);
<!-- Variable (HTML escaped) -->
Docs - yNet
<!-- Raw output (no escaping) -->
<!-- Conditional -->
<!-- Loop -->
<!-- Partial include -->
app.ws("/chat", [](ynet::WebSocket& ws) {
ws.onMessage([&](const std::string& msg) {
ws.send("Echo: " + msg);
});
});
Configure via config/projectname.conf in the project root.
A default file is auto-generated if missing.
# config/myapp.conf
port=8080
bind=0.0.0.0
tls=off
cert=cert.pem
key=key.pem
max_body=1MB
max_upload=10MB
max_header=8KB
max_headers=64
header_timeout=5000
body_timeout=10000
max_connections=1024
max_cache=1024
# Generate certificate
$ openssl req -x509 -newkey rsa:2048 -keyout config/key.pem -out config/cert.pem -days 365 -nodes
# config/myapp.conf
port=443
tls=on
cert=cert.pem
key=key.pem
#include <ynet/app.h>
int main() {
ynet::App app;
app.get("/").handle([](ynet::Request& req, ynet::Response& res) {
res.html("<h1>Hello, yNet!</h1>");
});
app.listen();
return 0;
}
#include <ynet/app.h>
#include <ynet/util/template_engine.h>
#include <ynet/util/json.h>
int main() {
ynet::App app;
app.serveStatic("/static", "static");
app.logger();
app.secureHeaders();
app.get("/").handle([](ynet::Request& req, ynet::Response& res) {
ynet::Cache cache(256);
ynet::TemplateEngine engine(cache);
ynet::Object vars;
vars["title"] = ynet::JsonValue{std::string("Home")};
std::string html = engine.render("templates/index.html", ynet::JsonValue{vars});
res.html(html);
});
app.listen();
return 0;
}
app.get("/api/status").handle([](ynet::Request& req, ynet::Response& res) {
res.json("{\"status\":\"ok\",\"version\":\"0.1.0\"}");
});