在不从输入流中删除的情况下查看 asio https ssl 流

Peek asio https ssl stream without deleting from input stream

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

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

但是每当我在握手之前手动读取套接字时,输入流中的字节丢失并且无法将那些丢失的字节提供给 handshake/reading 进程。

所以我认为将字节留在输入流上而不是偷看会更容易,但我还没有找到偷看 asio::ssl::stream 的方法。 (async_receive 和标志 message_peek 应该有效,但我找不到它。我找到的唯一文档是 boost::beast)

我唯一的看法是重写的 accept 函数,如果握手成功,将在其中调用读取:

(来自 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 和原始套接字)的任何其他示例?

谢谢 自然

事实证明,窥视套接字是不鼓励的,而且也很难完成,对于 asio standalone 更是如此。 我发现的解决方法是这样的:

  • 从 Asio 独立库切换到 boost::asio,因为 boost::asio 对 asio 命名空间有额外的重载(至少在比较 atm 最新版本 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);

    });
}