短读错误-Boost asio同步https调用

问题描述

我正在使用Boost Asio https synchronous call- Error code 400 bad request中所述的boost asio进行https同步调用,现在我们正在获取响应代码,但是从套接​​字读取时,我得到了短读取错误

   boost::asio::write(socket,request_);
   boost::asio::read_until(socket,response_,"\r\n");
    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 response\n");
    }
    if (status_code != 200)
    {
            fast_ostringstream oss;
            oss << "Response returned with status code: " << status_code << "\n";
            PBLOG_WARN(oss.str());
    }

    boost::asio::read(socket,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。请帮助我解决此问题。

谢谢

解决方法

正如我在the other answer中解释的那样,当服务器通过简单地关闭连接来实现关闭时,应该会进行短读(“ stream_truncaed”)。在这种情况下,常规的SSL关闭会丢失,提示“短读”。

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

在您的情况下,您不会显式解析响应,因此您无法真正做出明智的决定。这使您容易遭受潜在的滥用,在这种情况下,攻击者可能会接受您的格式错误的请求。

1。累了(解决方法)

您幼稚的解决方法是:

Live On 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 response\n" << std::endl;
}

打印

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

----

2。有线:​​正确的实现方式

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

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

注意:此方法还使许多其他事情更不易出错,并且不安全性降低。诸如编码标头值(您不需要),正确使用CRLF(这是您上一个问题中的错误),正确计算内容长度等内容。

在响应上解析您-随机-检查HTTP版本以“ HTTP /”开头。但是,使用Beast,您将实际上检查输入的健全性相应地更改行为。这意味着,如果您的服务器执行了您意想不到的操作(例如,您实际上使用了分块编码的HTTP / 1.1功能),您仍然会读取响应,就好像您完全知道这一点一样。

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

Live On 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,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的复杂性
  • 不要重新发明轮子-充分利用库!

学习愉快!