Schannel: Send more than one message at a time

By encrypting multiple 'Messages' before we send them we reduce
the overhead on both ourselves and the receiving end.
This brings some synthetic benchmark I have written from taking
around 50-60+ seconds to around 10.
With OpenSSL it is taking around 2 seconds, so there is still some
headroom, but this should be okay for now.

Change-Id: Ibcbc04a503a4b49197296ceaea2f0c38f528dfc4
Reviewed-by: Mate Barany <mate.barany@qt.io>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Mårten Nordheim 2024-06-11 12:59:50 +02:00
parent b4b90bfbaf
commit 4e60a6b556

View File

@ -1765,23 +1765,41 @@ auto TlsCryptographSchannel::getNextEncryptedMessage() -> MessageBufferResult
Q_ASSERT(d); Q_ASSERT(d);
auto &writeBuffer = d->tlsWriteBuffer(); auto &writeBuffer = d->tlsWriteBuffer();
auto allocateMessage = [&fullMessage](qsizetype size) -> QSpan<char> {
auto targetSize = fullMessage.size() + size;
if (fullMessage.capacity() < targetSize) {
qsizetype newSize = fullMessage.capacity() * 2;
if (newSize < targetSize)
newSize = targetSize;
fullMessage.reserve(newSize);
}
fullMessage.resizeForOverwrite(targetSize);
return QSpan(fullMessage).subspan(fullMessage.size() - size);
};
const int headerSize = int(streamSizes.cbHeader); const int headerSize = int(streamSizes.cbHeader);
const int trailerSize = int(streamSizes.cbTrailer); const int trailerSize = int(streamSizes.cbTrailer);
constexpr qsizetype MessageBufferThreshold = 128 * 1024;
qint64 writeBufferSize = 0;
while ((writeBufferSize = writeBuffer.size()) > 0
&& fullMessage.size() < MessageBufferThreshold) {
// Try to read 'cbMaximumMessage' bytes from buffer before encrypting. // Try to read 'cbMaximumMessage' bytes from buffer before encrypting.
const int size = int(std::min(writeBuffer.size(), qint64(streamSizes.cbMaximumMessage))); const int bodySize = int(std::min(writeBufferSize, qint64(streamSizes.cbMaximumMessage)));
fullMessage.resizeForOverwrite(headerSize + trailerSize + size); auto messageSize = headerSize + bodySize + trailerSize;
char *header = fullMessage.data(); QSpan buffer = allocateMessage(messageSize);
char *header = buffer.data();
char *body = header + headerSize; char *body = header + headerSize;
char *trailer = body + size; char *trailer = body + bodySize;
{ {
// Use peek() here instead of read() so we don't lose data if encryption fails. // Use peek() here instead of read() so we don't lose data if encryption fails.
qint64 copied = writeBuffer.peek(body, size); qint64 copied = writeBuffer.peek(body, bodySize);
Q_ASSERT(copied == size); Q_ASSERT(copied == bodySize);
} }
SecBuffer inputBuffers[] = { SecBuffer inputBuffers[] = {
createSecBuffer(header, headerSize, SECBUFFER_STREAM_HEADER), createSecBuffer(header, headerSize, SECBUFFER_STREAM_HEADER),
createSecBuffer(body, size, SECBUFFER_DATA), createSecBuffer(body, bodySize, SECBUFFER_DATA),
createSecBuffer(trailer, trailerSize, SECBUFFER_STREAM_TRAILER), createSecBuffer(trailer, trailerSize, SECBUFFER_STREAM_TRAILER),
createSecBuffer(nullptr, 0, SECBUFFER_EMPTY) createSecBuffer(nullptr, 0, SECBUFFER_EMPTY)
}; };
@ -1790,6 +1808,7 @@ auto TlsCryptographSchannel::getNextEncryptedMessage() -> MessageBufferResult
ARRAYSIZE(inputBuffers), ARRAYSIZE(inputBuffers),
inputBuffers inputBuffers
}; };
if (auto status = EncryptMessage(&contextHandle, 0, &message, 0); status != SEC_E_OK) { if (auto status = EncryptMessage(&contextHandle, 0, &message, 0); status != SEC_E_OK) {
setErrorAndEmit(d, QAbstractSocket::SslInternalError, setErrorAndEmit(d, QAbstractSocket::SslInternalError,
QSslSocket::tr("Schannel failed to encrypt data: %1") QSslSocket::tr("Schannel failed to encrypt data: %1")
@ -1797,11 +1816,13 @@ auto TlsCryptographSchannel::getNextEncryptedMessage() -> MessageBufferResult
return result; return result;
} }
// Data was encrypted successfully, so we free() what we peek()ed earlier // Data was encrypted successfully, so we free() what we peek()ed earlier
writeBuffer.free(size); writeBuffer.free(bodySize);
// The trailer's size is not final, so resize fullMessage to not send trailing junk // The trailer's size is not final, so resize fullMessage to not send trailing junk
fullMessage.resize(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer auto finalSize = qsizetype(inputBuffers[0].cbBuffer + inputBuffers[1].cbBuffer
+ inputBuffers[2].cbBuffer); + inputBuffers[2].cbBuffer);
fullMessage.chop(messageSize - finalSize);
}
result.ok = true; result.ok = true;
return result; return result;
} }