Files
http-router-cpp/router.cpp
2024-11-13 16:01:17 +01:00

120 lines
2.7 KiB
C++

#include "router.hpp"
#include "http.hpp"
#include "util.hpp"
#include <csignal>
#include <cstdint>
#include <iostream>
#include <memory>
#include <mutex>
#include <strings.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
using namespace http;
Router::Router(int port) {
m_socket = socket(AF_INET, SOCK_STREAM, 0);
m_address.sin_family = AF_INET;
m_address.sin_port = htons(port);
m_address.sin_addr.s_addr = INADDR_ANY;
}
int Router::start() {
int err = bind(m_socket, (struct sockaddr *)&m_address, sizeof(m_address));
if (err != 0)
return err;
start_thread_loop();
err = listen(m_socket, 5);
if (err != 0)
return err;
while (true) {
int client = accept(m_socket, nullptr, nullptr);
queue_client(client);
}
stop_thread_loop();
}
void Router::queue_client(int fd) {
{
std::unique_lock<std::mutex> lock(m_mutex);
m_clients.push(fd);
}
m_cond.notify_one();
}
void Router::start_thread_loop() {
const uint32_t numThreads = std::thread::hardware_concurrency();
for (uint32_t i = 0; i < numThreads; ++i) {
m_threads.emplace_back(std::thread(&Router::thread_loop, this));
}
}
void Router::stop_thread_loop() {
{
std::unique_lock<std::mutex> lock(m_mutex);
m_should_terminate = true;
}
m_cond.notify_all();
for (std::thread &active_thread : m_threads) {
active_thread.join();
}
m_threads.clear();
}
void Router::thread_loop() {
std::vector<std::byte> buffer(1024);
while (true) {
int client;
{
std::unique_lock<std::mutex> lock(m_mutex);
m_cond.wait(lock,
[this] { return !m_clients.empty() || m_should_terminate; });
if (m_should_terminate)
return;
client = m_clients.front();
m_clients.pop();
}
recv(client, buffer.data(), buffer.size(), 0);
Request req(buffer);
Response res = Route(req);
res.send(client);
shutdown(client, SHUT_WR);
while (recv(client, buffer.data(), buffer.size(), 0) > 0) {
std::cout << buffer.data() << std::endl;
}
close(client);
}
}
void Router::handle(std::string pathPattern,
std::function<void(Request, Response *)> func) {
auto route = split(pathPattern, " ");
// TODO: UNSAFE CHECK BOUNDS
auto tree = m_routes[route[0]];
if (!tree) {
tree = std::make_shared<Tree>(Tree(route[0]));
m_routes.insert_or_assign(route[0], tree);
}
tree->add_path(route[1], func);
}
Response Router::Route(Request req) {
auto tree = m_routes[req.method()];
auto route = tree->get(req.path.base());
if (!route.has_value()) {
return Response(StatusCode::NOT_FOUND);
}
Response res(StatusCode::OK);
route.value()(req, &res);
return res;
}