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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +209 -0
  3. data/README.md +81 -28
  4. data/bin/up_ruby +4 -0
  5. data/bin/up_ruby_cluster +4 -0
  6. data/ext/up_ext/App.h +606 -0
  7. data/ext/up_ext/AsyncSocket.h +355 -0
  8. data/ext/up_ext/AsyncSocketData.h +87 -0
  9. data/ext/up_ext/BloomFilter.h +83 -0
  10. data/ext/up_ext/ChunkedEncoding.h +236 -0
  11. data/ext/up_ext/ClientApp.h +36 -0
  12. data/ext/up_ext/HttpContext.h +502 -0
  13. data/ext/up_ext/HttpContextData.h +56 -0
  14. data/ext/up_ext/HttpErrors.h +53 -0
  15. data/ext/up_ext/HttpParser.h +680 -0
  16. data/ext/up_ext/HttpResponse.h +578 -0
  17. data/ext/up_ext/HttpResponseData.h +95 -0
  18. data/ext/up_ext/HttpRouter.h +380 -0
  19. data/ext/up_ext/Loop.h +204 -0
  20. data/ext/up_ext/LoopData.h +112 -0
  21. data/ext/up_ext/MoveOnlyFunction.h +377 -0
  22. data/ext/up_ext/PerMessageDeflate.h +315 -0
  23. data/ext/up_ext/ProxyParser.h +163 -0
  24. data/ext/up_ext/QueryParser.h +120 -0
  25. data/ext/up_ext/TopicTree.h +363 -0
  26. data/ext/up_ext/Utilities.h +66 -0
  27. data/ext/up_ext/WebSocket.h +381 -0
  28. data/ext/up_ext/WebSocketContext.h +434 -0
  29. data/ext/up_ext/WebSocketContextData.h +109 -0
  30. data/ext/up_ext/WebSocketData.h +86 -0
  31. data/ext/up_ext/WebSocketExtensions.h +256 -0
  32. data/ext/up_ext/WebSocketHandshake.h +145 -0
  33. data/ext/up_ext/WebSocketProtocol.h +506 -0
  34. data/ext/up_ext/bsd.c +767 -0
  35. data/ext/up_ext/bsd.h +109 -0
  36. data/ext/up_ext/context.c +524 -0
  37. data/ext/up_ext/epoll_kqueue.c +458 -0
  38. data/ext/up_ext/epoll_kqueue.h +67 -0
  39. data/ext/up_ext/extconf.rb +5 -0
  40. data/ext/up_ext/internal.h +224 -0
  41. data/ext/up_ext/libusockets.h +350 -0
  42. data/ext/up_ext/libuwebsockets.cpp +1374 -0
  43. data/ext/up_ext/libuwebsockets.h +260 -0
  44. data/ext/up_ext/loop.c +386 -0
  45. data/ext/up_ext/loop_data.h +38 -0
  46. data/ext/up_ext/socket.c +231 -0
  47. data/ext/up_ext/up_ext.c +278 -0
  48. data/lib/up/node/rack_env.rb +2 -2
  49. data/lib/up/ruby/cluster_cli.rb +10 -0
  50. data/lib/up/ruby/rack_cluster.rb +26 -0
  51. data/lib/up/ruby/rack_env.rb +97 -0
  52. data/lib/up/ruby/rack_server.rb +26 -0
  53. data/lib/up/ruby/server_cli.rb +10 -0
  54. data/lib/up/u_web_socket/rack_env.rb +1 -1
  55. data/lib/up/version.rb +1 -1
  56. metadata +71 -18
  57. data/.gitignore +0 -5
  58. data/Gemfile +0 -2
  59. data/example_rack_app/Gemfile +0 -3
  60. data/example_rack_app/config.ru +0 -6
  61. data/example_rack_app/rack_app.rb +0 -5
  62. data/example_roda_app/Gemfile +0 -6
  63. data/example_roda_app/config.ru +0 -6
  64. data/example_roda_app/roda_app.rb +0 -37
  65. data/example_sinatra_app/Gemfile +0 -6
  66. data/example_sinatra_app/config.ru +0 -6
  67. data/example_sinatra_app/sinatra_app.rb +0 -7
  68. data/opal-up.gemspec +0 -27
  69. 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