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/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 0 : name() const noexcept override
73 : {
74 0 : return "boost.http.proto.zlib";
75 : }
76 :
77 : std::string
78 0 : message( int ev ) const override
79 : {
80 0 : return message( ev, nullptr, 0 );
81 : }
82 :
83 : char const*
84 0 : message(
85 : int ev,
86 : char*,
87 : std::size_t) const noexcept override
88 : {
89 0 : switch(static_cast<error>(ev))
90 : {
91 0 : case error::ok: return "Z_OK";
92 0 : case error::stream_end: return "Z_STREAM_END";
93 0 : case error::need_dict: return "Z_NEED_DICT";
94 0 : case error::errno_: return "Z_ERRNO";
95 0 : case error::stream_err: return "Z_STREAM_ERROR";
96 0 : case error::data_err: return "Z_DATA_ERROR";
97 0 : case error::mem_err: return "Z_MEM_ERROR";
98 0 : case error::buf_err: return "Z_BUF_ERROR";
99 0 : case error::version_err: return "Z_VERSION_ERROR";
100 0 : default:
101 0 : 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 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 0 : throw_zlib_error(
120 : int e,
121 : source_location const& loc = BOOST_CURRENT_LOCATION)
122 : {
123 0 : throw_exception(
124 0 : 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 240 : return ws->reserve_front(n);
238 : }
239 0 : catch(std::length_error const&) // represents OOM
240 : {
241 0 : return Z_NULL;
242 0 : }
243 : }
244 :
245 0 : 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 0 : }
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 144944 : if(x >= (std::numeric_limits<::uInt>::max)())
261 0 : 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 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 48 : if(ret != Z_OK)
312 0 : 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 0 : inflator(
334 : http_proto::detail::workspace& ws,
335 : int window_bits)
336 0 : {
337 0 : zs_.zalloc = &zalloc_impl;
338 0 : zs_.zfree = &zfree_impl;
339 0 : zs_.opaque = &ws;
340 :
341 0 : auto ret = inflateInit2(&zs_, window_bits);
342 0 : if(ret != Z_OK)
343 0 : throw_zlib_error(ret);
344 0 : }
345 :
346 : virtual results
347 0 : write(
348 : buffers::mutable_buffer out,
349 : buffers::const_buffer in,
350 : flush flush) noexcept override
351 : {
352 0 : sync(&zs_, out, in);
353 0 : return make_results(zs_, out, in,
354 0 : 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 0 : make_inflator(
382 : http_proto::detail::workspace& ws,
383 : int window_bits) const override
384 : {
385 0 : 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
|