opal-up 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +209 -0
- data/README.md +97 -29
- 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 +1344 -0
- data/ext/up_ext/libuwebsockets.h +396 -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 +930 -0
- data/lib/up/bun/rack_env.rb +1 -13
- data/lib/up/bun/server.rb +93 -19
- data/lib/up/cli.rb +3 -0
- data/lib/up/client.rb +68 -0
- data/lib/up/ruby/cluster.rb +39 -0
- data/lib/up/ruby/cluster_cli.rb +10 -0
- data/lib/up/{node → ruby}/rack_cluster.rb +5 -4
- data/lib/up/{node → ruby}/rack_server.rb +4 -4
- data/lib/up/ruby/server_cli.rb +10 -0
- data/lib/up/u_web_socket/cluster.rb +18 -3
- data/lib/up/u_web_socket/server.rb +108 -15
- data/lib/up/version.rb +1 -1
- metadata +72 -30
- data/.gitignore +0 -5
- data/Gemfile +0 -2
- data/bin/up_node +0 -12
- data/bin/up_node_cluster +0 -12
- 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/lib/up/node/cluster.rb +0 -39
- data/lib/up/node/cluster_cli.rb +0 -15
- data/lib/up/node/rack_env.rb +0 -106
- data/lib/up/node/server.rb +0 -84
- data/lib/up/node/server_cli.rb +0 -15
- data/lib/up/u_web_socket/rack_env.rb +0 -101
- data/opal-up.gemspec +0 -27
- data/up_logo.svg +0 -256
@@ -0,0 +1,377 @@
|
|
1
|
+
/*
|
2
|
+
MIT License
|
3
|
+
|
4
|
+
Copyright (c) 2020 Oleg Fatkhiev
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
23
|
+
*/
|
24
|
+
|
25
|
+
/* Sources fetched from https://github.com/ofats/any_invocable on 2021-02-19. */
|
26
|
+
|
27
|
+
#ifndef _ANY_INVOKABLE_H_
|
28
|
+
#define _ANY_INVOKABLE_H_
|
29
|
+
|
30
|
+
#include <functional>
|
31
|
+
#include <memory>
|
32
|
+
#include <type_traits>
|
33
|
+
|
34
|
+
// clang-format off
|
35
|
+
/*
|
36
|
+
namespace std {
|
37
|
+
template<class Sig> class any_invocable; // never defined
|
38
|
+
|
39
|
+
template<class R, class... ArgTypes>
|
40
|
+
class any_invocable<R(ArgTypes...) cv ref noexcept(noex)> {
|
41
|
+
public:
|
42
|
+
using result_type = R;
|
43
|
+
|
44
|
+
// SECTION.3, construct/copy/destroy
|
45
|
+
any_invocable() noexcept;
|
46
|
+
any_invocable(nullptr_t) noexcept;
|
47
|
+
any_invocable(any_invocable&&) noexcept;
|
48
|
+
template<class F> any_invocable(F&&);
|
49
|
+
|
50
|
+
template<class T, class... Args>
|
51
|
+
explicit any_invocable(in_place_type_t<T>, Args&&...);
|
52
|
+
template<class T, class U, class... Args>
|
53
|
+
explicit any_invocable(in_place_type_t<T>, initializer_list<U>, Args&&...);
|
54
|
+
|
55
|
+
any_invocable& operator=(any_invocable&&) noexcept;
|
56
|
+
any_invocable& operator=(nullptr_t) noexcept;
|
57
|
+
template<class F> any_invocable& operator=(F&&);
|
58
|
+
template<class F> any_invocable& operator=(reference_wrapper<F>) noexcept;
|
59
|
+
|
60
|
+
~any_invocable();
|
61
|
+
|
62
|
+
// SECTION.4, any_invocable modifiers
|
63
|
+
void swap(any_invocable&) noexcept;
|
64
|
+
|
65
|
+
// SECTION.5, any_invocable capacity
|
66
|
+
explicit operator bool() const noexcept;
|
67
|
+
|
68
|
+
// SECTION.6, any_invocable invocation
|
69
|
+
R operator()(ArgTypes...) cv ref noexcept(noex);
|
70
|
+
|
71
|
+
// SECTION.7, null pointer comparisons
|
72
|
+
friend bool operator==(const any_invocable&, nullptr_t) noexcept;
|
73
|
+
|
74
|
+
// SECTION.8, specialized algorithms
|
75
|
+
friend void swap(any_invocable&, any_invocable&) noexcept;
|
76
|
+
};
|
77
|
+
}
|
78
|
+
*/
|
79
|
+
// clang-format on
|
80
|
+
|
81
|
+
namespace ofats {
|
82
|
+
|
83
|
+
namespace any_detail {
|
84
|
+
|
85
|
+
using buffer = std::aligned_storage_t<sizeof(void*) * 2, alignof(void*)>;
|
86
|
+
|
87
|
+
template <class T>
|
88
|
+
inline constexpr bool is_small_object_v =
|
89
|
+
sizeof(T) <= sizeof(buffer) && alignof(buffer) % alignof(T) == 0 &&
|
90
|
+
std::is_nothrow_move_constructible_v<T>;
|
91
|
+
|
92
|
+
union storage {
|
93
|
+
void* ptr_ = nullptr;
|
94
|
+
buffer buf_;
|
95
|
+
};
|
96
|
+
|
97
|
+
enum class action { destroy, move };
|
98
|
+
|
99
|
+
template <class R, class... ArgTypes>
|
100
|
+
struct handler_traits {
|
101
|
+
template <class Derived>
|
102
|
+
struct handler_base {
|
103
|
+
static void handle(action act, storage* current, storage* other = nullptr) {
|
104
|
+
switch (act) {
|
105
|
+
case (action::destroy):
|
106
|
+
Derived::destroy(*current);
|
107
|
+
break;
|
108
|
+
case (action::move):
|
109
|
+
Derived::move(*current, *other);
|
110
|
+
break;
|
111
|
+
}
|
112
|
+
}
|
113
|
+
};
|
114
|
+
|
115
|
+
template <class T>
|
116
|
+
struct small_handler : handler_base<small_handler<T>> {
|
117
|
+
template <class... Args>
|
118
|
+
static void create(storage& s, Args&&... args) {
|
119
|
+
new (static_cast<void*>(&s.buf_)) T(std::forward<Args>(args)...);
|
120
|
+
}
|
121
|
+
|
122
|
+
static void destroy(storage& s) noexcept {
|
123
|
+
T& value = *static_cast<T*>(static_cast<void*>(&s.buf_));
|
124
|
+
value.~T();
|
125
|
+
}
|
126
|
+
|
127
|
+
static void move(storage& dst, storage& src) noexcept {
|
128
|
+
create(dst, std::move(*static_cast<T*>(static_cast<void*>(&src.buf_))));
|
129
|
+
destroy(src);
|
130
|
+
}
|
131
|
+
|
132
|
+
static R call(storage& s, ArgTypes... args) {
|
133
|
+
return std::invoke(*static_cast<T*>(static_cast<void*>(&s.buf_)),
|
134
|
+
std::forward<ArgTypes>(args)...);
|
135
|
+
}
|
136
|
+
};
|
137
|
+
|
138
|
+
template <class T>
|
139
|
+
struct large_handler : handler_base<large_handler<T>> {
|
140
|
+
template <class... Args>
|
141
|
+
static void create(storage& s, Args&&... args) {
|
142
|
+
s.ptr_ = new T(std::forward<Args>(args)...);
|
143
|
+
}
|
144
|
+
|
145
|
+
static void destroy(storage& s) noexcept { delete static_cast<T*>(s.ptr_); }
|
146
|
+
|
147
|
+
static void move(storage& dst, storage& src) noexcept {
|
148
|
+
dst.ptr_ = src.ptr_;
|
149
|
+
}
|
150
|
+
|
151
|
+
static R call(storage& s, ArgTypes... args) {
|
152
|
+
return std::invoke(*static_cast<T*>(s.ptr_),
|
153
|
+
std::forward<ArgTypes>(args)...);
|
154
|
+
}
|
155
|
+
};
|
156
|
+
|
157
|
+
template <class T>
|
158
|
+
using handler = std::conditional_t<is_small_object_v<T>, small_handler<T>,
|
159
|
+
large_handler<T>>;
|
160
|
+
};
|
161
|
+
|
162
|
+
template <class T>
|
163
|
+
struct is_in_place_type : std::false_type {};
|
164
|
+
|
165
|
+
template <class T>
|
166
|
+
struct is_in_place_type<std::in_place_type_t<T>> : std::true_type {};
|
167
|
+
|
168
|
+
template <class T>
|
169
|
+
inline constexpr auto is_in_place_type_v = is_in_place_type<T>::value;
|
170
|
+
|
171
|
+
template <class R, bool is_noexcept, class... ArgTypes>
|
172
|
+
class any_invocable_impl {
|
173
|
+
template <class T>
|
174
|
+
using handler =
|
175
|
+
typename any_detail::handler_traits<R, ArgTypes...>::template handler<T>;
|
176
|
+
|
177
|
+
using storage = any_detail::storage;
|
178
|
+
using action = any_detail::action;
|
179
|
+
using handle_func = void (*)(any_detail::action, any_detail::storage*,
|
180
|
+
any_detail::storage*);
|
181
|
+
using call_func = R (*)(any_detail::storage&, ArgTypes...);
|
182
|
+
|
183
|
+
public:
|
184
|
+
using result_type = R;
|
185
|
+
|
186
|
+
any_invocable_impl() noexcept = default;
|
187
|
+
any_invocable_impl(std::nullptr_t) noexcept {}
|
188
|
+
any_invocable_impl(any_invocable_impl&& rhs) noexcept {
|
189
|
+
if (rhs.handle_) {
|
190
|
+
handle_ = rhs.handle_;
|
191
|
+
handle_(action::move, &storage_, &rhs.storage_);
|
192
|
+
call_ = rhs.call_;
|
193
|
+
rhs.handle_ = nullptr;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
any_invocable_impl& operator=(any_invocable_impl&& rhs) noexcept {
|
198
|
+
any_invocable_impl{std::move(rhs)}.swap(*this);
|
199
|
+
return *this;
|
200
|
+
}
|
201
|
+
any_invocable_impl& operator=(std::nullptr_t) noexcept {
|
202
|
+
destroy();
|
203
|
+
return *this;
|
204
|
+
}
|
205
|
+
|
206
|
+
~any_invocable_impl() { destroy(); }
|
207
|
+
|
208
|
+
void swap(any_invocable_impl& rhs) noexcept {
|
209
|
+
if (handle_) {
|
210
|
+
if (rhs.handle_) {
|
211
|
+
storage tmp;
|
212
|
+
handle_(action::move, &tmp, &storage_);
|
213
|
+
rhs.handle_(action::move, &storage_, &rhs.storage_);
|
214
|
+
handle_(action::move, &rhs.storage_, &tmp);
|
215
|
+
std::swap(handle_, rhs.handle_);
|
216
|
+
std::swap(call_, rhs.call_);
|
217
|
+
} else {
|
218
|
+
rhs.swap(*this);
|
219
|
+
}
|
220
|
+
} else if (rhs.handle_) {
|
221
|
+
rhs.handle_(action::move, &storage_, &rhs.storage_);
|
222
|
+
handle_ = rhs.handle_;
|
223
|
+
call_ = rhs.call_;
|
224
|
+
rhs.handle_ = nullptr;
|
225
|
+
}
|
226
|
+
}
|
227
|
+
|
228
|
+
explicit operator bool() const noexcept { return handle_ != nullptr; }
|
229
|
+
|
230
|
+
protected:
|
231
|
+
template <class F, class... Args>
|
232
|
+
void create(Args&&... args) {
|
233
|
+
using hdl = handler<F>;
|
234
|
+
hdl::create(storage_, std::forward<Args>(args)...);
|
235
|
+
handle_ = &hdl::handle;
|
236
|
+
call_ = &hdl::call;
|
237
|
+
}
|
238
|
+
|
239
|
+
void destroy() noexcept {
|
240
|
+
if (handle_) {
|
241
|
+
handle_(action::destroy, &storage_, nullptr);
|
242
|
+
handle_ = nullptr;
|
243
|
+
}
|
244
|
+
}
|
245
|
+
|
246
|
+
R call(ArgTypes... args) noexcept(is_noexcept) {
|
247
|
+
return call_(storage_, std::forward<ArgTypes>(args)...);
|
248
|
+
}
|
249
|
+
|
250
|
+
friend bool operator==(const any_invocable_impl& f, std::nullptr_t) noexcept {
|
251
|
+
return !f;
|
252
|
+
}
|
253
|
+
friend bool operator==(std::nullptr_t, const any_invocable_impl& f) noexcept {
|
254
|
+
return !f;
|
255
|
+
}
|
256
|
+
friend bool operator!=(const any_invocable_impl& f, std::nullptr_t) noexcept {
|
257
|
+
return static_cast<bool>(f);
|
258
|
+
}
|
259
|
+
friend bool operator!=(std::nullptr_t, const any_invocable_impl& f) noexcept {
|
260
|
+
return static_cast<bool>(f);
|
261
|
+
}
|
262
|
+
|
263
|
+
friend void swap(any_invocable_impl& lhs, any_invocable_impl& rhs) noexcept {
|
264
|
+
lhs.swap(rhs);
|
265
|
+
}
|
266
|
+
|
267
|
+
private:
|
268
|
+
storage storage_;
|
269
|
+
handle_func handle_ = nullptr;
|
270
|
+
call_func call_;
|
271
|
+
};
|
272
|
+
|
273
|
+
template <class T>
|
274
|
+
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
275
|
+
|
276
|
+
template <class AI, class F, bool noex, class R, class FCall, class... ArgTypes>
|
277
|
+
using can_convert = std::conjunction<
|
278
|
+
std::negation<std::is_same<remove_cvref_t<F>, AI>>,
|
279
|
+
std::negation<any_detail::is_in_place_type<remove_cvref_t<F>>>,
|
280
|
+
std::is_invocable_r<R, FCall, ArgTypes...>,
|
281
|
+
std::bool_constant<(!noex ||
|
282
|
+
std::is_nothrow_invocable_r_v<R, FCall, ArgTypes...>)>,
|
283
|
+
std::is_constructible<std::decay_t<F>, F>>;
|
284
|
+
|
285
|
+
} // namespace any_detail
|
286
|
+
|
287
|
+
template <class Signature>
|
288
|
+
class any_invocable;
|
289
|
+
|
290
|
+
#define __OFATS_ANY_INVOCABLE(cv, ref, noex, inv_quals) \
|
291
|
+
template <class R, class... ArgTypes> \
|
292
|
+
class any_invocable<R(ArgTypes...) cv ref noexcept(noex)> \
|
293
|
+
: public any_detail::any_invocable_impl<R, noex, ArgTypes...> { \
|
294
|
+
using base_type = any_detail::any_invocable_impl<R, noex, ArgTypes...>; \
|
295
|
+
\
|
296
|
+
public: \
|
297
|
+
using base_type::base_type; \
|
298
|
+
\
|
299
|
+
template < \
|
300
|
+
class F, \
|
301
|
+
class = std::enable_if_t<any_detail::can_convert< \
|
302
|
+
any_invocable, F, noex, R, F inv_quals, ArgTypes...>::value>> \
|
303
|
+
any_invocable(F&& f) { \
|
304
|
+
base_type::template create<std::decay_t<F>>(std::forward<F>(f)); \
|
305
|
+
} \
|
306
|
+
\
|
307
|
+
template <class T, class... Args, class VT = std::decay_t<T>, \
|
308
|
+
class = std::enable_if_t< \
|
309
|
+
std::is_move_constructible_v<VT> && \
|
310
|
+
std::is_constructible_v<VT, Args...> && \
|
311
|
+
std::is_invocable_r_v<R, VT inv_quals, ArgTypes...> && \
|
312
|
+
(!noex || std::is_nothrow_invocable_r_v<R, VT inv_quals, \
|
313
|
+
ArgTypes...>)>> \
|
314
|
+
explicit any_invocable(std::in_place_type_t<T>, Args&&... args) { \
|
315
|
+
base_type::template create<VT>(std::forward<Args>(args)...); \
|
316
|
+
} \
|
317
|
+
\
|
318
|
+
template < \
|
319
|
+
class T, class U, class... Args, class VT = std::decay_t<T>, \
|
320
|
+
class = std::enable_if_t< \
|
321
|
+
std::is_move_constructible_v<VT> && \
|
322
|
+
std::is_constructible_v<VT, std::initializer_list<U>&, Args...> && \
|
323
|
+
std::is_invocable_r_v<R, VT inv_quals, ArgTypes...> && \
|
324
|
+
(!noex || \
|
325
|
+
std::is_nothrow_invocable_r_v<R, VT inv_quals, ArgTypes...>)>> \
|
326
|
+
explicit any_invocable(std::in_place_type_t<T>, \
|
327
|
+
std::initializer_list<U> il, Args&&... args) { \
|
328
|
+
base_type::template create<VT>(il, std::forward<Args>(args)...); \
|
329
|
+
} \
|
330
|
+
\
|
331
|
+
template <class F, class FDec = std::decay_t<F>> \
|
332
|
+
std::enable_if_t<!std::is_same_v<FDec, any_invocable> && \
|
333
|
+
std::is_move_constructible_v<FDec>, \
|
334
|
+
any_invocable&> \
|
335
|
+
operator=(F&& f) { \
|
336
|
+
any_invocable{std::forward<F>(f)}.swap(*this); \
|
337
|
+
return *this; \
|
338
|
+
} \
|
339
|
+
template <class F> \
|
340
|
+
any_invocable& operator=(std::reference_wrapper<F> f) { \
|
341
|
+
any_invocable{f}.swap(*this); \
|
342
|
+
return *this; \
|
343
|
+
} \
|
344
|
+
\
|
345
|
+
R operator()(ArgTypes... args) cv ref noexcept(noex) { \
|
346
|
+
return base_type::call(std::forward<ArgTypes>(args)...); \
|
347
|
+
} \
|
348
|
+
};
|
349
|
+
|
350
|
+
// cv -> {`empty`, const}
|
351
|
+
// ref -> {`empty`, &, &&}
|
352
|
+
// noex -> {true, false}
|
353
|
+
// inv_quals -> (is_empty(ref) ? & : ref)
|
354
|
+
__OFATS_ANY_INVOCABLE(, , false, &) // 000
|
355
|
+
__OFATS_ANY_INVOCABLE(, , true, &) // 001
|
356
|
+
__OFATS_ANY_INVOCABLE(, &, false, &) // 010
|
357
|
+
__OFATS_ANY_INVOCABLE(, &, true, &) // 011
|
358
|
+
__OFATS_ANY_INVOCABLE(, &&, false, &&) // 020
|
359
|
+
__OFATS_ANY_INVOCABLE(, &&, true, &&) // 021
|
360
|
+
__OFATS_ANY_INVOCABLE(const, , false, const&) // 100
|
361
|
+
__OFATS_ANY_INVOCABLE(const, , true, const&) // 101
|
362
|
+
__OFATS_ANY_INVOCABLE(const, &, false, const&) // 110
|
363
|
+
__OFATS_ANY_INVOCABLE(const, &, true, const&) // 111
|
364
|
+
__OFATS_ANY_INVOCABLE(const, &&, false, const&&) // 120
|
365
|
+
__OFATS_ANY_INVOCABLE(const, &&, true, const&&) // 121
|
366
|
+
|
367
|
+
#undef __OFATS_ANY_INVOCABLE
|
368
|
+
|
369
|
+
} // namespace ofats
|
370
|
+
|
371
|
+
/* We, uWebSockets define our own type */
|
372
|
+
namespace uWS {
|
373
|
+
template <class T>
|
374
|
+
using MoveOnlyFunction = ofats::any_invocable<T>;
|
375
|
+
}
|
376
|
+
|
377
|
+
#endif // _ANY_INVOKABLE_H_
|
@@ -0,0 +1,315 @@
|
|
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
|
+
/* This standalone module implements deflate / inflate streams */
|
19
|
+
|
20
|
+
#ifndef UWS_PERMESSAGEDEFLATE_H
|
21
|
+
#define UWS_PERMESSAGEDEFLATE_H
|
22
|
+
|
23
|
+
#include <cstdint>
|
24
|
+
#include <cstring>
|
25
|
+
|
26
|
+
/* We always define these options no matter if ZLIB is enabled or not */
|
27
|
+
namespace uWS {
|
28
|
+
/* Compressor mode is 8 lowest bits where HIGH4(windowBits), LOW4(memLevel).
|
29
|
+
* Decompressor mode is 8 highest bits LOW4(windowBits).
|
30
|
+
* If compressor or decompressor bits are 1, then they are shared.
|
31
|
+
* If everything is just simply 0, then everything is disabled. */
|
32
|
+
enum CompressOptions : uint16_t {
|
33
|
+
/* These are not actual compression options */
|
34
|
+
_COMPRESSOR_MASK = 0x00FF,
|
35
|
+
_DECOMPRESSOR_MASK = 0x0F00,
|
36
|
+
/* Disabled, shared, shared are "special" values */
|
37
|
+
DISABLED = 0,
|
38
|
+
SHARED_COMPRESSOR = 1,
|
39
|
+
SHARED_DECOMPRESSOR = 1 << 8,
|
40
|
+
/* Highest 4 bits describe decompressor */
|
41
|
+
DEDICATED_DECOMPRESSOR_32KB = 15 << 8,
|
42
|
+
DEDICATED_DECOMPRESSOR_16KB = 14 << 8,
|
43
|
+
DEDICATED_DECOMPRESSOR_8KB = 13 << 8,
|
44
|
+
DEDICATED_DECOMPRESSOR_4KB = 12 << 8,
|
45
|
+
DEDICATED_DECOMPRESSOR_2KB = 11 << 8,
|
46
|
+
DEDICATED_DECOMPRESSOR_1KB = 10 << 8,
|
47
|
+
DEDICATED_DECOMPRESSOR_512B = 9 << 8,
|
48
|
+
/* Same as 32kb */
|
49
|
+
DEDICATED_DECOMPRESSOR = 15 << 8,
|
50
|
+
|
51
|
+
/* Lowest 8 bit describe compressor */
|
52
|
+
DEDICATED_COMPRESSOR_3KB = 9 << 4 | 1,
|
53
|
+
DEDICATED_COMPRESSOR_4KB = 9 << 4 | 2,
|
54
|
+
DEDICATED_COMPRESSOR_8KB = 10 << 4 | 3,
|
55
|
+
DEDICATED_COMPRESSOR_16KB = 11 << 4 | 4,
|
56
|
+
DEDICATED_COMPRESSOR_32KB = 12 << 4 | 5,
|
57
|
+
DEDICATED_COMPRESSOR_64KB = 13 << 4 | 6,
|
58
|
+
DEDICATED_COMPRESSOR_128KB = 14 << 4 | 7,
|
59
|
+
DEDICATED_COMPRESSOR_256KB = 15 << 4 | 8,
|
60
|
+
/* Same as 256kb */
|
61
|
+
DEDICATED_COMPRESSOR = 15 << 4 | 8
|
62
|
+
};
|
63
|
+
}
|
64
|
+
|
65
|
+
#if !defined(UWS_NO_ZLIB) && !defined(UWS_MOCK_ZLIB)
|
66
|
+
#include <zlib.h>
|
67
|
+
#endif
|
68
|
+
|
69
|
+
#include <string>
|
70
|
+
#include <optional>
|
71
|
+
|
72
|
+
#ifdef UWS_USE_LIBDEFLATE
|
73
|
+
#include "libdeflate.h"
|
74
|
+
#include <cstring>
|
75
|
+
#endif
|
76
|
+
|
77
|
+
namespace uWS {
|
78
|
+
|
79
|
+
/* Do not compile this module if we don't want it */
|
80
|
+
#if defined(UWS_NO_ZLIB) || defined(UWS_MOCK_ZLIB)
|
81
|
+
struct ZlibContext {};
|
82
|
+
struct InflationStream {
|
83
|
+
std::optional<std::string_view> inflate(ZlibContext * /*zlibContext*/, std::string_view compressed, size_t maxPayloadLength, bool /*reset*/) {
|
84
|
+
return compressed.substr(0, std::min(maxPayloadLength, compressed.length()));
|
85
|
+
}
|
86
|
+
InflationStream(CompressOptions /*compressOptions*/) {
|
87
|
+
}
|
88
|
+
};
|
89
|
+
struct DeflationStream {
|
90
|
+
std::string_view deflate(ZlibContext * /*zlibContext*/, std::string_view raw, bool /*reset*/) {
|
91
|
+
return raw;
|
92
|
+
}
|
93
|
+
DeflationStream(CompressOptions /*compressOptions*/) {
|
94
|
+
}
|
95
|
+
};
|
96
|
+
#else
|
97
|
+
|
98
|
+
#define LARGE_BUFFER_SIZE 1024 * 16 // todo: fix this
|
99
|
+
|
100
|
+
struct ZlibContext {
|
101
|
+
/* Any returned data is valid until next same-class call.
|
102
|
+
* We need to have two classes to allow inflation followed
|
103
|
+
* by many deflations without modifying the inflation */
|
104
|
+
std::string dynamicDeflationBuffer;
|
105
|
+
std::string dynamicInflationBuffer;
|
106
|
+
char *deflationBuffer;
|
107
|
+
char *inflationBuffer;
|
108
|
+
|
109
|
+
#ifdef UWS_USE_LIBDEFLATE
|
110
|
+
libdeflate_decompressor *decompressor;
|
111
|
+
libdeflate_compressor *compressor;
|
112
|
+
#endif
|
113
|
+
|
114
|
+
ZlibContext() {
|
115
|
+
deflationBuffer = (char *) malloc(LARGE_BUFFER_SIZE);
|
116
|
+
inflationBuffer = (char *) malloc(LARGE_BUFFER_SIZE);
|
117
|
+
|
118
|
+
#ifdef UWS_USE_LIBDEFLATE
|
119
|
+
decompressor = libdeflate_alloc_decompressor();
|
120
|
+
compressor = libdeflate_alloc_compressor(6);
|
121
|
+
#endif
|
122
|
+
}
|
123
|
+
|
124
|
+
~ZlibContext() {
|
125
|
+
free(deflationBuffer);
|
126
|
+
free(inflationBuffer);
|
127
|
+
|
128
|
+
#ifdef UWS_USE_LIBDEFLATE
|
129
|
+
libdeflate_free_decompressor(decompressor);
|
130
|
+
libdeflate_free_compressor(compressor);
|
131
|
+
#endif
|
132
|
+
}
|
133
|
+
};
|
134
|
+
|
135
|
+
struct DeflationStream {
|
136
|
+
z_stream deflationStream = {};
|
137
|
+
|
138
|
+
DeflationStream(CompressOptions compressOptions) {
|
139
|
+
|
140
|
+
/* Sliding inflator should be about 44kb by default, less than compressor */
|
141
|
+
|
142
|
+
/* Memory usage is given by 2 ^ (windowBits + 2) + 2 ^ (memLevel + 9) */
|
143
|
+
int windowBits = -(int) ((compressOptions & _COMPRESSOR_MASK) >> 4), memLevel = compressOptions & 0xF;
|
144
|
+
|
145
|
+
//printf("windowBits: %d, memLevel: %d\n", windowBits, memLevel);
|
146
|
+
|
147
|
+
deflateInit2(&deflationStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits, memLevel, Z_DEFAULT_STRATEGY);
|
148
|
+
}
|
149
|
+
|
150
|
+
/* Deflate and optionally reset. You must not deflate an empty string. */
|
151
|
+
std::string_view deflate(ZlibContext *zlibContext, std::string_view raw, bool reset) {
|
152
|
+
|
153
|
+
#ifdef UWS_USE_LIBDEFLATE
|
154
|
+
/* Run a fast path in case of shared_compressor */
|
155
|
+
if (reset) {
|
156
|
+
size_t written = 0;
|
157
|
+
static unsigned char buf[1024 + 1];
|
158
|
+
|
159
|
+
written = libdeflate_deflate_compress(zlibContext->compressor, raw.data(), raw.length(), buf, 1024);
|
160
|
+
|
161
|
+
if (written) {
|
162
|
+
memcpy(&buf[written], "\x00", 1);
|
163
|
+
return std::string_view((char *) buf, written + 1);
|
164
|
+
}
|
165
|
+
}
|
166
|
+
#endif
|
167
|
+
|
168
|
+
/* Odd place to clear this one, fix */
|
169
|
+
zlibContext->dynamicDeflationBuffer.clear();
|
170
|
+
|
171
|
+
deflationStream.next_in = (Bytef *) raw.data();
|
172
|
+
deflationStream.avail_in = (unsigned int) raw.length();
|
173
|
+
|
174
|
+
/* This buffer size has to be at least 6 bytes for Z_SYNC_FLUSH to work */
|
175
|
+
const int DEFLATE_OUTPUT_CHUNK = LARGE_BUFFER_SIZE;
|
176
|
+
|
177
|
+
int err;
|
178
|
+
do {
|
179
|
+
deflationStream.next_out = (Bytef *) zlibContext->deflationBuffer;
|
180
|
+
deflationStream.avail_out = DEFLATE_OUTPUT_CHUNK;
|
181
|
+
|
182
|
+
err = ::deflate(&deflationStream, Z_SYNC_FLUSH);
|
183
|
+
if (Z_OK == err && deflationStream.avail_out == 0) {
|
184
|
+
zlibContext->dynamicDeflationBuffer.append(zlibContext->deflationBuffer, DEFLATE_OUTPUT_CHUNK - deflationStream.avail_out);
|
185
|
+
continue;
|
186
|
+
} else {
|
187
|
+
break;
|
188
|
+
}
|
189
|
+
} while (true);
|
190
|
+
|
191
|
+
/* This must not change avail_out */
|
192
|
+
if (reset) {
|
193
|
+
deflateReset(&deflationStream);
|
194
|
+
}
|
195
|
+
|
196
|
+
if (zlibContext->dynamicDeflationBuffer.length()) {
|
197
|
+
zlibContext->dynamicDeflationBuffer.append(zlibContext->deflationBuffer, DEFLATE_OUTPUT_CHUNK - deflationStream.avail_out);
|
198
|
+
|
199
|
+
return std::string_view((char *) zlibContext->dynamicDeflationBuffer.data(), zlibContext->dynamicDeflationBuffer.length() - 4);
|
200
|
+
}
|
201
|
+
|
202
|
+
/* Note: We will get an interger overflow resulting in heap buffer overflow if Z_BUF_ERROR is returned
|
203
|
+
* from passing 0 as avail_in. Therefore we must not deflate an empty string */
|
204
|
+
return {
|
205
|
+
zlibContext->deflationBuffer,
|
206
|
+
DEFLATE_OUTPUT_CHUNK - deflationStream.avail_out - 4
|
207
|
+
};
|
208
|
+
}
|
209
|
+
|
210
|
+
~DeflationStream() {
|
211
|
+
deflateEnd(&deflationStream);
|
212
|
+
}
|
213
|
+
};
|
214
|
+
|
215
|
+
struct InflationStream {
|
216
|
+
z_stream inflationStream = {};
|
217
|
+
|
218
|
+
InflationStream(CompressOptions compressOptions) {
|
219
|
+
/* Inflation windowBits are the top 8 bits of the 16 bit compressOptions */
|
220
|
+
inflateInit2(&inflationStream, -(compressOptions >> 8));
|
221
|
+
}
|
222
|
+
|
223
|
+
~InflationStream() {
|
224
|
+
inflateEnd(&inflationStream);
|
225
|
+
}
|
226
|
+
|
227
|
+
/* Zero length inflates are possible and valid */
|
228
|
+
std::optional<std::string_view> inflate(ZlibContext *zlibContext, std::string_view compressed, size_t maxPayloadLength, bool reset) {
|
229
|
+
|
230
|
+
#ifdef UWS_USE_LIBDEFLATE
|
231
|
+
/* Try fast path first */
|
232
|
+
size_t written = 0;
|
233
|
+
static char buf[1024];
|
234
|
+
|
235
|
+
/* We have to pad 9 bytes and restore those bytes when done since 9 is more than 6 of next WebSocket message */
|
236
|
+
char tmp[9];
|
237
|
+
memcpy(tmp, (char *) compressed.data() + compressed.length(), 9);
|
238
|
+
memcpy((char *) compressed.data() + compressed.length(), "\x00\x00\xff\xff\x01\x00\x00\xff\xff", 9);
|
239
|
+
libdeflate_result res = libdeflate_deflate_decompress(zlibContext->decompressor, compressed.data(), compressed.length() + 9, buf, 1024, &written);
|
240
|
+
memcpy((char *) compressed.data() + compressed.length(), tmp, 9);
|
241
|
+
|
242
|
+
if (res == 0) {
|
243
|
+
/* Fast path wins */
|
244
|
+
return std::string_view(buf, written);
|
245
|
+
}
|
246
|
+
#endif
|
247
|
+
|
248
|
+
/* Save off the bytes we're about to overwrite */
|
249
|
+
char* tailLocation = (char*)compressed.data() + compressed.length();
|
250
|
+
char preTailBytes[4];
|
251
|
+
memcpy(preTailBytes, tailLocation, 4);
|
252
|
+
|
253
|
+
/* Append tail to chunk */
|
254
|
+
unsigned char tail[4] = {0x00, 0x00, 0xff, 0xff};
|
255
|
+
memcpy(tailLocation, tail, 4);
|
256
|
+
compressed = {compressed.data(), compressed.length() + 4};
|
257
|
+
|
258
|
+
/* We clear this one here, could be done better */
|
259
|
+
zlibContext->dynamicInflationBuffer.clear();
|
260
|
+
|
261
|
+
inflationStream.next_in = (Bytef *) compressed.data();
|
262
|
+
inflationStream.avail_in = (unsigned int) compressed.length();
|
263
|
+
|
264
|
+
int err;
|
265
|
+
do {
|
266
|
+
inflationStream.next_out = (Bytef *) zlibContext->inflationBuffer;
|
267
|
+
inflationStream.avail_out = LARGE_BUFFER_SIZE;
|
268
|
+
|
269
|
+
err = ::inflate(&inflationStream, Z_SYNC_FLUSH);
|
270
|
+
if (err == Z_OK && inflationStream.avail_out) {
|
271
|
+
break;
|
272
|
+
}
|
273
|
+
|
274
|
+
zlibContext->dynamicInflationBuffer.append(zlibContext->inflationBuffer, LARGE_BUFFER_SIZE - inflationStream.avail_out);
|
275
|
+
|
276
|
+
|
277
|
+
} while (inflationStream.avail_out == 0 && zlibContext->dynamicInflationBuffer.length() <= maxPayloadLength);
|
278
|
+
|
279
|
+
if (reset) {
|
280
|
+
inflateReset(&inflationStream);
|
281
|
+
}
|
282
|
+
|
283
|
+
/* Restore the bytes we used for the tail */
|
284
|
+
memcpy(tailLocation, preTailBytes, 4);
|
285
|
+
|
286
|
+
if ((err != Z_BUF_ERROR && err != Z_OK) || zlibContext->dynamicInflationBuffer.length() > maxPayloadLength) {
|
287
|
+
return std::nullopt;
|
288
|
+
}
|
289
|
+
|
290
|
+
if (zlibContext->dynamicInflationBuffer.length()) {
|
291
|
+
zlibContext->dynamicInflationBuffer.append(zlibContext->inflationBuffer, LARGE_BUFFER_SIZE - inflationStream.avail_out);
|
292
|
+
|
293
|
+
/* Let's be strict about the max size */
|
294
|
+
if (zlibContext->dynamicInflationBuffer.length() > maxPayloadLength) {
|
295
|
+
return std::nullopt;
|
296
|
+
}
|
297
|
+
|
298
|
+
return std::string_view(zlibContext->dynamicInflationBuffer.data(), zlibContext->dynamicInflationBuffer.length());
|
299
|
+
}
|
300
|
+
|
301
|
+
/* Let's be strict about the max size */
|
302
|
+
if ((LARGE_BUFFER_SIZE - inflationStream.avail_out) > maxPayloadLength) {
|
303
|
+
return std::nullopt;
|
304
|
+
}
|
305
|
+
|
306
|
+
return std::string_view(zlibContext->inflationBuffer, LARGE_BUFFER_SIZE - inflationStream.avail_out);
|
307
|
+
}
|
308
|
+
|
309
|
+
};
|
310
|
+
|
311
|
+
#endif
|
312
|
+
|
313
|
+
}
|
314
|
+
|
315
|
+
#endif // UWS_PERMESSAGEDEFLATE_H
|