Add correctly shutting down server and stopping threads
This commit is contained in:
5
Makefile
5
Makefile
@@ -22,7 +22,10 @@ fclean:
|
|||||||
|
|
||||||
re: fclean $(NAME)
|
re: fclean $(NAME)
|
||||||
|
|
||||||
run: all
|
build: all
|
||||||
|
g++ -std=c++20 -o server main.cpp -L. -lhttpablu
|
||||||
|
|
||||||
|
run: build
|
||||||
g++ -std=c++20 -o server main.cpp -L. -lhttpablu
|
g++ -std=c++20 -o server main.cpp -L. -lhttpablu
|
||||||
./server
|
./server
|
||||||
|
|
||||||
|
|||||||
22
main.cpp
22
main.cpp
@@ -1,18 +1,33 @@
|
|||||||
#include "http.hpp"
|
#include "http.hpp"
|
||||||
#include "router.hpp"
|
#include "router.hpp"
|
||||||
|
#include <csignal>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
using namespace http;
|
using namespace http;
|
||||||
|
|
||||||
void HelloWorld(Request req, Response *res) {
|
void hello_world(Request req, Response *res) {
|
||||||
res->set_payload("Hello World!");
|
res->set_payload("Hello World!");
|
||||||
res->set_content_type("text/plain");
|
res->set_content_type("text/plain");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Router router(8181);
|
||||||
|
|
||||||
|
void quit(int s) {
|
||||||
|
router.stop();
|
||||||
|
std::cout << "Stopping Server" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
Router router(8181);
|
struct sigaction sig_int_handler;
|
||||||
|
sig_int_handler.sa_handler = quit;
|
||||||
|
sigemptyset(&sig_int_handler.sa_mask);
|
||||||
|
sig_int_handler.sa_flags = 0;
|
||||||
|
|
||||||
|
sigaction(SIGINT, &sig_int_handler, nullptr);
|
||||||
|
|
||||||
// Allow all Methods
|
// Allow all Methods
|
||||||
router.handle("GET /helloWorld", HelloWorld);
|
router.handle("GET /helloWorld", hello_world);
|
||||||
router.handle("GET /healthz", [](Request req, Response *res) {
|
router.handle("GET /healthz", [](Request req, Response *res) {
|
||||||
res->set_status_code(StatusCode::OK);
|
res->set_status_code(StatusCode::OK);
|
||||||
res->set_payload(std::vector<std::byte>());
|
res->set_payload(std::vector<std::byte>());
|
||||||
@@ -38,6 +53,7 @@ int main() {
|
|||||||
res->set_content_type("text/plain");
|
res->set_content_type("text/plain");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
std::cout << "Starting Server" << std::endl;
|
||||||
router.start();
|
router.start();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
16
request.cpp
16
request.cpp
@@ -10,7 +10,7 @@ bool Request::protocol(std::stringstream *ss, int *procPart, char c) {
|
|||||||
m_method = ss->str();
|
m_method = ss->str();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_pathRaw = ss->str();
|
m_path_raw = ss->str();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
m_protocol = ss->str();
|
m_protocol = ss->str();
|
||||||
@@ -72,17 +72,17 @@ Request::Request(std::vector<std::byte> buf) : path("") {
|
|||||||
ss << c;
|
ss << c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
path = Path{m_pathRaw};
|
path = Path{m_path_raw};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::Print() {
|
void Request::print() {
|
||||||
std::cout << "Protocol: " << m_protocol << "\n"
|
std::cout << "Protocol: " << m_protocol << "\n"
|
||||||
<< "Req: " << m_method << " " << m_pathRaw << std::endl;
|
<< "Req: " << m_method << " " << m_path_raw << std::endl;
|
||||||
for (const auto &[key, value] : m_headers) {
|
for (const auto &[key, value] : m_headers) {
|
||||||
std::cout << "[" << key << "]: [" << value << "]\n";
|
std::cout << "[" << key << "]: [" << value << "]\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasData()) {
|
if (has_data()) {
|
||||||
std::cout << "Payload: " << std::endl;
|
std::cout << "Payload: " << std::endl;
|
||||||
for (auto &byte : m_payload) {
|
for (auto &byte : m_payload) {
|
||||||
std::cout << static_cast<char>(byte);
|
std::cout << static_cast<char>(byte);
|
||||||
@@ -91,6 +91,6 @@ void Request::Print() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Request::Method() { return m_method; }
|
std::string Request::method() { return m_method; }
|
||||||
bool Request::HasData() { return m_payload.size() != 0; }
|
bool Request::has_data() { return m_payload.size() != 0; }
|
||||||
std::vector<std::byte> Request::Data() { return m_payload; }
|
std::vector<std::byte> Request::data() { return m_payload; }
|
||||||
|
|||||||
24
response.cpp
24
response.cpp
@@ -8,26 +8,26 @@
|
|||||||
|
|
||||||
using namespace http;
|
using namespace http;
|
||||||
|
|
||||||
Response::Response(statuscode::statusCode statusCode) {
|
Response::Response(StatusCode::statusCode statusCode) {
|
||||||
m_statusCode = statusCode;
|
m_statusCode = statusCode;
|
||||||
SetHeader("Connection", "close");
|
set_header("Connection", "close");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::SetHeader(const std::string name, const std::string value) {
|
void Response::set_header(const std::string name, const std::string value) {
|
||||||
m_headers.insert_or_assign(name, value);
|
m_headers.insert_or_assign(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::SetPayload(const std::vector<std::byte> data) {
|
void Response::set_payload(const std::vector<std::byte> data) {
|
||||||
m_headers.insert(std::pair<std::string, std::string>(
|
m_headers.insert(std::pair<std::string, std::string>(
|
||||||
"Content-Length", std::to_string(data.size())));
|
"Content-Length", std::to_string(data.size())));
|
||||||
m_payload = data;
|
m_payload = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::SetStatusCode(statuscode::statusCode statuscode) {
|
void Response::set_status_code(StatusCode::statusCode statuscode) {
|
||||||
m_statusCode = statuscode;
|
m_statusCode = statuscode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::SetPayload(const std::string data) {
|
void Response::set_payload(const std::string data) {
|
||||||
m_headers.insert(std::pair<std::string, std::string>(
|
m_headers.insert(std::pair<std::string, std::string>(
|
||||||
"Content-Length", std::to_string(std::strlen(data.data()))));
|
"Content-Length", std::to_string(std::strlen(data.data()))));
|
||||||
|
|
||||||
@@ -36,14 +36,14 @@ void Response::SetPayload(const std::string data) {
|
|||||||
[](char c) { return std::byte(c); });
|
[](char c) { return std::byte(c); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::SetContentType(const std::string type) {
|
void Response::set_content_type(const std::string type) {
|
||||||
m_headers.insert_or_assign("Content-Type", type);
|
m_headers.insert_or_assign("Content-Type", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::Send(int clientSocket) {
|
std::string Response::compile() {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "HTTP/1.1 " << m_statusCode << " "
|
ss << "HTTP/1.1 " << m_statusCode << " "
|
||||||
<< statuscode::StatusCodeString(m_statusCode) << "\n";
|
<< StatusCode::status_code_string(m_statusCode) << "\n";
|
||||||
for (const auto &[key, value] : m_headers) {
|
for (const auto &[key, value] : m_headers) {
|
||||||
ss << key << ": " << value << "\n";
|
ss << key << ": " << value << "\n";
|
||||||
}
|
}
|
||||||
@@ -54,11 +54,11 @@ void Response::Send(int clientSocket) {
|
|||||||
ss << c;
|
ss << c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::string p = ss.str();
|
return ss.str();
|
||||||
send(clientSocket, p.data(), p.size(), 0);
|
// ::send(clientSocket, p.data(), p.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::Print() {
|
void Response::print() {
|
||||||
for (const auto &[key, value] : m_headers) {
|
for (const auto &[key, value] : m_headers) {
|
||||||
std::cout << "[" << key << "]: [" << value << "]\n";
|
std::cout << "[" << key << "]: [" << value << "]\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public:
|
|||||||
void set_payload(const std::string data);
|
void set_payload(const std::string data);
|
||||||
void set_content_type(const std::string type);
|
void set_content_type(const std::string type);
|
||||||
void set_status_code(const StatusCode::statusCode status_code);
|
void set_status_code(const StatusCode::statusCode status_code);
|
||||||
void send(int client_socket);
|
std::string compile();
|
||||||
void print();
|
void print();
|
||||||
};
|
};
|
||||||
} // namespace http
|
} // namespace http
|
||||||
|
|||||||
10
router.cpp
10
router.cpp
@@ -18,9 +18,11 @@ Router::Router(int port) {
|
|||||||
m_address.sin_family = AF_INET;
|
m_address.sin_family = AF_INET;
|
||||||
m_address.sin_port = htons(port);
|
m_address.sin_port = htons(port);
|
||||||
m_address.sin_addr.s_addr = INADDR_ANY;
|
m_address.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
m_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Router::start() {
|
int Router::start() {
|
||||||
|
m_running = true;
|
||||||
int err = bind(m_socket, (struct sockaddr *)&m_address, sizeof(m_address));
|
int err = bind(m_socket, (struct sockaddr *)&m_address, sizeof(m_address));
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
return err;
|
return err;
|
||||||
@@ -31,14 +33,17 @@ int Router::start() {
|
|||||||
if (err != 0)
|
if (err != 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
while (true) {
|
while (m_running.load()) {
|
||||||
int client = accept(m_socket, nullptr, nullptr);
|
int client = accept(m_socket, nullptr, nullptr);
|
||||||
queue_client(client);
|
queue_client(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_thread_loop();
|
stop_thread_loop();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Router::stop() { m_running.store(false); }
|
||||||
|
|
||||||
void Router::queue_client(int fd) {
|
void Router::queue_client(int fd) {
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(m_mutex);
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
@@ -83,7 +88,8 @@ void Router::thread_loop() {
|
|||||||
recv(client, buffer.data(), buffer.size(), 0);
|
recv(client, buffer.data(), buffer.size(), 0);
|
||||||
Request req(buffer);
|
Request req(buffer);
|
||||||
Response res = Route(req);
|
Response res = Route(req);
|
||||||
res.send(client);
|
auto payload = res.compile();
|
||||||
|
send(client, payload.data(), payload.size(), 0);
|
||||||
|
|
||||||
shutdown(client, SHUT_WR);
|
shutdown(client, SHUT_WR);
|
||||||
while (recv(client, buffer.data(), buffer.size(), 0) > 0) {
|
while (recv(client, buffer.data(), buffer.size(), 0) > 0) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ private:
|
|||||||
int m_socket;
|
int m_socket;
|
||||||
sockaddr_in m_address;
|
sockaddr_in m_address;
|
||||||
Response Route(Request req);
|
Response Route(Request req);
|
||||||
|
std::atomic<bool> m_running;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
@@ -36,7 +37,7 @@ public:
|
|||||||
void handle(std::string path_pattern,
|
void handle(std::string path_pattern,
|
||||||
std::function<void(Request, Response *)> func);
|
std::function<void(Request, Response *)> func);
|
||||||
int start();
|
int start();
|
||||||
int stop();
|
void stop();
|
||||||
};
|
};
|
||||||
} // namespace http
|
} // namespace http
|
||||||
|
|
||||||
|
|||||||
14
tree.cpp
14
tree.cpp
@@ -25,7 +25,7 @@ Node::Node(std::string sub, bool isValue,
|
|||||||
|
|
||||||
Tree::Tree(std::string method) { m_method = method; }
|
Tree::Tree(std::string method) { m_method = method; }
|
||||||
|
|
||||||
void addNode(std::shared_ptr<Node> const &parent, std::string path,
|
void add_node(std::shared_ptr<Node> const &parent, std::string path,
|
||||||
std::vector<std::string> rest,
|
std::vector<std::string> rest,
|
||||||
std::function<void(Request, Response *)> func) {
|
std::function<void(Request, Response *)> func) {
|
||||||
std::shared_ptr<Node> curr = parent->m_next[path];
|
std::shared_ptr<Node> curr = parent->m_next[path];
|
||||||
@@ -45,13 +45,13 @@ void addNode(std::shared_ptr<Node> const &parent, std::string path,
|
|||||||
auto newPath = rest.front();
|
auto newPath = rest.front();
|
||||||
// Ineffiecient, use deque
|
// Ineffiecient, use deque
|
||||||
rest.erase(rest.begin());
|
rest.erase(rest.begin());
|
||||||
addNode(curr, newPath, rest, func);
|
add_node(curr, newPath, rest, func);
|
||||||
} else {
|
} else {
|
||||||
auto newPath = rest.front();
|
auto newPath = rest.front();
|
||||||
rest.erase(rest.begin());
|
rest.erase(rest.begin());
|
||||||
std::shared_ptr<Node> leaf = std::make_shared<Node>(Node{path});
|
std::shared_ptr<Node> leaf = std::make_shared<Node>(Node{path});
|
||||||
parent->m_next.insert_or_assign(path, leaf);
|
parent->m_next.insert_or_assign(path, leaf);
|
||||||
addNode(leaf, newPath, rest, func);
|
add_node(leaf, newPath, rest, func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,10 +71,10 @@ void Tree::add_path(std::string path,
|
|||||||
|
|
||||||
auto newPath = subPaths.front();
|
auto newPath = subPaths.front();
|
||||||
subPaths.erase(subPaths.begin());
|
subPaths.erase(subPaths.begin());
|
||||||
addNode(m_root, newPath, subPaths, func);
|
add_node(m_root, newPath, subPaths, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
void printNode(std::shared_ptr<Node> node, size_t depth, size_t max_depth) {
|
void print_node(std::shared_ptr<Node> node, size_t depth, size_t max_depth) {
|
||||||
if (depth >= max_depth) {
|
if (depth >= max_depth) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ void printNode(std::shared_ptr<Node> node, size_t depth, size_t max_depth) {
|
|||||||
std::cout << std::string(depth, ' ') << "sub: \"" << node->m_sub_path
|
std::cout << std::string(depth, ' ') << "sub: \"" << node->m_sub_path
|
||||||
<< "\" IsDummy: " << node->m_is_dummy << std::endl;
|
<< "\" IsDummy: " << node->m_is_dummy << std::endl;
|
||||||
for (auto &next : node->m_next) {
|
for (auto &next : node->m_next) {
|
||||||
printNode(next.second, depth + 1, max_depth);
|
print_node(next.second, depth + 1, max_depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,4 +123,4 @@ Tree::get(std::string path) {
|
|||||||
return traverse(m_root, newPath, subs);
|
return traverse(m_root, newPath, subs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tree::debug_Print() { printNode(m_root, 0, 10); }
|
void Tree::debug_Print() { print_node(m_root, 0, 10); }
|
||||||
|
|||||||
Reference in New Issue
Block a user