opal-up 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,10 +18,10 @@
18
18
  #ifndef UWS_WEBSOCKET_H
19
19
  #define UWS_WEBSOCKET_H
20
20
 
21
- #include "WebSocketData.h"
22
- #include "WebSocketProtocol.h"
23
21
  #include "AsyncSocket.h"
24
22
  #include "WebSocketContextData.h"
23
+ #include "WebSocketData.h"
24
+ #include "WebSocketProtocol.h"
25
25
 
26
26
  #include <string_view>
27
27
 
@@ -29,353 +29,419 @@ namespace uWS {
29
29
 
30
30
  template <bool SSL, bool isServer, typename USERDATA>
31
31
  struct WebSocket : AsyncSocket<SSL> {
32
- template <bool> friend struct TemplatedApp;
33
- template <bool> friend struct HttpResponse;
34
- private:
35
- typedef AsyncSocket<SSL> Super;
36
-
37
- void *init(bool perMessageDeflate, CompressOptions compressOptions, BackPressure &&backpressure) {
38
- new (us_socket_ext(SSL, (us_socket_t *) this)) WebSocketData(perMessageDeflate, compressOptions, std::move(backpressure));
39
- return this;
40
- }
41
- public:
42
-
43
- /* Returns pointer to the per socket user data */
44
- USERDATA *getUserData() {
45
- WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) this);
46
- /* We just have it overallocated by sizeof type */
47
- return (USERDATA *) (webSocketData + 1);
48
- }
49
-
50
- /* See AsyncSocket */
51
- using Super::getBufferedAmount;
52
- using Super::getRemoteAddress;
53
- using Super::getRemoteAddressAsText;
54
- using Super::getNativeHandle;
55
-
56
- /* WebSocket close cannot be an alias to AsyncSocket::close since
57
- * we need to check first if it was shut down by remote peer */
58
- us_socket_t *close() {
59
- if (us_socket_is_closed(SSL, (us_socket_t *) this)) {
60
- return nullptr;
61
- }
62
- WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData();
63
- if (webSocketData->isShuttingDown) {
64
- return nullptr;
65
- }
32
+ template <bool> friend struct TemplatedApp;
33
+ template <bool> friend struct HttpResponse;
66
34
 
67
- return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr);
68
- }
35
+ private:
36
+ typedef AsyncSocket<SSL> Super;
69
37
 
70
- enum SendStatus : int {
71
- BACKPRESSURE,
72
- SUCCESS,
73
- DROPPED
74
- };
38
+ void *init(bool perMessageDeflate, CompressOptions compressOptions,
39
+ BackPressure &&backpressure) {
40
+ new (us_socket_ext(SSL, (us_socket_t *)this)) WebSocketData(
41
+ perMessageDeflate, compressOptions, std::move(backpressure));
42
+ return this;
43
+ }
75
44
 
