Add memory limited dedicated compressors

This commit is contained in:
Alex Hultman
2020-02-29 04:06:27 +01:00
parent 7313e0168d
commit 3a856b610b
4 changed files with 70 additions and 22 deletions
+7 -15
View File
@@ -27,19 +27,10 @@
#include "WebSocket.h"
#include "WebSocketExtensions.h"
#include "WebSocketHandshake.h"
#include "PerMessageDeflate.h"
namespace uWS {
/* Compress options (really more like PerMessageDeflateOptions) */
enum CompressOptions {
/* Compression disabled */
DISABLED = 0,
/* We compress using a shared non-sliding window. No added memory usage, worse compression. */
SHARED_COMPRESSOR = 1,
/* We compress using a dedicated sliding window. Major memory usage added, better compression of similarly repeated messages. */
DEDICATED_COMPRESSOR = 2
};
template <bool SSL>
struct TemplatedApp {
private:
@@ -130,7 +121,7 @@ public:
if (!loopData->zlibContext) {
loopData->zlibContext = new ZlibContext;
loopData->inflationStream = new InflationStream;
loopData->deflationStream = new DeflationStream;
loopData->deflationStream = new DeflationStream(CompressOptions::DEDICATED_COMPRESSOR);
}
}
@@ -173,9 +164,10 @@ public:
res->writeHeader("Sec-WebSocket-Protocol", secWebSocketProtocol.substr(0, secWebSocketProtocol.find(',')));
}
/* Negotiate compression */
/* Negotiate compression, we may use a smaller compression window than we negotiate */
bool perMessageDeflate = false;
bool slidingDeflateWindow = false;
/* We are always allowed to share compressor, if perMessageDeflate */
int compressOptions = behavior.compression & SHARED_COMPRESSOR;
if (behavior.compression != DISABLED) {
std::string_view extensions = req->getHeader("sec-websocket-extensions");
if (extensions.length()) {
@@ -205,7 +197,7 @@ public:
/* Is the server allowed to compress with a sliding window? */
if (!(extensionsNegotiator.getNegotiatedOptions() & SERVER_NO_CONTEXT_TAKEOVER)) {
slidingDeflateWindow = true;
compressOptions = behavior.compression;
}
}
}
@@ -231,7 +223,7 @@ public:
/* Initialize websocket with any moved backpressure intact */
httpContext->upgradeToWebSocket(
webSocket->init(perMessageDeflate, slidingDeflateWindow, std::move(backpressure))
webSocket->init(perMessageDeflate, compressOptions, std::move(backpressure))
);
/* Arm idleTimeout */
+58 -2
View File
@@ -20,6 +20,28 @@
#ifndef UWS_PERMESSAGEDEFLATE_H
#define UWS_PERMESSAGEDEFLATE_H
/* We always define these options no matter if ZLIB is enabled or not */
namespace uWS {
/* Compress options (really more like PerMessageDeflateOptions) */
enum CompressOptions : int {
/* Compression disabled */
DISABLED = 0,
/* We compress using a shared non-sliding window. No added memory usage, worse compression. */
SHARED_COMPRESSOR = 1,
/* We compress using a dedicated sliding window. Major memory usage added, better compression of similarly repeated messages. */
DEDICATED_COMPRESSOR = 2,
/* Flags for limiting memory usage of dedicated compressor */
DEDICATED_COMPRESSOR_2KB = 2 | 4,
DEDICATED_COMPRESSOR_4KB = 2 | 8,
DEDICATED_COMPRESSOR_8KB = 2 | 16,
DEDICATED_COMPRESSOR_16KB = 2 | 32,
DEDICATED_COMPRESSOR_32KB = 2 | 64,
DEDICATED_COMPRESSOR_64KB = 2 | 128,
DEDICATED_COMPRESSOR_128KB = 2 | 256,
DEDICATED_COMPRESSOR_256KB = 2 | 512
};
}
#ifndef UWS_NO_ZLIB
#include <zlib.h>
#endif
@@ -68,8 +90,42 @@ struct ZlibContext {
struct DeflationStream {
z_stream deflationStream = {};
DeflationStream() {
deflateInit2(&deflationStream, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
DeflationStream(int compressOptions) {
/* Sliding inflator should be about 44kb by default, less than compressor */
/* Memory usage is given by 2 ^ (windowBits + 2) + 2 ^ (memLevel + 9) */
int windowBits = -15, memLevel = 8;
if (compressOptions == DEDICATED_COMPRESSOR_2KB) {
windowBits = -8;
memLevel = 1;
} else if (compressOptions == DEDICATED_COMPRESSOR_4KB) {
windowBits = -9;
memLevel = 2;
} else if (compressOptions == DEDICATED_COMPRESSOR_8KB) {
windowBits = -10;
memLevel = 3;
} else if (compressOptions == DEDICATED_COMPRESSOR_16KB) {
windowBits = -11;
memLevel = 4;
} else if (compressOptions == DEDICATED_COMPRESSOR_32KB) {
windowBits = -12;
memLevel = 5;
} else if (compressOptions == DEDICATED_COMPRESSOR_64KB) {
windowBits = -13;
memLevel = 6;
} else if (compressOptions == DEDICATED_COMPRESSOR_128KB) {
windowBits = -14;
memLevel = 7;
} else if (compressOptions == DEDICATED_COMPRESSOR_256KB) {
windowBits = -15;
memLevel = 8;
}
/* DEDICATED_COMPRESSOR_256KB is the same as DEDICATED_COMPRESSOR */
deflateInit2(&deflationStream, 1, Z_DEFLATED, windowBits, memLevel, Z_DEFAULT_STRATEGY);
}
/* Deflate and optionally reset */
+2 -2
View File
@@ -33,8 +33,8 @@ struct WebSocket : AsyncSocket<SSL> {
private:
typedef AsyncSocket<SSL> Super;
void *init(bool perMessageDeflate, bool slidingCompression, std::string &&backpressure) {
new (us_socket_ext(SSL, (us_socket_t *) this)) WebSocketData(perMessageDeflate, slidingCompression, std::move(backpressure));
void *init(bool perMessageDeflate, int compressOptions, std::string &&backpressure) {
new (us_socket_ext(SSL, (us_socket_t *) this)) WebSocketData(perMessageDeflate, compressOptions, std::move(backpressure));
return this;
}
public:
+3 -3
View File
@@ -45,12 +45,12 @@ private:
/* We could be a subscriber */
Subscriber *subscriber = nullptr;
public:
WebSocketData(bool perMessageDeflate, bool slidingCompression, std::string &&backpressure) : AsyncSocketData<false>(std::move(backpressure)), WebSocketState<true>() {
WebSocketData(bool perMessageDeflate, int compressOptions, std::string &&backpressure) : AsyncSocketData<false>(std::move(backpressure)), WebSocketState<true>() {
compressionStatus = perMessageDeflate ? ENABLED : DISABLED;
/* Initialize the dedicated sliding window */
if (perMessageDeflate && slidingCompression) {
deflationStream = new DeflationStream;
if (perMessageDeflate && (compressOptions & CompressOptions::DEDICATED_COMPRESSOR)) {
deflationStream = new DeflationStream(compressOptions);
}
}