短读取错误Boost asio syncoronous https调用



我正在使用boost asio进行https同步调用,如boost asio https同步呼叫中所述-错误代码400错误请求,现在我们得到了响应代码,但从套接字读取时,我得到了短读错误:

boost::asio::write(socket, request_);
boost::asio::read_until(socket, response_, "rn");
string res=make_string(response_);
// Check that response is OK.
std::istream response_stream(&response_);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
PBLOG_WARN("Invalid responsen");
}
if (status_code != 200)
{
fast_ostringstream oss;
oss << "Response returned with status code: " << status_code << "n";
PBLOG_WARN(oss.str());
}
boost::asio::read(socket, response_, boost::asio::transfer_all(), error);
if (error != boost::asio::error::eof)
{
fast_ostringstream oss;
oss << "Error : " << error.message();
PBLOG_WARN(oss.str());
return false;
}
else
{
//parse the original resposne
}

在上面的逻辑中,它进入if循环,并将错误作为error:short-read。请帮我解决这个问题。

感谢

正如我在另一个答案中所解释的,当服务器通过简单地关闭连接来实现关闭时,会出现短读("stream_truncaed"(。在这种情况下缺少常规SSL关闭;短读";。

我在那里链接的问题描述了如何在HTTP响应解析的上下文中处理错误,并得出结论,当响应被完全解析时,可以安全地忽略。

在您的情况下,您没有明确地解析响应,因此您无法真正做出明智的决定。这会使您面临潜在的滥用,攻击者可能会让您接受格式错误的请求。

1.疲劳(解决方法(

你天真的解决方法是:

在Coliru上直播

std::istream response_stream(&response_);
if (std::getline(response_stream >> http_version >> status_code, status_message)
&& http_version.substr(0, 5) == "HTTP/")
{
if (status_code != 200) {
std::clog << "Response returned with status code: " << status_code << "n";
}
boost::system::error_code error;
boost::asio::read(socket, response_, boost::asio::transfer_all(), error);
if (error != boost::asio::error::eof &&
error != boost::asio::ssl::error::stream_truncated)
{
std::clog << "Error : " << error.message() << std::endl;
return 1;
}
else
{
//parse the original resposne

}
} else {
std::clog << "Invalid responsen" << std::endl;
}

打印

Payload:
----
<!DOCTYPE html>
<html>
<title>Coliru Viewer</title>
<head>
....
....
</body>
</html>
----

2.有线:正确执行

不过,我建议您升级游戏并使用Beast解析响应。这样做的好处是您可以信任响应。它解析预期的协议,而不是盲目地等待EOF(它可能永远不会出现?(,并弄明白为什么会导致短时间的读取。

由于我们现在在响应完成后停止阅读,因此我们不会遇到EOF或Short Read。

注意:这种方法还降低了许多其他事情的错误发生率,也大大降低了不安全性。比如编码头值(你没有(,正确使用CRLF(这是你之前问题中的一个错误(,正确计算内容长度等。

在响应解析时,您可以随机检查HTTP版本是否以"开头;HTTP/";。但是,使用Beast,您将实际检查输入健全性相应地更改行为。这意味着,如果你的服务器做了一些你没有预料到的事情(比如,你知道,实际上使用了HTTP/1.1功能,如分块编码(,你仍然会读取响应,就好像你完全知道这一点一样。

相反,如果有人通过发送无效响应来攻击你的服务器,你更有可能不会崩溃或更糟。

在Coliru上直播

#include <boost/asio.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/asio/ssl.hpp>
#include <iostream>
#include <iomanip>
namespace http = boost::beast::http;
using boost::asio::ip::tcp;
// https://postman-echo.com/post see https://docs.postman-echo.com/?version=latest
static std::string
verb = "POST",
server_endpoint = "/post",
hostname = "postman-echo.com",
port_no = "443",
authorization_token = "c3RhdGljIGNvbnN0IHN0ZDo6c3RyaW5nIGF1dGhvcml6YXRpb"
"25fdG9rZW4gPSAiQXV0aDogIj"
"sK",
client_name = "demo program 0.01",
req_str = R"(name=blabla&password=bloblo)";
int main() {
boost::asio::io_service io_service;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
ctx.set_verify_mode(boost::asio::ssl::verify_peer);
//ctx.load_verify_file("ca.pem");
ctx.add_verify_path("/etc/ssl/certs");
ctx.set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>
socket(io_service, ctx);
{
#ifdef COLIRU
verb = "GET";
server_endpoint = "/a/cf2748285fa3343a";
hostname = "coliru.stacked-crooked.com";
socket.set_verify_mode(boost::asio::ssl::verify_none);
socket.lowest_layer().connect({ boost::asio::ip::address::from_string("173.203.57.63"), 443 });
#else
tcp::resolver resolver(io_service);
tcp::resolver::query query(hostname, port_no);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
boost::system::error_code error = boost::asio::error::host_not_found;
boost::asio::connect(socket.lowest_layer(), endpoint_iterator, error);
#endif
}
{
socket.handshake(boost::asio::ssl::stream_base::client);
using http::field;
http::request<http::string_body> request;
request.method_string(::verb);
request.target(server_endpoint);
request.version(11);
request.set(field::host, hostname);
request.set(field::accept, "*/*");
request.set(field::authorization, authorization_token);
request.set(field::user_agent, client_name);
request.set(field::content_type, "application/x-www-form-urlencoded");
request.set(field::connection, field::close);
request.body() = req_str;
request.prepare_payload();
write(socket, request);
// std::clog << request << "n"; return 1;
}
{
boost::system::error_code ec;
using http::field;
http::response<http::string_body> response_;
boost::beast::flat_buffer buf;
read(socket, buf, response_, ec);

{
//std::clog << "Response: " << response_ << std::endl;
}
// Check that response is OK.
if (!ec && response_.version() == 11) {
if (response_.result() != http::status::ok) {
std::clog << "Response returned with status code: " << response_.result_int() << "n";
}
std::clog << "Payload:n----n" << response_.body() << "n----" << std::endl;
} else {
std::clog << "Error: " << ec.message() << "n";
}
}
}

摘要

经验教训:

  • 不要低估HTTP或SSL的复杂性
  • 不要重新发明轮子——使用图书馆对你有利

学习愉快!

相关内容