76
- /* Sending fragmented messages puts a bit of effort on the user; you must not interleave regular sends
77
- * with fragmented sends and you must sendFirstFragment, [sendFragment], then finally sendLastFragment. */
78
- SendStatus sendFirstFragment(std::string_view message, OpCode opCode = OpCode::BINARY, bool compress = false) {
79
- return send(message, opCode, compress, false);
45
+ public:
46
+ /* Returns pointer to the per socket user data */
47
+ USERDATA *getUserData() {
48
+ WebSocketData *webSocketData =
49
+ (WebSocketData *)us_socket_ext(SSL, (us_socket_t *)this);
50
+ /* We just have it overallocated by sizeof type */
51
+ return (USERDATA *)(webSocketData + 1);
52
+ }
53
+
54
+ /* See AsyncSocket */
55
+ using Super::getBufferedAmount;
56
+ using Super::getNativeHandle;
57
+ using Super::getRemoteAddress;
58
+ using Super::getRemoteAddressAsText;
59
+
60
+ /* WebSocket close cannot be an alias to AsyncSocket::close since
61
+ * we need to check first if it was shut down by remote peer */
62
+ us_socket_t *close() {
63
+ if (us_socket_is_closed(SSL, (us_socket_t *)this)) {
64
+ return nullptr;
80
65
  }
81
-
82
- SendStatus sendFragment(std::string_view message, bool compress = false) {
83
- return send(message, CONTINUATION, compress, false);
66
+ WebSocketData *webSocketData = (WebSocketData *)Super::getAsyncSocketData();
67
+ if (webSocketData->isShuttingDown) {
68
+ return nullptr;
84
69
  }
85
70
 
86
- SendStatus sendLastFragment(std::string_view message, bool compress = false) {
87
- return send(message, CONTINUATION, compress, true);
71
+ return us_socket_close(SSL, (us_socket_t *)this, 0, nullptr);
72
+ }
73
+
74
+ enum SendStatus : int { BACKPRESSURE, SUCCESS, DROPPED };
75
+
76
+ /* Sending fragmented messages puts a bit of effort on the user; you must not
77
+ * interleave regular sends with fragmented sends and you must
78
+ * sendFirstFragment, [sendFragment], then finally sendLastFragment. */
79
+ SendStatus sendFirstFragment(std::string_view message,
80
+ OpCode opCode = OpCode::BINARY,
81
+ bool compress = false) {
82
+ return send(message, opCode, compress, false);
83
+ }
84
+
85
+ SendStatus sendFragment(std::string_view message, bool compress = false) {
86
+ return send(message, CONTINUATION, compress, false);
87
+ }
88
+
89
+ SendStatus sendLastFragment(std::string_view message, bool compress = false) {
90
+ return send(message, CONTINUATION, compress, true);
91
+ }
92
+
93
+ /* Send or buffer a WebSocket frame, compressed or not. Returns BACKPRESSURE
94
+ * on increased user space backpressure, DROPPED on dropped message (due to
95
+ * backpressure) or SUCCCESS if you are free to send even more now. */
96
+ SendStatus send(std::string_view message, OpCode opCode = OpCode::BINARY,
97
+ bool compress = false, bool fin = true) {
98
+ WebSocketContextData<SSL, USERDATA> *webSocketContextData =
99
+ (WebSocketContextData<SSL, USERDATA> *)us_socket_context_ext(
100
+ SSL,
101
+ (us_socket_context_t *)us_socket_context(SSL, (us_socket_t *)this));
102
+
103
+ /* Skip sending and report success if we are over the limit of
104
+ * maxBackpressure */
105
+ if (webSocketContextData->maxBackpressure &&
106
+ webSocketContextData->maxBackpressure < getBufferedAmount()) {
107
+ /* Also defer a close if we should */
108
+ if (webSocketContextData->closeOnBackpressureLimit) {
109
+ us_socket_shutdown_read(SSL, (us_socket_t *)this);
110
+ }
111
+
112
+ /* It is okay to call send again from within this callback since we
113
+ * immediately return with DROPPED afterwards */
114
+ if (webSocketContextData->droppedHandler) {
115
+ webSocketContextData->droppedHandler(this, message, opCode);
116
+ }
117
+
118
+ return DROPPED;
88
119
  }
89
120
 
90
- /* Send or buffer a WebSocket frame, compressed or not. Returns BACKPRESSURE on increased user space backpressure,
91
- * DROPPED on dropped message (due to backpressure) or SUCCCESS if you are free to send even more now. */
92
- SendStatus send(std::string_view message, OpCode opCode = OpCode::BINARY, bool compress = false, bool fin = true) {
93
- WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
94
- (us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
95
- );
96
-
97
- /* Skip sending and report success if we are over the limit of maxBackpressure */
98
- if (webSocketContextData->maxBackpressure && webSocketContextData->maxBackpressure < getBufferedAmount()) {
99
- /* Also defer a close if we should */
100
- if (webSocketContextData->closeOnBackpressureLimit) {
101
- us_socket_shutdown_read(SSL, (us_socket_t *) this);
102
- }
103
-
104
- /* It is okay to call send again from within this callback since we immediately return with DROPPED afterwards */
105
- if (webSocketContextData->droppedHandler) {
106
- webSocketContextData->droppedHandler(this, message, opCode);
107
- }
108
-
109
- return DROPPED;
121
+ /* If we are subscribers and have messages to drain we need to drain them
122
+ * here to stay synced */
123
+ WebSocketData *webSocketData = (WebSocketData *)Super::getAsyncSocketData();
124
+
125
+ /* Special path for long sends of non-compressed, non-SSL messages */
126
+ if (message.length() >= 16 * 1024 && !compress && !SSL &&
127
+ !webSocketData->subscriber && getBufferedAmount() == 0 &&
128
+ Super::getLoopData()->corkOffset == 0) {
129
+ char header[10];
130
+ int header_length = (int)protocol::formatMessage<isServer>(
131
+ header, nullptr, 0, opCode, message.length(), compress, fin);
132
+ int written =
133
+ us_socket_write2(0, (struct us_socket_t *)this, header, header_length,
134
+ message.data(), (int)message.length());
135
+
136
+ if (written != header_length + (int)message.length()) {
137
+ /* Buffer up backpressure */
138
+ if (written > header_length) {
139
+ webSocketData->buffer.append(message.data() + written - header_length,
140
+ message.length() -
141
+ (size_t)(written - header_length));
142
+ } else {
143
+ webSocketData->buffer.append(header + written,
144
+ (size_t)header_length - (size_t)written);
145
+ webSocketData->buffer.append(message.data(), message.length());
110
146
  }
111
-
112
- /* If we are subscribers and have messages to drain we need to drain them here to stay synced */
113
- WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData();
114
-
115
- /* Special path for long sends of non-compressed, non-SSL messages */
116
- if (message.length() >= 16 * 1024 && !compress && !SSL && !webSocketData->subscriber && getBufferedAmount() == 0 && Super::getLoopData()->corkOffset == 0) {
117
- char header[10];
118
- int header_length = (int) protocol::formatMessage<isServer>(header, nullptr, 0, opCode, message.length(), compress, fin);
119
- int written = us_socket_write2(0, (struct us_socket_t *)this, header, header_length, message.data(), (int) message.length());
120
-
121
- if (written != header_length + (int) message.length()) {
122
- /* Buffer up backpressure */
123
- if (written > header_length) {
124
- webSocketData->buffer.append(message.data() + written - header_length, message.length() - (size_t) (written - header_length));
125
- } else {
126
- webSocketData->buffer.append(header + written, (size_t) header_length - (size_t) written);
127
- webSocketData->buffer.append(message.data(), message.length());
128
- }
129
- /* We cannot still be corked if we have backpressure.
130
- * We also cannot uncork normally since it will re-write the already buffered
131
- * up backpressure again. */
132
- Super::uncorkWithoutSending();
133
- return BACKPRESSURE;
134
- }
147
+ /* We cannot still be corked if we have backpressure.
148
+ * We also cannot uncork normally since it will re-write the already
149
+ * buffered up backpressure again. */
150
+ Super::uncorkWithoutSending();
151
+ return BACKPRESSURE;
152
+ }
153
+ } else {
154
+
155
+ if (webSocketData->subscriber) {
156
+ /* This will call back into us, send. */
157
+ webSocketContextData->topicTree->drain(webSocketData->subscriber);
158
+ }
159
+
160
+ /* Transform the message to compressed domain if requested */
161
+ if (compress) {
162
+ WebSocketData *webSocketData =
163
+ (WebSocketData *)Super::getAsyncSocketData();
164
+
165
+ /* Check and correct the compress hint. It is never valid to compress 0
166
+ * bytes */
167
+ if (message.length() && opCode < 3 &&
168
+ webSocketData->compressionStatus == WebSocketData::ENABLED) {
169
+ LoopData *loopData = Super::getLoopData();
170
+ /* Compress using either shared or dedicated deflationStream */
171
+ if (webSocketData->deflationStream) {
172
+ message = webSocketData->deflationStream->deflate(
173
+ loopData->zlibContext, message, false);
174
+ } else {
175
+ message = loopData->deflationStream->deflate(loopData->zlibContext,
176
+ message, true);
177
+ }
135
178
  } else {
136
-
137
- if (webSocketData->subscriber) {
138
- /* This will call back into us, send. */
139
- webSocketContextData->topicTree->drain(webSocketData->subscriber);
140
- }
141
-
142
- /* Transform the message to compressed domain if requested */
143
- if (compress) {
144
- WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData();
145
-
146
- /* Check and correct the compress hint. It is never valid to compress 0 bytes */
147
- if (message.length() && opCode < 3 && webSocketData->compressionStatus == WebSocketData::ENABLED) {
148
- LoopData *loopData = Super::getLoopData();
149
- /* Compress using either shared or dedicated deflationStream */
150
- if (webSocketData->deflationStream) {
151
- message = webSocketData->deflationStream->deflate(loopData->zlibContext, message, false);
152
- } else {
153
- message = loopData->deflationStream->deflate(loopData->zlibContext, message, true);
154
- }
155
- } else {
156
- compress = false;
157
- }
158
- }
159
-
160
- /* Get size, allocate size, write if needed */
161
- size_t messageFrameSize = protocol::messageFrameSize(message.length());
162
- auto [sendBuffer, sendBufferAttribute] = Super::getSendBuffer(messageFrameSize);
163
- protocol::formatMessage<isServer>(sendBuffer, message.data(), message.length(), opCode, message.length(), compress, fin);
164
-
165
- /* Depending on size of message we have different paths */
166
- if (sendBufferAttribute == SendBufferAttribute::NEEDS_DRAIN) {
167
- /* This is a drain */
168
- auto[written, failed] = Super::write(nullptr, 0);
169
- if (failed) {
170
- /* Return false for failure, skipping to reset the timeout below */
171
- return BACKPRESSURE;
172
- }
173
- } else if (sendBufferAttribute == SendBufferAttribute::NEEDS_UNCORK) {
174
- /* Uncork if we came here uncorked */
175
- auto [written, failed] = Super::uncork();
176
- if (failed) {
177
- return BACKPRESSURE;
178
- }
179
- }
180
-
179
+ compress = false;
181
180
  }
182
-
183
- /* Every successful send resets the timeout */
184
- if (webSocketContextData->resetIdleTimeoutOnSend) {
185
- Super::timeout(webSocketContextData->idleTimeoutComponents.first);
186
- WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData();
187
- webSocketData->hasTimedOut = false;
181
+ }
182
+
183
+ /* Get size, allocate size, write if needed */
184
+ size_t messageFrameSize = protocol::messageFrameSize(message.length());
185
+ auto [sendBuffer, sendBufferAttribute] =
186
+ Super::getSendBuffer(messageFrameSize);
187
+ protocol::formatMessage<isServer>(sendBuffer, message.data(),
188
+ message.length(), opCode,
189
+ message.length(), compress, fin);
190
+
191
+ /* Depending on size of message we have different paths */
192
+ if (sendBufferAttribute == SendBufferAttribute::NEEDS_DRAIN) {
193
+ /* This is a drain */
194
+ auto [written, failed] = Super::write(nullptr, 0);
195
+ if (failed) {
196
+ /* Return false for failure, skipping to reset the timeout below */
197
+ return BACKPRESSURE;
188
198
  }
189
-
190
- /* Return success */
191
- return SUCCESS;
192
- }
193
-
194
- /* Send websocket close frame, emit close event, send FIN if successful.
195
- * Will not append a close reason if code is 0 or 1005. */
196
- void end(int code = 0, std::string_view message = {}) {
197
- /* Check if we already called this one */
198
- WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) this);
199
- if (webSocketData->isShuttingDown) {
200
- return;
199
+ } else if (sendBufferAttribute == SendBufferAttribute::NEEDS_UNCORK) {
200
+ /* Uncork if we came here uncorked */
201
+ auto [written, failed] = Super::uncork();
202
+ if (failed) {
203
+ return BACKPRESSURE;
201
204
  }
205
+ }
206
+ }
202
207
 
203
- /* We postpone any FIN sending to either drainage or uncorking */
204
- webSocketData->isShuttingDown = true;
205
-
206
- /* Format and send the close frame */
207
- static const int MAX_CLOSE_PAYLOAD = 123;
208
- size_t length = std::min<size_t>(MAX_CLOSE_PAYLOAD, message.length());
209
- char closePayload[MAX_CLOSE_PAYLOAD + 2];
210
- size_t closePayloadLength = protocol::formatClosePayload(closePayload, (uint16_t) code, message.data(), length);
211
- bool ok = send(std::string_view(closePayload, closePayloadLength), OpCode::CLOSE);
212
-
213
- /* FIN if we are ok and not corked */
214
- if (!this->isCorked()) {
215
- if (ok) {
216
- /* If we are not corked, and we just sent off everything, we need to FIN right here.
217
- * In all other cases, we need to fin either if uncork was successful, or when drainage is complete. */
218
- this->shutdown();
219
- }
220
- }
208
+ /* Every successful send resets the timeout */
209
+ if (webSocketContextData->resetIdleTimeoutOnSend) {
210
+ Super::timeout(webSocketContextData->idleTimeoutComponents.first);
211
+ WebSocketData *webSocketData =
212
+ (WebSocketData *)Super::getAsyncSocketData();
213
+ webSocketData->hasTimedOut = false;
214
+ }
221
215
 
222
- WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
223
- (us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
224
- );
216
+ /* Return success */
217
+ return SUCCESS;
218
+ }
219
+
220
+ /* Send websocket close frame, emit close event, send FIN if successful.
221
+ * Will not append a close reason if code is 0 or 1005. */
222
+ void end(int code = 0, std::string_view message = {}) {
223
+ /* Check if we already called this one */
224
+ WebSocketData *webSocketData =
225
+ (WebSocketData *)us_socket_ext(SSL, (us_socket_t *)this);
226
+ if (webSocketData->isShuttingDown) {
227
+ return;
228
+ }
225
229
 
226
- /* Set shorter timeout (use ping-timeout) to avoid long hanging sockets after end() on broken connections */
227
- Super::timeout(webSocketContextData->idleTimeoutComponents.second);
230
+ /* We postpone any FIN sending to either drainage or uncorking */
231
+ webSocketData->isShuttingDown = true;
232
+
233
+ /* Format and send the close frame */
234
+ static const int MAX_CLOSE_PAYLOAD = 123;
235
+ size_t length = std::min<size_t>(MAX_CLOSE_PAYLOAD, message.length());
236
+ char closePayload[MAX_CLOSE_PAYLOAD + 2];
237
+ size_t closePayloadLength = protocol::formatClosePayload(
238
+ closePayload, (uint16_t)code, message.data(), length);
239
+ bool ok =
240
+ send(std::string_view(closePayload, closePayloadLength), OpCode::CLOSE);
241
+
242
+ /* FIN if we are ok and not corked */
243
+ if (!this->isCorked()) {
244
+ if (ok) {
245
+ /* If we are not corked, and we just sent off everything, we need to FIN
246
+ * right here. In all other cases, we need to fin either if uncork was
247
+ * successful, or when drainage is complete. */
248
+ this->shutdown();
249
+ }
250
+ }
228
251
 
229
- /* At this point we iterate all currently held subscriptions and emit an event for all of them */
230
- if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) {
231
- for (Topic *t : webSocketData->subscriber->topics) {
232
- webSocketContextData->subscriptionHandler(this, t->name, (int) t->size() - 1, (int) t->size());
233
- }
234
- }
252
+ WebSocketContextData<SSL, USERDATA> *webSocketContextData =
253
+ (WebSocketContextData<SSL, USERDATA> *)us_socket_context_ext(
254
+ SSL,
255
+ (us_socket_context_t *)us_socket_context(SSL, (us_socket_t *)this));
256
+
257
+ /* Set shorter timeout (use ping-timeout) to avoid long hanging sockets
258
+ * after end() on broken connections */
259
+ Super::timeout(webSocketContextData->idleTimeoutComponents.second);
260
+
261
+ /* At this point we iterate all currently held subscriptions and emit an
262
+ * event for all of them */
263
+ if (webSocketData->subscriber &&
264
+ webSocketContextData->subscriptionHandler) {
265
+ for (Topic *t : webSocketData->subscriber->topics) {
266
+ webSocketContextData->subscriptionHandler(
267
+ this, t->name, (int)t->size() - 1, (int)t->size());
268
+ }
269
+ }
235
270
 
236
- /* Make sure to unsubscribe from any pub/sub node at exit */
237
- webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber);
238
- webSocketData->subscriber = nullptr;
271
+ /* Make sure to unsubscribe from any pub/sub node at exit */
272
+ webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber);
273
+ webSocketData->subscriber = nullptr;
239
274
 
240
- /* Emit close event */
241
- if (webSocketContextData->closeHandler) {
242
- webSocketContextData->closeHandler(this, code, message);
243
- }
275
+ /* Emit close event */
276
+ if (webSocketContextData->closeHandler) {
277
+ webSocketContextData->closeHandler(this, code, message);
244
278
  }
245
-
246
- /* Corks the response if possible. Leaves already corked socket be. */
247
- void cork(MoveOnlyFunction<void()> &&handler) {
248
- if (!Super::isCorked() && Super::canCork()) {
249
- Super::cork();
250
- handler();
251
-
252
- /* There is no timeout when failing to uncork for WebSockets,
253
- * as that is handled by idleTimeout */
254
- auto [written, failed] = Super::uncork();
255
- (void)written;
256
- (void)failed;
257
- } else {
258
- /* We are already corked, or can't cork so let's just call the handler */
259
- handler();
260
- }
279
+ }
280
+
281
+ /* Corks the response if possible. Leaves already corked socket be. */
282
+ void cork(MoveOnlyFunction<void()> &&handler) {
283
+ if (!Super::isCorked() && Super::canCork()) {
284
+ Super::cork();
285
+ handler();
286
+
287
+ /* There is no timeout when failing to uncork for WebSockets,
288
+ * as that is handled by idleTimeout */
289
+ auto [written, failed] = Super::uncork();
290
+ (void)written;
291
+ (void)failed;
292
+ } else {
293
+ /* We are already corked, or can't cork so let's just call the handler */
294
+ handler();
295
+ }
296
+ }
297
+
298
+ /* Subscribe to a topic according to MQTT rules and syntax. Returns success */
299
+ bool subscribe(std::string_view topic, bool = false) {
300
+ WebSocketContextData<SSL, USERDATA> *webSocketContextData =
301
+ (WebSocketContextData<SSL, USERDATA> *)us_socket_context_ext(
302
+ SSL,
303
+ (us_socket_context_t *)us_socket_context(SSL, (us_socket_t *)this));
304
+
305
+ /* Make us a subscriber if we aren't yet */
306
+ WebSocketData *webSocketData =
307
+ (WebSocketData *)us_socket_ext(SSL, (us_socket_t *)this);
308
+ if (!webSocketData->subscriber) {
309
+ webSocketData->subscriber =
310
+ webSocketContextData->topicTree->createSubscriber();
311
+ webSocketData->subscriber->user = this;
261
312
  }
262
313
 
263
- /* Subscribe to a topic according to MQTT rules and syntax. Returns success */
264
- bool subscribe(std::string_view topic, bool = false) {
265
- WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
266
- (us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
267
- );
268
-
269
- /* Make us a subscriber if we aren't yet */
270
- WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) this);
271
- if (!webSocketData->subscriber) {
272
- webSocketData->subscriber = webSocketContextData->topicTree->createSubscriber();
273
- webSocketData->subscriber->user = this;
274
- }
314
+ /* Cannot return numSubscribers as this is only for this particular
315
+ * websocket context */
316
+ Topic *topicOrNull = webSocketContextData->topicTree->subscribe(
317
+ webSocketData->subscriber, topic);
318
+ if (topicOrNull && webSocketContextData->subscriptionHandler) {
319
+ /* Emit this socket, the topic, new count, old count */
320
+ webSocketContextData->subscriptionHandler(
321
+ this, topic, (int)topicOrNull->size(), (int)topicOrNull->size() - 1);
322
+ }
275
323
 
276
- /* Cannot return numSubscribers as this is only for this particular websocket context */
277
- Topic *topicOrNull = webSocketContextData->topicTree->subscribe(webSocketData->subscriber, topic);
278
- if (topicOrNull && webSocketContextData->subscriptionHandler) {
279
- /* Emit this socket, the topic, new count, old count */
280
- webSocketContextData->subscriptionHandler(this, topic, (int) topicOrNull->size(), (int) topicOrNull->size() - 1);
281
- }
324
+ /* Subscribe always succeeds */
325
+ return true;
326
+ }
282
327
 
283
- /* Subscribe always succeeds */
284
- return true;
285
- }
328
+ /* Unsubscribe from a topic, returns true if we were subscribed. */
329
+ bool unsubscribe(std::string_view topic, bool = false) {
330
+ WebSocketContextData<SSL, USERDATA> *webSocketContextData =
331
+ (WebSocketContextData<SSL, USERDATA> *)us_socket_context_ext(
332
+ SSL,
333
+ (us_socket_context_t *)us_socket_context(SSL, (us_socket_t *)this));
286
334
 
287
- /* Unsubscribe from a topic, returns true if we were subscribed. */
288
- bool unsubscribe(std::string_view topic, bool = false) {
289
- WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
290
- (us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
291
- );
292
-
293
- WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) this);
294
-
295
- if (!webSocketData->subscriber) { return false; }
296
-
297
- /* Cannot return numSubscribers as this is only for this particular websocket context */
298
- auto [ok, last, newCount] = webSocketContextData->topicTree->unsubscribe(webSocketData->subscriber, topic);
299
- /* Emit subscription event if last */
300
- if (ok && webSocketContextData->subscriptionHandler) {
301
- webSocketContextData->subscriptionHandler(this, topic, newCount, newCount + 1);
302
- }
335
+ WebSocketData *webSocketData =
336
+ (WebSocketData *)us_socket_ext(SSL, (us_socket_t *)this);
303
337
 
304
- /* Leave us as subscribers even if we subscribe to nothing (last unsubscribed topic might miss its message otherwise) */
338
+ if (!webSocketData->subscriber) {
339
+ return false;
340
+ }
305
341
 
306
- return ok;
342
+ /* Cannot return numSubscribers as this is only for this particular
343
+ * websocket context */
344
+ auto [ok, last, newCount] = webSocketContextData->topicTree->unsubscribe(
345
+ webSocketData->subscriber, topic);
346
+ /* Emit subscription event if last */
347
+ if (ok && webSocketContextData->subscriptionHandler) {
348
+ webSocketContextData->subscriptionHandler(this, topic, newCount,
349
+ newCount + 1);
307
350
  }
308
351
 
309
- /* Returns whether this socket is subscribed to the specified topic */
310
- bool isSubscribed(std::string_view topic) {
311
- WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
312
- (us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
313
- );
352
+ /* Leave us as subscribers even if we subscribe to nothing (last
353
+ * unsubscribed topic might miss its message otherwise) */
314
354
 
315
- WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) this);
316
- if (!webSocketData->subscriber) {
317
- return false;
318
- }
355
+ return ok;
356
+ }
319
357
 
