我从gitlab(https://gitlab.com/eidheim/Simple-Web-Server)上的Eidheim那里获得了SimpleWebServer,我想使该https服务器也可以处理http和特殊的普通套接字查询。它们都可以正常工作,但是在进行压力测试时,https有很多错误,在更改之前我没有看到。顺便说一句:它始终是相同的查询,并且作为单个查询,它可以正常工作。编辑:服务器使用自签名证书。
每秒大约有3个https查询(不多),偶尔会出现以下错误:
我正在使用-k在批处理循环中使用curl测试不安全的连接:
:START
C:\curl\src\curl.exe -k https://192.168.0.228:3012/status
ping 127.0.0.1 -n 2 > nul
goto START
我更改了服务器的握手和接受状态。我正好读取了流的一个字节: ->确定是否是来自套接字查询的信号字节 ->否则,如果第一个字节不等于0x16(TLS握手从0x16开始,没有它只是一个HTTP查询) ->否则应该是https查询
仅对于https,我会读取其余的握手数据,并继续执行async_handshake。 最初,async_handshake自己读取握手数据,而不是由我提供sslbuffer,我担心这会导致不稳定。
编辑:要查看困扰我的ssl握手,请跳到// -----------------------我的第三次更改
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);
//-----------------------MY FIRST CHANGE - read first byte
//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));
asio::async_read(session->connection->socket->next_layer(), boost::asio::buffer(sslBuffer, bytesToRead), asio::transfer_exactly(bytesToRead), [this, sslBuffer, bytesToRead, session](const error_code& ecRead, std::size_t /*bytes_transferred*/){
//Get the read data from the buffer in a readable form
unsigned char * firstByte = asio::buffer_cast<unsigned char*>(sslBuffer);
//-----------------------MY SECOND CHANGE - use first byte to determine if https, http or socket
if (SocketQuery::CheckForSocketQuery(firstByte[0])) {
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if (!lock)
return;
session->isHttps = false;
this->read_socket(session, firstByte[0]);
}
else if (!SocketQuery::is_client_TLS_handshake(firstByte))
{
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if (!lock)
return;
session->isHttps = false;
this->read_http(session, firstByte[0]);
}
//-----------------------MY THIRD CHANGE - read the handshake manually and pass it to async_handshake
else
{
try
{
//read handshake, 4000 Bytes should be way more than any handshake needs (which is something between 200 and 400 bytes usually)
session->connection->socket->next_layer().async_read_some(boost::asio::buffer(sslBuffer + bytesToRead, 4000), [this, sslBuffer, bytesToRead, session](const error_code& ecReadHandshake, std::size_t bytesOfHandshake) {
try
{
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) {
try
{
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 handshake error: " + wxString(ecHttps.message()));
}
}
catch (const std::exception& e)
{
wxLogMessage("cought server handshake o3 error: " + wxString(e.what()));
}
});
}
catch (const std::exception& e)
{
wxLogMessage("cought server handshake o2 error: " + wxString(e.what()));
}
});
}
catch (const std::exception& e)
{
wxLogMessage("cought server handshake o1 error: " + wxString(e.what()));
}
}
});
}
else if(this->on_error)
this->on_error(session->request, ec);
});
我试图通过try / catches for https阻止服务器崩溃,但是崩溃发生在ssl accept的更深层,到目前为止,我无法停止服务器崩溃。 我之前读过,有可能async_read尝试从套接字读取所有实际数据之前。也许我认为尝试从套接字读取4000字节进行握手是安全的,这是错误的。知道如何改进吗?
编辑:我刚刚发现:如果证书相当长,则握手数据集可以大于4000字节。因此,另一个问题是:如何从https标头数据中划分握手数据?还有双换行符吗?
答案 0 :(得分:0)
好吧,我真的不能确定这是否是最好的解决方案,但它当然是更好的解决方案。我找不到所有问题的答案,因此,如果还有其他人仍想解释一些错误,我全耳听着。
我找不到ssl和标头之间的分隔符,因此我尝试了适用于标头和正文分隔的同一分隔符。 对于ssl数据,我现在使用asio :: async_read_until并读取直到双端行“ \ r \ n \ r \ n”。 更改之后,https的崩溃急剧下降。
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::shared_ptr<asio::streambuf> streambuf = std::make_shared<asio::streambuf>();
asio::async_read(session->connection->socket->next_layer(), *streambuf, asio::transfer_exactly(bytesToRead), [this, streambuf, bytesToRead, session](const error_code& ecRead, std::size_t bytes_transferred) mutable {
//Get the read data from the buffer in a readable form
const std::vector<unsigned char> vectorBuffer = { asio::buffers_begin(streambuf->data()), asio::buffers_end(streambuf->data()) };
if (SocketQuery::CheckForSocketQuery(vectorBuffer.at(0))) {
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if (!lock)
return;
session->isHttps = false;
this->read_socket(session, vectorBuffer.at(0));
}
else if (!SocketQuery::is_client_TLS_handshake(vectorBuffer.at(0)))
{
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if (!lock)
return;
session->isHttps = false;
this->read_http(session, vectorBuffer.at(0));
}
else
{
try
{
auto readUntil = "\r\n\r\n";
asio::async_read_until(*session->connection->socket, *streambuf, readUntil, [this, streambuf, bytesToRead, session](const error_code&ecReadHandshake, std::size_t bytesOfHandshake) mutable {
try
{
const std::vector<unsigned char> vectorBuf = { asio::buffers_begin(streambuf->data()), asio::buffers_end(streambuf->data()) };
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(vectorBuf, bytesOfHandshake), [this,session](const error_code& ecHttps, std::size_t bufferSize) mutable {
try
{
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 handshake error: " + wxString(ecHttps.message()));
}
}
catch (const std::exception& e)
{
wxLogMessage("cought server handshake o3 error: " + wxString(e.what()));
}
});
}
catch (const std::exception& e)
{
wxLogMessage("cought server handshake o2 error: " + wxString(e.what()));
}
});
}
catch (const std::exception& e)
{
wxLogMessage("cought server handshake o1 error: " + wxString(e.what()));
}
}
});
}
else if(this->on_error)
this->on_error(session->request, ec);
});
编辑:我现在发现了逐字节的解释,因此有一种更好的方式来读取握手。 (如果再次遇到问题,我可能会更改代码) https://tls.ulfheim.net/