Search code examples
c++httphttpserver

When trying to reuse an HTTP connection to process the Keep-Alive header, recv is blocked by reading the second request from the client


Writing a small http server, I have to handle "Connection: keep-alive" header. Before that I used the standard request processing model: one connection is opened for one request, then processed, a response is sent, and then the connection is closed. But “Keep Alive” allows you to re-use the connection. The question is, how can I do that? What algorithm should I use?

I tried to do it like this: opening a connection using accept -> reading data from the client socket using recv -> processing a request -> sending a response using send. This loop continues until recv returns a value = 0, and on exiting the loop the connection is closed. But the problem is that on the second iteration of the loop, after processing the first request, recv is blocked. Please tell me at what step I was wrong.

    for(;;)
        {
          client *current = client::listen_port(cone.get_socket());//called here to accept
          httpHandler worker(current);//this class handles requests, we pass a pointer to the class object in it, which contains information about the client
          for(;;)
          {
              httpParser* temp = new httpParser(current->get_client());// recv is called and the httpParser class parses the request
              if (temp->get_recvByte() > 0)
                worker.handle(temp);//if recv returned something, we process the request and respond to it
              if (temp->get_recvByte() == 0)
                  break;
              if (temp->get_recvByte())
                  std::cout << "error";
              delete temp;
          }
        }

this constructor forms the header

   heading::heading(const int content_size, const std::string &file)
{
    head = "HTTP/1.1 200 OK\r\n";

    std::string Content_Type, Content_Length;
    std::string extension = file.substr(file.find(".") + 1);

    if (extension == "png" || extension == "gif")
        Content_Type = "Content-Type: image/apng\r\n";
    else if(extension == "jpg")
        Content_Type = "Content-Type: image/jpeg\r\n";
    else
        Content_Type = "Content-Type: text/html\r\n";
    Content_Length = "Content-Lenght: " + std::to_string(content_size) + "\r\n";
    head = head + "Server: Cone \r\n" + Content_Type + Content_Length + "Connection: keep-alive\r\n\r\n";
}

processing function

void httpHandler::handle(httpParser *temp)
{
    parser = temp;
    if (parser->get_type() == HEAD)
    {
        heading head;
        send(newclient->get_client(), head.get_head().c_str(), head.get_head().length(), 0);
        return;
    }
    if (parser->get_type() == UNKNOWN)
        send(newclient->get_client(), heading::error404().c_str(), heading::error404().length(), 0);

    if (!parser->get_dynamic())
        static_handle();

    else
        dynamic_handle();
    parser = nullptr;
}

static content processing

void httpHandler::static_handle()
{
    std::string buffer; 
    std::ifstream file(getenv("path") + parser->get_file(), std::ifstream::binary); 
    if (file)
     {
         auto const size{file.tellg()}; 
         file.seekg(0); 
         char current;
         buffer.reserve(size);
         while(file.get(current)) //читаем файл
             buffer.push_back(current);
         heading head(buffer.length(), parser->get_file());
         buffer = head.get_head() + buffer;  заголовок к буферу
         send(newclient->get_client(), buffer.c_str(), buffer.length(), 0); 
      }
      else
         send(newclient->get_client(), heading::error404().c_str(), heading::error404().length(), 0);

     file.close();
}

parser

httpParser::httpParser(int client_d)
{
    char buffer[req_buff_size];
    std::cout << "Calling recv() on socket " << client_d << std::endl;
    recvByte = recv(client_d, buffer,req_buff_size - 1, 0); // получаем сообщение из сокета
    if (recvByte > 0)
    {
        buffer[recvByte] = '\0';
        reqest = buffer;
        std::cout << reqest <<std::endl; // для тестов
        if (recvByte < 0)
           throw std::invalid_argument( "error recv\n" );

        if (reqest.find("GET") == 0)
        {
          type = GET;
          GET_parse();
        }
          else if (reqest.find("POST") == 0)
        {
          type = POST;
          POST_Parse();
        }
        else if (reqest.find("HEAD") == 0)
          type = HEAD;
        else
          throw std::invalid_argument( "invalid REQ\n" );

        requestedFile();
    }
}

connection closes in client destructor


Solution

  • You have:

    "Connection: keep-alive\n\n";
    

    Should be:

    "Connection: keep-alive\r\n";
    

    Similarly, this doesn't look correct either with the forward slashes

    "Server: Cone /r/n"
    

    Should be:

    "Server: Cone\r\n"