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

Short read error-Boost asio synchoronous https call

我正在使用 中描述的 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, 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。累了(解决方法)

我们天真的解决方法是:

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 或 Short Read。

Note: this method also makes a lot of other things less error-prone and way less insecure. Things like encoding header values (which you don't), proper use of CRLF (which was an error in your previous question), correct calculation of content length etc.

On the response parsing you - randomly - check that HTTP version starts with "HTTP/". However, using Beast you will actually check input sanity and change behaviour accordingly. This means that if your server does something you didn't expect (like, you know, actually using HTTP/1.1 features like chunked encoding) you will still read the response, as if you totally knew about that.

Conversely, if someone attacks your server by sending invalid responses, you have a better chance of not crashing or worse.

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, 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 的复杂性
  • 不要重新发明轮子 - 使用库对您有利!

祝您学习愉快!