参考源代码:
https://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/examples/cpp11_examples.html
以上链接里面的源代码至少有下面两个问题:
1.只解析了http协议的头部信息(header)。
2.没有对tcp数据包进行合并的处理,如果客户端的tcp数据是分包发过来的,该代码的解析会出错,所以这是不可预料的错误。
以下是修改后的部分代码:
1.在 request.hpp 中添加两个成员变量
std::string short_uri;//客户端请求的uri,去掉‘?’后面的参数
std::vector params;//客户端请求所带的参数,包括get和post表单里的参数
2.在 request_parser.cpp 里添加成员方法 parse_param ,用来获得表单提交的参数:
void request_parser::parse_param(request& req, std::string& data_)
{
//开始解析get参数
int index = req.uri.find_first_of("?");
if (index >= 0)
{
req.short_uri = req.uri.substr(0, index);
std::string param_str = req.uri.substr(index + 1, req.uri.size());
std::vector split_result;
boost::split(split_result, param_str, boost::is_any_of("&"));
for (int i = 0; i < split_result.size(); i++)
{
std::vector split_result_temp;
boost::split(split_result_temp, split_result.at(i), boost::is_any_of("="));
if (split_result_temp.size() >= 2)
{
req.params.push_back(header());
req.params.back().name = split_result_temp.at(0);
req.params.back().value = split_result_temp.at(1);
}
}
}
else
{
req.short_uri = req.uri;
}
//解析get参数结束
std::string content_type;
for (int i = 0; i < req.headers.size(); i++)
{
if (boost::algorithm::iequals(req.headers[i].name, "content_type"))
{
content_type = req.headers[i].value;
break;
}
}
int index_content_type = content_type.find_first_of(';');
if (index_content_type > 0)
{
content_type = content_type.substr(0, index_content_type);
}
//开始解析post参数
if (boost::algorithm::iequals(req.method, "post")
&& boost::algorithm::iequals(content_type, "multipart/form-data"))
{
const char* find_str = "\nContent-Disposition: form-data; name=\"";
int index = 0;
auto it = boost::algorithm::ifind_nth(data_, find_str, index);
while (!it.empty())
{
req.params.push_back(header());
auto it_temp = it.end();
while (it_temp != data_.end())
{
if (*it_temp == '\"')
{
break;
}
req.params.back().name.push_back(*it_temp);
it_temp++;
}
it_temp++;
while (*it_temp == '\r' || *it_temp == '\n')
{
it_temp++;
}
while (it_temp != data_.end())
{
if (*it_temp == '\r')
{
break;
}
req.params.back().value.push_back(*it_temp);
it_temp++;
}
index++;
it = boost::algorithm::ifind_nth(data_, find_str, index);
}
}
//解析post参数结束
}
3.在 connection.hpp 里添加两个成员变量:
std::string data_;//合并tcp数据包后的数据
bool is_header_parsed;//是否已经解析过header信息
4.对 connection.cpp 里的 do_read() 方法修改如下(关键就是修改这个方法,让它能够合并tcp数据包)
void connection::do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(buffer_),
[this, self](boost::system::error_code ec, std::size_t bytes_transferred)
{
if (!ec)
{
std::string this_data(buffer_.data(), bytes_transferred);
data_.append(this_data);
int header_end = data_.find("\r\n\r\n");//http协议的头部结束标识
if (header_end < 0)
{
do_read();
return;
}
request_parser::result_type result;
if (!is_header_parsed)
{
std::tie(result, std::ignore) = request_parser_.parse(
request_, data_.begin(), data_.end());
}
if (is_header_parsed || result == request_parser::good)
{
is_header_parsed = true;
std::string length_str;
for (int i = 0; i < request_.headers.size(); i++)
{
if (boost::algorithm::iequals(request_.headers[i].name, "content-length"))
{
length_str = request_.headers[i].value;
break;
}
}
int content_length = 0;
if (!length_str.empty())
{
content_length = atoi(length_str.c_str());
}
if (data_.size() < (content_length + header_end + 4))
{//数据不全,继续接受tcp数据
do_read();
return;
}
//data_ = utf8_to_ascii(data_);//
request_parser_.parse_param(request_, data_);//新加的方法
request_handler_.handle_request(request_, reply_);
do_write();
}
else if (result == request_parser::bad)
{
reply_ = reply::stock_reply(reply::bad_request);
do_write();
}
else
{
do_read();
}
}
else if (ec != boost::asio::error::operation_aborted)
{
connection_manager_.stop(shared_from_this());
}
});
}