Skip to content

Commit d47fedb

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 646bccd + 286b974 commit d47fedb

File tree

7 files changed

+107
-10
lines changed

7 files changed

+107
-10
lines changed

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,23 @@ require("gwsockets")
3737
GWSockets.addVerifyPath( "/etc/ssl/certs" )
3838
```
3939

40+
* If you would like to enable the `permessage-deflate` extension which allows you to send and receive compressed messages, you can enable it with the following functions:
41+
42+
```LUA
43+
-- Do note this will only be enabled if the websocket server supports permessage-deflate and enables it during handshake.
44+
WEBSOCKET:setMessageCompression(true)
45+
```
46+
47+
* You can also disable context takeover during compression, which will prevent re-using the same compression context over multiple messages.
48+
This will decrease the memory usage at the cost of a worse compression ratio.
49+
50+
```LUA
51+
WEBSOCKET:setDisableContextTakeover(true)
52+
```
53+
54+
*WARNING:* Enabling compression over encrypted connections (`WSS://`) may make you vulnerable to [CRIME](https://en.wikipedia.org/wiki/CRIME)/[BREACH](https://en.wikipedia.org/wiki/BREACH) attacks.
55+
Make sure you know what you are doing, or avoid sending sensitive information over websocket messages.
56+
4057
* Next add any cookies or headers you would like to send with the initial request (Optional)
4158

4259
```LUA
@@ -67,9 +84,13 @@ require("gwsockets")
6784
* Lastly open the connection
6885

6986
```LUA
70-
WEBSOCKET:open()
87+
WEBSOCKET:open( shouldClearQueue = true )
7188
```
7289

90+
*NOTE:* By default, opening a connection will clear the queued messages. This is due to the fact there
91+
is no way of knowing what's in the queue, and what has been received by the remote. If you would like to
92+
disable this, you may use `open(false)`.
93+
7394
* Once the socket has been opened you can send messages using the `write` function
7495

7596
```LUA

src/GLua.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,17 @@ LUA_FUNCTION(socketOpen)
168168
{
169169
LUA->ThrowError("Cannot open socket that is already connected");
170170
}
171+
172+
const bool shouldClearQueue = LUA->IsType(2, Type::BOOL) ? LUA->GetBool(2) : true;
173+
171174
//As soon as the socket starts connecting we want to keep a reference to the table so that it does not
172175
//get garbage collected, so that the callbacks can be called.
173176
if (socketTableReferences.find(socket) == socketTableReferences.end())
174177
{
175178
LUA->Push(1);
176179
socketTableReferences[socket] = LUA->ReferenceCreate();
177180
}
178-
socket->open();
181+
socket->open(shouldClearQueue);
179182
return 0;
180183
}
181184

@@ -218,6 +221,34 @@ LUA_FUNCTION(socketSetHeader)
218221
return 0;
219222
}
220223

224+
LUA_FUNCTION(socketSetMessageCompression)
225+
{
226+
GWSocket* socket = getCppObject<GWSocket>(LUA);
227+
if (socket->state != STATE_DISCONNECTED)
228+
{
229+
LUA->ThrowError("Cannot set message compression for an already connected websocket");
230+
}
231+
232+
LUA->CheckType(2, Type::BOOL);
233+
socket->setPerMessageDeflate(LUA->GetBool(2));
234+
235+
return 0;
236+
}
237+
238+
LUA_FUNCTION(socketSetDisableContextTakeover)
239+
{
240+
GWSocket* socket = getCppObject<GWSocket>(LUA);
241+
if (socket->state != STATE_DISCONNECTED)
242+
{
243+
LUA->ThrowError("Cannot set compression takeover for an already connected websocket");
244+
}
245+
246+
LUA->CheckType(2, Type::BOOL);
247+
socket->setDisableContextTakeover(LUA->GetBool(2));
248+
249+
return 0;
250+
}
251+
221252
LUA_FUNCTION(socketIsConnected)
222253
{
223254
GWSocket* socket = getCppObject<GWSocket>(LUA);
@@ -421,6 +452,10 @@ GMOD_MODULE_OPEN()
421452
LUA->SetField(-2, "setCookie");
422453
LUA->PushCFunction(socketSetHeader);
423454
LUA->SetField(-2, "setHeader");
455+
LUA->PushCFunction(socketSetMessageCompression);
456+
LUA->SetField(-2, "setMessageCompression");
457+
LUA->PushCFunction(socketSetDisableContextTakeover);
458+
LUA->SetField(-2, "setDisableContextTakeover");
424459
LUA->PushCFunction(socketIsConnected);
425460
LUA->SetField(-2, "isConnected");
426461
LUA->PushCFunction(socketClearQueue);

src/GWSocket.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,14 +211,20 @@ void GWSocket::hostResolvedStep(const boost::system::error_code &ec, tcp::resolv
211211
}
212212
}
213213

