Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions examples/auth/session_cookies.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// vix::middleware::HttpPipeline p;

// p.use(vix::middleware::auth::session({
// .store = std::make_shared<vix::middleware::auth::InMemorySessionStore>(),
// .secret = "change-me-32bytes-min",
// .cookie_name = "sid",
// .secure = true,
// .same_site = "Lax"
// }));

// p.use(vix::middleware::auth::jwt({
// .secret = "jwt-secret",
// }));
// auto *sess = ctx.state_ptr<vix::middleware::auth::Session>();
// if (sess)
// {
// sess->set("user_id", "123");
// }
21 changes: 21 additions & 0 deletions examples/cookies/cookie.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <vix.hpp>
#include <vix/middleware/http/cookies.hpp>
using namespace vix;

int main()
{
App app;

app.get("/cookie", [](Request &, Response &res){

vix::middleware::cookies::Cookie c;
c.name = "hello";
c.value = "vix";
c.max_age = 3600;
vix::middleware::cookies::set(res, c);

res.text("cookie set");
});

app.run(8080);
}
173 changes: 173 additions & 0 deletions examples/middleware_http/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# HTTP Middleware Mega Example (Vix.cpp)

This directory contains a **single, large, practical example** demonstrating how to
build HTTP routes and middleware with **Vix.cpp**.

The goal is to show, in one place, how everything fits together:
routing, middleware, security, parsing, cookies, sessions, caching, etc.

---

## Run the example

```bash
vix run examples/http_middleware/mega_middleware_routes.cpp
```

Server starts on:

```
http://127.0.0.1:8080
```

---

## Quick sanity check

```bash
curl -i http://127.0.0.1:8080/
curl -i http://127.0.0.1:8080/_routes
```

---

## Cookies example

### Set a cookie

```bash
curl -i http://127.0.0.1:8080/api/cookie/set
```

You should see a `Set-Cookie` header:

```
Set-Cookie: demo=hello; Path=/; Max-Age=3600; HttpOnly; SameSite=Lax
```

### Read the cookie

```bash
curl -i --cookie "demo=hello" http://127.0.0.1:8080/api/cookie/get
```

Response:

```json
{
"ok": true,
"cookie_demo": "hello",
"has_cookie": true
}
```

---

## Session example (signed cookie + server store)

Sessions are handled by the **session middleware**:

- Session id stored in a signed cookie (`sid`)
- Session data stored server-side
- Automatic creation on first request
- Persisted across requests
- Destroyable (logout)

### First request (creates session)

```bash
curl -i http://127.0.0.1:8080/api/session/whoami
```

Response example:

```json
{
"ok": true,
"session": true,
"sid": "e4a1f0...",
"is_new": true,
"name": "guest",
"visits": 1
}
```

### Persist session using cookie jar

```bash
curl -i -c jar.txt http://127.0.0.1:8080/api/session/whoami
curl -i -b jar.txt http://127.0.0.1:8080/api/session/whoami
```

You should see `visits` increase on each request.

---

### Update session data

```bash
curl -i -b jar.txt -X POST http://127.0.0.1:8080/api/session/setname/gaspard
```

Then:

```bash
curl -i -b jar.txt http://127.0.0.1:8080/api/session/whoami
```

Response:

```json
{
"name": "gaspard",
"visits": 3
}
```

---

### Logout (destroy session)

```bash
curl -i -b jar.txt -X POST http://127.0.0.1:8080/api/session/logout
```

This will:

- Delete server-side session
- Clear the session cookie (`Max-Age=0`)

Next request creates a new session.

---

## What this example demonstrates

- App routing (`GET`, `POST`, path params)
- Global middleware vs prefix middleware
- Context-based middleware (`adapt_ctx`)
- Legacy HTTP middleware adaptation
- RequestState (typed state storage)
- Cookies (parse + set)
- Sessions (signed cookie + store)
- JSON / Form / Multipart parsing
- API key protection
- RBAC-style guards
- HTTP GET cache
- CSRF, CORS, rate limiting
- Debug and observability patterns

---

## Philosophy

This file is intentionally **big and repetitive**.

It is meant to answer:

> "How do I actually write routes and middleware with Vix.cpp?"

By reading a single file.

If you understand this example, you understand **90% of Vix.cpp HTTP middleware usage**.

Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
#include <vix/middleware/app/adapter.hpp>
#include <vix/middleware/app/presets.hpp>
#include <vix/middleware/app/http_cache.hpp>
#include <vix/middleware/http/cookies.hpp>
#include <vix/middleware/auth/session.hpp>

// Some projects place these in different paths; keep includes minimal.
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -491,6 +493,113 @@ static void register_api_routes(vix::App &app)
"t2_after_api_mw_ms", t2,
}),
})); });

