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
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module(
name = "khttpd",
version = "0.5.0",
version = "0.5.1",
)

bazel_dep(name = "platforms", version = "1.1.0")
Expand Down
34 changes: 17 additions & 17 deletions example/HelloController.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,33 @@
#define HELLOCONTROLLER_HPP
#include "controller/http_controller.hpp"

class HelloController : public khttpd::framework::BaseController<HelloController>
{
private:
std::string base_path() override
{
return "/hello";
}
class HelloController
: public khttpd::framework::BaseController<HelloController> {
private:
std::string base_path() override { return "/hello"; }

public:
static std::shared_ptr<HelloController> create() { return std::make_shared<HelloController>(); }
public:
static std::shared_ptr<HelloController> create() {
return std::make_shared<HelloController>();
}

std::shared_ptr<BaseController> register_routes(khttpd::framework::HttpRouter& router) override
{
KHTTPD_DOCUMENTED_ROUTE(get, "/hello", handle_hello,
{"Controller greeting", "Returns a greeting from a controller-managed route."});
std::shared_ptr<BaseController> register_routes(
khttpd::framework::HttpRouter& router) override {
KHTTPD_DOCUMENTED_ROUTE(
get, "/hello", handle_hello,
{"Controller greeting",
"Returns a greeting from a controller-managed route."});

return shared_from_this();
}

private:
void handle_hello(khttpd::framework::HttpContext& ctx)
{
private:
void handle_hello(khttpd::framework::HttpContext& ctx) {
std::string name = ctx.get_query_param("name").value_or("Guest");
ctx.set_status(boost::beast::http::status::ok);
ctx.set_content_type("text/plain");
ctx.set_body(fmt::format("Hello, {}!", name));
}
};

#endif //HELLOCONTROLLER_HPP
#endif // HELLOCONTROLLER_HPP
46 changes: 25 additions & 21 deletions example/HelloStreamController.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,47 @@
#define HELLOSTREAMCONTROLLER_HPP
#include "controller/http_controller.hpp"

class HelloStreamController : public khttpd::framework::BaseController<HelloStreamController>
{
public:
static std::shared_ptr<HelloStreamController> create() { return std::make_shared<HelloStreamController>(); }
class HelloStreamController
: public khttpd::framework::BaseController<HelloStreamController> {
public:
static std::shared_ptr<HelloStreamController> create() {
return std::make_shared<HelloStreamController>();
}

std::shared_ptr<BaseController> register_routes(khttpd::framework::HttpRouter& router) override
{
KHTTPD_DOCUMENTED_ROUTE(get, "/stream/:size", handle_stream,
{"Stream response chunks", "Streams up to 100 JSON chunks for the requested size."});
std::shared_ptr<BaseController> register_routes(
khttpd::framework::HttpRouter& router) override {
KHTTPD_DOCUMENTED_ROUTE(
get, "/stream/:size", handle_stream,
{"Stream response chunks",
"Streams up to 100 JSON chunks for the requested size."});

return shared_from_this();
}

private:
private:
size_t num_chunks_to_send_ = 0;

void handle_stream(khttpd::framework::HttpContext& ctx)
{
void handle_stream(khttpd::framework::HttpContext& ctx) {
std::string num_str = ctx.get_path_param("size").value_or("0");
num_chunks_to_send_ = std::stoul(num_str);
if (num_chunks_to_send_ > 100)
{
if (num_chunks_to_send_ > 100) {
num_chunks_to_send_ = 100;
}
ctx.set_status(boost::beast::http::status::ok);
ctx.set_content_type("application/json");
auto do_stream_chunk = [this, num_str](auto& session, const auto& writeHandler)
{
for (int i = 0; i < num_chunks_to_send_; i++)
{
auto json = fmt::format(R"("id": {}, "url": "/stream/{}", "args": , "headers": {})",
i, num_str, "\n");
if (!writeHandler(json)) { break; }
auto do_stream_chunk = [this, num_str](auto& session,
const auto& writeHandler) {
for (int i = 0; i < num_chunks_to_send_; i++) {
auto json = fmt::format(
R"("id": {}, "url": "/stream/{}", "args": , "headers": {})", i,
num_str, "\n");
if (!writeHandler(json)) {
break;
}
}
};
ctx.chunked(do_stream_chunk);
}
};

#endif //HELLOSTREAMCONTROLLER_HPP
#endif // HELLOSTREAMCONTROLLER_HPP
49 changes: 25 additions & 24 deletions example/HelloWsController.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,28 @@
#ifndef HELLOWSCONTROLLER_HPP
#define HELLOWSCONTROLLER_HPP

#include "controller/http_controller.hpp"
#include <spdlog/spdlog.h>

class HelloWsController : public khttpd::framework::BaseController<HelloWsController>
{
public:
static std::shared_ptr<HelloWsController> create() { return std::make_shared<HelloWsController>(); }
#include "controller/http_controller.hpp"

class HelloWsController
: public khttpd::framework::BaseController<HelloWsController> {
public:
static std::shared_ptr<HelloWsController> create() {
return std::make_shared<HelloWsController>();
}

std::shared_ptr<BaseController> register_routes(khttpd::framework::HttpRouter& router) override
{
KHTTPD_DOCUMENTED_ROUTE(get, "/hellows", handle_hello,
{"WebSocket echo upgrade", "Explains how to connect to the echo WebSocket endpoint."});
std::shared_ptr<BaseController> register_routes(
khttpd::framework::HttpRouter& router) override {
KHTTPD_DOCUMENTED_ROUTE(
get, "/hellows", handle_hello,
{"WebSocket echo upgrade",
"Explains how to connect to the echo WebSocket endpoint."});
return shared_from_this();
}

std::shared_ptr<BaseController> register_routes(khttpd::framework::WebsocketRouter& router) override
{
std::shared_ptr<BaseController> register_routes(
khttpd::framework::WebsocketRouter& router) override {
// KHTTPD_WSROUTE("/hellows", onopen, onmessage, onclose, onerror);
router.add_handler("/hellows",
bind_handler(&std::decay_t<decltype(*this)>::onopen),
Expand All @@ -31,37 +36,33 @@ class HelloWsController : public khttpd::framework::BaseController<HelloWsContro
return shared_from_this();
}

private:
void handle_hello(khttpd::framework::HttpContext& ctx)
{
private:
void handle_hello(khttpd::framework::HttpContext& ctx) {
ctx.set_status(boost::beast::http::status::upgrade_required);
ctx.set_header(boost::beast::http::field::upgrade, "websocket");
ctx.set_content_type("text/html");
ctx.set_body(
"<h1>WebSocket Echo Endpoint</h1><p>This is a WebSocket endpoint. Please use a WebSocket client to connect.</p>");
"<h1>WebSocket Echo Endpoint</h1><p>This is a WebSocket endpoint. "
"Please use a WebSocket client to connect.</p>");
}

void onopen(khttpd::framework::WebsocketContext& ctx)
{
void onopen(khttpd::framework::WebsocketContext& ctx) {
spdlog::info("[WS: {}] Connection opened.", ctx.path);
ctx.send("Welcome to the echo service!");
}

void onclose(khttpd::framework::WebsocketContext& ctx)
{
void onclose(khttpd::framework::WebsocketContext& ctx) {
spdlog::info("[WS: {}] Connection closed.", ctx.path);
}

void onerror(khttpd::framework::WebsocketContext& ctx)
{
void onerror(khttpd::framework::WebsocketContext& ctx) {
spdlog::error("[WS: {}] Error: {}", ctx.path, ctx.error_code.message());
}

void onmessage(khttpd::framework::WebsocketContext& ctx)
{
void onmessage(khttpd::framework::WebsocketContext& ctx) {
spdlog::info("[WS: {}] Received: {}", ctx.path, ctx.message);
ctx.send("Echo: " + ctx.message, ctx.is_text);
}
};

#endif //HELLOWSCONTROLLER_HPP
#endif // HELLOWSCONTROLLER_HPP
2 changes: 1 addition & 1 deletion example/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ bazel_dep(name = "boost", version = "1.90.0.bcr.1")
bazel_dep(name = "boost.asio", version = "1.90.0.bcr.1")
bazel_dep(name = "boost.mysql", version = "1.90.0.bcr.1")
bazel_dep(name = "spdlog", version = "1.17.0")
bazel_dep(name = "khttpd", version = "0.5.0")
bazel_dep(name = "khttpd", version = "0.5.1")
local_path_override(
module_name = "khttpd",
path = "..",
Expand Down
76 changes: 39 additions & 37 deletions example/SseDemo.hpp
Original file line number Diff line number Diff line change
@@ -1,45 +1,48 @@
#ifndef KHTTPD_EXAMPLE_SSE_DEMO_HPP
#define KHTTPD_EXAMPLE_SSE_DEMO_HPP

#include <fmt/format.h>

#include <boost/asio/steady_timer.hpp>
#include <chrono>
#include <memory>
#include <optional>

#include <fmt/format.h>

#include "io_context_pool.hpp"
#include "router/http_router.hpp"
#include "sse/sse_session.hpp"

class SseDemo
{
public:
static void register_routes(khttpd::framework::HttpRouter& router)
{
router.get("/events-demo", [](khttpd::framework::HttpContext& ctx)
{
ctx.set_status(boost::beast::http::status::ok);
ctx.set_content_type("text/html; charset=utf-8");
ctx.set_body(demo_page());
}, {"SSE browser demo", "Displays connection state and events received from the example SSE stream."});
class SseDemo {
public:
static void register_routes(khttpd::framework::HttpRouter& router) {
router.get("/events-demo",
[](khttpd::framework::HttpContext& ctx) {
ctx.set_status(boost::beast::http::status::ok);
ctx.set_content_type("text/html; charset=utf-8");
ctx.set_body(demo_page());
},
{"SSE browser demo",
"Displays connection state and events received from the "
"example SSE stream."});

router.sse("/events", [](khttpd::framework::HttpContext&,
std::shared_ptr<khttpd::framework::sse::SseSession> session)
{
auto state = std::make_shared<State>(khttpd::framework::IoContextPool::instance().get_io_context());
session->on_close([timer = state->timer](boost::system::error_code)
{
timer->cancel();
});
session->send({"welcome", R"({"message":"Connected to the khttpd SSE demo"})", "1", 1000});
publish_tick(state, session);
});
router.sse(
"/events",
[](khttpd::framework::HttpContext&,
std::shared_ptr<khttpd::framework::sse::SseSession> session) {
auto state = std::make_shared<State>(
khttpd::framework::IoContextPool::instance().get_io_context());
session->on_close([timer = state->timer](boost::system::error_code) {
timer->cancel();
});
session->send({"welcome",
R"({"message":"Connected to the khttpd SSE demo"})",
"1", 1000});
publish_tick(state, session);
});
}

private:
static const char* demo_page()
{
private:
static const char* demo_page() {
return R"HTML(<!doctype html>
<html lang="en">
<head>
Expand Down Expand Up @@ -166,27 +169,26 @@ class SseDemo
</html>)HTML";
}

struct State
{
struct State {
explicit State(boost::asio::io_context& ioc)
: timer(std::make_shared<boost::asio::steady_timer>(ioc)) {}
: timer(std::make_shared<boost::asio::steady_timer>(ioc)) {}

std::shared_ptr<boost::asio::steady_timer> timer;
unsigned int sequence = 1;
};

static void publish_tick(const std::shared_ptr<State>& state,
const std::shared_ptr<khttpd::framework::sse::SseSession>& session)
{
static void publish_tick(
const std::shared_ptr<State>& state,
const std::shared_ptr<khttpd::framework::sse::SseSession>& session) {
if (!session->is_open()) return;
if (!session->send({"tick", fmt::format(R"({{"sequence":{}}})", state->sequence++), "", std::nullopt}))
{
if (!session->send({"tick",
fmt::format(R"({{"sequence":{}}})", state->sequence++),
"", std::nullopt})) {
session->close();
return;
}
state->timer->expires_after(std::chrono::seconds(1));
state->timer->async_wait([state, session](boost::system::error_code ec)
{
state->timer->async_wait([state, session](boost::system::error_code ec) {
if (!ec) publish_tick(state, session);
});
}
Expand Down
Loading
Loading