我正在開發一個 C 專案,在該專案中我偵聽套接字并根據我在 fds 上從客戶端收到的請求生成 HTTP 回應,簡而言之,我使用瀏覽器發送請求,最終得到原始請求,然后進行決議它并生成相應的http回應。
然而,在大型 POST 請求的情況下,通常發生的是我得到部分請求,所以在第一部分我通常只會找到第一行(版本/方法/uri),一些標題但沒有正文,我猜是應該以某種方式獲得身體的其余部分,但是我無法弄清楚兩件事,
首先,我怎么知道我收到的請求是部分還是僅從第一部分完成?我沒有得到任何與范圍有關的資訊,這是我的客戶向我發送 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 內容長度但主體大小不同,我需要生成 400 錯誤)
第二個問題是,假設我已經知道它是否是部分請求,我如何在將整個請求發送到我的決議器并生成回應之前將整個請求存盤在緩沖區中?這是我的接收功能(我已經知道客戶的 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),而且我通常只會在獲取大塊檔案或具有非常大主體的檔案上傳時遇到此問題。謝謝
PS:如果需要,我可以鏈接當前專案的 github repo 如果您想進一步查看代碼
uj5u.com熱心網友回復:
我怎么能從這個中找出是否收到了部分請求或只是一個錯誤的請求(如果請求說它具有 X 內容長度但主體大小不同,我需要生成 400 錯誤)
根據定義,主體大小是Content-Length欄位的大小。您之后收到的任何位元組都屬于下一個 HTTP 請求(請參閱HTTP 流水線)。如果Content-Length在合理的時間段內沒有收到位元組,則可以使服務器發出408 請求超時錯誤。
第二個問題是,假設我已經知道它是否是部分請求,我如何在將整個請求發送到我的決議器并生成回應之前將整個請求存盤在緩沖區中?這是我的接收功能(我已經知道客戶的 fd,所以我只是在上面接收
您發布的代碼至少存在以下問題:
- 你應該檢查的回傳值
recv來確定函式是成功還是失敗,如果失敗,你應該適當地處理錯誤。在您當前的代碼中,如果recv回傳值失敗-1,那么您將buffer越界寫入陣列,從而導致未定義的行為。 - 使用該行似乎不合適
if (buffer[ret-1] == '\n')。當您遇到 時,HTTP 請求頭將結束"\r\n\r\n",當您讀取Content-Length正文的位元組時,HTTP 請求正文將結束。header 和 body 的結尾不一定出現在讀取的資料的末尾recv,但也可以出現在中間。如果你想支持 HTTP 流水線,那么額外的資料應該由處理程式為下一個 HTTP 請求處理。如果您不想支持 HTTP 流水線,那么您可以簡單地丟棄額外的資料并Connection: close在 HTTP 回應標頭中使用。 - 您似乎在使用空終止字符來標記
recv. 但是,如果具有該值的位元組0是 HTTP 請求的一部分,這將不起作用。假設這樣的位元組不應該是 HTTP 請求頭的一部分可能是安全的,但假設這樣的位元組不會是 HTTP 請求正文的一部分可能是不安全的(例如,當使用帶有二進制的 POST 時資料)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/385060.html
