如何使用 C++ 和 Boost Asio 从 HTTP post 请求中获取键值
How to get key values from HTTP post request using C++ and Boost Asio
我接到了一项作业,其中涉及使用 C++ 和 Boost Asio 库编写 Web 服务器。
我已经建立了工作服务器,它可以使用一本名为 "Boost.Asio C++ Network Programming Cookbook" 的书将 html 文件发送回客户端浏览器,但我正在努力处理来自客户端的 POST 请求。
当客户端连接到服务器时,他们会得到一个简单的 HTML 表单,其中包含用于登录服务器的用户名和密码字段,然后使用 POST 将其发送到服务器请求。
我已经把接收到的POST请求的内容输出到控制台,可以看到所有的header信息,但是看不到表单数据。我已经使用 Wireshark 检查数据包,数据正在通过网络发送。
服务器正在接收数据作为 Boost Asio streambuf,我正在解析它以获取请求的 HTML 文件,方法是将其读入向量,然后获取相关元素,例如方法或目标。
对于在哪里可以找到有关如何解析表单数据的教程,有人有什么建议吗?
下面的代码是解析 POST 请求并根据请求内容处理响应的 cpp 文件的一部分。 '&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>() );
处理 POST 请求
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 << " \r\n";
oss << "Cache-Control: no-cache, private" << "\r\n";
oss << "Content-Type: text/html" << "\r\n";
oss << "Content-Length: " << content.size() << "\r\n";
oss << "\r\n\r\n";
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
您需要更具体地进行解析,而不是将每个空格分隔 "word" 放入向量中。
从这样的事情开始:
#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.1\r\n"
"User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)\r\n"
"Host: www.tutorialspoint.com\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: 49\r\n"
"Accept-Language: en-us\r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Connection: Keep-Alive\r\n"
"\r\n"
"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"
读取“Content-Length:”的简单 ASIO HTTP 解析器,然后继续使用它来读取非 header 部分的其余部分(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, "\r\n\r\n");
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;
}
我接到了一项作业,其中涉及使用 C++ 和 Boost Asio 库编写 Web 服务器。
我已经建立了工作服务器,它可以使用一本名为 "Boost.Asio C++ Network Programming Cookbook" 的书将 html 文件发送回客户端浏览器,但我正在努力处理来自客户端的 POST 请求。
当客户端连接到服务器时,他们会得到一个简单的 HTML 表单,其中包含用于登录服务器的用户名和密码字段,然后使用 POST 将其发送到服务器请求。
我已经把接收到的POST请求的内容输出到控制台,可以看到所有的header信息,但是看不到表单数据。我已经使用 Wireshark 检查数据包,数据正在通过网络发送。
服务器正在接收数据作为 Boost Asio streambuf,我正在解析它以获取请求的 HTML 文件,方法是将其读入向量,然后获取相关元素,例如方法或目标。
对于在哪里可以找到有关如何解析表单数据的教程,有人有什么建议吗?
下面的代码是解析 POST 请求并根据请求内容处理响应的 cpp 文件的一部分。 '&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>() );
处理 POST 请求
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 << " \r\n";
oss << "Cache-Control: no-cache, private" << "\r\n";
oss << "Content-Type: text/html" << "\r\n";
oss << "Content-Length: " << content.size() << "\r\n";
oss << "\r\n\r\n";
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
您需要更具体地进行解析,而不是将每个空格分隔 "word" 放入向量中。
从这样的事情开始:
#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.1\r\n"
"User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)\r\n"
"Host: www.tutorialspoint.com\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: 49\r\n"
"Accept-Language: en-us\r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Connection: Keep-Alive\r\n"
"\r\n"
"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"
读取“Content-Length:”的简单 ASIO HTTP 解析器,然后继续使用它来读取非 header 部分的其余部分(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, "\r\n\r\n");
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;
}