短读错误-Boost asio同步https调用 [英] Short read error-Boost asio synchoronous https call

查看:137
本文介绍了短读错误-Boost asio同步https调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用boost asio进行https同步调用,如

I am making https synchoronous call using the boost asio as described in Boost Asio https synchronous call- Error code 400 bad request and now we are getting response code but while reading it from the socket, i am getting short read error:

   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:短读.请帮助我解决此问题.

in the above logic, it goes to if loop and getting the error as Error : short read. Please help me in fixing this issue.

谢谢

推荐答案

正如我在其他答案中所述当服务器通过简单地关闭连接来实现关闭时,期望读取("stream_truncaed").在这种情况下,常规的SSL关闭会丢失,提示短读".

As I explained in the other answer, short read ("stream_truncaed") is to be expected when servers are implementing shutdown by simply closing the connection. The regular SSL shutdown is missing in such a case, prompting a "Short Read".

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

The issue I linked back there describes how to treat the error in the context of HTTP response parsing - and concludes that it is safe to ignore when the response has been parsed completely.

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

In your case, you DO NOT explicitly parse the response, so you can't really make an informed decision. This leaves you open to potential abuse where you can be made to accept malformed requests by an attacker.

您幼稚的解决方法是:

在Coliru上直播

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(可能永远不会出现?),并scratch不休,为什么会导致短读.

2. Wired: correct implementation

However, I suggest you up your game and use Beast to parse the response. This has the benefit that you can trust the response. It parses the expected protocol, rather than blindly waiting for EOF (which might never come?) and scratching your head why that results in short reads.

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

Since we now stop reading once the response is complete, we don't encounter EOF nor the Short Read.

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

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.

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

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.

在Coliru上直播

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的复杂性
  • 不要重新发明轮子-利用图书馆来发挥自己的优势!

学习愉快!

这篇关于短读错误-Boost asio同步https调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