如何知道http请求是否是部分的以及如何在生成响应c ++之前完全解析它

how to know if a http request is partial and how to fully parse it before generating a response c++

我正在开发一个 C++ 项目,我在该项目中侦听套接字并根据我在 fds 上从客户端收到的请求生成 HTTP 响应,简而言之,我使用我的浏览器发送请求,我最终得到了原始请求,我解析它并生成相应的 http 响应。

然而,在大 POST 请求的情况下,通常发生的情况是我收到部分请求,因此在第一部分我通常只会找到第一行 (version/method/uri),一些headers 但没有 body,我想我应该以某种方式得到 body 的其余部分,但是我无法弄清楚两件事,

首先,我如何知道我收到的请求是部分请求还是仅从第一部分完成的?我没有收到任何与范围相关的信息,这是我的客户向我发送 POST 请求时收到的第一部分。

POST / HTTP/1.1
Host: localhost:8081
Connection: keep-alive
Content-Length: 8535833
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Origin: http://127.0.0.1:8081
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryOs6fsdbaegBIumqh
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://127.0.0.1:8081/
Accept-Encoding: gzip, deflate, br
Accept-Language: fr,en-US;q=0.9,en;q=0.8

我怎样才能从中判断是收到部分请求还是收到错误请求(如果请求有 X content-length,我需要生成 400 错误但是 body 尺寸不同)

第二个问题是,假设我已经知道它是否是部分的,在将整个请求发送到我的解析器并生成响应之前,我如何继续将其存储在缓冲区中?这是我的接收函数(我已经知道客户的 fd,所以我只是接收它

void    Client::receive_request(void)
{
    char buffer[2024];
    int ret;

    ret = recv(_fd, buffer, 2024, 0);
    buffer[ret] = 0;
    _received_request += buffer;
    _bytes_request += ret;
    std::cout << "Raw Request:\n" << _received_request << std::endl;
    if (buffer[ret-1] == '\n')
    {
        _ready_request = true;
        _request.parse(_received_request, _server->get_config());
    }
}

下面是检查客户端是否尝试发送请求、解析并生成响应的代码

int Connections::check_clients() {
    int fd;

    for (std::vector<Client*>::iterator client = clients.begin(); 
    client != clients.end() && ready_fd != 0 ; client++)
    {
        fd = (*client)->get_fd();
        if (FD_ISSET(fd, &ready_rset))
        {
            ready_fd--;
            (*client)->receive_request();
            if ((*client)->request_is_ready())
            {   
                (*client)->wait_response();

                close(fd);
                FD_CLR(fd, &active_set);
                fd_list.remove(fd);
                max_fd = *std::max_element(fd_list.begin(), fd_list.end());
                free(*client);
                client = clients.erase(client);
            }
        }
    }
    return 0;
}

如您所见,我正在用 C++ (98) 编写所有代码,并且宁愿不要得到只是忽略我的问题并向我推荐不同技术或库的答案,除非它能帮助我理解哪里做错了以及如何做处理部分请求。

有关信息,我只处理 HTTP 1.1(仅 GET/POST/DELETE),我通常只会在获取大块文件或上传非常大 body 的文件时遇到此问题。谢谢

PS : 如果需要,我可以 link 如果您想进一步查看代码 github 当前项目的回购

how can i figure out just from this whether or not am getting a partial request or just a faulty request (I need to generate a 400 error in the case of a request that says it has X content-length but the body size is different)

根据定义,body 大小是 Content-Length 字段的大小。您之后收到的任何字节都属于下一个 HTTP 请求(请参阅 HTTP pipelining). If you do not receive Content-Length bytes within a reasonable time period, then you can make the server issue a 408 Request Timeout 错误。

second question is, suppose i already know whether or not its partial, how do i proceed with storing the entire request in a buffer before sending it to my parser and generating a response ? here's my reception function (i already know the client's fd, so i just recv on it

您发布的代码至少存在以下问题:

  1. 你应该检查recv的return值来判断函数是成功还是失败,如果失败,你应该适当地处理错误。在您当前的代码中,如果 recv 因 return 值 -1 而失败,那么您将越界写入数组 buffer,导致未定义的行为。
  2. 使用if (buffer[ret-1] == '\n')这行似乎不合适。 HTTP请求header遇到"\r\n\r\n"就结束,HTTP请求body读完body的Content-Length字节就结束. header和body的结尾不一定会出现在recv读取数据的末尾,也可以出现在中间。如果你想支持 HTTP 管道,那么额外的数据应该由下一个 HTTP 请求的处理程序处理。如果你不想支持 HTTP 管道,那么你可以简单地丢弃额外的数据并在 HTTP 响应 header.
  3. 中使用 Connection: close
  4. 您似乎在使用空终止符来标记 recv 读取的数据的结尾。但是,如果值为 0 的字节是 HTTP 请求的一部分,这将不起作用。假设这样一个字节不应该是 HTTP 请求的一部分可能是安全的 header,但是假设这样一个字节不会成为 HTTP 请求的一部分可能是不安全的 body (例如,将 POST 与二进制数据一起使用时)。