我正在使用独立的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
事实证明,不鼓励偷看套接字,也很难实现,对于独立的asio而言更是如此。我发现的解决方法是这样的:
对我来说,看起来像这样:
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);
});
}