Line | Branch | Exec | Source |
---|---|---|---|
1 | // | ||
2 | // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) | ||
3 | // | ||
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
6 | // | ||
7 | // Official repository: https://github.com/cppalliance/http_proto | ||
8 | // | ||
9 | |||
10 | #ifndef BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP | ||
11 | #define BOOST_HTTP_PROTO_SERVICE_IMPL_ZLIB_SERVICE_IPP | ||
12 | |||
13 | #include <boost/http_proto/metadata.hpp> | ||
14 | #include <boost/http_proto/service/zlib_service.hpp> | ||
15 | |||
16 | #include <boost/assert/source_location.hpp> | ||
17 | #include <boost/buffers/circular_buffer.hpp> | ||
18 | #include <boost/config.hpp> | ||
19 | #include <boost/system/result.hpp> | ||
20 | #include <boost/throw_exception.hpp> | ||
21 | |||
22 | #include <zlib.h> | ||
23 | |||
24 | namespace boost { | ||
25 | namespace http_proto { | ||
26 | namespace zlib { | ||
27 | namespace { | ||
28 | |||
29 | /* | ||
30 | DEFLATE Compressed Data Format Specification version 1.3 | ||
31 | https://www.rfc-editor.org/rfc/rfc1951 | ||
32 | */ | ||
33 | |||
34 | enum class error | ||
35 | { | ||
36 | ok = 0, | ||
37 | stream_end = 1, | ||
38 | need_dict = 2, | ||
39 | errno_ = -1, | ||
40 | stream_err = -2, | ||
41 | data_err = -3, | ||
42 | mem_err = -4, | ||
43 | buf_err = -5, | ||
44 | version_err = -6 | ||
45 | }; | ||
46 | |||
47 | } // namespace | ||
48 | } // zlib | ||
49 | } // http_proto | ||
50 | namespace system { | ||
51 | template<> | ||
52 | struct is_error_code_enum< | ||
53 | ::boost::http_proto::zlib::error> | ||
54 | { | ||
55 | static bool const value = true; | ||
56 | }; | ||
57 | } // system | ||
58 | namespace http_proto { | ||
59 | namespace zlib { | ||
60 | namespace { | ||
61 | struct error_cat_type | ||
62 | : system::error_category | ||
63 | { | ||
64 | BOOST_SYSTEM_CONSTEXPR | ||
65 | 1 | error_cat_type() noexcept | |
66 | 1 | : error_category( | |
67 | 1 | 0xe6c6d0215d1d6e22) | |
68 | { | ||
69 | 1 | } | |
70 | |||
71 | const char* | ||
72 | ✗ | name() const noexcept override | |
73 | { | ||
74 | ✗ | return "boost.http.proto.zlib"; | |
75 | } | ||
76 | |||
77 | std::string | ||
78 | ✗ | message( int ev ) const override | |
79 | { | ||
80 | ✗ | return message( ev, nullptr, 0 ); | |
81 | } | ||
82 | |||
83 | char const* | ||
84 | ✗ | message( | |
85 | int ev, | ||
86 | char*, | ||
87 | std::size_t) const noexcept override | ||
88 | { | ||
89 | ✗ | switch(static_cast<error>(ev)) | |
90 | { | ||
91 | ✗ | case error::ok: return "Z_OK"; | |
92 | ✗ | case error::stream_end: return "Z_STREAM_END"; | |
93 | ✗ | case error::need_dict: return "Z_NEED_DICT"; | |
94 | ✗ | case error::errno_: return "Z_ERRNO"; | |
95 | ✗ | case error::stream_err: return "Z_STREAM_ERROR"; | |
96 | ✗ | case error::data_err: return "Z_DATA_ERROR"; | |
97 | ✗ | case error::mem_err: return "Z_MEM_ERROR"; | |
98 | ✗ | case error::buf_err: return "Z_BUF_ERROR"; | |
99 | ✗ | case error::version_err: return "Z_VERSION_ERROR"; | |
100 | ✗ | default: | |
101 | ✗ | return "unknown"; | |
102 | } | ||
103 | } | ||
104 | }; | ||
105 | |||
106 | system::error_code | ||
107 | 12320 | make_error_code( | |
108 | error ev) noexcept | ||
109 | { | ||
110 | static BOOST_SYSTEM_CONSTEXPR | ||
111 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12319 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
12320 | error_cat_type cat{}; |
112 | return system::error_code{static_cast< | ||
113 | std::underlying_type< | ||
114 | 12320 | error>::type>(ev), cat}; | |
115 | } | ||
116 | |||
117 | BOOST_NOINLINE BOOST_NORETURN | ||
118 | void | ||
119 | ✗ | throw_zlib_error( | |
120 | int e, | ||
121 | source_location const& loc = BOOST_CURRENT_LOCATION) | ||
122 | { | ||
123 | ✗ | throw_exception( | |
124 | ✗ | system::system_error(static_cast<error>(e)), loc); | |
125 | } | ||
126 | |||
127 | // probes memory usage for a config | ||
128 | class probe | ||
129 | { | ||
130 | public: | ||
131 | explicit | ||
132 | probe() noexcept | ||
133 | { | ||
134 | zs_.zalloc = &zalloc; | ||
135 | zs_.zfree = &zfree; | ||
136 | zs_.opaque = this; | ||
137 | } | ||
138 | |||
139 | system::result<std::size_t> | ||
140 | deflate_init( | ||
141 | int level) | ||
142 | { | ||
143 | n_ = 0; | ||
144 | system::error_code ec; | ||
145 | ec = static_cast<error>( | ||
146 | deflateInit(&zs_, level)); | ||
147 | if(ec.failed()) | ||
148 | return ec; | ||
149 | Bytef tmp[24]{}; | ||
150 | zs_.next_in = &tmp[0]; | ||
151 | zs_.avail_in = 1; | ||
152 | zs_.next_out = &tmp[1]; | ||
153 | zs_.avail_out = 23; | ||
154 | ec = static_cast<error>( | ||
155 | deflate(&zs_, | ||
156 | Z_FINISH)); | ||
157 | if( ec.failed() && | ||
158 | ec != error::stream_end) | ||
159 | return ec; | ||
160 | ec = static_cast<error>( | ||
161 | deflateEnd(&zs_)); | ||
162 | if(ec.failed()) | ||
163 | return ec; | ||
164 | return n_; | ||
165 | } | ||
166 | |||
167 | system::result<std::size_t> | ||
168 | deflate_init2( | ||
169 | int level, | ||
170 | int method, | ||
171 | int windowBits, | ||
172 | int memLevel, | ||
173 | int strategy) | ||
174 | { | ||
175 | n_ = 0; | ||
176 | system::error_code ec; | ||
177 | ec = static_cast<error>( | ||
178 | deflateInit2(&zs_, | ||
179 | level, | ||
180 | method, | ||
181 | windowBits, | ||
182 | memLevel, | ||
183 | strategy)); | ||
184 | if(ec.failed()) | ||
185 | return ec; | ||
186 | Bytef tmp[2]; | ||
187 | zs_.next_in = &tmp[0]; | ||
188 | zs_.avail_in = 0; | ||
189 | zs_.next_out = &tmp[1]; | ||
190 | zs_.avail_out = 0; | ||
191 | ec = static_cast<error>( | ||
192 | deflate(&zs_, | ||
193 | Z_FULL_FLUSH)); | ||
194 | if(ec.failed()) | ||
195 | return ec; | ||
196 | ec = static_cast<error>( | ||
197 | deflateEnd(&zs_)); | ||
198 | if(ec.failed()) | ||
199 | return ec; | ||
200 | return n_; | ||
201 | } | ||
202 | |||
203 | private: | ||
204 | static void* zalloc(void* opaque, | ||
205 | uInt num, uInt size) | ||
206 | { | ||
207 | auto& self = | ||
208 | *reinterpret_cast< | ||
209 | probe*>(opaque); | ||
210 | self.n_ += num * size; | ||
211 | return new char[num * size]; | ||
212 | } | ||
213 | |||
214 | static void zfree( | ||
215 | void*, void* address) | ||
216 | { | ||
217 | delete[] reinterpret_cast< | ||
218 | char*>(address); | ||
219 | } | ||
220 | |||
221 | z_stream_s zs_{}; | ||
222 | std::size_t n_ = 0; | ||
223 | }; | ||
224 | |||
225 | 240 | void* zalloc_impl( | |
226 | void* opaque, | ||
227 | unsigned items, | ||
228 | unsigned size) | ||
229 | { | ||
230 | try | ||
231 | { | ||
232 | 240 | auto n = items * size; | |
233 | 240 | auto* ws = | |
234 | reinterpret_cast< | ||
235 | http_proto::detail::workspace*>(opaque); | ||
236 | |||
237 |
1/2✓ Branch 1 taken 240 times.
✗ Branch 2 not taken.
|
240 | return ws->reserve_front(n); |
238 | } | ||
239 | ✗ | catch(std::length_error const&) // represents OOM | |
240 | { | ||
241 | ✗ | return Z_NULL; | |
242 | ✗ | } | |
243 | } | ||
244 | |||
245 | ✗ | void zfree_impl(void* /* opaque */, void* /* addr */) | |
246 | { | ||
247 | // we call ws_.clear() before the serializer is reused | ||
248 | // so all the allocations are passively freed | ||
249 | ✗ | } | |
250 | } // namespace | ||
251 | |||
252 | struct service_impl | ||
253 | : public service | ||
254 | { | ||
255 | using key_type = service; | ||
256 | |||
257 | static ::uInt | ||
258 | 144944 | clamp(std::size_t x) noexcept | |
259 | { | ||
260 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 144944 times.
|
144944 | if(x >= (std::numeric_limits<::uInt>::max)()) |
261 | ✗ | return (std::numeric_limits<::uInt>::max)(); | |
262 | 144944 | return static_cast<::uInt>(x); | |
263 | } | ||
264 | |||
265 | static void | ||
266 | 36236 | sync( | |
267 | z_stream* zs, | ||
268 | buffers::mutable_buffer const& out, | ||
269 | buffers::const_buffer const& in) noexcept | ||
270 | { | ||
271 | 36236 | zs->next_in = reinterpret_cast< | |
272 | 36236 | unsigned char*>(const_cast<void*>(in.data())); | |
273 | 36236 | zs->avail_in = clamp(in.size()); | |
274 | 36236 | zs->next_out = reinterpret_cast< | |
275 | 36236 | unsigned char*>(out.data()); | |
276 | 36236 | zs->avail_out = clamp(out.size()); | |
277 | 36236 | } | |
278 | |||
279 | static stream::results | ||
280 | 36236 | make_results( | |
281 | z_stream const& zs, | ||
282 | buffers::mutable_buffer const& out, | ||
283 | buffers::const_buffer const& in, | ||
284 | int ret) noexcept | ||
285 | { | ||
286 | return { | ||
287 | 36236 | clamp(out.size()) - zs.avail_out, | |
288 | 36236 | clamp(in.size()) - zs.avail_in, | |
289 | ret < 0 ? static_cast<error>(ret) : system::error_code{}, | ||
290 |
2/2✓ Branch 1 taken 12320 times.
✓ Branch 2 taken 23916 times.
|
72472 | ret == Z_STREAM_END }; |
291 | } | ||
292 | |||
293 | class deflator | ||
294 | : public stream | ||
295 | { | ||
296 | z_stream zs_; | ||
297 | |||
298 | public: | ||
299 | 48 | deflator( | |
300 | http_proto::detail::workspace& ws, | ||
301 | int level, | ||
302 | int window_bits, | ||
303 | int mem_level) | ||
304 | 48 | { | |
305 | 48 | zs_.zalloc = &zalloc_impl; | |
306 | 48 | zs_.zfree = &zfree_impl; | |
307 | 48 | zs_.opaque = &ws; | |
308 | |||
309 | 48 | auto ret = deflateInit2(&zs_, level, Z_DEFLATED, | |
310 | window_bits, mem_level, Z_DEFAULT_STRATEGY); | ||
311 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
|
48 | if(ret != Z_OK) |
312 | ✗ | throw_zlib_error(ret); | |
313 | 48 | } | |
314 | |||
315 | virtual results | ||
316 | 36236 | write( | |
317 | buffers::mutable_buffer out, | ||
318 | buffers::const_buffer in, | ||
319 | flush flush) noexcept override | ||
320 | { | ||
321 | 36236 | sync(&zs_, out, in); | |
322 | 36236 | return make_results(zs_, out, in, | |
323 | 36236 | deflate(&zs_, static_cast<int>(flush))); | |
324 | } | ||
325 | }; | ||
326 | |||
327 | class inflator | ||
328 | : public stream | ||
329 | { | ||
330 | z_stream zs_; | ||
331 | |||
332 | public: | ||
333 | ✗ | inflator( | |
334 | http_proto::detail::workspace& ws, | ||
335 | int window_bits) | ||
336 | ✗ | { | |
337 | ✗ | zs_.zalloc = &zalloc_impl; | |
338 | ✗ | zs_.zfree = &zfree_impl; | |
339 | ✗ | zs_.opaque = &ws; | |
340 | |||
341 | ✗ | auto ret = inflateInit2(&zs_, window_bits); | |
342 | ✗ | if(ret != Z_OK) | |
343 | ✗ | throw_zlib_error(ret); | |
344 | ✗ | } | |
345 | |||
346 | virtual results | ||
347 | ✗ | write( | |
348 | buffers::mutable_buffer out, | ||
349 | buffers::const_buffer in, | ||
350 | flush flush) noexcept override | ||
351 | { | ||
352 | ✗ | sync(&zs_, out, in); | |
353 | ✗ | return make_results(zs_, out, in, | |
354 | ✗ | inflate(&zs_, static_cast<int>(flush))); | |
355 | } | ||
356 | }; | ||
357 | |||
358 | explicit | ||
359 | 26 | service_impl(context&) noexcept | |
360 | 26 | { | |
361 | 26 | } | |
362 | |||
363 | virtual std::size_t | ||
364 | 1 | space_needed() const noexcept override | |
365 | { | ||
366 | 1 | return 0; // TODO | |
367 | } | ||
368 | |||
369 | virtual stream& | ||
370 | 48 | make_deflator( | |
371 | http_proto::detail::workspace& ws, | ||
372 | int level, | ||
373 | int window_bits, | ||
374 | int mem_level) const override | ||
375 | { | ||
376 | 48 | return ws.emplace<deflator>( | |
377 | 48 | ws, level, window_bits, mem_level); | |
378 | } | ||
379 | |||
380 | virtual stream& | ||
381 | ✗ | make_inflator( | |
382 | http_proto::detail::workspace& ws, | ||
383 | int window_bits) const override | ||
384 | { | ||
385 | ✗ | return ws.emplace<inflator>(ws, window_bits); | |
386 | } | ||
387 | }; | ||
388 | |||
389 | void BOOST_HTTP_PROTO_ZLIB_DECL | ||
390 | 26 | install_service(context& ctx) | |
391 | { | ||
392 | 26 | ctx.make_service<service_impl>(); | |
393 | 26 | } | |
394 | } // zlib | ||
395 | } // http_proto | ||
396 | } // boost | ||
397 | |||
398 | #endif | ||
399 |