opal-up 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/LICENSE +209 -0
- data/README.md +81 -28
- data/bin/up_ruby +4 -0
- data/bin/up_ruby_cluster +4 -0
- data/ext/up_ext/App.h +606 -0
- data/ext/up_ext/AsyncSocket.h +355 -0
- data/ext/up_ext/AsyncSocketData.h +87 -0
- data/ext/up_ext/BloomFilter.h +83 -0
- data/ext/up_ext/ChunkedEncoding.h +236 -0
- data/ext/up_ext/ClientApp.h +36 -0
- data/ext/up_ext/HttpContext.h +502 -0
- data/ext/up_ext/HttpContextData.h +56 -0
- data/ext/up_ext/HttpErrors.h +53 -0
- data/ext/up_ext/HttpParser.h +680 -0
- data/ext/up_ext/HttpResponse.h +578 -0
- data/ext/up_ext/HttpResponseData.h +95 -0
- data/ext/up_ext/HttpRouter.h +380 -0
- data/ext/up_ext/Loop.h +204 -0
- data/ext/up_ext/LoopData.h +112 -0
- data/ext/up_ext/MoveOnlyFunction.h +377 -0
- data/ext/up_ext/PerMessageDeflate.h +315 -0
- data/ext/up_ext/ProxyParser.h +163 -0
- data/ext/up_ext/QueryParser.h +120 -0
- data/ext/up_ext/TopicTree.h +363 -0
- data/ext/up_ext/Utilities.h +66 -0
- data/ext/up_ext/WebSocket.h +381 -0
- data/ext/up_ext/WebSocketContext.h +434 -0
- data/ext/up_ext/WebSocketContextData.h +109 -0
- data/ext/up_ext/WebSocketData.h +86 -0
- data/ext/up_ext/WebSocketExtensions.h +256 -0
- data/ext/up_ext/WebSocketHandshake.h +145 -0
- data/ext/up_ext/WebSocketProtocol.h +506 -0
- data/ext/up_ext/bsd.c +767 -0
- data/ext/up_ext/bsd.h +109 -0
- data/ext/up_ext/context.c +524 -0
- data/ext/up_ext/epoll_kqueue.c +458 -0
- data/ext/up_ext/epoll_kqueue.h +67 -0
- data/ext/up_ext/extconf.rb +5 -0
- data/ext/up_ext/internal.h +224 -0
- data/ext/up_ext/libusockets.h +350 -0
- data/ext/up_ext/libuwebsockets.cpp +1374 -0
- data/ext/up_ext/libuwebsockets.h +260 -0
- data/ext/up_ext/loop.c +386 -0
- data/ext/up_ext/loop_data.h +38 -0
- data/ext/up_ext/socket.c +231 -0
- data/ext/up_ext/up_ext.c +278 -0
- data/lib/up/node/rack_env.rb +2 -2
- data/lib/up/ruby/cluster_cli.rb +10 -0
- data/lib/up/ruby/rack_cluster.rb +26 -0
- data/lib/up/ruby/rack_env.rb +97 -0
- data/lib/up/ruby/rack_server.rb +26 -0
- data/lib/up/ruby/server_cli.rb +10 -0
- data/lib/up/u_web_socket/rack_env.rb +1 -1
- data/lib/up/version.rb +1 -1
- metadata +71 -18
- data/.gitignore +0 -5
- data/Gemfile +0 -2
- data/example_rack_app/Gemfile +0 -3
- data/example_rack_app/config.ru +0 -6
- data/example_rack_app/rack_app.rb +0 -5
- data/example_roda_app/Gemfile +0 -6
- data/example_roda_app/config.ru +0 -6
- data/example_roda_app/roda_app.rb +0 -37
- data/example_sinatra_app/Gemfile +0 -6
- data/example_sinatra_app/config.ru +0 -6
- data/example_sinatra_app/sinatra_app.rb +0 -7
- data/opal-up.gemspec +0 -27
- data/up_logo.svg +0 -256
@@ -0,0 +1,355 @@
|
|
1
|
+
/*
|
2
|
+
* Authored by Alex Hultman, 2018-2020.
|
3
|
+
* Intellectual property of third-party.
|
4
|
+
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
* you may not use this file except in compliance with the License.
|
7
|
+
* You may obtain a copy of the License at
|
8
|
+
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
* See the License for the specific language governing permissions and
|
15
|
+
* limitations under the License.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#ifndef UWS_ASYNCSOCKET_H
|
19
|
+
#define UWS_ASYNCSOCKET_H
|
20
|
+
|
21
|
+
/* This class implements async socket memory management strategies */
|
22
|
+
|
23
|
+
/* NOTE: Many unsigned/signed conversion warnings could be solved by moving from int length
|
24
|
+
* to unsigned length for everything to/from uSockets - this would however remove the opportunity
|
25
|
+
* to signal error with -1 (which is how the entire UNIX syscalling is built). */
|
26
|
+
|
27
|
+
#include <cstring>
|
28
|
+
#include <iostream>
|
29
|
+
|
30
|
+
#include "libusockets.h"
|
31
|
+
|
32
|
+
#include "LoopData.h"
|
33
|
+
#include "AsyncSocketData.h"
|
34
|
+
|
35
|
+
namespace uWS {
|
36
|
+
|
37
|
+
enum SendBufferAttribute {
|
38
|
+
NEEDS_NOTHING,
|
39
|
+
NEEDS_DRAIN,
|
40
|
+
NEEDS_UNCORK
|
41
|
+
};
|
42
|
+
|
43
|
+
template <bool, bool, typename> struct WebSocketContext;
|
44
|
+
|
45
|
+
template <bool SSL>
|
46
|
+
struct AsyncSocket {
|
47
|
+
/* This guy is promiscuous */
|
48
|
+
template <bool> friend struct HttpContext;
|
49
|
+
template <bool, bool, typename> friend struct WebSocketContext;
|
50
|
+
template <bool> friend struct TemplatedApp;
|
51
|
+
template <bool, typename> friend struct WebSocketContextData;
|
52
|
+
template <typename, typename> friend struct TopicTree;
|
53
|
+
template <bool> friend struct HttpResponse;
|
54
|
+
|
55
|
+
private:
|
56
|
+
/* Helper, do not use directly (todo: move to uSockets or de-crazify) */
|
57
|
+
void throttle_helper(int toggle) {
|
58
|
+
/* These should be exposed by uSockets */
|
59
|
+
static thread_local int us_events[2] = {0, 0};
|
60
|
+
|
61
|
+
struct us_poll_t *p = (struct us_poll_t *) this;
|
62
|
+
struct us_loop_t *loop = us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this));
|
63
|
+
|
64
|
+
if (toggle) {
|
65
|
+
/* Pause */
|
66
|
+
int events = us_poll_events(p);
|
67
|
+
if (events) {
|
68
|
+
us_events[getBufferedAmount() ? 1 : 0] = events;
|
69
|
+
}
|
70
|
+
us_poll_change(p, loop, 0);
|
71
|
+
} else {
|
72
|
+
/* Resume */
|
73
|
+
int events = us_events[getBufferedAmount() ? 1 : 0];
|
74
|
+
us_poll_change(p, loop, events);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
protected:
|
79
|
+
/* Returns SSL pointer or FD as pointer */
|
80
|
+
void *getNativeHandle() {
|
81
|
+
return us_socket_get_native_handle(SSL, (us_socket_t *) this);
|
82
|
+
}
|
83
|
+
|
84
|
+
/* Get loop data for socket */
|
85
|
+
LoopData *getLoopData() {
|
86
|
+
return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this)));
|
87
|
+
}
|
88
|
+
|
89
|
+
/* Get socket extension */
|
90
|
+
AsyncSocketData<SSL> *getAsyncSocketData() {
|
91
|
+
return (AsyncSocketData<SSL> *) us_socket_ext(SSL, (us_socket_t *) this);
|
92
|
+
}
|
93
|
+
|
94
|
+
/* Socket timeout */
|
95
|
+
void timeout(unsigned int seconds) {
|
96
|
+
us_socket_timeout(SSL, (us_socket_t *) this, seconds);
|
97
|
+
}
|
98
|
+
|
99
|
+
/* Shutdown socket without any automatic drainage */
|
100
|
+
void shutdown() {
|
101
|
+
us_socket_shutdown(SSL, (us_socket_t *) this);
|
102
|
+
}
|
103
|
+
|
104
|
+
/* Experimental pause */
|
105
|
+
us_socket_t *pause() {
|
106
|
+
throttle_helper(1);
|
107
|
+
return (us_socket_t *) this;
|
108
|
+
}
|
109
|
+
|
110
|
+
/* Experimental resume */
|
111
|
+
us_socket_t *resume() {
|
112
|
+
throttle_helper(0);
|
113
|
+
return (us_socket_t *) this;
|
114
|
+
}
|
115
|
+
|
116
|
+
/* Immediately close socket */
|
117
|
+
us_socket_t *close() {
|
118
|
+
return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr);
|
119
|
+
}
|
120
|
+
|
121
|
+
void corkUnchecked() {
|
122
|
+
/* What if another socket is corked? */
|
123
|
+
getLoopData()->corkedSocket = this;
|
124
|
+
}
|
125
|
+
|
126
|
+
void uncorkWithoutSending() {
|
127
|
+
if (isCorked()) {
|
128
|
+
getLoopData()->corkedSocket = nullptr;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
/* Cork this socket. Only one socket may ever be corked per-loop at any given time */
|
133
|
+
void cork() {
|
134
|
+
/* Extra check for invalid corking of others */
|
135
|
+
if (getLoopData()->corkOffset && getLoopData()->corkedSocket != this) {
|
136
|
+
std::cerr << "Error: Cork buffer must not be acquired without checking canCork!" << std::endl;
|
137
|
+
std::terminate();
|
138
|
+
}
|
139
|
+
|
140
|
+
/* What if another socket is corked? */
|
141
|
+
getLoopData()->corkedSocket = this;
|
142
|
+
}
|
143
|
+
|
144
|
+
/* Returns wheter we are corked or not */
|
145
|
+
bool isCorked() {
|
146
|
+
return getLoopData()->corkedSocket == this;
|
147
|
+
}
|
148
|
+
|
149
|
+
/* Returns whether we could cork (it is free) */
|
150
|
+
bool canCork() {
|
151
|
+
return getLoopData()->corkedSocket == nullptr;
|
152
|
+
}
|
153
|
+
|
154
|
+
/* Returns a suitable buffer for temporary assemblation of send data */
|
155
|
+
std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) {
|
156
|
+
/* First step is to determine if we already have backpressure or not */
|
157
|
+
LoopData *loopData = getLoopData();
|
158
|
+
BackPressure &backPressure = getAsyncSocketData()->buffer;
|
159
|
+
size_t existingBackpressure = backPressure.length();
|
160
|
+
if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) {
|
161
|
+
/* Cork automatically if we can */
|
162
|
+
if (isCorked()) {
|
163
|
+
char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
|
164
|
+
loopData->corkOffset += (unsigned int) size;
|
165
|
+
return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING};
|
166
|
+
} else {
|
167
|
+
cork();
|
168
|
+
char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
|
169
|
+
loopData->corkOffset += (unsigned int) size;
|
170
|
+
return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK};
|
171
|
+
}
|
172
|
+
} else {
|
173
|
+
|
174
|
+
/* If we are corked and there is already data in the cork buffer,
|
175
|
+
mark how much is ours and reset it */
|
176
|
+
unsigned int ourCorkOffset = 0;
|
177
|
+
if (isCorked() && loopData->corkOffset) {
|
178
|
+
ourCorkOffset = loopData->corkOffset;
|
179
|
+
loopData->corkOffset = 0;
|
180
|
+
}
|
181
|
+
|
182
|
+
/* Fallback is to use the backpressure as buffer */
|
183
|
+
backPressure.resize(ourCorkOffset + existingBackpressure + size);
|
184
|
+
|
185
|
+
/* And copy corkbuffer in front */
|
186
|
+
memcpy((char *) backPressure.data() + existingBackpressure, loopData->corkBuffer, ourCorkOffset);
|
187
|
+
|
188
|
+
return {(char *) backPressure.data() + ourCorkOffset + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN};
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
/* Returns the user space backpressure. */
|
193
|
+
unsigned int getBufferedAmount() {
|
194
|
+
/* We return the actual amount of bytes in backbuffer, including pendingRemoval */
|
195
|
+
return (unsigned int) getAsyncSocketData()->buffer.totalLength();
|
196
|
+
}
|
197
|
+
|
198
|
+
/* Returns the text representation of an IPv4 or IPv6 address */
|
199
|
+
std::string_view addressAsText(std::string_view binary) {
|
200
|
+
static thread_local char buf[64];
|
201
|
+
int ipLength = 0;
|
202
|
+
|
203
|
+
if (!binary.length()) {
|
204
|
+
return {};
|
205
|
+
}
|
206
|
+
|
207
|
+
unsigned char *b = (unsigned char *) binary.data();
|
208
|
+
|
209
|
+
if (binary.length() == 4) {
|
210
|
+
ipLength = snprintf(buf, 64, "%u.%u.%u.%u", b[0], b[1], b[2], b[3]);
|
211
|
+
} else {
|
212
|
+
ipLength = snprintf(buf, 64, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
213
|
+
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11],
|
214
|
+
b[12], b[13], b[14], b[15]);
|
215
|
+
}
|
216
|
+
|
217
|
+
return {buf, (unsigned int) ipLength};
|
218
|
+
}
|
219
|
+
|
220
|
+
/* Returns the remote IP address or empty string on failure */
|
221
|
+
std::string_view getRemoteAddress() {
|
222
|
+
static thread_local char buf[16];
|
223
|
+
int ipLength = 16;
|
224
|
+
us_socket_remote_address(SSL, (us_socket_t *) this, buf, &ipLength);
|
225
|
+
return std::string_view(buf, (unsigned int) ipLength);
|
226
|
+
}
|
227
|
+
|
228
|
+
/* Returns the text representation of IP */
|
229
|
+
std::string_view getRemoteAddressAsText() {
|
230
|
+
return addressAsText(getRemoteAddress());
|
231
|
+
}
|
232
|
+
|
233
|
+
/* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer. Always drain if possible.
|
234
|
+
* Returns pair of bytes written (anywhere) and wheter or not this call resulted in the polling for
|
235
|
+
* writable (or we are in a state that implies polling for writable). */
|
236
|
+
std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) {
|
237
|
+
/* Fake success if closed, simple fix to allow uncork of closed socket to succeed */
|
238
|
+
if (us_socket_is_closed(SSL, (us_socket_t *) this)) {
|
239
|
+
return {length, false};
|
240
|
+
}
|
241
|
+
|
242
|
+
LoopData *loopData = getLoopData();
|
243
|
+
AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData();
|
244
|
+
|
245
|
+
/* We are limited if we have a per-socket buffer */
|
246
|
+
if (asyncSocketData->buffer.length()) {
|
247
|
+
/* Write off as much as we can */
|
248
|
+
int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length);
|
249
|
+
|
250
|
+
/* On failure return, otherwise continue down the function */
|
251
|
+
if ((unsigned int) written < asyncSocketData->buffer.length()) {
|
252
|
+
|
253
|
+
/* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */
|
254
|
+
asyncSocketData->buffer.erase((unsigned int) written);
|
255
|
+
|
256
|
+
if (optionally) {
|
257
|
+
/* Thankfully we can exit early here */
|
258
|
+
return {0, true};
|
259
|
+
} else {
|
260
|
+
/* This path is horrible and points towards erroneous usage */
|
261
|
+
asyncSocketData->buffer.append(src, (unsigned int) length);
|
262
|
+
|
263
|
+
return {length, true};
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
/* At this point we simply have no buffer and can continue as normal */
|
268
|
+
asyncSocketData->buffer.clear();
|
269
|
+
}
|
270
|
+
|
271
|
+
if (length) {
|
272
|
+
if (loopData->corkedSocket == this) {
|
273
|
+
/* We are corked */
|
274
|
+
if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) {
|
275
|
+
/* If the entire chunk fits in cork buffer */
|
276
|
+
memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length);
|
277
|
+
loopData->corkOffset += (unsigned int) length;
|
278
|
+
/* Fall through to default return */
|
279
|
+
} else {
|
280
|
+
/* Strategy differences between SSL and non-SSL regarding syscall minimizing */
|
281
|
+
if constexpr (false) {
|
282
|
+
/* Cork up as much as we can */
|
283
|
+
unsigned int stripped = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset;
|
284
|
+
memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped);
|
285
|
+
loopData->corkOffset = LoopData::CORK_BUFFER_SIZE;
|
286
|
+
|
287
|
+
auto [written, failed] = uncork(src + stripped, length - (int) stripped, optionally);
|
288
|
+
return {written + (int) stripped, failed};
|
289
|
+
}
|
290
|
+
|
291
|
+
/* For non-SSL we take the penalty of two syscalls */
|
292
|
+
return uncork(src, length, optionally);
|
293
|
+
}
|
294
|
+
} else {
|
295
|
+
/* We are not corked */
|
296
|
+
int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0);
|
297
|
+
|
298
|
+
/* Did we fail? */
|
299
|
+
if (written < length) {
|
300
|
+
/* If the write was optional then just bail out */
|
301
|
+
if (optionally) {
|
302
|
+
return {written, true};
|
303
|
+
}
|
304
|
+
|
305
|
+
/* Fall back to worst possible case (should be very rare for HTTP) */
|
306
|
+
/* At least we can reserve room for next chunk if we know it up front */
|
307
|
+
if (nextLength) {
|
308
|
+
asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength));
|
309
|
+
}
|
310
|
+
|
311
|
+
/* Buffer this chunk */
|
312
|
+
asyncSocketData->buffer.append(src + written, (size_t) (length - written));
|
313
|
+
|
314
|
+
/* Return the failure */
|
315
|
+
return {length, true};
|
316
|
+
}
|
317
|
+
/* Fall through to default return */
|
318
|
+
}
|
319
|
+
}
|
320
|
+
|
321
|
+
/* Default fall through return */
|
322
|
+
return {length, false};
|
323
|
+
}
|
324
|
+
|
325
|
+
/* Uncork this socket and flush or buffer any corked and/or passed data. It is essential to remember doing this. */
|
326
|
+
/* It does NOT count bytes written from cork buffer (they are already accounted for in the write call responsible for its corking)! */
|
327
|
+
std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) {
|
328
|
+
LoopData *loopData = getLoopData();
|
329
|
+
|
330
|
+
if (loopData->corkedSocket == this) {
|
331
|
+
loopData->corkedSocket = nullptr;
|
332
|
+
|
333
|
+
if (loopData->corkOffset) {
|
334
|
+
/* Corked data is already accounted for via its write call */
|
335
|
+
auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length);
|
336
|
+
loopData->corkOffset = 0;
|
337
|
+
|
338
|
+
if (failed) {
|
339
|
+
/* We do not need to care for buffering here, write does that */
|
340
|
+
return {0, true};
|
341
|
+
}
|
342
|
+
}
|
343
|
+
|
344
|
+
/* We should only return with new writes, not things written to cork already */
|
345
|
+
return write(src, length, optionally, 0);
|
346
|
+
} else {
|
347
|
+
/* We are not even corked! */
|
348
|
+
return {0, false};
|
349
|
+
}
|
350
|
+
}
|
351
|
+
};
|
352
|
+
|
353
|
+
}
|
354
|
+
|
355
|
+
#endif // UWS_ASYNCSOCKET_H
|
@@ -0,0 +1,87 @@
|
|
1
|
+
/*
|
2
|
+
* Authored by Alex Hultman, 2018-2021.
|
3
|
+
* Intellectual property of third-party.
|
4
|
+
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
* you may not use this file except in compliance with the License.
|
7
|
+
* You may obtain a copy of the License at
|
8
|
+
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
* See the License for the specific language governing permissions and
|
15
|
+
* limitations under the License.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#ifndef UWS_ASYNCSOCKETDATA_H
|
19
|
+
#define UWS_ASYNCSOCKETDATA_H
|
20
|
+
|
21
|
+
#include <string>
|
22
|
+
|
23
|
+
namespace uWS {
|
24
|
+
|
25
|
+
struct BackPressure {
|
26
|
+
std::string buffer;
|
27
|
+
unsigned int pendingRemoval = 0;
|
28
|
+
BackPressure(BackPressure &&other) {
|
29
|
+
buffer = std::move(other.buffer);
|
30
|
+
pendingRemoval = other.pendingRemoval;
|
31
|
+
}
|
32
|
+
BackPressure() = default;
|
33
|
+
void append(const char *data, size_t length) {
|
34
|
+
buffer.append(data, length);
|
35
|
+
}
|
36
|
+
void erase(unsigned int length) {
|
37
|
+
pendingRemoval += length;
|
38
|
+
/* Always erase a minimum of 1/32th the current backpressure */
|
39
|
+
if (pendingRemoval > (buffer.length() >> 5)) {
|
40
|
+
buffer.erase(0, pendingRemoval);
|
41
|
+
pendingRemoval = 0;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
size_t length() {
|
45
|
+
return buffer.length() - pendingRemoval;
|
46
|
+
}
|
47
|
+
void clear() {
|
48
|
+
pendingRemoval = 0;
|
49
|
+
buffer.clear();
|
50
|
+
}
|
51
|
+
void reserve(size_t length) {
|
52
|
+
buffer.reserve(length + pendingRemoval);
|
53
|
+
}
|
54
|
+
void resize(size_t length) {
|
55
|
+
buffer.resize(length + pendingRemoval);
|
56
|
+
}
|
57
|
+
const char *data() {
|
58
|
+
return buffer.data() + pendingRemoval;
|
59
|
+
}
|
60
|
+
size_t size() {
|
61
|
+
return length();
|
62
|
+
}
|
63
|
+
/* The total length, incuding pending removal */
|
64
|
+
size_t totalLength() {
|
65
|
+
return buffer.length();
|
66
|
+
}
|
67
|
+
};
|
68
|
+
|
69
|
+
/* Depending on how we want AsyncSocket to function, this will need to change */
|
70
|
+
|
71
|
+
template <bool SSL>
|
72
|
+
struct AsyncSocketData {
|
73
|
+
/* This will do for now */
|
74
|
+
BackPressure buffer;
|
75
|
+
|
76
|
+
/* Allow move constructing us */
|
77
|
+
AsyncSocketData(BackPressure &&backpressure) : buffer(std::move(backpressure)) {
|
78
|
+
|
79
|
+
}
|
80
|
+
|
81
|
+
/* Or emppty */
|
82
|
+
AsyncSocketData() = default;
|
83
|
+
};
|
84
|
+
|
85
|
+
}
|
86
|
+
|
87
|
+
#endif // UWS_ASYNCSOCKETDATA_H
|
@@ -0,0 +1,83 @@
|
|
1
|
+
/*
|
2
|
+
* Authored by Alex Hultman, 2018-2022.
|
3
|
+
* Intellectual property of third-party.
|
4
|
+
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
* you may not use this file except in compliance with the License.
|
7
|
+
* You may obtain a copy of the License at
|
8
|
+
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
* See the License for the specific language governing permissions and
|
15
|
+
* limitations under the License.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#ifndef UWS_BLOOMFILTER_H
|
19
|
+
#define UWS_BLOOMFILTER_H
|
20
|
+
|
21
|
+
/* This filter has no false positives or collisions for the standard
|
22
|
+
* and non-standard common request headers */
|
23
|
+
|
24
|
+
#include <cstdint>
|
25
|
+
#include <string_view>
|
26
|
+
#include <bitset>
|
27
|
+
|
28
|
+
namespace uWS {
|
29
|
+
|
30
|
+
struct BloomFilter {
|
31
|
+
private:
|
32
|
+
std::bitset<256> filter;
|
33
|
+
static inline uint32_t perfectHash(uint32_t features) {
|
34
|
+
return features *= 1843993368;
|
35
|
+
}
|
36
|
+
|
37
|
+
union ScrambleArea {
|
38
|
+
unsigned char p[4];
|
39
|
+
uint32_t val;
|
40
|
+
};
|
41
|
+
|
42
|
+
ScrambleArea getFeatures(std::string_view key) {
|
43
|
+
ScrambleArea s;
|
44
|
+
s.p[0] = reinterpret_cast<const unsigned char&>(key[0]);
|
45
|
+
s.p[1] = reinterpret_cast<const unsigned char&>(key[key.length() - 1]);
|
46
|
+
s.p[2] = reinterpret_cast<const unsigned char&>(key[key.length() - 2]);
|
47
|
+
s.p[3] = reinterpret_cast<const unsigned char&>(key[key.length() >> 1]);
|
48
|
+
return s;
|
49
|
+
}
|
50
|
+
|
51
|
+
public:
|
52
|
+
bool mightHave(std::string_view key) {
|
53
|
+
if (key.length() < 2) {
|
54
|
+
return true;
|
55
|
+
}
|
56
|
+
|
57
|
+
ScrambleArea s = getFeatures(key);
|
58
|
+
s.val = perfectHash(s.val);
|
59
|
+
return filter[s.p[0]] &&
|
60
|
+
filter[s.p[1]] &&
|
61
|
+
filter[s.p[2]] &&
|
62
|
+
filter[s.p[3]];
|
63
|
+
}
|
64
|
+
|
65
|
+
void add(std::string_view key) {
|
66
|
+
if (key.length() >= 2) {
|
67
|
+
ScrambleArea s = getFeatures(key);
|
68
|
+
s.val = perfectHash(s.val);
|
69
|
+
filter[s.p[0]] = 1;
|
70
|
+
filter[s.p[1]] = 1;
|
71
|
+
filter[s.p[2]] = 1;
|
72
|
+
filter[s.p[3]] = 1;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
void reset() {
|
77
|
+
filter.reset();
|
78
|
+
}
|
79
|
+
};
|
80
|
+
|
81
|
+
}
|
82
|
+
|
83
|
+
#endif // UWS_BLOOMFILTER_H
|