214-
void GWSocket::open()
214+
void GWSocket::open(bool shouldClearQueue)
215215
{
216216
auto expected = STATE_DISCONNECTED;
217217
if (!this->state.compare_exchange_strong(expected, STATE_CONNECTING))
218218
{
219219
return;
220220
}
221-
this->clearQueue();
221+
222+
// Clear the queue if it was requested.
223+
if (shouldClearQueue)
224+
{
225+
this->clearQueue();
226+
}
227+
222228
// Look up the domain name
223229
this->resolver.async_resolve(host, std::to_string(port), boost::bind(&GWSocket::hostResolvedStep, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator));
224230
}
@@ -327,4 +333,14 @@ bool GWSocket::setHeader(std::string key, std::string value)
327333
}
328334
this->headers[key] = value;
329335
return true;
330-
}
336+
}
337+
338+
void GWSocket::setPerMessageDeflate(bool value)
339+
{
340+
perMessageDeflate = value;
341+
}
342+
343+
void GWSocket::setDisableContextTakeover(bool value)
344+
{
345+
disableContextTakeover = value;
346+
}

src/GWSocket.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,16 @@ class GWSocket
6363
GWSocket(std::string host, unsigned short port, std::string path) : host(host), port(port), path(path) { };
6464
virtual ~GWSocket() { };
6565

66-
void open();
66+
void open(bool shouldClearQueue = true);
6767
void onDisconnected(const boost::system::error_code & ec);
6868
bool close();
6969
bool closeNow();
7070
void write(std::string message);
7171
bool setCookie(std::string key, std::string value);
7272
bool setHeader(std::string key, std::string value);
73+
void setPerMessageDeflate(bool value);
74+
void setDisableContextTakeover(bool value);
75+
7376
BlockingQueue<GWSocketMessageIn> messageQueue;
7477
bool isConnected() { return state == STATE_CONNECTED; };
7578
bool canBeDeleted() { return state == STATE_DISCONNECTED; };
@@ -78,6 +81,8 @@ class GWSocket
7881
std::string host;
7982
unsigned int port;
8083
std::atomic<SocketState> state{ STATE_DISCONNECTED };
84+
bool perMessageDeflate;
85+
bool disableContextTakeover;
8186

8287
static std::unique_ptr<boost::asio::io_context> ioc; //Needs to be initialized on module load
8388
protected:

src/SSLWebSocket.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class SSLWebSocket : public GWSocket
2828
void closeSocket();
2929
void sslHandshakeComplete(const boost::system::error_code& ec, std::string host, std::string path, std::function<void(websocket::request_type&)> decorator);
3030
bool verifyCertificate(bool preverified, boost::asio::ssl::verify_context& ctx);
31-
std::atomic<websocket::stream<ssl::stream<tcp::socket>>*> ws{ nullptr };
31+
std::atomic<websocket::stream<ssl::stream<tcp::socket>, true>*> ws{ nullptr };
3232
//This is not an atomic function, it only ensures visibility.
3333
//Callers have to make sure that atomicity is not required/ensured otherwise
3434
void resetWS()
@@ -38,8 +38,18 @@ class SSLWebSocket : public GWSocket
3838
{
3939
delete stream;
4040
}
41-
ws = new websocket::stream<ssl::stream<tcp::socket>>(*ioc, *sslContext);
41+
42+
auto newWS = new websocket::stream<ssl::stream<tcp::socket>, true>(*ioc, *sslContext);
43+
44+
// Set permessage-deflate options for message compression if requested.
45+
websocket::permessage_deflate opts;
46+
opts.client_enable = perMessageDeflate;
47+
opts.client_no_context_takeover = disableContextTakeover;
48+
newWS->set_option(opts);
49+
50+
ws = newWS;
4251
}
52+
4353
websocket::stream<ssl::stream<tcp::socket>>* getWS()
4454
{
4555
return this->ws.load();

src/WebSocket.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,18 @@ class WebSocket : public GWSocket
3232
{
3333
delete stream;
3434
}
35-
ws = new websocket::stream<tcp::socket>(*ioc);
35+
36+
auto newWS = new websocket::stream<tcp::socket, true>(*ioc);
37+
38+
// Set permessage-deflate options for message compression if requested.
39+
websocket::permessage_deflate opts;
40+
opts.client_enable = perMessageDeflate;
41+
opts.client_no_context_takeover = disableContextTakeover;
42+
newWS->set_option(opts);
43+
44+
ws = newWS;
3645
}
46+
3747
websocket::stream<tcp::socket>* getWS()
3848
{
3949
return this->ws.load();

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.2.0
1+
1.3.0

0 commit comments

Comments
 (0)