i have tried asking chatGPT, reading documentation, etc for days but have been unsuccesful at finding the root of the problem (am relatively new to c++)
RELEVANT INFORMATION: mint linux, g++ compiler, firefox used for testing
webserver.hpp:
#ifndef WEBSERVER_OBJ
#define WEBSERVER_OBJ
#ifndef THREAD_AMT
#define THREAD_AMT 4
#endif
//amount of clients allowed to queue inside socket buffer
#ifndef SOCK_BACKLOG
#define SOCK_BACKLOG 256
#endif
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <sys/poll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <unordered_map>
#include <map>
#define uint_64 unsigned long
#define uint_32 unsigned int
std::vector<pollfd> clients;
constexpr int buffer_size = 8192;
constexpr int optval = 1;
typedef struct {
int socket_fd;
std::string method;
std::string path;
std::string connection;
std::unordered_map<std::string, std::string> cookies;
} headers;
typedef void (*route_item)(headers);
typedef struct {
const char *request;
int socket_fd;
} th_task;
// used by server_routes to compare string's in hashmap
struct const_str_cmp {
bool operator()(const char* str1, const char* str2) const {
return std::strcmp(str1, str2) < 0;
}
};
int th_route(pollfd socket_fd);
//it works aight, dont fckin touch it
class thread_pool {
public:
thread_pool(int thread_count) : thread_max(thread_count - 1), thread_running(true) {
for (int i = 0; i < thread_max; i++)
{
slave.push_back(std::thread(&thread_pool::worker, this));
}
}
unsigned int excv(pollfd fd) {
std::unique_lock<std::mutex> lock(mtx);
queue.push_back(fd);
lock.unlock();
cv.notify_one();
return queue.size();
}
~thread_pool() {
{
std::unique_lock<std::mutex> lock(mtx);
thread_running = false;
}
cv.notify_all();
for (std::thread& t : slave)
{
if (t.joinable())
t.join();
}
}
private:
std::vector<pollfd> queue;
std::vector<std::thread> slave;
int thread_max;
bool thread_running;
std::mutex mtx;
std::condition_variable cv;
void worker() {
while (thread_running)
{
std::cout << "threadpool" << std::endl;
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] { return !queue.empty() || !thread_running; });
if (!queue.empty())
{
pollfd tmp = queue.front();
queue.erase(queue.begin());
lock.unlock();
th_route(tmp);
}
}
}
};
headers parse_http(const char* request) {
std::cout << "parsing\n";
headers httpRequest;
std::string buffer(request);
// find method
int methodEnd = buffer.find(' ');
if (methodEnd != std::string::npos) {
httpRequest.method = buffer.substr(0, methodEnd);
}
// find path
int pathStart = methodEnd + 1;
int pathEnd = buffer.find(' ', pathStart);
if (pathEnd != std::string::npos)
httpRequest.path = buffer.substr(pathStart, pathEnd - pathStart);
// find cookies (if any)
int cookieStart = buffer.find("Cookie: ");
if (cookieStart != std::string::npos) {
cookieStart += 8; // Skip "Cookie: "
int cookieEnd = buffer.find("\r\n", cookieStart);
std::string cookies = buffer.substr(cookieStart, cookieEnd - cookieStart);
int separatorPos = cookies.find(';');
int keyValueSeparatorPos;
while (separatorPos != std::string::npos) {
std::string cookie = cookies.substr(0, separatorPos);
keyValueSeparatorPos = cookie.find('=');
if (keyValueSeparatorPos != std::string::npos) {
std::string key = cookie.substr(0, keyValueSeparatorPos);
std::string value = cookie.substr(keyValueSeparatorPos + 1);
httpRequest.cookies[key] = value;
}
cookies.erase(0, separatorPos + 1);
separatorPos = cookies.find(';');
}
keyValueSeparatorPos = cookies.find('=');
if (keyValueSeparatorPos != std::string::npos) {
std::string key = cookies.substr(0, keyValueSeparatorPos);
std::string value = cookies.substr(keyValueSeparatorPos + 1);
httpRequest.cookies[key] = value;
}
}
// find connection header
int connectionStart = buffer.find("Connection: ");
if (connectionStart != std::string::npos) {
connectionStart += 12; // Skip "Connection: "
int connectionEnd = buffer.find("\r\n", connectionStart);
httpRequest.connection = buffer.substr(connectionStart, connectionEnd - connectionStart);
}
return httpRequest;
}
// executed by thread pool, returns true if connection should be terminated
int th_route(pollfd socket_fd) {
std::cout << socket_fd.fd << std::endl;
socket_fd.revents == 0;
return 0;
}
namespace lib_http {
class app // yes this is copying py-flask design patterns
{
public:
app(const char *addr, int port): th_pool(THREAD_AMT) {
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
server.sin_port = htons(port);
server.sin_family = AF_INET;
if (strcmp(addr, "0.0.0.0") == 0)
server.sin_addr.s_addr = INADDR_ANY;
else
server.sin_addr.s_addr = inet_addr(addr);
server_size = sizeof(server);
std::cout << "port:" <<setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) << std::endl;
//connection needs to be properly terminated for this to work
if (bind(socket_fd, (struct sockaddr *)&server, sizeof(server)) == -1)
throw std::runtime_error("Unable to bind socket\n");
if (listen(socket_fd, SOCK_BACKLOG) == -1)
throw std::runtime_error("Unable to listen on socket");
std::cout << "Listening on port: " << port << std::endl;
}
int run() {
th_flag.store(true);
th_connection = std::thread(&lib_http::app::accept_handler, this);
th_packets = std::thread(&lib_http::app::packet_handler, this);
return 0;
}
void route(const char *Src, route_item Dst) {
server_routes[Src] = Dst;
}
void exit() { //close threads
th_flag.store(false);
if (th_connection.joinable())
th_connection.join();
if (th_packets.joinable())
th_packets.join();
}
private:
int socket_fd;
struct sockaddr_in server;
socklen_t server_size;
std::atomic<bool> th_flag;
thread_pool th_pool;
std::thread th_connection;
std::thread th_packets;
std::mutex mtx;
std::map<const char *, route_item, const_str_cmp> server_routes;
int accept_handler() {
std::cout << "Thread: (accept_handler) start\n";
while (th_flag.load()) {
int sock_buf;
if ((sock_buf = accept(socket_fd, (struct sockaddr*)&server, &server_size)) == -1)
continue;
std::cout << "Connection RECV: " << sock_buf << std::endl;
mtx.lock();
pollfd buffer = {sock_buf, POLLIN, 0};
clients.push_back(buffer);
mtx.unlock();
}
return 0;
}
int packet_handler() {
std::cout << "Thread: (packet_handler) start\n";
while (th_flag.load()) {
mtx.lock();
int res = poll(clients.data(), clients.size(), 5);
//std::cout << "poll: " << res << std::endl;
mtx.unlock();
if (res > 0) {
std::cout << "size result: " << res << " || " << clients.size() << std::endl;
for (int i = 0; i<clients.size(); i++) {
if (clients[i].revents != 0) {
th_pool.excv(clients[i]);
}
}
}
}
return 0;
}
};
//caller takes ownership
std::string fetch(const char *path) {
std::ifstream file(path, std::ios::in | std::ios::binary );
if (file.is_open()){
file.seekg(0, std::ios::end);
long size_fd = file.tellg();
file.seekg(0, std::ios::beg);
char *tmp = new char[size_fd];
std::memset(tmp, 0, size_fd);
file.read(tmp, size_fd);
file.close();
std::cout << "not empty\n";
std::string buffer(tmp);
delete[] tmp;
return buffer;
}
std::cout << "empty\n";
return nullptr;
}
}
#endif
main.cpp:
#include <iostream>
#include "webserver.hpp"
int main() {
lib_http::app server("127.0.0.1", 5555);
server.run();
sleep(10);
server.exit();
return 0;
}
when i run it and try to connect to it from firefox webbrowser it shows the terminal output infinately:
threadpool
threadpool
port:threadpool0
Listening on port: 5555
Thread: (accept_handler) start
Thread: (packet_handler) start
Connection RECV: 4
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
i believe that it has something to do with the nonblocking recv() call but i have been unsuccesful at finding the root of the problem, threadpool
threadpool
port:threadpool0
Listening on port: 5555
Thread: (accept_handler) start
Thread: (packet_handler) start
Connection RECV: 4
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4
threadpool
size result: 1 || 1
4