Line data Source code
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/service/zlib_service.hpp>
14 :
15 : #include <boost/http_proto/metadata.hpp>
16 :
17 : #include <boost/assert/source_location.hpp>
18 : #include <boost/buffers/circular_buffer.hpp>
19 : #include <boost/config.hpp>
20 : #include <boost/system/result.hpp>
21 : #include <boost/throw_exception.hpp>
22 :
23 : #include <zlib.h>
24 :
25 : #include "../../src/zlib_service.hpp"
26 :
27 : namespace boost {
28 : namespace http_proto {
29 : namespace zlib {
30 : namespace detail {
31 :
32 : /*
33 : DEFLATE Compressed Data Format Specification version 1.3
34 : https://www.rfc-editor.org/rfc/rfc1951
35 : */
36 :
37 : //------------------------------------------------
38 :
39 : enum class error
40 : {
41 : ok = 0,
42 : stream_end = 1,
43 : need_dict = 2,
44 : errno_ = -1,
45 : stream_err = -2,
46 : data_err = -3,
47 : mem_err = -4,
48 : buf_err = -5,
49 : version_err = -6
50 : };
51 :
52 : //------------------------------------------------
53 : } // detail
54 : } // zlib
55 : } // http_proto
56 : namespace system {
57 : template<>
58 : struct is_error_code_enum<
59 : ::boost::http_proto::zlib::detail::error>
60 : {
61 : static bool const value = true;
62 : };
63 : } // system
64 : namespace http_proto {
65 : namespace zlib {
66 : namespace detail {
67 : //------------------------------------------------
68 :
69 : struct error_cat_type
70 : : system::error_category
71 : {
72 : BOOST_SYSTEM_CONSTEXPR
73 3 : error_cat_type() noexcept
74 3 : : error_category(
75 3 : 0xe6c6d0215d1d6e22)
76 : {
77 3 : }
78 :
79 : const char*
80 0 : name() const noexcept override
81 : {
82 0 : return "boost.http.proto.zlib";
83 : }
84 :
85 : std::string
86 0 : message( int ev ) const override
87 : {
88 0 : return message( ev, nullptr, 0 );
89 : }
90 :
91 : char const*
92 0 : message(
93 : int ev,
94 : char*,
95 : std::size_t) const noexcept override
96 : {
97 0 : switch(static_cast<error>(ev))
98 : {
99 0 : case error::ok: return "Z_OK";
100 0 : case error::stream_end: return "Z_STREAM_END";
101 0 : case error::need_dict: return "Z_NEED_DICT";
102 0 : case error::errno_: return "Z_ERRNO";
103 0 : case error::stream_err: return "Z_STREAM_ERROR";
104 0 : case error::data_err: return "Z_DATA_ERROR";
105 0 : case error::mem_err: return "Z_MEM_ERROR";
106 0 : case error::buf_err: return "Z_BUF_ERROR";
107 0 : case error::version_err: return "Z_VERSION_ERROR";
108 0 : default:
109 0 : return "unknown";
110 : }
111 : }
112 : };
113 :
114 : system::error_code
115 104 : make_error_code(
116 : error ev) noexcept
117 : {
118 : static BOOST_SYSTEM_CONSTEXPR
119 104 : error_cat_type cat{};
120 : return system::error_code{static_cast<
121 : std::underlying_type<
122 104 : error>::type>(ev), cat};
123 : }
124 :
125 : BOOST_NOINLINE BOOST_NORETURN
126 : void
127 0 : throw_zlib_error(
128 : int e,
129 : source_location const& loc = BOOST_CURRENT_LOCATION)
130 : {
131 0 : throw_exception(
132 0 : system::system_error(static_cast<error>(e)), loc);
133 : }
134 :
135 : //------------------------------------------------
136 :
137 : // probes memory usage for a config
138 : class probe
139 : {
140 : public:
141 : explicit
142 26 : probe() noexcept
143 26 : {
144 26 : zs_.zalloc = &zalloc;
145 26 : zs_.zfree = &zfree;
146 26 : zs_.opaque = this;
147 26 : }
148 :
149 : system::result<std::size_t>
150 26 : deflate_init(
151 : int level)
152 : {
153 26 : n_ = 0;
154 26 : system::error_code ec;
155 : ec = static_cast<error>(
156 26 : deflateInit(&zs_, level));
157 26 : if(ec.failed())
158 0 : return ec;
159 26 : Bytef tmp[24]{};
160 26 : zs_.next_in = &tmp[0];
161 26 : zs_.avail_in = 1;
162 26 : zs_.next_out = &tmp[1];
163 26 : zs_.avail_out = 23;
164 : ec = static_cast<error>(
165 26 : deflate(&zs_,
166 26 : Z_FINISH));
167 52 : if( ec.failed() &&
168 52 : ec != error::stream_end)
169 0 : return ec;
170 : ec = static_cast<error>(
171 26 : deflateEnd(&zs_));
172 26 : if(ec.failed())
173 0 : return ec;
174 26 : return n_;
175 : }
176 :
177 : system::result<std::size_t>
178 : deflate_init2(
179 : int level,
180 : int method,
181 : int windowBits,
182 : int memLevel,
183 : int strategy)
184 : {
185 : n_ = 0;
186 : system::error_code ec;
187 : ec = static_cast<error>(
188 : deflateInit2(&zs_,
189 : level,
190 : method,
191 : windowBits,
192 : memLevel,
193 : strategy));
194 : if(ec.failed())
195 : return ec;
196 : Bytef tmp[2];
197 : zs_.next_in = &tmp[0];
198 : zs_.avail_in = 0;
199 : zs_.next_out = &tmp[1];
200 : zs_.avail_out = 0;
201 : ec = static_cast<error>(
202 : deflate(&zs_,
203 : Z_FULL_FLUSH));
204 : if(ec.failed())
205 : return ec;
206 : ec = static_cast<error>(
207 : deflateEnd(&zs_));
208 : if(ec.failed())
209 : return ec;
210 : return n_;
211 : }
212 :
213 : private:
214 130 : static void* zalloc(void* opaque,
215 : uInt num, uInt size)
216 : {
217 130 : auto& self =
218 : *reinterpret_cast<
219 : probe*>(opaque);
220 130 : self.n_ += num * size;
221 130 : return new char[num * size];
222 : }
223 :
224 130 : static void zfree(
225 : void*, void* address)
226 : {
227 130 : delete[] reinterpret_cast<
228 130 : char*>(address);
229 130 : }
230 :
231 : z_stream_s zs_{};
232 : std::size_t n_ = 0;
233 : };
234 :
235 : //------------------------------------------------
236 :
237 :
238 : namespace {
239 240 : void* zalloc_impl(
240 : void* opaque,
241 : unsigned items,
242 : unsigned size)
243 : {
244 : try
245 : {
246 240 : auto n = items * size;
247 240 : auto* ws =
248 : reinterpret_cast<
249 : http_proto::detail::workspace*>(opaque);
250 :
251 240 : return ws->reserve_front(n);
252 : }
253 0 : catch(std::length_error const&) // represents OOM
254 : {
255 0 : return Z_NULL;
256 0 : }
257 : }
258 :
259 240 : void zfree_impl(void* /* opaque */, void* /* addr */)
260 : {
261 : // we call ws_.clear() before the serializer is reused
262 : // so all the allocations are passively freed
263 240 : }
264 :
265 : } // namespace
266 :
267 : class deflate_filter final
268 : : public http_proto::detail::filter
269 : {
270 : private:
271 : z_stream stream_;
272 : http_proto::detail::workspace& ws_;
273 :
274 : void init(bool use_gzip);
275 :
276 : public:
277 : deflate_filter(
278 : http_proto::detail::workspace& ws,
279 : bool use_gzip = false);
280 : ~deflate_filter();
281 :
282 : deflate_filter(deflate_filter const&) = delete;
283 : deflate_filter& operator=(
284 : deflate_filter const&) = delete;
285 :
286 : filter::results
287 : on_process(
288 : buffers::mutable_buffer out,
289 : buffers::const_buffer in,
290 : bool more) override;
291 : };
292 :
293 48 : deflate_filter::
294 : deflate_filter(
295 : http_proto::detail::workspace& ws,
296 48 : bool use_gzip)
297 48 : : ws_(ws)
298 : {
299 48 : stream_.zalloc = &zalloc_impl;
300 48 : stream_.zfree = &zfree_impl;
301 48 : stream_.opaque = &ws_;
302 48 : init(use_gzip);
303 48 : }
304 :
305 48 : deflate_filter::
306 48 : ~deflate_filter()
307 : {
308 48 : deflateEnd(&stream_);
309 48 : }
310 :
311 : void
312 48 : deflate_filter::
313 : init(bool use_gzip)
314 : {
315 48 : int ret = -1;
316 :
317 48 : int window_bits = 15;
318 48 : if( use_gzip )
319 24 : window_bits += 16;
320 :
321 48 : int mem_level = 8;
322 :
323 48 : ret = deflateInit2(
324 : &stream_, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
325 : window_bits, mem_level, Z_DEFAULT_STRATEGY);
326 :
327 48 : if( ret != Z_OK )
328 0 : throw_zlib_error(ret);
329 :
330 48 : stream_.next_out = nullptr;
331 48 : stream_.avail_out = 0;
332 :
333 48 : stream_.next_in = nullptr;
334 48 : stream_.avail_in = 0;
335 48 : }
336 :
337 : http_proto::detail::filter::results
338 23724 : deflate_filter::
339 : on_process(
340 : buffers::mutable_buffer out,
341 : buffers::const_buffer in,
342 : bool more)
343 : {
344 23724 : auto& zstream = stream_;
345 :
346 23724 : auto flush = more ? Z_NO_FLUSH : Z_FINISH;
347 23724 : int ret = -1;
348 23724 : filter::results results;
349 :
350 : for(;;)
351 : {
352 45988 : zstream.next_in =
353 : reinterpret_cast<unsigned char*>(
354 45988 : const_cast<void*>(in.data()));
355 45988 : zstream.avail_in = static_cast<unsigned>(
356 45988 : in.size());
357 :
358 45988 : zstream.next_out =
359 : reinterpret_cast<unsigned char*>(
360 45988 : out.data());
361 45988 : zstream.avail_out =
362 45988 : static_cast<unsigned>(out.size());
363 :
364 45988 : auto n1 = zstream.avail_in;
365 45988 : auto n2 = zstream.avail_out;
366 45988 : ret = deflate(&zstream, flush);
367 :
368 45988 : in += (n1 - zstream.avail_in);
369 45988 : out += (n2 - zstream.avail_out);
370 :
371 45988 : results.in_bytes += (n1 - zstream.avail_in);
372 45988 : results.out_bytes += (n2 - zstream.avail_out);
373 :
374 45988 : auto is_empty = (in.size() == 0);
375 :
376 45988 : if( ret != Z_OK &&
377 96 : ret != Z_BUF_ERROR &&
378 : ret != Z_STREAM_END )
379 0 : throw_zlib_error(ret);
380 :
381 45988 : if( is_empty &&
382 44648 : n2 == zstream.avail_out &&
383 : ret == Z_OK )
384 : {
385 11104 : flush = Z_SYNC_FLUSH;
386 11104 : continue;
387 : }
388 :
389 34884 : if( ret == Z_STREAM_END )
390 96 : results.finished = true;
391 :
392 34884 : if( ret == Z_BUF_ERROR )
393 22104 : break;
394 :
395 12780 : if( ret == Z_STREAM_END )
396 96 : break;
397 :
398 25368 : if( ret == Z_OK &&
399 12684 : out.size() == 0 )
400 1524 : break;
401 22264 : }
402 23724 : return results;
403 : }
404 :
405 : //------------------------------------------------
406 :
407 : struct
408 : deflate_decoder_service_impl
409 : : deflate_decoder_service
410 : {
411 : using key_type =
412 : deflate_decoder_service;
413 :
414 : explicit
415 26 : deflate_decoder_service_impl(
416 : context& ctx,
417 : config const& cfg)
418 26 : : cfg_(cfg)
419 : {
420 : (void)ctx;
421 26 : probe p;
422 26 : auto n0 = p.deflate_init(
423 26 : Z_DEFAULT_COMPRESSION).value();
424 : (void)n0;
425 26 : }
426 :
427 : private:
428 : config cfg_;
429 :
430 : config const&
431 0 : get_config() const noexcept override
432 : {
433 0 : return cfg_;
434 : }
435 :
436 : std::size_t
437 1 : space_needed() const noexcept override
438 : {
439 1 : return 0;
440 : }
441 :
442 : http_proto::detail::filter&
443 24 : make_deflate_filter(
444 : http_proto::detail::workspace& ws) const override
445 : {
446 24 : return ws.emplace<deflate_filter>(ws, false);
447 : }
448 :
449 : http_proto::detail::filter&
450 24 : make_gzip_filter(
451 : http_proto::detail::workspace& ws) const override
452 : {
453 24 : return ws.emplace<deflate_filter>(ws, true);
454 : }
455 : };
456 :
457 : } // detail
458 :
459 : void BOOST_HTTP_PROTO_ZLIB_DECL
460 26 : install_deflate_encoder(context& ctx)
461 : {
462 26 : detail::deflate_decoder_service::config cfg;
463 : ctx.make_service<
464 26 : detail::deflate_decoder_service_impl>(cfg);
465 26 : }
466 :
467 : } // zlib
468 : } // http_proto
469 : } // boost
470 :
471 : #endif
|