@encharm/cws 4.8.4 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/CLAUDE.md +5 -1
- package/README.md +12 -0
- package/binding.gyp +4 -2
- package/deps/readerwriterqueue/LICENSE.md +28 -0
- package/deps/readerwriterqueue/README.md +186 -0
- package/deps/readerwriterqueue/atomicops.h +761 -0
- package/deps/readerwriterqueue/readerwriterqueue.h +979 -0
- package/dist/bindings/cws_darwin_arm64_node115.node +0 -0
- package/dist/bindings/cws_darwin_arm64_node127.node +0 -0
- package/dist/bindings/cws_darwin_arm64_node137.node +0 -0
- package/dist/bindings/cws_darwin_arm64_node147.node +0 -0
- package/dist/bindings/cws_linux_arm64_node115.node +0 -0
- package/dist/bindings/cws_linux_arm64_node127.node +0 -0
- package/dist/bindings/cws_linux_arm64_node137.node +0 -0
- package/dist/bindings/cws_linux_arm64_node147.node +0 -0
- package/dist/bindings/cws_linux_x64_node115.node +0 -0
- package/dist/bindings/cws_linux_x64_node127.node +0 -0
- package/dist/bindings/cws_linux_x64_node137.node +0 -0
- package/dist/bindings/cws_linux_x64_node147.node +0 -0
- package/dist/bindings/cws_win32_x64_node115.node +0 -0
- package/dist/bindings/cws_win32_x64_node127.node +0 -0
- package/dist/bindings/cws_win32_x64_node137.node +0 -0
- package/dist/bindings/cws_win32_x64_node147.node +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -0
- package/dist/shared.d.ts +1 -0
- package/dist/shared.js +2 -1
- package/package.json +1 -1
- package/src/Addon.cpp +2 -0
- package/src/Addon.h +6 -0
- package/src/Hub.cpp +4 -2
- package/src/MicroDeflate.h +109 -0
- package/src/Networking.h +5 -2
- package/src/SendWorker.cpp +229 -0
- package/src/SendWorker.h +25 -0
- package/src/Socket.h +195 -4
- package/src/WebSocket.cpp +20 -2
- package/src/WebSocket.h +1 -0
- package/src/Zlib.cpp +33 -1
- package/src/Zlib.h +6 -0
package/src/Socket.h
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
#define SOCKET_CWS_H
|
|
3
3
|
|
|
4
4
|
#include "Networking.h"
|
|
5
|
+
#include "SendWorker.h"
|
|
6
|
+
#include "Zlib.h"
|
|
7
|
+
#include <vector>
|
|
5
8
|
#ifndef _WIN32
|
|
6
9
|
#include <sys/uio.h>
|
|
7
10
|
#endif
|
|
@@ -42,6 +45,7 @@ protected:
|
|
|
42
45
|
bool corkable = false;
|
|
43
46
|
bool corkPending = false;
|
|
44
47
|
|
|
48
|
+
public:
|
|
45
49
|
// this is not needed by HttpSocket!
|
|
46
50
|
struct Queue {
|
|
47
51
|
struct Message {
|
|
@@ -54,12 +58,21 @@ protected:
|
|
|
54
58
|
// -1: heap (new char[]). Messages are allocated as raw char arrays, so every
|
|
55
59
|
// allocation site must set this explicitly (the initializers above never run).
|
|
56
60
|
int poolIndex = -1;
|
|
61
|
+
// data lives in its own new[] buffer (not inside this block); freed with delete[].
|
|
62
|
+
bool ownsData = false;
|
|
63
|
+
// data is a raw, unframed payload still to be deflated and framed (by the send
|
|
64
|
+
// worker, or by materializeCb if a main-thread write path reaches it first).
|
|
65
|
+
bool compressPending = false;
|
|
66
|
+
unsigned char opCode = 0;
|
|
57
67
|
};
|
|
58
68
|
|
|
59
69
|
Message *head = nullptr, *tail = nullptr;
|
|
60
70
|
size_t totalLength = 0;
|
|
61
71
|
|
|
62
72
|
static void release(NodeData *nodeData, Message *message) {
|
|
73
|
+
if (message->ownsData) {
|
|
74
|
+
delete [] (char *) message->data;
|
|
75
|
+
}
|
|
63
76
|
if (message->poolIndex >= 0) {
|
|
64
77
|
nodeData->freeSmallMemoryBlock((char *) message, message->poolIndex);
|
|
65
78
|
} else {
|
|
@@ -98,6 +111,55 @@ protected:
|
|
|
98
111
|
}
|
|
99
112
|
} messageQueue;
|
|
100
113
|
|
|
114
|
+
// One in-flight worker-thread send per socket (see SendWorker.h). The frames it
|
|
115
|
+
// covers are moved out of messageQueue into the op, so nothing the worker reads
|
|
116
|
+
// can be freed underneath it; the main thread takes them back on completion.
|
|
117
|
+
// `socket` is nulled if the socket closes first, and the fd is then closed by the
|
|
118
|
+
// completion (never while the worker may still be inside a send on it).
|
|
119
|
+
struct SendOp {
|
|
120
|
+
Socket *socket;
|
|
121
|
+
NodeData *nodeData;
|
|
122
|
+
Queue::Message *head, *tail;
|
|
123
|
+
size_t bytes;
|
|
124
|
+
int count;
|
|
125
|
+
uv_os_sock_t fd;
|
|
126
|
+
ssize_t result;
|
|
127
|
+
int error;
|
|
128
|
+
bool closeFd;
|
|
129
|
+
void *deflateWindow; // this socket's sliding window (nullptr: worker's shared compressor)
|
|
130
|
+
void *destroyWindow; // set when the socket closed mid-flight: completion destroys it
|
|
131
|
+
#ifdef _WIN32
|
|
132
|
+
WSABUF bufs[512];
|
|
133
|
+
#else
|
|
134
|
+
struct iovec iov[512];
|
|
135
|
+
#endif
|
|
136
|
+
};
|
|
137
|
+
static void performSend(SendOp *op); // worker thread
|
|
138
|
+
static void sendComplete(SendOp *op); // main thread
|
|
139
|
+
static void materializeOnMain(Socket *s, Queue::Message *m, cWS::zlib::Stream *stream, bool resetAfter, char *buffer, size_t bufferSize, std::string &dynamic);
|
|
140
|
+
protected:
|
|
141
|
+
SendOp *sendOp = nullptr;
|
|
142
|
+
// Set by setState<STATE>(): STATE::onEnd, so the completion path can end a socket on a hard error.
|
|
143
|
+
void (*endCb)(Socket *) = nullptr;
|
|
144
|
+
// Per-socket deflate window handed to worker ops (WebSocket sets it); once an op has
|
|
145
|
+
// been orphaned by close, the op owns the window and the socket must not destroy it.
|
|
146
|
+
void *workerDeflateWindow = nullptr;
|
|
147
|
+
bool workerOwnsWindow = false;
|
|
148
|
+
// Deflates + frames a compressPending message on the main thread (set by WebSocket).
|
|
149
|
+
void (*materializeCb)(Socket *, Queue::Message *) = nullptr;
|
|
150
|
+
|
|
151
|
+
void materializePending() {
|
|
152
|
+
for (Queue::Message *m = messageQueue.front(); m; m = m->nextMessage) {
|
|
153
|
+
if (m->compressPending && materializeCb) {
|
|
154
|
+
materializeCb(this, m);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
bool sendBusy() {
|
|
160
|
+
return sendOp != nullptr;
|
|
161
|
+
}
|
|
162
|
+
|
|
101
163
|
int getPoll() {
|
|
102
164
|
return state.poll;
|
|
103
165
|
}
|
|
@@ -258,10 +320,13 @@ protected:
|
|
|
258
320
|
}
|
|
259
321
|
|
|
260
322
|
if (events & UV_WRITABLE) {
|
|
261
|
-
if (!socket->messageQueue.empty() && (events & UV_WRITABLE)) {
|
|
323
|
+
if (!socket->messageQueue.empty() && (events & UV_WRITABLE) && !socket->sendBusy()) {
|
|
262
324
|
socket->cork(true);
|
|
263
325
|
while (true) {
|
|
264
326
|
Queue::Message *messagePtr = socket->messageQueue.front();
|
|
327
|
+
if (messagePtr->compressPending && socket->materializeCb) {
|
|
328
|
+
socket->materializeCb(socket, messagePtr);
|
|
329
|
+
}
|
|
265
330
|
ssize_t sent = ::send(socket->getFd(), messagePtr->data, messagePtr->length, MSG_NOSIGNAL);
|
|
266
331
|
if (sent == (ssize_t) messagePtr->length) {
|
|
267
332
|
// Pop before the callback: it may close/terminate this socket,
|
|
@@ -310,6 +375,7 @@ protected:
|
|
|
310
375
|
|
|
311
376
|
template<class STATE>
|
|
312
377
|
void setState() {
|
|
378
|
+
endCb = STATE::onEnd;
|
|
313
379
|
if (ssl) {
|
|
314
380
|
setCb(sslIoHandler<STATE>);
|
|
315
381
|
} else {
|
|
@@ -334,6 +400,9 @@ protected:
|
|
|
334
400
|
messagePtr->callbackData = nullptr;
|
|
335
401
|
messagePtr->reserved = nullptr;
|
|
336
402
|
messagePtr->poolIndex = -1;
|
|
403
|
+
messagePtr->ownsData = false;
|
|
404
|
+
messagePtr->compressPending = false;
|
|
405
|
+
messagePtr->opCode = 0;
|
|
337
406
|
|
|
338
407
|
if (data) {
|
|
339
408
|
memcpy((char *) messagePtr->data, data, messagePtr->length);
|
|
@@ -348,6 +417,12 @@ protected:
|
|
|
348
417
|
|
|
349
418
|
bool write(Queue::Message *message, bool &wasTransferred) {
|
|
350
419
|
ssize_t sent = 0;
|
|
420
|
+
if (sendBusy()) {
|
|
421
|
+
// a worker send is in flight: anything written now must follow it
|
|
422
|
+
messageQueue.push(message);
|
|
423
|
+
wasTransferred = true;
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
351
426
|
if (messageQueue.empty()) {
|
|
352
427
|
|
|
353
428
|
if (ssl) {
|
|
@@ -408,6 +483,9 @@ protected:
|
|
|
408
483
|
messagePtr = (Queue::Message *) nodeData->getSmallMemoryBlock(memoryIndex);
|
|
409
484
|
messagePtr->data = ((char *) messagePtr) + sizeof(Queue::Message);
|
|
410
485
|
messagePtr->poolIndex = memoryIndex;
|
|
486
|
+
messagePtr->ownsData = false;
|
|
487
|
+
messagePtr->compressPending = false;
|
|
488
|
+
messagePtr->opCode = 0;
|
|
411
489
|
} else {
|
|
412
490
|
messagePtr = allocMessage(estimatedLength - sizeof(Queue::Message));
|
|
413
491
|
}
|
|
@@ -435,6 +513,9 @@ protected:
|
|
|
435
513
|
messagePtr->nextMessage = nullptr;
|
|
436
514
|
messagePtr->reserved = nullptr;
|
|
437
515
|
messagePtr->poolIndex = memoryIndex;
|
|
516
|
+
messagePtr->ownsData = false;
|
|
517
|
+
messagePtr->compressPending = false;
|
|
518
|
+
messagePtr->opCode = 0;
|
|
438
519
|
|
|
439
520
|
bool wasTransferred;
|
|
440
521
|
if (write(messagePtr, wasTransferred)) {
|
|
@@ -486,7 +567,7 @@ protected:
|
|
|
486
567
|
|
|
487
568
|
public:
|
|
488
569
|
auto getBufferedAmount() {
|
|
489
|
-
return messageQueue.totalLength;
|
|
570
|
+
return messageQueue.totalLength + (sendOp ? sendOp->bytes : 0);
|
|
490
571
|
}
|
|
491
572
|
|
|
492
573
|
Socket(NodeData *nodeData, Loop *loop, uv_os_sock_t fd, SSL *ssl) : Poll(loop, fd), ssl(ssl), nodeData(nodeData) {
|
|
@@ -639,6 +720,20 @@ public:
|
|
|
639
720
|
return;
|
|
640
721
|
}
|
|
641
722
|
|
|
723
|
+
if (!ssl && SendWorker::active()) {
|
|
724
|
+
if (sendBusy()) {
|
|
725
|
+
return; // the completion submits what has queued up meanwhile
|
|
726
|
+
}
|
|
727
|
+
if (getPoll() & UV_WRITABLE) {
|
|
728
|
+
change(nodeData->loop, this, setPoll(UV_READABLE));
|
|
729
|
+
}
|
|
730
|
+
if (submitToWorker()) {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
// worker queue full: fall through and send synchronously this tick
|
|
734
|
+
}
|
|
735
|
+
materializePending();
|
|
736
|
+
|
|
642
737
|
std::vector<PendingCallback> callbacks;
|
|
643
738
|
bool stalled = false;
|
|
644
739
|
while (!messageQueue.empty() && !stalled) {
|
|
@@ -675,9 +770,10 @@ public:
|
|
|
675
770
|
return;
|
|
676
771
|
}
|
|
677
772
|
unregisterCork();
|
|
678
|
-
if (isClosed()) {
|
|
773
|
+
if (isClosed() || sendBusy()) {
|
|
679
774
|
return;
|
|
680
775
|
}
|
|
776
|
+
materializePending();
|
|
681
777
|
while (!messageQueue.empty()) {
|
|
682
778
|
ssize_t sent = writeQueueGathered();
|
|
683
779
|
if (sent <= 0 || consumeSent(sent, nullptr)) {
|
|
@@ -701,13 +797,108 @@ public:
|
|
|
701
797
|
}
|
|
702
798
|
}
|
|
703
799
|
|
|
800
|
+
// Moves up to 512 queued frames into an op and hands it to the worker thread.
|
|
801
|
+
bool submitToWorker() {
|
|
802
|
+
if (sendOp || messageQueue.empty() || isClosed()) {
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
SendOp *op = new SendOp();
|
|
806
|
+
op->socket = this;
|
|
807
|
+
op->nodeData = nodeData;
|
|
808
|
+
op->fd = getFd();
|
|
809
|
+
op->head = messageQueue.head;
|
|
810
|
+
op->bytes = 0;
|
|
811
|
+
op->count = 0;
|
|
812
|
+
op->result = 0;
|
|
813
|
+
op->error = 0;
|
|
814
|
+
op->closeFd = false;
|
|
815
|
+
op->deflateWindow = workerDeflateWindow;
|
|
816
|
+
op->destroyWindow = nullptr;
|
|
817
|
+
Queue::Message *m = messageQueue.head, *last = nullptr;
|
|
818
|
+
while (m && op->count < 512) {
|
|
819
|
+
// iov entries are filled by the worker once pending messages are compressed
|
|
820
|
+
op->bytes += m->length;
|
|
821
|
+
op->count++;
|
|
822
|
+
last = m;
|
|
823
|
+
m = m->nextMessage;
|
|
824
|
+
}
|
|
825
|
+
op->tail = last;
|
|
826
|
+
last->nextMessage = nullptr;
|
|
827
|
+
messageQueue.head = m;
|
|
828
|
+
if (!m) {
|
|
829
|
+
messageQueue.tail = nullptr;
|
|
830
|
+
messageQueue.totalLength = 0;
|
|
831
|
+
} else {
|
|
832
|
+
messageQueue.totalLength -= op->bytes;
|
|
833
|
+
}
|
|
834
|
+
if (!SendWorker::submit(op)) {
|
|
835
|
+
requeueFront(op);
|
|
836
|
+
delete op;
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
sendOp = op;
|
|
840
|
+
return true;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Queues a raw payload to be deflated and framed on the send worker (WebSocket::send
|
|
844
|
+
// uses this for compressed messages when the worker is active). The payload is copied
|
|
845
|
+
// because callers may reuse their buffer as soon as send() returns.
|
|
846
|
+
void enqueueCompressPending(const char *payload, size_t length, unsigned char opCode,
|
|
847
|
+
void(*callback)(void *socket, void *data, bool cancelled, void *reserved), void *callbackData) {
|
|
848
|
+
int memoryIndex = nodeData->getMemoryBlockIndex(sizeof(Queue::Message));
|
|
849
|
+
Queue::Message *messagePtr = (Queue::Message *) nodeData->getSmallMemoryBlock(memoryIndex);
|
|
850
|
+
char *raw = new char[length ? length : 1];
|
|
851
|
+
memcpy(raw, payload, length);
|
|
852
|
+
messagePtr->data = raw;
|
|
853
|
+
messagePtr->length = length;
|
|
854
|
+
messagePtr->nextMessage = nullptr;
|
|
855
|
+
messagePtr->callback = callback;
|
|
856
|
+
messagePtr->callbackData = callbackData;
|
|
857
|
+
messagePtr->reserved = nullptr;
|
|
858
|
+
messagePtr->poolIndex = memoryIndex;
|
|
859
|
+
messagePtr->ownsData = true;
|
|
860
|
+
messagePtr->compressPending = true;
|
|
861
|
+
messagePtr->opCode = opCode;
|
|
862
|
+
enqueue(messagePtr);
|
|
863
|
+
if (!corkPending) {
|
|
864
|
+
corkPending = true;
|
|
865
|
+
nodeData->corkState->pending.push_back(this);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Puts what is left of an op back at the head of the queue, in order.
|
|
870
|
+
void requeueFront(SendOp *op) {
|
|
871
|
+
if (!op->head) {
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
op->tail->nextMessage = messageQueue.head;
|
|
875
|
+
messageQueue.head = op->head;
|
|
876
|
+
if (!messageQueue.tail) {
|
|
877
|
+
messageQueue.tail = op->tail;
|
|
878
|
+
}
|
|
879
|
+
messageQueue.totalLength += op->bytes;
|
|
880
|
+
}
|
|
881
|
+
|
|
704
882
|
template <class T>
|
|
705
883
|
void closeSocket() {
|
|
706
884
|
unregisterCork();
|
|
707
885
|
uv_os_sock_t fd = getFd();
|
|
708
886
|
Context *netContext = nodeData->netContext;
|
|
709
887
|
stop(nodeData->loop);
|
|
710
|
-
|
|
888
|
+
if (sendOp) {
|
|
889
|
+
// The worker may be inside a send on this fd: closing it now could let the
|
|
890
|
+
// number be reused by a new connection and receive our bytes. Orphan the op
|
|
891
|
+
// and let its completion close the fd.
|
|
892
|
+
sendOp->socket = nullptr;
|
|
893
|
+
sendOp->closeFd = true;
|
|
894
|
+
if (workerDeflateWindow) {
|
|
895
|
+
sendOp->destroyWindow = workerDeflateWindow; // the worker may still be using it
|
|
896
|
+
workerOwnsWindow = true;
|
|
897
|
+
}
|
|
898
|
+
sendOp = nullptr;
|
|
899
|
+
} else {
|
|
900
|
+
netContext->closeSocket(fd);
|
|
901
|
+
}
|
|
711
902
|
|
|
712
903
|
if (ssl) {
|
|
713
904
|
SSL_free(ssl);
|
package/src/WebSocket.cpp
CHANGED
|
@@ -14,6 +14,8 @@ WebSocket<isServer>::WebSocket(bool perMessageDeflate, cS::Socket *socket) : cS:
|
|
|
14
14
|
Group<isServer> *group = Group<isServer>::from(this);
|
|
15
15
|
slidingDeflateWindow = Hub::allocateDefaultCompressor(group->deflateLevel, group->deflateWindowBits, group->deflateMemLevel);
|
|
16
16
|
}
|
|
17
|
+
workerDeflateWindow = slidingDeflateWindow;
|
|
18
|
+
materializeCb = WebSocket<isServer>::materialize;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
/*
|
|
@@ -61,9 +63,25 @@ void WebSocket<isServer>::send(const char *message, size_t length, OpCode opCode
|
|
|
61
63
|
}
|
|
62
64
|
};
|
|
63
65
|
|
|
66
|
+
if (transformData.compress && !ssl && corkActive() && cS::SendWorker::active()) {
|
|
67
|
+
// deflate + framing happen on the send worker, not on the JS thread
|
|
68
|
+
enqueueCompressPending(message, length, (unsigned char) opCode, (void(*)(void *, void *, bool, void *)) callback, callbackData);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
64
72
|
sendTransformed<WebSocketTransformer>((char *) message, length, (void(*)(void *, void *, bool, void *)) callback, callbackData, transformData);
|
|
65
73
|
}
|
|
66
74
|
|
|
75
|
+
// Main-thread fallback for a compressPending message that a synchronous write path reached
|
|
76
|
+
// (same-tick terminate, full worker queue, drain loop after a short write).
|
|
77
|
+
template <bool isServer>
|
|
78
|
+
void WebSocket<isServer>::materialize(cS::Socket *s, cS::Socket::Queue::Message *m) {
|
|
79
|
+
WebSocket<isServer> *webSocket = static_cast<WebSocket<isServer> *>(s);
|
|
80
|
+
Hub *hub = Group<isServer>::from(webSocket)->hub;
|
|
81
|
+
zlib::Stream *stream = webSocket->slidingDeflateWindow ? (zlib::Stream *) webSocket->slidingDeflateWindow : hub->deflationStream;
|
|
82
|
+
cS::Socket::materializeOnMain(s, m, stream, !webSocket->slidingDeflateWindow, hub->zlibBuffer, Hub::LARGE_BUFFER_SIZE, hub->dynamicZlibBuffer);
|
|
83
|
+
}
|
|
84
|
+
|
|
67
85
|
/*
|
|
68
86
|
* Prepares a single message for use with sendPrepared.
|
|
69
87
|
*
|
|
@@ -320,8 +338,8 @@ void WebSocket<isServer>::onEnd(cS::Socket *s) {
|
|
|
320
338
|
|
|
321
339
|
webSocket->nodeData->clearPendingPollChanges(webSocket);
|
|
322
340
|
|
|
323
|
-
// remove any per-websocket zlib memory
|
|
324
|
-
if (webSocket->slidingDeflateWindow) {
|
|
341
|
+
// remove any per-websocket zlib memory (unless an orphaned send op took it over)
|
|
342
|
+
if (webSocket->slidingDeflateWindow && !webSocket->workerOwnsWindow) {
|
|
325
343
|
// this relates to Hub::allocateDefaultCompressor
|
|
326
344
|
zlib::destroy((zlib::Stream *) webSocket->slidingDeflateWindow);
|
|
327
345
|
webSocket->slidingDeflateWindow = nullptr;
|
package/src/WebSocket.h
CHANGED
package/src/Zlib.cpp
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#include "Zlib.h"
|
|
2
|
+
#include "MicroDeflate.h"
|
|
3
|
+
#include <cstdlib>
|
|
4
|
+
#include <cstring>
|
|
2
5
|
|
|
3
6
|
#ifdef CWS_ZLIB_NG
|
|
4
7
|
#include "zlib-ng.h"
|
|
@@ -133,8 +136,37 @@ char *inflate(Stream *stream, char *data, size_t &length, size_t maxPayload, cha
|
|
|
133
136
|
return buffer;
|
|
134
137
|
}
|
|
135
138
|
|
|
139
|
+
bool microDeflateEnabled() {
|
|
140
|
+
static int enabled = -1;
|
|
141
|
+
if (enabled < 0) {
|
|
142
|
+
const char *v = getenv("CWS_MICRO_DEFLATE");
|
|
143
|
+
enabled = !(v && (!strcmp(v, "0") || !strcmp(v, "false") || !strcmp(v, "off") || !strcmp(v, "no")));
|
|
144
|
+
}
|
|
145
|
+
return enabled == 1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
char *deflateIndependent(Stream *fallback, char *data, size_t &length, char *buffer, size_t bufferSize, std::string &dynamic) {
|
|
149
|
+
if (!microDeflateEnabled()) {
|
|
150
|
+
return deflate(fallback, data, length, buffer, bufferSize, dynamic, true);
|
|
151
|
+
}
|
|
152
|
+
static thread_local microdeflate::Encoder *encoder = nullptr;
|
|
153
|
+
if (!encoder) {
|
|
154
|
+
encoder = new microdeflate::Encoder();
|
|
155
|
+
}
|
|
156
|
+
size_t need = microdeflate::bound(length);
|
|
157
|
+
uint8_t *out;
|
|
158
|
+
if (need <= bufferSize) {
|
|
159
|
+
out = (uint8_t *) buffer;
|
|
160
|
+
} else {
|
|
161
|
+
dynamic.resize(need);
|
|
162
|
+
out = (uint8_t *) &dynamic[0];
|
|
163
|
+
}
|
|
164
|
+
length = encoder->compress((const uint8_t *) data, length, out);
|
|
165
|
+
return (char *) out;
|
|
166
|
+
}
|
|
167
|
+
|
|
136
168
|
const char *backend() {
|
|
137
|
-
return CWS_ZLIB_BACKEND;
|
|
169
|
+
return microDeflateEnabled() ? CWS_ZLIB_BACKEND " + microdeflate" : CWS_ZLIB_BACKEND;
|
|
138
170
|
}
|
|
139
171
|
|
|
140
172
|
}
|
package/src/Zlib.h
CHANGED
|
@@ -29,6 +29,12 @@ char *deflate(Stream *stream, char *data, size_t &length, char *buffer, size_t b
|
|
|
29
29
|
// and length 0 on error or when the output would exceed maxPayload. Always resets.
|
|
30
30
|
char *inflate(Stream *stream, char *data, size_t &length, size_t maxPayload, char *buffer, size_t bufferSize, std::string &dynamic);
|
|
31
31
|
|
|
32
|
+
// Compresses one independent message (no context takeover). Uses the built-in
|
|
33
|
+
// microdeflate encoder unless CWS_MICRO_DEFLATE=0, in which case `fallback` (a
|
|
34
|
+
// shared zlib stream, reset after) is used. Same output contract as deflate().
|
|
35
|
+
char *deflateIndependent(Stream *fallback, char *data, size_t &length, char *buffer, size_t bufferSize, std::string &dynamic);
|
|
36
|
+
bool microDeflateEnabled();
|
|
37
|
+
|
|
32
38
|
// e.g. "zlib-ng 2.2.4" or "zlib 1.3.1"
|
|
33
39
|
const char *backend();
|
|
34
40
|
|