Peek asio https ssl流,但不从输入流中删除

问题描述 投票:0回答:1

我正在使用独立的asio和Eidheim(Eidheims SimpleHttpsServer)的HTTPS包装器在Windows上通过异步请求处理和线程池设置HTTPS服务器。 HTTPS服务器偶尔会获取原始套接字查询,因为我想替换旧的套接字服务器,并且如果客户端应用程序不是最新的,则它们将不会发送HTTP格式的查询。对于HTTP,这没问题,因为如果传入的查询没有HTTP格式,我可以将Read(从套接字)方法更改为使用旧代码进行请求处理。

现在,在HTTPS ssl套接字流上尝试相同的操作,在进行任何读取之前,服务器首先需要执行ssl握手,因此我需要在握手之前先读取(窥视)套接字以验证其是否需要纯套接字后备方法或标准HTTPS方法。

但是每当我在握手之前手动读取套接字时,输入流上都会丢失字节,并且无法将这些丢失的字节提供给握手/读取过程。

因此,我认为将字节保留在输入流上而不是窥视会更容易,但是我还没有找到窥视asio :: ssl :: stream的方法。 (async_receive和标志message_peek应该可以工作,但是我找不到它。我发现的唯一文档是关于boost :: beast的)

我唯一的角度是覆盖的接受函数,如果握手成功,将在其中调用读取:

(来自https://gitlab.com/eidheim/Simple-Web-Server/-/blob/master/server_https.hpp

void accept() override {
    auto connection = create_connection(*io_service, context);

    acceptor->async_accept(connection->socket->lowest_layer(), [this, connection](const error_code& ec) {
        auto lock = connection->handler_runner->continue_lock();
        if (!lock)
            return;

        if (ec != error::operation_aborted)
            this->accept();

        auto session = std::make_shared<Session>(config.max_request_streambuf_size, connection);

        if (!ec) {
            asio::ip::tcp::no_delay option(true);
            error_code ec;
            session->connection->socket->lowest_layer().set_option(option, ec);

            session->connection->set_timeout(config.timeout_request);

// ***** I need to read (peek) before this to decide if a handshake is needed *****

            session->connection->socket->async_handshake(asio::ssl::stream_base::server, [this, session](const error_code& ec) {
                session->connection->cancel_timeout();
                auto lock = session->connection->handler_runner->continue_lock();
                if (!lock)
                    return;
                if (!ec)
                    this->read(session);
                else if (this->on_error)
                    this->on_error(session->request, ec);
            });
        }
        else if (this->on_error)
            this->on_error(session->request, ec);
    });
}

有人有什么见识如何窥视asio ssl流(我实际上只需要第一个字节)?还是有人知道这个图书馆,并且对如何解决这个问题有另一个想法?我可以研究混合的(asio)服务器(https和原始套接字)的任何其他示例吗?

谢谢Natulux

c++ https stream handshake asio
1个回答
0
投票

事实证明,不鼓励偷看套接字,也很难实现,对于独立的asio而言更是如此。我发现的解决方法是这样的:

  • 从Asio Standalone库切换到boost :: asio,因为boost :: asio对asio命名空间有额外的重载(至少在比较最新的atm版本build boost 1.72.0和asio 1.13.0时]
  • 如本文中所述(Is it possible to do async_handshake after reading from socket prior using Boost::asio?),请阅读整个握手,如果您需要之前从ssl流中进行任何读取,并将读取缓冲区作为第二个参数传递给async_handshake重载(请参见第一点)

对我来说,看起来像这样:

void accept() override {
    auto connection = create_connection(*io_service, context);

    acceptor->async_accept(connection->socket->lowest_layer(), [this, connection](const error_code &ec) {
    auto lock = connection->handler_runner->continue_lock();
    if(!lock)
        return;

    if(ec != error::operation_aborted)
        this->accept();

    auto session = std::make_shared<Session>(config.max_request_streambuf_size, connection);

    if(!ec) {
        asio::ip::tcp::no_delay option(true);
        error_code ec;
        session->connection->socket->lowest_layer().set_option(option, ec);

        //read some bytes, needed before the handshake
        const unsigned int bytesToRead = 1;
        int size_of_the_data = 100;
        std::vector<unsigned char> _raw_buffer(size_of_the_data);
        asio::mutable_buffers_1 sslBuffer(asio::buffer(_raw_buffer, size_of_the_data));

        //You should make this async!
        asio::read(session->connection->socket->next_layer(), boost::asio::buffer(sslBuffer, bytesToRead), asio::transfer_exactly(bytesToRead));

        //Get the read data from the buffer in a readable form
        unsigned char * firstByte = asio::buffer_cast<unsigned char*>(sslBuffer);

        //Use the data somehow (in my case, use the first Byte to see if I need raw socket handling or ssl handshake + https handling)
        if (SocketQuery::CheckForSocketQuery(firstByte[0])) {
            this->read_socket(session, firstByte[0]);
        }
        else
        {
            //read handshake, 4000 Bytes should be way more than any handshake needs (which is something between 200 and 400 bytes usually)
            //You should make this async!
            std::size_t bytesOfHandshake = session->connection->socket->next_layer().read_some(boost::asio::buffer(sslBuffer + bytesToRead, 4000));
            bytesOfHandshake += bytesToRead;

            session->connection->set_timeout(config.timeout_request);

            //Use overload of async_handshake with buffer as second parameter
            //Note that the async callback lambda is expected to take the buffer and buffer size as you see below
            session->connection->socket->async_handshake(asio::ssl::stream_base::server, asio::buffer(sslBuffer, bytesOfHandshake), [this, sslBuffer, session](const error_code& ecHttps, std::size_t bufferSize) {
            session->connection->cancel_timeout();
            auto lock = session->connection->handler_runner->continue_lock();
            if (!lock)
                return;

            if (!ecHttps)
            {
                this->read(session);
            }
            else if (this->on_error)
            {
                this->on_error(session->request, ecHttps);
                wxLogMessage("server error: " + wxString(ecHttps.message()));
            }
            else
            {
                wxLogMessage("server error: " + wxString(ecHttps.message()));
            }
        });
        }
    }
    else if(this->on_error)
        this->on_error(session->request, ec);

    });
}
© www.soinside.com 2019 - 2024. All rights reserved.