如何使用C++和Boost Asio从HTTP发布请求中获取键值



我接到了一项任务,涉及使用 c++ 和 Boost Asio 库编写 Web 服务器。

我已经有了工作服务器,可以使用一本名为"Boost.Asio C++ Network Programming Cookbook"的书将 html 文件发送回客户端浏览器,但我正在努力处理来自客户端的 POST 请求。

当客户端连接到服务器时,它们将获得简单的HTML表单,该表单由用户名和密码字段组成,用于登录服务器,然后使用POST请求将其发送到服务器。

我已将收到的 POST 请求的内容输出到控制台,我可以看到所有标头信息,但我看不到表单数据。我已经使用Wireshark来检查数据包,并且数据正在通过网络发送。

服务器正在接收数据作为 Boost Asio streambuf 并且我正在解析它以获取请求的 HTML 文件,方法是将其读取到向量中,然后获取相关元素,例如方法或目标。

有没有人对在哪里寻找有关如何解析表单数据的教程有任何建议?

下面的代码是 cpp 文件的一部分,该文件解析 POST 请求,并将根据请求的内容处理响应。 "&request"参数是 Boost Asio streambuf

我在网络编程方面的经验很少,如有任何建议,我将不胜感激!

用于分析请求的代码

// Prepare and return the response message.
// Parse the request from the client to find requested document 
std::istream buffer(&request);
std::vector<std::string> parsed((std::istream_iterator<std::string>(buffer)), std::istream_iterator<std::string>() );   

处理开机自检请求


else if (parsed.size() >= 3 && parsed[0] == "POST") {
htmlFile = "/files.html";
// Retrieve files from server file system. The second element in 'parsed' vector is file name
std::ifstream fileStream(".\directory" + htmlFile);
// If the file exists then iterate it and assign the value to the content string variable, else return 404.
if (fileStream.good()) {
std::string fileContents((std::istreambuf_iterator<char>(fileStream)), std::istreambuf_iterator<char>());
content = fileContents;
code = "200 ok";
}
else {
std::ifstream fileStream(".\directory\404.html");
std::string fileContents((std::istreambuf_iterator<char>(fileStream)), std::istreambuf_iterator<char>());
content = fileContents;
code = "404";
}// End of nested if-else statement 
}// End of else-if statement
else {
std::ifstream fileStream(".\directory\401.html");
std::string fileContents((std::istreambuf_iterator<char>(fileStream)), std::istreambuf_iterator<char>());
content = fileContents;
code = "401";
// Write bad request to log file for security audits if not "GET" request
logging.logAction("Illegal request by client IP " + m_sock->remote_endpoint().address().to_string());
}//End of if-else statement
std::ostringstream oss;
oss << "GET HTTP/1.1 " << code << " rn";
oss << "Cache-Control: no-cache, private" << "rn";
oss << "Content-Type: text/html" << "rn";
oss << "Content-Length: " << content.size() << "rn";
oss << "rnrn";
oss << content;
response = oss.str().c_str();

HTTP 是一个逐行协议。样品:https://www.tutorialspoint.com/http/http_requests.htm

POST /cgi-bin/process.cgi HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Host: www.tutorialspoint.com
Content-Type: application/x-www-form-urlencoded
Content-Length: length
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
licenseID=string&content=string&/paramsXML=string

您需要更具体地解析,而不是将每个空格分隔的"单词"放入向量中。

从这样开始:

住在科里鲁

#include <iostream>
#include <iomanip>
#include <boost/asio.hpp>
int main() {
boost::asio::streambuf request;
{
std::ostream sample(&request);
sample <<
"POST /cgi-bin/process.cgi HTTP/1.1rn"
"User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)rn"
"Host: www.tutorialspoint.comrn"
"Content-Type: application/x-www-form-urlencodedrn"
"Content-Length: 49rn"
"Accept-Language: en-usrn"
"Accept-Encoding: gzip, deflatern"
"Connection: Keep-Alivern"
"rn"
"licenseID=string&content=string&/paramsXML=string"
;
}
std::istream buffer(&request);
std::string line;
// parsing the headers
while (getline(buffer, line, 'n')) {
if (line.empty() || line == "r") {
break; // end of headers reached
}
if (line.back() == 'r') {
line.resize(line.size()-1);
}
// simply ignoring headers for now
std::cout << "Ignore header: " << std::quoted(line) << "n";
}
std::string const body(std::istreambuf_iterator<char>{buffer}, {});
std::cout << "Parsed content: " << std::quoted(body) << "n";
}

印刷

Ignore header: "POST /cgi-bin/process.cgi HTTP/1.1"
Ignore header: "User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)"
Ignore header: "Host: www.tutorialspoint.com"
Ignore header: "Content-Type: application/x-www-form-urlencoded"
Ignore header: "Content-Length: 49"
Ignore header: "Accept-Language: en-us"
Ignore header: "Accept-Encoding: gzip, deflate"
Ignore header: "Connection: Keep-Alive"
Parsed content: "licenseID=string&content=string&/paramsXML=string"

一个简单的ASIO HTTP解析器,读取"Content-Length:",然后继续使用它来读取非标头部分的其余部分(POST消息(

int http::parse(asio::ip::tcp::socket& sock, http_msg_t& http)
{
std::string line;
asio::streambuf buf;
std::stringstream ss;
asio::error_code error;
size_t content_size = 0;
size_t read_left = 0;
try
{
//read until end of HTTP header
//Note:: after a successful read_until operation, the streambuf may contain additional data 
//beyond the delimiter. An application will typically leave that data in the streambuf for a subsequent 
//read_until operation to examine.
asio::read_until(sock, buf, "rnrn");
std::istream stream(&buf);
while (std::getline(stream, line) && line != "r")
{
http.header.push_back(line);
}
//store method and url
line = http.header.at(0);
http.method = http::http_get_method(line);
http.url = http::http_get_url(line);
//find 'Content-Length'
for (int idx = 0; idx < http.header.size(); idx++)
{
line = http.header.at(idx);
if (line.find("Content-Length: ") != std::string::npos)
{
size_t start = line.find(":");
start += 2; //space
size_t end = line.find("r");
std::string s = line.substr(start, end - 1);
try
{
content_size = std::atoi(s.c_str());
}
catch (std::invalid_argument&)
{
events::log("invalid Content-Length");
return -1;
}
http.content_size = content_size;
}
}
if (http.content_size == 0)
{
//nothing to read; not a POST; must be an acknowledgement response 
return 0;
}
//read end of message left
//dump whatever content we already have
if (buf.size() > 0)
{
ss << &buf;
std::string s = ss.str();
read_left = content_size - s.size();
}
else
{
read_left = content_size;
}
//asio::read reads exact number of bytes
size_t recv = asio::read(sock, buf, asio::transfer_exactly(read_left));
ss << &buf;
}
catch (std::exception& e)
{
events::log(e.what());
return -1;
}
http.msg = ss.str();
return 0;
}

最新更新