Add large non-ssl, non-compressed WebSocket message sending optimization

This commit is contained in:
Alex Hultman
2023-05-11 11:18:58 +02:00
parent c9724db3cf
commit f6f5a3d6e8
2 changed files with 54 additions and 35 deletions
+53 -34
View File
@@ -105,48 +105,67 @@ public:
/* If we are subscribers and have messages to drain we need to drain them here to stay synced */
WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData();
if (webSocketData->subscriber) {
/* This will call back into us, send. */
webSocketContextData->topicTree->drain(webSocketData->subscriber);
}
/* Transform the message to compressed domain if requested */
if (compress) {
WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData();
/* Check and correct the compress hint. It is never valid to compress 0 bytes */
if (message.length() && opCode < 3 && webSocketData->compressionStatus == WebSocketData::ENABLED) {
LoopData *loopData = Super::getLoopData();
/* Compress using either shared or dedicated deflationStream */
if (webSocketData->deflationStream) {
message = webSocketData->deflationStream->deflate(loopData->zlibContext, message, false);
/* Special path for long sends of non-compressed, non-SSL messages */
if (message.length() >= 16 * 1024 && !compress && !SSL && !webSocketData->subscriber && getBufferedAmount() == 0 && Super::getLoopData()->corkOffset == 0) {
char header[14];
int header_length = (int) protocol::formatMessage<isServer>(header, nullptr, 0, opCode, message.length(), compress, fin);
int written = us_socket_write2(0, (struct us_socket_t *)this, header, header_length, message.data(), (int) message.length());
if (written != header_length + (int) message.length()) {
/* Buffer up backpressure */
if (written > header_length) {
webSocketData->buffer.append(message.data() + written, message.length() - (size_t) written);
} else {
message = loopData->deflationStream->deflate(loopData->zlibContext, message, true);
webSocketData->buffer.append(header + written, (size_t) header_length - (size_t) written);
}
} else {
compress = false;
}
}
} else {
/* Get size, allocate size, write if needed */
size_t messageFrameSize = protocol::messageFrameSize(message.length());
auto [sendBuffer, sendBufferAttribute] = Super::getSendBuffer(messageFrameSize);
protocol::formatMessage<isServer>(sendBuffer, message.data(), message.length(), opCode, message.length(), compress, fin);
if (webSocketData->subscriber) {
/* This will call back into us, send. */
webSocketContextData->topicTree->drain(webSocketData->subscriber);
}
/* Depending on size of message we have different paths */
if (sendBufferAttribute == SendBufferAttribute::NEEDS_DRAIN) {
/* This is a drain */
auto[written, failed] = Super::write(nullptr, 0);
if (failed) {
/* Return false for failure, skipping to reset the timeout below */
return BACKPRESSURE;
/* Transform the message to compressed domain if requested */
if (compress) {
WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData();
/* Check and correct the compress hint. It is never valid to compress 0 bytes */
if (message.length() && opCode < 3 && webSocketData->compressionStatus == WebSocketData::ENABLED) {
LoopData *loopData = Super::getLoopData();
/* Compress using either shared or dedicated deflationStream */
if (webSocketData->deflationStream) {
message = webSocketData->deflationStream->deflate(loopData->zlibContext, message, false);
} else {
message = loopData->deflationStream->deflate(loopData->zlibContext, message, true);
}
} else {
compress = false;
}
}
} else if (sendBufferAttribute == SendBufferAttribute::NEEDS_UNCORK) {
/* Uncork if we came here uncorked */
auto [written, failed] = Super::uncork();
if (failed) {
return BACKPRESSURE;
/* Get size, allocate size, write if needed */
size_t messageFrameSize = protocol::messageFrameSize(message.length());
auto [sendBuffer, sendBufferAttribute] = Super::getSendBuffer(messageFrameSize);
protocol::formatMessage<isServer>(sendBuffer, message.data(), message.length(), opCode, message.length(), compress, fin);
/* Depending on size of message we have different paths */
if (sendBufferAttribute == SendBufferAttribute::NEEDS_DRAIN) {
/* This is a drain */
auto[written, failed] = Super::write(nullptr, 0);
if (failed) {
/* Return false for failure, skipping to reset the timeout below */
return BACKPRESSURE;
}
} else if (sendBufferAttribute == SendBufferAttribute::NEEDS_UNCORK) {
/* Uncork if we came here uncorked */
auto [written, failed] = Super::uncork();
if (failed) {
return BACKPRESSURE;
}
}
}
/* Every successful send resets the timeout */