0

I'm trying to create an HTTP server using C++ 98.

The issue is that, every time I launch my server, I get the response, sending again the request from the same browser tab, and the browser keeps loading. In my kqueue, it seems like the block for checking the read event is not being executed on the second time.

This is my code:

void    webserv::Server::lunch(){

    this->kq.create_event(this->sock.getSocket(), EVFILT_READ);

    while(1)
        this->_lunch_worker();
}
    
void    webserv::Server::_lunch_worker(void)
{
    int     n_ev;
    int     accept_sock;
    int     address_size;
    char    buff[1000];
    webserv::Header header;

    // register the events in ev_list
    std::cout << GREEN << "---- WAITING FOR CONNECTION ----" << RESET << std::endl;
    n_ev = this->kq.get_event();
    for (int i = 0; i < n_ev; i++)
    {
        if (this->kq.get_fd(i) < 0)
            continue;
        if (this->kq.get_fd(i) == this->sock.getSocket())
        {
            std::cout << "--- RECEIVED NEW CONNECTION ---" << std::endl;
            address_size = sizeof(this->sock.getAddress());
            accept_sock = accept(
                    this->sock.getSocket(),
                    (struct sockaddr*)&this->sock.getAddress(),
                    (socklen_t *)&address_size
                    );
            this->sock.test_error(accept_sock);
            this->kq.create_event(accept_sock, EVFILT_READ);
            int flags;
            if ((flags = fcntl(accept_sock, F_GETFL, 0)) < 0) {
                perror("fcntl");
                close(accept_sock);
                close(this->sock.getSocket());
            }
            if (fcntl(accept_sock, F_SETFL, flags | O_NONBLOCK) < 0) {
                perror("fcntl");
                close(accept_sock);
                close(this->sock.getSocket());
            }
            this->kq.create_event(accept_sock, EVFILT_WRITE, EV_ADD | EV_ONESHOT);
        }
        else if (this->kq.is_read_available(i))
        {
            int bytes_read;
            std::cout << "START: is_read_available" << std::endl;
            if ((bytes_read = recv(this->kq.get_fd(i), buff, 999, 0)) > 0)
            {
            }
        }
        else if (this->kq.is_write_available(i))
        {
            std::string hello = "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 12\n\nHello world!";
            if (send(this->kq.get_fd(i), hello.c_str(), hello.length(), 0) != 0)
            {
                std::cout << "TEST2" << std::endl;
                this->kq.set_event(this->kq.get_fd(i), EVFILT_WRITE, EV_DELETE);
                this->kq.get_event_list()[i].ident = -1;
                close(this->kq.get_fd(i));
            }
            std::cout << "END: is_write_available" << std::endl;
        }
    }
}

And this is the kqueue class:

/************************ CONSTRUCTORS/DESTRUCTOR ************************/
webserv::Kqueue::Kqueue()
{
    this->_kq = kqueue();
    std::cout << "KQUEUE CREATED" << std::endl;
    this->test_error(this->_kq, "Creating Kqueue :");
    this->_n_ev = 0;
}

webserv::Kqueue::~Kqueue()
{
    close(this->_kq);
}
    
/************************ MEMBER FUNCTIONS ************************/
void    webserv::Kqueue::set_event(int fd, int filter, int flags, void *udata)
{
    EV_SET(&this->_ev_set, fd, filter, flags, 0, 0, udata);
}
    
void    webserv::Kqueue::add_event(void)
{
    int ret;

    ret = kevent(this->_kq, &this->_ev_set, 1, NULL, 0, NULL);
    this->test_error(ret, "Kqueue/add_even functions");
}
    
int     webserv::Kqueue::get_event(void)
{
    this->_n_ev = kevent(this->_kq, NULL, 0, this->_ev_list, __EV_LIST_SIZE__, NULL);
    this->test_error(this->_n_ev, "Kqueue/get_event function:");
    return (this->_n_ev);
}

void    webserv::Kqueue::create_event(int fd, int filter, int flags, void *udata)
{
    this->set_event(fd, filter, flags, udata);
    this->add_event();
}

bool    webserv::Kqueue::isEOF(int index)
{
    if (this->_n_ev <= index)
        this->test_error(-1, "Kqueue/isEOF function:");
    return (this->_ev_list[index].flags & EV_EOF);
}

bool    webserv::Kqueue::is_read_available(int index)
{
    if (this->_n_ev <= index)
        this->test_error(-1, "Kqueue/is_read_available function:");
    return (this->_ev_list[index].filter == EVFILT_READ);
}

bool    webserv::Kqueue::is_write_available(int index)
{
    if (this->_n_ev <= index)
        this->test_error(-1, "Kqueue/is_write_available function:");
    return (this->_ev_list[index].filter == EVFILT_WRITE);
}

void    webserv::Kqueue::test_error(int fd, const std::string &str)
{
    if (fd < 0)
    {
        std::cerr << RED << str << " ";
        perror("The following error occured: ");
        std::cerr << RESET;
        exit(EXIT_FAILURE);
    }
}

/************************ GETTERS/SETTERS ************************/
struct kevent   *webserv::Kqueue::get_event_list()
{
    return (this->_ev_list);
}

int             webserv::Kqueue::get_fd(int index)
{
    if (this->_n_ev <= index)
        this->test_error(-1, "Kqueue/get_ev_list function:");
    return (this->_ev_list[index].ident);
}

void    webserv::Kqueue::set_kqueue(int fd)
{
    this->_kq = fd;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
REVERSI
  • 59
  • 5
  • Lots of problems here. You aren't reading the entire HTTP request per response. You aren't handling the possible case of short sends. The line terminator in HTTP is defined as `\r\n`, not `\n`. If `bytes_read` is zero you must close the socket. You are printing ` "--- RECEIVED NEW CONNECTION ---"` *before* it actually happens. HTTP is not a trivial protocol: you need a good knowledge of RFC 2616 and successors. – user207421 May 16 '22 at 04:07
  • Could you please write this as an answer so I can put it as the best answer. – REVERSI May 16 '22 at 11:35
  • this is Linux? Linux doesn't have kqueue. And `strace` or wireshark might be useful debugging tools – user253751 May 16 '22 at 17:16

0 Answers0