在不从输入流中删除的情况下查看 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);
});
}
我正在使用独立的 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);
});
}