// GET /api/cookie/set
// Sets a cookie "demo" = "hello"
app.get("/api/cookie/set", [](Request &, Response &res)
{
vix::middleware::cookies::Cookie c;
c.name = "demo";
c.value = "hello";
c.path = "/";
c.http_only = true;
c.secure = false;
c.same_site = "Lax";
c.max_age = 3600;

vix::middleware::cookies::set(res, c);

res.json(J::obj({
"ok", true,
"cookie_set", true,
"name", "demo",
"value", "hello",
})); });

// GET /api/cookie/get
// Reads cookie "demo" from request header "cookie"
app.get("/api/cookie/get", [](Request &req, Response &res)
{
auto v = vix::middleware::cookies::get(req, "demo");
res.json(J::obj({
"ok", true,
"cookie_demo", v ? *v : "",
"has_cookie", (bool)v,
})); });

// GET /api/session/whoami
// Uses Session stored in RequestState by session middleware
app.get("/api/session/whoami", [](Request &req, Response &res)
{
auto *s = req.try_state<vix::middleware::auth::Session>();
if (!s)
{
res.status(500).json(J::obj({
"ok", false,
"error", "session_missing",
"hint", "Session middleware not installed on this route",
}));
return;
}

// read or init
auto name = s->get("name").value_or("guest");
long long visits = 0;

if (auto v = s->get("visits"))
{
try { visits = std::stoll(*v); } catch (...) { visits = 0; }
}

visits += 1;
s->set("name", name);
s->set("visits", std::to_string(visits));

res.json(J::obj({
"ok", true,
"session", true,
"sid", s->id,
"is_new", s->is_new,
"name", name,
"visits", visits,
})); });

// POST /api/session/setname/{name}
app.post("/api/session/setname/{name}", [](Request &req, Response &res)
{
auto *s = req.try_state<vix::middleware::auth::Session>();
if (!s)
{
res.status(500).json(J::obj({"ok", false, "error", "session_missing"}));
return;
}

const std::string name = req.param("name", "guest");
s->set("name", name);

res.json(J::obj({
"ok", true,
"updated", true,
"name", name,
})); });

// POST /api/session/logout
// Destroys session and clears cookie
app.post("/api/session/logout", [](Request &req, Response &res)
{
auto *s = req.try_state<vix::middleware::auth::Session>();
if (!s)
{
res.status(500).json(J::obj({"ok", false, "error", "session_missing"}));
return;
}

s->destroy();

res.json(J::obj({
"ok", true,
"logout", true,
})); });
}

static void register_dev_routes(vix::App &app)
Expand Down Expand Up @@ -614,6 +723,20 @@ static void install_api_middlewares(vix::App &app)
// 7) Example: legacy HttpMiddleware adaptation (header gate) on exact path
// Protect /api/ping with x-demo: 1
install_exact(app, "/api/ping", adapt(mw_require_header("x-demo", "1")));
// 8) Session middleware for /api/session/
{
vix::middleware::auth::SessionOptions sopt{};
sopt.secret = "dev_session_secret"; // required
sopt.cookie_name = "sid";
sopt.cookie_path = "/";
sopt.secure = false; // set true behind https
sopt.http_only = true;
sopt.same_site = "Lax";
sopt.auto_create = true;
sopt.ttl = std::chrono::hours(24 * 7);

install(app, "/api/session/", adapt_ctx(vix::middleware::auth::session(std::move(sopt))));
}
}

static void install_dev_middlewares(vix::App &app)
Expand Down Expand Up @@ -681,6 +804,12 @@ int main()

push("GET", "/dev/trace", "debug route; may be IP-filtered");
push("GET", "/dev/boom", "throws to test dev error handling");
push("GET", "/api/cookie/set", "sets demo cookie");
push("GET", "/api/cookie/get", "reads demo cookie");

push("GET", "/api/session/whoami", "session counter demo (needs session middleware)");
push("POST", "/api/session/setname/{name}", "writes session key 'name'");
push("POST", "/api/session/logout", "destroy session + clear cookie");

res.json(J::obj({
"ok", true,
Expand All @@ -692,6 +821,12 @@ int main()
"For /api/secure/whoami add: -H 'x-api-key: dev_key_123'",
"For /api/admin/stats add: -H 'x-user: gaspard' -H 'x-role: admin'",
"To bypass cache: -H 'x-vix-cache: bypass'",
"Cookie demo: curl -i http://127.0.0.1:8080/api/cookie/set",
"Then: curl -i --cookie 'demo=hello' http://127.0.0.1:8080/api/cookie/get",
"Session: curl -i http://127.0.0.1:8080/api/session/whoami",
"Reuse cookie: curl -i -c jar.txt http://127.0.0.1:8080/api/session/whoami && curl -i -b jar.txt http://127.0.0.1:8080/api/session/whoami",
"Set name: curl -i -b jar.txt -X POST http://127.0.0.1:8080/api/session/setname/gaspard",
"Logout: curl -i -b jar.txt -X POST http://127.0.0.1:8080/api/session/logout",
}),
})); });

Expand Down
Loading
Loading