0

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
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
sam_sep1ol
  • 21
  • 2
  • Have you tried running your code line-by-line in a debugger while monitoring the control flow and the values of all variables, in order to determine in which line your program stops behaving as intended? If you did not try this, then you may want to read this: [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/q/25385173/12149471) You may also want to read this: [How to debug small programs?](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – Andreas Wenzel Jul 16 '23 at 19:10
  • You aren't actually reading anything. The problem does have something to do with `recv` call - the fact that there ain't no `recv` call. So of course `poll` keeps telling you that there's data available to read. – Igor Tandetnik Jul 17 '23 at 00:09

0 Answers0