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