320
- Topic *topicPtr = webSocketContextData->topicTree->lookupTopic(topic);
321
- if (!topicPtr) {
322
- return false;
323
- }
358
+ /* Returns whether this socket is subscribed to the specified topic */
359
+ bool isSubscribed(std::string_view topic) {
360
+ WebSocketContextData<SSL, USERDATA> *webSocketContextData =
361
+ (WebSocketContextData<SSL, USERDATA> *)us_socket_context_ext(
362
+ SSL,
363
+ (us_socket_context_t *)us_socket_context(SSL, (us_socket_t *)this));
324
364
 
325
- return topicPtr->count(webSocketData->subscriber);
365
+ WebSocketData *webSocketData =
366
+ (WebSocketData *)us_socket_ext(SSL, (us_socket_t *)this);
367
+ if (!webSocketData->subscriber) {
368
+ return false;
326
369
  }
327
370
 
328
- /* Iterates all topics of this WebSocket. Every topic is represented by its full name.
329
- * Can be called in close handler. It is possible to modify the subscription list while
330
- * inside the callback ONLY IF not modifying the topic passed to the callback.
331
- * Topic names are valid only for the duration of the callback. */
332
- void iterateTopics(MoveOnlyFunction<void(std::string_view)> cb) {
333
- WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
334
- (us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
335
- );
336
-
337
- WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) this);
338
- if (webSocketData->subscriber) {
339
- /* Lock this subscriber for unsubscription / subscription */
340
- webSocketContextData->topicTree->iteratingSubscriber = webSocketData->subscriber;
341
-
342
- for (Topic *topicPtr : webSocketData->subscriber->topics) {
343
- cb({topicPtr->name.data(), topicPtr->name.length()});
344
- }
345
-
346
- /* Unlock subscriber */
347
- webSocketContextData->topicTree->iteratingSubscriber = nullptr;
348
- }
371
+ Topic *topicPtr = webSocketContextData->topicTree->lookupTopic(topic);
372
+ if (!topicPtr) {
373
+ return false;
349
374
  }
