Add initial send compression

This commit is contained in:
Alex Hultman
2018-12-22 03:08:12 +01:00
parent a54aa23329
commit f8e299c312
7 changed files with 102 additions and 7 deletions
+2 -2
View File
@@ -28,11 +28,11 @@ int main(int argc, char **argv) {
.open = [](auto *ws, auto *req) { .open = [](auto *ws, auto *req) {
std::cout << "WebSocket connected" << std::endl; std::cout << "WebSocket connected" << std::endl;
/* Access per socket data */ /* Access per socket data */
/*PerSocketData *perSocketData = */ws->getUserData(); /*PerSocketData *perSocketData = *///ws->getUserData();
/*perSocketData->hello = 13;*/ /*perSocketData->hello = 13;*/
}, },
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) { .message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
ws->send(message, opCode); ws->send(message, opCode, true);
}, },
.drain = [](auto *ws) { .drain = [](auto *ws) {
std::cout << "Drainage: " << ws->getBufferedAmount() << std::endl; std::cout << "Drainage: " << ws->getBufferedAmount() << std::endl;
+12 -1
View File
@@ -76,6 +76,10 @@ public:
if (!loopData->inflationStream) { if (!loopData->inflationStream) {
loopData->inflationStream = new InflationStream; loopData->inflationStream = new InflationStream;
} }
if (!loopData->deflationStream) {
loopData->deflationStream = new DeflationStream;
}
} }
/* Copy all handlers */ /* Copy all handlers */
@@ -122,16 +126,23 @@ public:
/* Adopting a socket invalidates it, do not rely on it directly to carry any data */ /* Adopting a socket invalidates it, do not rely on it directly to carry any data */
WebSocket<SSL, true> *webSocket = (WebSocket<SSL, true> *) StaticDispatch<SSL>::static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)( WebSocket<SSL, true> *webSocket = (WebSocket<SSL, true> *) StaticDispatch<SSL>::static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)(
(typename StaticDispatch<SSL>::SOCKET_CONTEXT_TYPE *) webSocketContext, (typename StaticDispatch<SSL>::SOCKET_TYPE *) res, /*sizeof(WebSocketData)*/ 150); (typename StaticDispatch<SSL>::SOCKET_CONTEXT_TYPE *) webSocketContext, (typename StaticDispatch<SSL>::SOCKET_TYPE *) res, sizeof(WebSocketData));
/* Update corked socket in case we got a new one (assuming we always are corked in handlers). */
webSocket->cork();
httpContext->upgradeToWebSocket( httpContext->upgradeToWebSocket(
webSocket->init(perMessageDeflate) webSocket->init(perMessageDeflate)
); );
/* Emit open event */
if (behavior.open) { if (behavior.open) {
behavior.open(webSocket, req); behavior.open(webSocket, req);
} }
// todo: perform all the checks such as shutdown, closed, etc!
// bug? or does this happen automatically? no!
} else { } else {
/* For now we do not support having HTTP and websocket routes on the same URL */ /* For now we do not support having HTTP and websocket routes on the same URL */
res->close(); res->close();
+1
View File
@@ -48,6 +48,7 @@ public:
/* Compression data */ /* Compression data */
InflationStream *inflationStream = nullptr; InflationStream *inflationStream = nullptr;
DeflationStream *deflationStream = nullptr;
}; };
} }
+73
View File
@@ -30,9 +30,82 @@
// we also need DeflationStream // we also need DeflationStream
struct DeflationStream {
// share this under the Loop
std::string dynamicZlibBuffer;
z_stream deflationStream = {};
char *zlibBuffer;
DeflationStream() {
std::cout << "Constructing DeflationStream" << std::endl;
zlibBuffer = (char *) malloc(LARGE_BUFFER_SIZE);
deflateInit2(&deflationStream, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
}
std::string_view deflate(std::string_view raw) {
// slidingDeflateWindow är input, length är in/ut
z_stream *slidingDeflateWindow = nullptr;
dynamicZlibBuffer.clear();
z_stream *compressor = slidingDeflateWindow ? slidingDeflateWindow : &deflationStream;
compressor->next_in = (Bytef *) raw.data();
compressor->avail_in = (unsigned int) raw.length();
// note: zlib requires more than 6 bytes with Z_SYNC_FLUSH
const int DEFLATE_OUTPUT_CHUNK = LARGE_BUFFER_SIZE;
int err;
do {
compressor->next_out = (Bytef *) zlibBuffer;
compressor->avail_out = DEFLATE_OUTPUT_CHUNK;
err = ::deflate(compressor, Z_SYNC_FLUSH);
if (Z_OK == err && compressor->avail_out == 0) {
dynamicZlibBuffer.append(zlibBuffer, DEFLATE_OUTPUT_CHUNK - compressor->avail_out);
continue;
} else {
break;
}
} while (true);
// note: should not change avail_out
if (!slidingDeflateWindow) {
deflateReset(compressor);
}
if (dynamicZlibBuffer.length()) {
dynamicZlibBuffer.append(zlibBuffer, DEFLATE_OUTPUT_CHUNK - compressor->avail_out);
return {(char *) dynamicZlibBuffer.data(), dynamicZlibBuffer.length() - 4};
//length = dynamicZlibBuffer.length() - 4;
//return (char *) dynamicZlibBuffer.data();
}
return {
zlibBuffer,
DEFLATE_OUTPUT_CHUNK - compressor->avail_out - 4
};
//length = DEFLATE_OUTPUT_CHUNK - compressor->avail_out - 4;
//return zlibBuffer;
}
~DeflationStream() {
std::cout << "Destructing DeflationStream" << std::endl;
}
};
// the loop holds one of these // the loop holds one of these
struct InflationStream { struct InflationStream {
// share this under the Loop
std::string dynamicZlibBuffer; std::string dynamicZlibBuffer;
z_stream inflationStream = {}; z_stream inflationStream = {};
char *zlibBuffer; char *zlibBuffer;
+11 -3
View File
@@ -54,14 +54,22 @@ public:
bool send(std::string_view message, uWS::OpCode opCode = uWS::OpCode::BINARY, bool compress = false) { bool send(std::string_view message, uWS::OpCode opCode = uWS::OpCode::BINARY, bool compress = false) {
/* Transform the message to compressed domain if requested */ /* Transform the message to compressed domain if requested */
if (compress) { if (compress) {
//message = ; WebSocketData *webSocketData = (WebSocketData *) Super::getExt();
std::cout << "send compression ignored!" << std::endl;
/* Check and correct the compress hint */
if (opCode < 3 && webSocketData->compressionStatus == WebSocketData::ENABLED) {
// todo: shared deflate window
LoopData *loopData = Super::getLoopData();
message = loopData->deflationStream->deflate(message);
} else {
compress = false;
}
} }
/* Get size, alloate size, write if needed */ /* Get size, alloate size, write if needed */
size_t messageFrameSize = WebSocketProtocol<isServer, WebSocket<SSL, isServer>>::messageFrameSize(message.length()); size_t messageFrameSize = WebSocketProtocol<isServer, WebSocket<SSL, isServer>>::messageFrameSize(message.length());
auto[sendBuffer, requiresWrite] = Super::getSendBuffer(messageFrameSize); auto[sendBuffer, requiresWrite] = Super::getSendBuffer(messageFrameSize);
WebSocketProtocol<isServer, WebSocket<SSL, isServer>>::formatMessage(sendBuffer, message.data(), message.length(), opCode, message.length(), false); WebSocketProtocol<isServer, WebSocket<SSL, isServer>>::formatMessage(sendBuffer, message.data(), message.length(), opCode, message.length(), compress);
if (requiresWrite) { if (requiresWrite) {
auto[written, failed] = Super::write(sendBuffer, messageFrameSize); auto[written, failed] = Super::write(sendBuffer, messageFrameSize);
+1 -1
View File
@@ -39,7 +39,7 @@ private:
} compressionStatus; } compressionStatus;
public: public:
WebSocketData(bool perMessageDeflate) : WebSocketState<true>() { WebSocketData(bool perMessageDeflate) : WebSocketState<true>() {
std::cout << "perMessageDeflate: " << perMessageDeflate << std::endl; //std::cout << "perMessageDeflate: " << perMessageDeflate << std::endl;
compressionStatus = perMessageDeflate ? ENABLED : DISABLED; compressionStatus = perMessageDeflate ? ENABLED : DISABLED;
} }
}; };
+2
View File
@@ -17,6 +17,8 @@
#ifndef WEBSOCKETPROTOCOL_UWS_H #ifndef WEBSOCKETPROTOCOL_UWS_H
#define WEBSOCKETPROTOCOL_UWS_H #define WEBSOCKETPROTOCOL_UWS_H
// this module depends on windows ws2_lib being linked which is nonsense, fix!
/* This segment is not cross-platform! Fix! */ /* This segment is not cross-platform! Fix! */
/* PortableEndianConversion.h */ /* PortableEndianConversion.h */
#ifdef __linux #ifdef __linux