Skip to content

Commit 2a3110c

Browse files
committed
implemented server runtime
1 parent 26b833c commit 2a3110c

14 files changed

Lines changed: 328 additions & 33 deletions

File tree

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@ add_subdirectory(src)
3232
if(BUILD_TESTING)
3333
enable_testing()
3434
add_subdirectory(tests)
35+
endif()
36+
37+
if(BUILD_EXAMPLE)
38+
add_subdirectory(example)
3539
endif()

example/CMakeLists.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
set(HYPERPAGE_ARCHIVE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
3+
set(HYPERPAGE_ARCHIVE_PATH ${CMAKE_CURRENT_BINARY_DIR}/hyperpage.db)
4+
5+
file(GLOB_RECURSE HYPERPAGE_FRONTEND_SOURCES CONFIGURE_DEPENDS
6+
${CMAKE_CURRENT_SOURCE_DIR}/frontend/*)
7+
8+
add_custom_command(
9+
OUTPUT ${HYPERPAGE_ARCHIVE_PATH}
10+
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}
11+
COMMAND hyperpage::hyperpack --output ${HYPERPAGE_ARCHIVE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/frontend
12+
DEPENDS ${HYPERPAGE_FRONTEND_SOURCES}
13+
COMMENT "Bundling example frontend with hyperpack"
14+
VERBATIM
15+
)
16+
17+
add_custom_target(example_frontend_bundle ALL
18+
DEPENDS ${HYPERPAGE_ARCHIVE_PATH})
19+
20+
configure_file(
21+
${CMAKE_CURRENT_SOURCE_DIR}/archive.hpp.in
22+
${CMAKE_CURRENT_BINARY_DIR}/archive.hpp
23+
@ONLY
24+
)
25+

example/archive.hpp.in

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef ARCHIVE_HPP
2+
#define ARCHIVE_HPP
3+
4+
#include <hyperpage.hpp>
5+
6+
constexpr const char* ARCHIVE_DIRECTORY = "@HYPERPAGE_ARCHIVE_DIRECTORY@";
7+
8+
9+
#endif

example/frontend/index.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>WebFrame</title>
7+
</head>
8+
<body>
9+
<header>
10+
<h1>WebFrame</h1>
11+
</header>
12+
13+
<footer>
14+
<p>&copy; 2026 Maxtek Consulting LLC. All rights reserved.</p>
15+
</footer>
16+
</body>
17+
</html>

example/main.cpp

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,62 @@
11
#include <webframe.hpp>
22

3-
#include <hyperpage.hpp>
3+
#include <archive.hpp>
4+
5+
static std::string get_archive_path()
6+
{
7+
std::string result(ARCHIVE_DIRECTORY);
8+
#ifdef _WIN32
9+
std::replace(result.begin(), result.end(), '/', '\\');
10+
result += "\\";
11+
#else
12+
result += "/";
13+
#endif
14+
result += "hyperpage.db";
15+
return result;
16+
}
417

518
class archive_handler : public webframe::handler
619
{
720
public:
8-
archive_handler(const std::string& archive_path)
21+
archive_handler() = default;
22+
~archive_handler() = default;
23+
void open(const std::string& archive_path)
924
{
10-
_archive = std::make_unique<hyperpage::reader>(archive_path);
25+
_archive.reset(new hyperpage::reader(archive_path));
1126
}
1227
protected:
1328
void handle_get(const webframe::request* req, webframe::response* res) override
1429
{
15-
std::string path = req->get_path();
16-
auto page = _archive->load(path);
30+
std::string page_path = req->get_path();
31+
auto page = _archive->load(page_path);
1732
if (page) {
1833
res->set_status(200);
1934
res->set_header("Content-Type", page->get_mime_type());
2035
res->set_body(page->get_content(), page->get_length());
21-
}
22-
else
23-
{
24-
throw webframe::exception::not_found;
36+
}
37+
else {
38+
res->set_status(404);
39+
res->set_header("Content-Type", "text/plain");
40+
const std::string not_found_msg = "404 Not Found";
41+
res->set_body(reinterpret_cast<const uint8_t*>(not_found_msg.data()), not_found_msg.size());
2542
}
2643
}
2744
private:
2845
std::unique_ptr<hyperpage::reader> _archive;
2946
};
3047

31-
class greeting_handler : public webframe::handler
32-
{
33-
protected:
34-
void handle_post(const webframe::request* req, webframe::response* res) override
35-
{
36-
37-
}
38-
};
3948

4049
class example_application : public webframe::application
4150
{
4251
public:
43-
void configure_router(webframe::router* router) override
52+
void configure_router(webframe::router *router) override
4453
{
45-
_archive_handler = std::make_unique<archive_handler>("path/to/archive");
46-
router->set_default(_archive_handler.get());
54+
_archive_handler.open(get_archive_path());
55+
router->set_default(&_archive_handler);
4756
}
4857
private:
49-
std::unique_ptr<archive_handler> _archive_handler;
58+
archive_handler _archive_handler;
5059
};
5160

52-
WEBFRAME_MAIN(example_application)
61+
62+
WEBFRAME_MAIN(example_application)

include/webframe.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ namespace webframe
4040
{
4141
enum class method
4242
{
43-
GET,
44-
POST,
45-
PUT,
46-
DELETE
43+
http_get,
44+
http_post,
45+
http_put,
46+
http_delete
4747
};
4848

4949
class request

src/CMakeLists.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@ set(WEBFRAME_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include)
33

44
if(BUILD_RUNTIME)
55
message(STATUS "Building desktop runtime")
6-
message(STATUS "Building browser runtime")
6+
file(GLOB WEBFRAME_SERVER_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/runtimes/server/*.cpp)
7+
webframe_add_runtime(NAME server
8+
SOURCES ${WEBFRAME_SOURCES} ${WEBFRAME_SERVER_SOURCES}
9+
PUBLIC_INCLUDE_DIRS ${WEBFRAME_INCLUDE_DIR}
10+
PRIVATE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/runtimes/server
11+
PUBLIC_LINK_LIBRARIES libevent::core)
712
endif()
813

914
if(BUILD_TESTING)
1015
file(GLOB WEBFRAME_MOCK_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/runtimes/mock/*.cpp)
1116
webframe_add_runtime(NAME mock
1217
SOURCES ${WEBFRAME_SOURCES} ${WEBFRAME_MOCK_SOURCES}
13-
PUBLIC_INCLUDE_DIRS ${WEBFRAME_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/runtimes/mock)
18+
PUBLIC_INCLUDE_DIRS ${WEBFRAME_INCLUDE_DIR}
19+
PRIVATE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/runtimes/mock)
1420
endif()

src/handler.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ namespace webframe
88
{
99
switch (req->get_method())
1010
{
11-
case method::GET:
11+
case method::http_get:
1212
handle_get(req, res);
1313
break;
14-
case method::POST:
14+
case method::http_post:
1515
handle_post(req, res);
1616
break;
17-
case method::PUT:
17+
case method::http_put:
1818
handle_put(req, res);
1919
break;
20-
case method::DELETE:
20+
case method::http_delete:
2121
handle_delete(req, res);
2222
break;
2323
default:

src/runtimes/browser/runtime.cpp

Whitespace-only changes.

src/runtimes/server/request.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include <server.hpp>
2+
3+
namespace webframe::server
4+
{
5+
request::request(struct evhttp_request *req) : _req(req)
6+
{
7+
_headers = evhttp_request_get_input_headers(_req);
8+
struct evbuffer *input_buffer = evhttp_request_get_input_buffer(_req);
9+
size_t body_size = evbuffer_get_length(input_buffer);
10+
_body.resize(body_size);
11+
evbuffer_copyout(input_buffer, _body.data(), body_size);
12+
}
13+
14+
webframe::method request::get_method() const
15+
{
16+
webframe::method method;
17+
switch(evhttp_request_get_command(_req))
18+
{
19+
case EVHTTP_REQ_GET:
20+
method = webframe::method::http_get;
21+
break;
22+
case EVHTTP_REQ_POST:
23+
method = webframe::method::http_post;
24+
break;
25+
case EVHTTP_REQ_PUT:
26+
method = webframe::method::http_put;
27+
break;
28+
case EVHTTP_REQ_DELETE:
29+
method = webframe::method::http_delete;
30+
break;
31+
default:
32+
method = static_cast<webframe::method>(-1);
33+
break;
34+
}
35+
return method;
36+
37+
}
38+
39+
std::string request::get_path() const
40+
{
41+
const evhttp_uri *uri = evhttp_request_get_evhttp_uri(_req);
42+
const char *path = evhttp_uri_get_path(uri);
43+
return path ? path : "";
44+
}
45+
46+
bool request::get_header(const std::string &key, std::string &value) const
47+
{
48+
const char *header_value = evhttp_find_header(_headers, key.c_str());
49+
bool result(false);
50+
if (header_value)
51+
{
52+
value = header_value;
53+
result = true;
54+
}
55+
return result;
56+
}
57+
58+
std::pair<const uint8_t *, size_t> request::get_body() const
59+
{
60+
return { _body.data(), _body.size() };
61+
}
62+
63+
void request::read_body(const std::function<void(const uint8_t *, size_t)> &callback) const
64+
{
65+
callback(_body.data(), _body.size());
66+
}
67+
68+
}

0 commit comments

Comments
 (0)