opal-up 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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