Skip to content

Commit 91592b0

Browse files
committed
finished server runtime and example
1 parent 2cae9ac commit 91592b0

6 files changed

Lines changed: 125 additions & 3 deletions

File tree

example/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ configure_file(
2323
@ONLY
2424
)
2525

26+
webframe_add_application(NAME example
27+
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
28+
INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}
29+
LINK_LIBRARIES yyjson::yyjson hyperpage::hyperpage
30+
RUNTIME server)

example/frontend/form.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
async function handleNameSubmit(event) {
2+
event.preventDefault();
3+
4+
const nameInput = document.querySelector("#name");
5+
if (!nameInput) {
6+
return;
7+
}
8+
9+
try {
10+
const response = await fetch("/greetingIPC", {
11+
method: "POST",
12+
headers: {
13+
"Content-Type": "application/json"
14+
},
15+
body: JSON.stringify({
16+
name: nameInput.value
17+
})
18+
});
19+
20+
if (!response.ok) {
21+
throw new Error(`Request failed with status ${response.status}`);
22+
}
23+
24+
const responseText = await response.text();
25+
if (!responseText) {
26+
throw new Error("Endpoint returned an empty response body.");
27+
}
28+
29+
const responseBody = JSON.parse(responseText);
30+
if (typeof responseBody.greeting !== "string") {
31+
throw new Error("Endpoint response is missing the 'greeting' string.");
32+
}
33+
34+
alert(responseBody.greeting);
35+
}
36+
catch (error) {
37+
alert(error instanceof Error ? error.message : "Greeting request failed.");
38+
}
39+
}

example/frontend/index.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,19 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>WebFrame</title>
7+
<script src="form.js"></script>
78
</head>
89
<body>
910
<header>
1011
<h1>WebFrame</h1>
1112
</header>
1213

14+
<form id="name-form" onsubmit="handleNameSubmit(event)">
15+
<label for="name">Name</label>
16+
<input id="name" name="name" type="text" required>
17+
<button type="submit">Submit</button>
18+
</form>
19+
1320
<footer>
1421
<p>&copy; 2026 Maxtek Consulting LLC. All rights reserved.</p>
1522
</footer>

example/main.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include <archive.hpp>
44

5+
#include <yyjson.h>
6+
57
static std::string get_archive_path()
68
{
79
std::string result(ARCHIVE_DIRECTORY);
@@ -18,8 +20,6 @@ static std::string get_archive_path()
1820
class archive_handler : public webframe::handler
1921
{
2022
public:
21-
archive_handler() = default;
22-
~archive_handler() = default;
2323
void open(const std::string& archive_path)
2424
{
2525
_archive.reset(new hyperpage::reader(archive_path));
@@ -28,6 +28,9 @@ class archive_handler : public webframe::handler
2828
void handle_get(const webframe::request* req, webframe::response* res) override
2929
{
3030
std::string page_path = req->get_path();
31+
if(page_path.empty() || page_path == "/") {
32+
page_path = "/index.html";
33+
}
3134
auto page = _archive->load(page_path);
3235
if (page) {
3336
res->set_status(200);
@@ -45,6 +48,37 @@ class archive_handler : public webframe::handler
4548
std::unique_ptr<hyperpage::reader> _archive;
4649
};
4750

51+
class greeting_ipc_handler : public webframe::handler
52+
{
53+
protected:
54+
void handle_post(const webframe::request* req, webframe::response* res) override
55+
{
56+
std::unique_ptr<yyjson_doc, decltype(&yyjson_doc_free)> request_body(nullptr, &yyjson_doc_free);
57+
std::unique_ptr<yyjson_mut_doc, decltype(&yyjson_mut_doc_free)> response_body(nullptr, &yyjson_mut_doc_free);
58+
request_body.reset(yyjson_read(reinterpret_cast<const char*>(req->get_body().first), req->get_body().second, 0));
59+
if (!request_body || !yyjson_is_obj(request_body->root)) {
60+
throw webframe::exception::bad_request;
61+
}
62+
yyjson_val* name_val = yyjson_obj_get(request_body->root, "name");
63+
if (!name_val || !yyjson_is_str(name_val)) {
64+
throw webframe::exception::bad_request;
65+
}
66+
const char* name = yyjson_get_str(name_val);
67+
68+
response_body.reset(yyjson_mut_doc_new(nullptr));
69+
yyjson_mut_val* root = yyjson_mut_obj(response_body.get());
70+
const std::string greeting = "Hello, " + std::string(name) + "!";
71+
yyjson_mut_obj_add_strcpy(response_body.get(), root, "greeting", greeting.c_str());
72+
yyjson_mut_doc_set_root(response_body.get(), root);
73+
74+
size_t response_len;
75+
const char *response_data = yyjson_mut_write(response_body.get(), 0, &response_len);
76+
res->set_status(200);
77+
res->set_header("Content-Type", "application/json");
78+
res->set_body(reinterpret_cast<const uint8_t*>(response_data), response_len);
79+
free(const_cast<char *>(response_data));
80+
}
81+
};
4882

4983
class example_application : public webframe::application
5084
{
@@ -53,9 +87,11 @@ class example_application : public webframe::application
5387
{
5488
_archive_handler.open(get_archive_path());
5589
router->set_default(&_archive_handler);
90+
router->add_route("/greetingIPC", &_greeting_ipc_handler);
5691
}
5792
private:
5893
archive_handler _archive_handler;
94+
greeting_ipc_handler _greeting_ipc_handler;
5995
};
6096

6197

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ if(BUILD_RUNTIME)
88
SOURCES ${WEBFRAME_SOURCES} ${WEBFRAME_SERVER_SOURCES}
99
PUBLIC_INCLUDE_DIRS ${WEBFRAME_INCLUDE_DIR}
1010
PRIVATE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/runtimes/server
11-
PUBLIC_LINK_LIBRARIES libevent::core)
11+
PUBLIC_LINK_LIBRARIES libevent::core libevent::extra)
1212
endif()
1313

1414
if(BUILD_TESTING)

src/runtimes/server/runtime.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
1+
#ifdef _WIN32
2+
#include <winsock2.h>
3+
#endif
4+
15
#include <server.hpp>
26

7+
#include <stdexcept>
8+
9+
#ifdef _WIN32
10+
namespace
11+
{
12+
class winsock_session
13+
{
14+
public:
15+
winsock_session()
16+
{
17+
const int result = WSAStartup(MAKEWORD(2, 2), &_data);
18+
if (result != 0)
19+
{
20+
throw std::runtime_error("WSAStartup failed");
21+
}
22+
}
23+
24+
~winsock_session()
25+
{
26+
WSACleanup();
27+
}
28+
29+
private:
30+
WSADATA _data{};
31+
};
32+
}
33+
#endif
34+
335
static void evhttp_callback(struct evhttp_request *req, void *arg)
436
{
537
bool sent_response = false;
@@ -21,6 +53,9 @@ namespace webframe
2153
{
2254
int runtime::dispatch(int argc, const char **argv, webframe::application *a, webframe::router *r)
2355
{
56+
#ifdef _WIN32
57+
winsock_session winsock;
58+
#endif
2459
a->configure_server(argc, argv);
2560
a->configure_router(r);
2661
_event_base.reset(event_base_new());

0 commit comments

Comments
 (0)