350
375
 
351
- /* Publish a message to a topic according to MQTT rules and syntax. Returns success.
352
- * We, the WebSocket, must be subscribed to the topic itself and if so - no message will be sent to ourselves.
353
- * Use App::publish for an unconditional publish that simply publishes to whomever might be subscribed. */
354
- bool publish(std::string_view topic, std::string_view message, OpCode opCode = OpCode::TEXT, bool compress = false) {
355
- WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL,
356
- (us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this)
357
- );
358
-
359
- /* We cannot be a subscriber of this topic if we are not a subscriber of anything */
360
- WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) this);
361
- if (!webSocketData->subscriber) {
362
- /* Failure, but still do return the number of subscribers */
363
- return false;
364
- }
365
-
366
- /* Publish as sender, does not receive its own messages even if subscribed to relevant topics */
367
- if (message.length() >= LoopData::CORK_BUFFER_SIZE) {
368
- return webSocketContextData->topicTree->publishBig(webSocketData->subscriber, topic, {message, opCode, compress}, [](Subscriber *s, TopicTreeBigMessage &message) {
369
- auto *ws = (WebSocket<SSL, true, int> *) s->user;
376
+ return topicPtr->count(webSocketData->subscriber);
377
+ }
378
+
379
+ /* Iterates all topics of this WebSocket. Every topic is represented by its
380
+ * full name. Can be called in close handler. It is possible to modify the
381
+ * subscription list while inside the callback ONLY IF not modifying the topic
382
+ * passed to the callback. Topic names are valid only for the duration of the
383
+ * callback. */
384
+ void iterateTopics(MoveOnlyFunction<void(std::string_view)> cb) {
385
+ WebSocketContextData<SSL, USERDATA> *webSocketContextData =
386
+ (WebSocketContextData<SSL, USERDATA> *)us_socket_context_ext(
387
+ SSL,
388
+ (us_socket_context_t *)us_socket_context(SSL, (us_socket_t *)this));
389
+
390
+ WebSocketData *webSocketData =
391
+ (WebSocketData *)us_socket_ext(SSL, (us_socket_t *)this);
392
+ if (webSocketData->subscriber) {
393
+ /* Lock this subscriber for unsubscription / subscription */
394
+ webSocketContextData->topicTree->iteratingSubscriber =
395
+ webSocketData->subscriber;
396
+
397
+ for (Topic *topicPtr : webSocketData->subscriber->topics) {
398
+ cb({topicPtr->name.data(), topicPtr->name.length()});
399
+ }
400
+
401
+ /* Unlock subscriber */
402
+ webSocketContextData->topicTree->iteratingSubscriber = nullptr;
403
+ }
404
+ }
405
+
406
+ /* Publish a message to a topic according to MQTT rules and syntax. Returns
407
+ * success. We, the WebSocket, must be subscribed to the topic itself and if
408
+ * so - no message will be sent to ourselves. Use App::publish for an
409
+ * unconditional publish that simply publishes to whomever might be
410
+ * subscribed. */
411
+ bool publish(std::string_view topic, std::string_view message,
412
+ OpCode opCode = OpCode::TEXT, bool compress = false) {
413
+ WebSocketContextData<SSL, USERDATA> *webSocketContextData =
414
+ (WebSocketContextData<SSL, USERDATA> *)us_socket_context_ext(
415
+ SSL,
416
+ (us_socket_context_t *)us_socket_context(SSL, (us_socket_t *)this));
417
+
418
+ /* We cannot be a subscriber of this topic if we are not a subscriber of
419
+ * anything */
420
+ WebSocketData *webSocketData =
421
+ (WebSocketData *)us_socket_ext(SSL, (us_socket_t *)this);
422
+ if (!webSocketData->subscriber) {
423
+ /* Failure, but still do return the number of subscribers */
424
+ return false;
425
+ }
370
426
 
371
- ws->send(message.message, (OpCode)message.opCode, message.compress);
372
- });
373
- } else {
374
- return webSocketContextData->topicTree->publish(webSocketData->subscriber, topic, {std::string(message), opCode, compress});
375
- }
427
+ /* Publish as sender, does not receive its own messages even if subscribed
428
+ * to relevant topics */
429
+ if (message.length() >= LoopData::CORK_BUFFER_SIZE) {
430
+ return webSocketContextData->topicTree->publishBig(
431
+ webSocketData->subscriber, topic, {message, opCode, compress},
432
+ [](Subscriber *s, TopicTreeBigMessage &message) {
433
+ auto *ws = (WebSocket<SSL, true, int> *)s->user;
434
+
435
+ ws->send(message.message, (OpCode)message.opCode, message.compress);
436
+ });
437
+ } else {
438
+ return webSocketContextData->topicTree->publish(
439
+ webSocketData->subscriber, topic,
440
+ {std::string(message), opCode, compress});
376
441
  }
442
+ }
377
443
  };
378
444
 
379
- }
445
+ } // namespace uWS
380
446
 
381
447
  #endif // UWS_WEBSOCKET_H