RESTinio
Loading...
Searching...
No Matches
zlib.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
5/*!
6 Transformator of data streams using zlib.
7
8 @since v.0.4.4
9*/
10
11#pragma once
12
13#include <restinio/impl/include_fmtlib.hpp>
14
15#include <restinio/impl/string_caseless_compare.hpp>
16
17#include <restinio/exception.hpp>
18#include <restinio/string_view.hpp>
19#include <restinio/message_builders.hpp>
20#include <restinio/request_handler.hpp>
21
22#include <zlib.h>
23
24#include <string>
25#include <cstring>
26
27namespace restinio
28{
29
30namespace transforms
31{
32
33namespace zlib
34{
35
36//! Default reserve buffer size for zlib transformator.
37//! @since v.0.4.4
38constexpr std::size_t default_output_reserve_buffer_size = 256 * 1024;
39
40/** @name Default values for zlib tuning parameters.
41 * @brief Constants are defined with values provided by zlib.
42 *
43 * @since v.0.4.4
44*/
45///@{
46constexpr int default_window_bits = MAX_WBITS;
47constexpr int default_mem_level = MAX_MEM_LEVEL;
48constexpr int default_strategy = Z_DEFAULT_STRATEGY;
49///@}
50
51//
52// params_t
53//
54
55//! Parameters of performing data transformation with zlib.
56/*
57 @since v.0.4.4
58
59 \note There is a special case for compression format: format_t::identity.
60 If this format is set that zlib transformator is transparently copies
61 input to output and so all other params are ignored.
62*/
64{
65 public:
66 //! Types of transformation.
67 enum class operation_t
68 {
69 //! Compress the input data.
71 //! Decompress input data.
73 };
74
75 //! Formats of compressed data.
76 enum class format_t
77 {
78 //! zlib format.
80 //! gzip format
82 //! Identity. With semantics descrobed here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
83 /*
84 Means that no compression will be used and no header/trailer will be applied.
85 */
87 };
88
89 //! Init constructor.
90 /*!
91 It's better to use special functions to cunstruct
92 initial params_t, see:
93 deflate_compress(), deflate_decompress(),
94 gzip_compress(), gzip_decompress(),
95 */
97 //! Operation: compress or decompress.
98 operation_t op,
99 //! Foramt: deflate or gzip.
100 format_t f,
101 //! Compression level.
102 int l = -1 )
103 : m_operation{ op }
104 , m_format{ f }
105 {
106 level( l );
107 }
108
109 //! Default constructor for identiry transformator
116
117 //! Get operation.
119
120 //! Get format.
121 format_t format() const { return m_format; }
122
123 //! Get compression level.
124 /*!
125 \note Makes sense only for compression operation.
126 */
127 int level() const { return m_level; }
128
129 //! Set compression level.
130 /*!
131 Must be an integer value in the range of -1 to 9.
132
133 \note Makes sense only for compression operation.
134 */
135 params_t &
136 level( int level_value ) &
137 {
138 if( level_value < -1 || level_value > 9 )
139 {
140 throw exception_t{
141 fmt::format(
143 "invalid compression level: {}, must be "
144 "an integer value in the range of -1 to 9" ),
145 level_value ) };
146 }
147
148 m_level = level_value;
149
150 return reference_to_self();
151 }
152
153 //! Set compression level.
154 params_t &&
155 level( int level_value ) &&
156 {
157 return std::move( this->level( level_value ) );
158 }
159
160 //! Get window_bits.
161 int window_bits() const { return m_window_bits; }
162
163 //! Set window_bits.
164 /*!
165 Must be an integer value in the range of 8 to MAX_WBITS.
166
167 \note For descompress operation it is better to use default
168 value of windows beats. See https://zlib.net/manual.html
169 inflateInit2() description for details.
170 */
171 params_t &
172 window_bits( int window_bits_value ) &
173 {
174 // From https://zlib.net/manual.html:
175 // For the current implementation of deflate(),
176 // a windowBits value of 8 (a window size of 256 bytes)
177 // is not supported. As a result, a request for 8 will result in 9
178 // (a 512-byte window). In that case, providing 8 to inflateInit2()
179 // will result in an error when the zlib header with 9 is
180 // checked against the initialization of inflate().
181 // The remedy is to not use 8 with deflateInit2()
182 // with this initialization, or at least in that case use 9
183 // with inflateInit2().
184 // ...
185 // windowBits can also be zero to request that inflate
186 // use the window size in the zlib header of the compressed
187 // stream.
188
189 if( ( window_bits_value < 8 || window_bits_value > MAX_WBITS ) &&
190 ( 0 != window_bits_value || operation_t::decompress != operation() ) )
191 {
192 throw exception_t{
193 fmt::format(
195 "invalid window_bits: {}, must be "
196 "an integer value in the range of 8 to {} or "
197 "0 for decompress operation" ),
198 window_bits_value,
199 MAX_WBITS ) };
200 }
201
202 if( 8 == window_bits_value )
203 window_bits_value = 9;
204
205 m_window_bits = window_bits_value;
206
207 return reference_to_self();
208 }
209
210 //! Set window_bits.
211 params_t &&
212 window_bits( int window_bits_value ) &&
213 {
214 return std::move( this->window_bits( window_bits_value ) );
215 }
216
217 //! Get compression mem_level.
218 /*!
219 \note Makes sense only for compression operation.
220 */
221 int mem_level() const { return m_mem_level; }
222
223 //! Set compression mem_level.
224 /*!
225 Must be an integer value in the range of 1 to 9.
226 1 stands for minimum memory usage and
227 9 stands for maximum memory usage.
228 The amount of memory that can be used
229 affects the quality of compression.
230
231 \note Makes sense only for compression operation.
232 */
233 params_t &
234 mem_level( int mem_level_value ) &
235 {
236 if( mem_level_value < 1 || mem_level_value > MAX_MEM_LEVEL )
237 {
238 throw exception_t{
239 fmt::format(
241 "invalid compression mem_level: {}, must be "
242 "an integer value in the range of 1 to {}" ),
243 mem_level_value,
244 MAX_MEM_LEVEL ) };
245 }
246
247 m_mem_level = mem_level_value;
248
249 return reference_to_self();
250 }
251
252 //! Set compression mem_level.
253 params_t &&
254 mem_level( int mem_level_value ) &&
255 {
256 return std::move( this->mem_level( mem_level_value ) );
257 }
258
259 //! Get compression strategy.
260 /*!
261 \note Makes sense only for compression operation.
262 */
263 int strategy() const { return m_strategy; }
264
265 //! Set compression strategy.
266 /*!
267 Must be an integer value defined by one of
268 zlib constants: Z_FILTERED, Z_HUFFMAN_ONLY,
269 Z_RLE, Z_DEFAULT_STRATEGY.
270
271 \note Makes sense only for compression operation.
272 */
273 params_t &
274 strategy( int strategy_value ) &
275 {
276 if( Z_DEFAULT_STRATEGY != strategy_value &&
277 Z_FILTERED != strategy_value &&
278 Z_HUFFMAN_ONLY != strategy_value &&
279 Z_RLE != strategy_value )
280 {
281 throw exception_t{
282 fmt::format(
284 "invalid compression strategy: {}, must be "
285 "one of: "
286 "Z_DEFAULT_STRATEGY({}), "
287 "Z_FILTERED({}), "
288 "Z_HUFFMAN_ONLY({}), "
289 "Z_RLE({})" ),
290 strategy_value,
291 Z_DEFAULT_STRATEGY,
292 Z_FILTERED,
293 Z_HUFFMAN_ONLY,
294 Z_RLE ) };
295 }
296
297 m_strategy = strategy_value;
298
299 return reference_to_self();
300 }
301
302 //! Set compression strategy.
303 params_t &&
304 strategy( int strategy_value ) &&
305 {
306 return std::move( this->strategy( strategy_value ) );
307 }
308
309 //! Get the size initially reserved for buffer.
310 /*!
311 When using zlib transformator the outout buffer is used.
312 The initial size of such buffer must be defined.
313 zlib_t instance will use this parameter
314 as the initial size of out buffer and as an increment size
315 if out buffer must be enlarged.
316 */
317 std::size_t reserve_buffer_size() const { return m_reserve_buffer_size; }
318
319 //! Set the size initially reserved for buffer.
320 params_t &
321 reserve_buffer_size( std::size_t size ) &
322 {
323 if( size < 10UL )
324 {
325 throw exception_t{ "too small reserve buffer size" };
326 }
327
329
330 return reference_to_self();
331 }
332
333 //! Set the size initially reserved for buffer.
334 params_t &&
335 reserve_buffer_size( std::size_t size ) &&
336 {
337 return std::move( this->reserve_buffer_size( size ) );
338 }
339
340 private:
341 //! Get the reference to self.
342 params_t & reference_to_self() { return *this; }
343
344 //! Transformation type.
346
347 //! Format to be used for compressed data.
349
350 //! Compression level.
351 /*!
352 An integer value in the range of -1 to 9.
353 */
355
359
360 //! Size initially reserved for buffer.
362};
363
364/** @name Create parameters for zlib transformators.
365 * @brief A set of function helping to create params_t objects
366 * ommiting some verbose deteils.
367 *
368 * Instead of writing something like this:
369 * \code
370 * params_t params{
371 * restinio::transforms::zlib::params_t::operation_t::compress,
372 * restinio::transforms::zlib::params_t::format_t::gzip,
373 * -1 };
374 * \endcode
375 * It is better to write the following:
376 * \code
377 * params_t params = restinio::transforms::zlib::make_gzip_compress_params();
378 * \endcode
379 *
380 * @since v.0.4.4
381*/
382///@{
383inline params_t
384make_deflate_compress_params( int compression_level = -1 )
385{
386 return params_t{
389 compression_level };
390}
391
392inline params_t
399
400inline params_t
401make_gzip_compress_params( int compression_level = -1 )
402{
403 return params_t{
406 compression_level };
407}
408
409inline params_t
416
417inline params_t
419{
420 return params_t{};
421}
422///@}
423
424//
425// zlib_t
426//
427
428//! Zlib transformator.
429/*!
430 Uses zlib (https://zlib.net) for gzip/deflate compresion/decompression.
431 All higher level functionality that minimizes boilerplate code and makes
432 compression and decompression logic less verbose is based on using zlib_t.
433
434 Simple usage:
435 \code
436 namespace rtz = restinio::transforms::zlib;
437 rtz::zlib_t z{ rtz::make_gzip_compress_params( compression_level ).level( 9 ) };
438 z.write( input_data );
439 z.complete();
440
441 auto gziped_data = z.giveaway_output();
442 \endcode
443
444 Advanced usage:
445 \code
446 namespace rtz = restinio::transforms::zlib;
447 rtz::zlib_t z{ rtz::make_gzip_compress_params( compression_level ).level( 9 ) };
448
449 std::size_t processed_data = 0;
450 for( const auto d : data_pieces )
451 {
452 z.write( d );
453
454 // Track how much data is already processed:
455 processed_data += d.size();
456
457 if( processed_data > 1024 * 1024 )
458 {
459 // If more than 1Mb is processed do flush.
460 z.flush();
461 }
462
463 if( z.output_size() > 100 * 1024 )
464 {
465 // If more than 100Kb of data is ready, then append it to something.
466 append_output( z.giveaway_output() );
467 }
468 }
469
470 // Complete the stream and append remeining putput data.
471 z.complete();
472 append_output( z.giveaway_output() );
473 \endcode
474
475
476 @since v.0.4.4
477*/
479{
480 public:
481 zlib_t( const params_t & transformation_params )
482 : m_params{ transformation_params }
483 {
484 if( !is_identity() )
485 {
486 // Setting allocator stuff before initializing
487 // TODO: allocation can be done with user defined allocator.
488 m_zlib_stream.zalloc = Z_NULL;
489 m_zlib_stream.zfree = Z_NULL;
490 m_zlib_stream.opaque = Z_NULL;
491
492 // Track initialization result.
493 int init_result;
494
495 // Compression.
496 auto current_window_bits = m_params.window_bits();
497
499 {
500 current_window_bits += 16;
501 }
502
504 {
505 // zlib format.
506 init_result =
507 deflateInit2(
510 Z_DEFLATED,
511 current_window_bits,
514 }
515 else
516 {
517 init_result =
518 inflateInit2(
520 current_window_bits );
521 }
522
523 if( Z_OK != init_result )
524 {
525 throw exception_t{
526 fmt::format(
528 "Failed to initialize zlib stream: {}, {}" ),
529 init_result,
530 get_error_msg() ) };
531 }
532
534
535 // Reserve initial buffer.
537 }
538 // else => Nothing to initialize and to reserve.
539 }
540
541 zlib_t( const zlib_t & ) = delete;
542 zlib_t( zlib_t && ) = delete;
543 zlib_t & operator = ( const zlib_t & ) = delete;
544 zlib_t & operator = ( zlib_t && ) = delete;
545
547 {
549 {
551 {
552 deflateEnd( &m_zlib_stream );
553 }
554 else
555 {
556 inflateEnd( &m_zlib_stream );
557 }
558 }
559 }
560
561 //! Get parameters of current transformation.
562 const params_t & params() const { return m_params; }
563
564 //! Append input data.
565 /*!
566 Pushes a given data to zlib transform.
567 \a input is the data to be compressed or decompressed.
568 */
569 void
570 write( string_view_t input )
571 {
573
574 if( is_identity() )
575 {
576 m_out_buffer.append( input.data(), input.size() );
577 m_write_pos = m_out_buffer.size();
578 }
579 else
580 {
581 if( std::numeric_limits< decltype( m_zlib_stream.avail_in ) >::max() < input.size() )
582 {
583 throw exception_t{
584 fmt::format(
586 "input data is too large: {} (max possible: {}), "
587 "try to break large data into pieces" ),
588 input.size(),
589 std::numeric_limits< decltype( m_zlib_stream.avail_in ) >::max() ) };
590 }
591
592 if( 0 < input.size() )
593 {
594 m_zlib_stream.next_in =
595 reinterpret_cast< Bytef* >( const_cast< char* >( input.data() ) );
596
597 m_zlib_stream.avail_in = static_cast< uInt >( input.size() );
598
600 {
601 write_compress_impl( Z_NO_FLUSH );
602 }
603 else
604 {
605 write_decompress_impl( Z_NO_FLUSH );
606 }
607 }
608 }
609 }
610
611 //! Flush the zlib stream.
612 /*!
613 Flushes underlying zlib stream.
614 All pending output is flushed to the output buffer.
615 */
616 void
618 {
620
621 if( !is_identity() )
622 {
623 m_zlib_stream.next_in = nullptr;
624 m_zlib_stream.avail_in = static_cast< uInt >( 0 );
625
627 {
628 write_compress_impl( Z_SYNC_FLUSH );
629 }
630 else
631 {
632 write_decompress_impl( Z_SYNC_FLUSH );
633 }
634 }
635 }
636
637 //! Complete the stream.
638 void
640 {
642
643 if( !is_identity() )
644 {
645 m_zlib_stream.next_in = nullptr;
646 m_zlib_stream.avail_in = static_cast< uInt >( 0 );
647
649 {
650 write_compress_impl( Z_FINISH );
651 }
652 else
653 {
654 write_decompress_impl( Z_FINISH );
655 }
656 }
657
659 }
660
661 //! Get current accumulated output data
662 /*!
663 On this request a current accumulated output data is reterned.
664 Move semantics is applied. Once current output is fetched
665 zlib_t object resets its internal out buffer.
666
667 In the following code:
668 \code
669 restinio::transformator::zlib_t z{ restinio::transformator::gzip_compress() };
670
671 z.write( A );
672 consume_out( z.giveaway_output() ); // (1)
673
674 z.write( B );
675 z.write( C );
676 consume_out( z.giveaway_output() ); // (2)
677
678 \endcode
679 At the point (2) `consume_out()` function receives
680 a string that is not an appended version of a string
681 received in point (1).
682 */
683 std::string
685 {
686 std::string result;
687 const auto data_size = m_write_pos;
688 std::swap( result, m_out_buffer );
689 m_write_pos = 0;
690 result.resize( data_size ); // Shrink output data.
691 return result;
692 }
693
694 //! Get current output size.
695 auto output_size() const { return m_write_pos; }
696
697 //! Is operation complete?
698 bool is_completed() const { return m_operation_is_complete; }
699
700 private:
701 bool is_identity() const
702 {
704 }
705
706 //! Get zlib error message if it exists.
707 const char *
709 {
710 const char * err_msg = "<no zlib error description>";
711 if( m_zlib_stream.msg )
712 err_msg = m_zlib_stream.msg;
713
714 return err_msg;
715 }
716
717 //! Checks completion flag and throws if operation is is already completed.
718 void
720 {
721 if( is_completed() )
722 throw exception_t{ "zlib operation is already completed" };
723 }
724
725 //! Increment internal buffer for receiving output.
726 void
728 {
729 m_out_buffer.resize(
731 }
732
733 //! Prepare out buffer for receiving data.
734 auto
736 {
737 m_zlib_stream.next_out =
738 reinterpret_cast< Bytef* >(
739 const_cast< char* >( m_out_buffer.data() + m_write_pos ) );
740
741 const auto provided_out_buffer_size =
742 m_out_buffer.size() - m_write_pos;
743 m_zlib_stream.avail_out =
744 static_cast<uInt>( provided_out_buffer_size );
745
746 return provided_out_buffer_size;
747 }
748
749 //! Handle incoming data for compression operation.
750 /*
751 Data and its size must be already in
752 `m_zlib_stream.next_in`, `m_zlib_stream.avail_in`.
753 */
754 void
756 {
757 while( true )
758 {
759 const auto provided_out_buffer_size = prepare_out_buffer();
760
761 int operation_result = deflate( &m_zlib_stream, flush );
762
763 if( !( Z_OK == operation_result ||
764 Z_BUF_ERROR == operation_result ||
765 ( Z_STREAM_END == operation_result && Z_FINISH == flush ) ) )
766 {
767 const char * err_msg = "<no error desc>";
768 if( m_zlib_stream.msg )
769 err_msg = m_zlib_stream.msg;
770
771 throw exception_t{
772 fmt::format(
774 "unexpected result of deflate() (zlib): {}, {}" ),
775 operation_result,
776 err_msg ) };
777 }
778
779 m_write_pos += provided_out_buffer_size - m_zlib_stream.avail_out;
780
781 if( 0 == m_zlib_stream.avail_out && Z_STREAM_END != operation_result )
782 {
783 // Looks like not all the output was obtained.
784 // There is a minor chance that it just happened to
785 // occupy exactly the same space that was available,
786 // in that case it would go for a one redundant call to deflate.
788 continue;
789 }
790
791 if( 0 == m_zlib_stream.avail_in )
792 {
793 // All the input was consumed.
794 break;
795 }
796 }
797 }
798
799 //! Handle incoming data for decompression operation.
800 /*
801 Data and its size must be already in
802 `m_zlib_stream.next_in`, `m_zlib_stream.avail_in`.
803 */
804 void
806 {
807 while( true )
808 {
809 const auto provided_out_buffer_size = prepare_out_buffer();
810
811 int operation_result = inflate( &m_zlib_stream, flush );
812 if( !( Z_OK == operation_result ||
813 Z_BUF_ERROR == operation_result ||
814 Z_STREAM_END == operation_result ) )
815 {
816 throw exception_t{
817 fmt::format(
819 "unexpected result of inflate() (zlib): {}, {}" ),
820 operation_result,
821 get_error_msg() ) };
822 }
823
824 m_write_pos += provided_out_buffer_size - m_zlib_stream.avail_out;
825
826 if( 0 == m_zlib_stream.avail_out && Z_STREAM_END != operation_result )
827 {
828 // Looks like not all the output was obtained.
829 // There is a minor chance that it just happened to
830 // occupy exactly the same space that was available,
831 // in that case it would go for a one redundant call to deflate.
833 continue;
834 }
835
836 if( Z_STREAM_END == operation_result )
837 {
838 // All data was processed. There is no sense to continue
839 // even if m_zlib_stream.avail_in isn't zero.
840 break;
841 }
842
843 if( 0 == m_zlib_stream.avail_in )
844 {
845 // All the input was consumed.
846 break;
847 }
848 }
849 }
850
851 //! Parameters for zlib.
853
854 //! Flag: was m_zlib_stream initialized properly.
855 /*!
856 If deflateInit2()/inflateInit2() was completed successfully
857 it is needed to call deflateEnd()/inflateEnd().
858 */
860
861 //! zlib stream.
863
864 //! Output buffer.
865 std::string m_out_buffer;
866 //! Next write pos in out buffer.
867 std::size_t m_write_pos{ 0 };
868
870};
871
872/** @name Helper functions for doing zlib transformation with less boilerplate.
873 * @brief A set of handy functions helping to perform zlib transform in one line.
874 *
875 * Instead of writing something like this:
876 * \code
877 * restinio::transforms::zlib::zlib_t z{ restinio::transforms::zlib::gzip_compress() };
878 * z.write( data );
879 * z.complete();
880 * body = z.giveaway_output();
881 * \endcode
882 * It is possible to write the following:
883 * \code
884 * body = restinio::transformators::zlib::gzip_compress( data );
885 * \endcode
886 *
887 * @since v.0.4.4
888*/
889///@{
890
891//! Do a specified zlib transformation.
892inline std::string
893transform( string_view_t input, const params_t & params )
894{
895 zlib_t z{ params };
896 z.write( input );
898
899 return z.giveaway_output();
900}
901
902inline std::string
903deflate_compress( string_view_t input, int compression_level = -1 )
904{
905 return transform( input, make_deflate_compress_params( compression_level ) );
906}
907
908inline std::string
909deflate_decompress( string_view_t input )
910{
911 return transform( input, make_deflate_decompress_params() );
912}
913
914inline std::string
915gzip_compress( string_view_t input, int compression_level = -1 )
916{
917 return transform( input, make_gzip_compress_params( compression_level ) );
918}
919
920inline std::string
921gzip_decompress( string_view_t input )
922{
923 return transform( input, make_gzip_decompress_params() );
924}
925///@}
926
927//
928// body_appender_t
929//
930
931template < typename Response_Output_Strategy >
933{
934 body_appender_t() = delete;
935};
936
937namespace impl
938{
939
940//! Check if operation is a copression, and throw if it is not.
942{
944 {
945 throw exception_t{ "operation is not copress" };
946 }
947}
948
949//! Check if a pointer to zlib transformator is valid.
950inline void ensure_valid_transforator( zlib_t * ztransformator )
951{
952 if( nullptr == ztransformator )
953 {
954 throw exception_t{ "invalid body appender" };
955 }
956}
957
958//! Get token for copression format.
960{
961 std::string result{ "identity" };
962
964 {
965 result.assign( "deflate" );
966 }
967 if( params_t::format_t::gzip == f )
968 {
969 result.assign( "gzip" );
970 }
971
972 return result;
973}
974
975} /* namespace impl */
976
977//
978// body_appender_base_t
979//
980
981//! Base class for body appenders.
982template < typename Response_Output_Strategy, typename Descendant >
984{
985 public:
986 using resp_t = response_builder_t< Response_Output_Strategy >;
987
988 body_appender_base_t( const params_t & params, resp_t & resp )
990 , m_resp{ resp }
991 {
992 impl::ensure_is_compression_operation(
993 m_ztransformator->params().operation() );
994
995 m_resp.append_header(
996 restinio::http_field::content_encoding,
997 impl::content_encoding_token(
998 m_ztransformator->params().format() ) );
999 }
1000
1004
1009
1011
1012 protected:
1015};
1016
1017//! Base class for body appenders with restinio or user controlled output.
1018template < typename X_Controlled_Output, typename Descendant >
1020 : public body_appender_base_t< X_Controlled_Output, Descendant >
1021{
1022 public:
1023 using base_type_t = body_appender_base_t< X_Controlled_Output, Descendant >;
1024
1025 using base_type_t::base_type_t;
1026
1027 //! Append a piece of data to response.
1028 Descendant &
1029 append( string_view_t input )
1030 {
1031 impl::ensure_valid_transforator( this->m_ztransformator.get() );
1032 this->m_ztransformator->write( input );
1033 return static_cast< Descendant & >( *this );
1034 }
1035
1036 //! Complete zlib transformation operation.
1037 void
1039 {
1040 impl::ensure_valid_transforator( this->m_ztransformator.get() );
1041
1042 this->m_ztransformator->complete();
1043
1044 this->m_resp.append_body( this->m_ztransformator->giveaway_output() );
1045 }
1046};
1047
1048/** @name Body appender.
1049 * @brief Helper class for setting the body of response_builder_t<restinio_controlled_output_t>.
1050 *
1051 * Sample usage:
1052 * \code
1053 * namespace rtz = restinio::transforms::zlib;
1054 * auto resp = req->create_response();
1055 *
1056 * resp.append_header( restinio::http_field::server, "RESTinio" )
1057 * .append_header_date_field()
1058 * .append_header( restinio::http_field::content_type, "text/plain; charset=utf-8" );
1059 *
1060 * auto ba = rtz::gzip_body_appender( resp );
1061 * ba.append( some_data );
1062 * // ...
1063 * ba.append( some_more_data );
1064 * ba.complete();
1065 * resp.done();
1066 * \endcode
1067 *
1068 * @since v.0.4.4
1069*/
1070template <>
1075{
1076 public:
1081
1082 //! Get the size of transformed body.
1083 auto
1084 size() const
1085 {
1086 impl::ensure_valid_transforator( m_ztransformator.get() );
1087
1088 return m_ztransformator->output_size();
1089 }
1090
1091 using base_type_t::base_type_t;
1092};
1093
1094/** @name Body appender.
1095 * @brief Helper class for setting the body of response_builder_t<user_controlled_output_t>.
1096 *
1097 * Sample usage:
1098 * \code
1099 * namespace rtz = restinio::transforms::zlib;
1100 * auto resp = req->create_response<user_controlled_output_t>();
1101 *
1102 * resp.append_header( restinio::http_field::server, "RESTinio" )
1103 * .append_header_date_field()
1104 * .append_header( restinio::http_field::content_type, "text/plain; charset=utf-8" );
1105 *
1106 * auto ba = rtz::gzip_body_appender( resp );
1107 * ba.append( some_data );
1108 * ba.flush();
1109 * // ...
1110 * ba.append( some_more_data );
1111 * ba.complete();
1112 * resp.done();
1113 * \endcode
1114 *
1115 * @since v.0.4.4
1116*/
1117template <>
1118class body_appender_t< user_controlled_output_t > final
1119 : public x_controlled_output_body_appender_base_t<
1120 user_controlled_output_t,
1121 body_appender_t< user_controlled_output_t > >
1122{
1123 public:
1128
1129 using base_type_t::base_type_t;
1130
1131 auto &
1133 {
1134 impl::ensure_valid_transforator( m_ztransformator.get() );
1135 m_ztransformator->flush();
1136 m_resp
1137 .append_body( m_ztransformator->giveaway_output() )
1138 .flush();
1139
1140 return *this;
1141 }
1142};
1143
1144
1145/** @name Body appender.
1146 * @brief Helper class for setting the body of response_builder_t<chunked_output_t>.
1147 *
1148 * Sample usage:
1149 * \code
1150 * namespace rtz = restinio::transforms::zlib;
1151 * auto resp = req->create_response<chunked_output_t>();
1152 *
1153 * resp.append_header( restinio::http_field::server, "RESTinio" )
1154 * .append_header_date_field()
1155 * .append_header( restinio::http_field::content_type, "text/plain; charset=utf-8" );
1156 *
1157 * auto ba = rtz::gzip_body_appender( resp );
1158 * ba.append( some_data );
1159 * ba.append( some_more_data );
1160 * ba.make_chunk(); // Flush copressed data and creates a chunk with it.
1161 * ba.flush(); // Send currently prepared chunks to client
1162 * // ...
1163 * // Copress the data and creates a chunk with it.
1164 * ba.make_chunk( even_more_data );
1165 * ba.flush(); // Send currently prepared chunks to client
1166 * // ...
1167 * ba.append( yet_even_more_data );
1168 * ba.append( last_data );
1169 * ba.complete(); // Creates last chunk, but doesn't send it to client.
1170 * ba.flush(); // Send chunk created by complete() call
1171 * // ...
1172 * resp.done();
1173 * \endcode
1174 *
1175 * @since v.0.4.4
1176*/
1177template <>
1178class body_appender_t< chunked_output_t > final
1179 : public body_appender_base_t<
1180 chunked_output_t,
1181 body_appender_t< chunked_output_t > >
1182{
1183 public:
1188
1189 using base_type_t::base_type_t;
1190
1191 //! Append data to be compressed.
1192 /*!
1193 Function only adds data to anderlying zlib stream
1194 and it doesn't affect target response right on here.
1195 */
1196 auto &
1197 append( string_view_t input )
1198 {
1199 impl::ensure_valid_transforator( m_ztransformator.get() );
1200
1201 m_ztransformator->write( input );
1202 return *this;
1203 }
1204
1205 //! Append data to be compressed and adds current zlib transformator output
1206 //! as a new chunk.
1207 /*!
1208 Adds data and flushes zlib transformator.
1209 Then ready compressed data is taken and used as a new chunk
1210 of target response.
1211 */
1212 auto &
1213 make_chunk( string_view_t input = string_view_t{} )
1214 {
1215 append( input ); // m_ztransformator is checked here.
1216
1217 m_ztransformator->flush(); // Flush already compressed data.
1218
1219 // Create a chunk with current output.
1220 m_resp.append_chunk( m_ztransformator->giveaway_output() );
1221
1222 return *this;
1223 }
1224
1225 //! Flushes currently available compressed data with possibly creating new chunk
1226 //! and then flushes target response.
1227 void
1229 {
1230 impl::ensure_valid_transforator( m_ztransformator.get() );
1231
1232 if( !m_ztransformator->is_completed() )
1233 {
1234 m_ztransformator->flush();
1235 m_resp.append_chunk( m_ztransformator->giveaway_output() );
1236 }
1237
1238 m_resp.flush();
1239 }
1240
1241 //! Complete zlib transformation operation.
1242 void
1244 {
1245 impl::ensure_valid_transforator( m_ztransformator.get() );
1246 m_ztransformator->complete();
1247 m_resp.append_chunk( m_ztransformator->giveaway_output() );
1248 }
1249};
1250
1251//! Create body appender with given zlib transformation parameters.
1252//! @since v.0.4.4
1253template < typename Response_Output_Strategy >
1254body_appender_t< Response_Output_Strategy >
1256 response_builder_t< Response_Output_Strategy > & resp,
1257 const params_t & params )
1258{
1259 return body_appender_t< Response_Output_Strategy >{ params, resp };
1260}
1261
1262//! Create body appender with deflate transformation and a given compression level.
1263//! @since v.0.4.4
1264template < typename Response_Output_Strategy >
1265body_appender_t< Response_Output_Strategy >
1267 response_builder_t< Response_Output_Strategy > & resp,
1268 int compression_level = -1 )
1269{
1270 return body_appender( resp, make_deflate_compress_params( compression_level ) );
1271}
1272
1273//! Create body appender with gzip transformation and a given compression level.
1274//! @since v.0.4.4
1275template < typename Response_Output_Strategy >
1276inline body_appender_t< Response_Output_Strategy >
1278 response_builder_t< Response_Output_Strategy > & resp,
1279 int compression_level = -1 )
1280{
1281 return body_appender( resp, make_gzip_compress_params( compression_level ) );
1282}
1283
1284//! Create body appender with gzip transformation and a given compression level.
1285//! @since v.0.4.4
1286template < typename Response_Output_Strategy >
1287inline body_appender_t< Response_Output_Strategy >
1289 response_builder_t< Response_Output_Strategy > & resp,
1290 int = -1 )
1291{
1292 return body_appender( resp, make_identity_params() );
1293}
1294
1295//! Call a handler over a request body.
1296/*!
1297 * If body is encoded with either 'deflate' or 'gzip'
1298 * then it is decompressed and the handler is called.
1299 *
1300 * If body is encoded with 'identity' (or not specified)
1301 * the handler is called with original body.
1302 *
1303 * In other cases an exception is thrown.
1304 *
1305 * \tparam Handler a function object capable to handle std::string as an argument.
1306 *
1307 * Sample usage:
1308 * \code
1309 * namespace rtz = restinio::transforms::zlib;
1310 * auto decompressed_echo_handler( restinio::request_handle_t req )
1311 * {
1312 * return
1313 * rtz::handle_body(
1314 * *req,
1315 * [&]( auto body ){
1316 * return
1317 * req->create_response()
1318 * .append_header( restinio::http_field::server, "RESTinio" )
1319 * .append_header_date_field()
1320 * .append_header(
1321 * restinio::http_field::content_type,
1322 * "text/plain; charset=utf-8" );
1323 * .set_body( std::move( body ) )
1324 * .done();
1325 * } );
1326 * }
1327 * \endcode
1328 * @since v.0.4.4
1329*/
1330template < typename Extra_Data, typename Handler >
1331decltype(auto)
1333 const generic_request_t<Extra_Data> & req,
1334 Handler && handler )
1335{
1336 using restinio::impl::is_equal_caseless;
1337
1338 const auto content_encoding =
1339 req.header().get_field_or( restinio::http_field::content_encoding, "identity" );
1340
1341 if( is_equal_caseless( content_encoding, "deflate" ) )
1342 {
1343 return handler( deflate_decompress( req.body() ) );
1344 }
1345 else if( is_equal_caseless( content_encoding, "gzip" ) )
1346 {
1347 return handler( gzip_decompress( req.body() ) );
1348 }
1349 else if( !is_equal_caseless( content_encoding, "identity" ) )
1350 {
1351 throw exception_t{
1352 fmt::format(
1353 RESTINIO_FMT_FORMAT_STRING( "content-encoding '{}' not supported" ),
1354 content_encoding )
1355 };
1356 }
1357
1358 return handler( req.body() );
1359}
1360
1361} /* namespace zlib */
1362
1363} /* namespace transforms */
1364
1365} /* namespace restinio */
Exception class for all exceptions thrown by RESTinio.
Definition exception.hpp:26
exception_t(const char *err)
Definition exception.hpp:29
Forbid arbitrary response_builder_t instantiations.
Base class for body appenders.
Definition zlib.hpp:984
std::unique_ptr< zlib_t > m_ztransformator
Definition zlib.hpp:1013
body_appender_base_t(body_appender_base_t &&ba) noexcept
Definition zlib.hpp:1005
body_appender_base_t & operator=(body_appender_base_t &&)=delete
body_appender_base_t(const body_appender_base_t &)=delete
body_appender_base_t(const params_t &params, resp_t &resp)
Definition zlib.hpp:988
response_builder_t< Response_Output_Strategy > resp_t
Definition zlib.hpp:986
body_appender_base_t & operator=(const body_appender_base_t &)=delete
void flush()
Flushes currently available compressed data with possibly creating new chunk and then flushes target ...
Definition zlib.hpp:1228
auto & append(string_view_t input)
Append data to be compressed.
Definition zlib.hpp:1197
auto & make_chunk(string_view_t input=string_view_t{})
Append data to be compressed and adds current zlib transformator output as a new chunk.
Definition zlib.hpp:1213
void complete()
Complete zlib transformation operation.
Definition zlib.hpp:1243
body_appender_base_t< chunked_output_t, body_appender_t< chunked_output_t > > base_type_t
Definition zlib.hpp:1184
x_controlled_output_body_appender_base_t< restinio_controlled_output_t, body_appender_t< restinio_controlled_output_t > > base_type_t
Definition zlib.hpp:1077
x_controlled_output_body_appender_base_t< user_controlled_output_t, body_appender_t< user_controlled_output_t > > base_type_t
Definition zlib.hpp:1124
Parameters of performing data transformation with zlib.
Definition zlib.hpp:64
params_t && strategy(int strategy_value) &&
Set compression strategy.
Definition zlib.hpp:304
params_t && window_bits(int window_bits_value) &&
Set window_bits.
Definition zlib.hpp:212
params_t & window_bits(int window_bits_value) &
Set window_bits.
Definition zlib.hpp:172
format_t format() const
Get format.
Definition zlib.hpp:121
params_t()
Default constructor for identiry transformator.
Definition zlib.hpp:110
params_t && level(int level_value) &&
Set compression level.
Definition zlib.hpp:155
int window_bits() const
Get window_bits.
Definition zlib.hpp:161
format_t
Formats of compressed data.
Definition zlib.hpp:77
@ identity
Identity. With semantics descrobed here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Ac...
Definition zlib.hpp:86
params_t & mem_level(int mem_level_value) &
Set compression mem_level.
Definition zlib.hpp:234
operation_t m_operation
Transformation type.
Definition zlib.hpp:345
int mem_level() const
Get compression mem_level.
Definition zlib.hpp:221
int m_level
Compression level.
Definition zlib.hpp:354
std::size_t m_reserve_buffer_size
Size initially reserved for buffer.
Definition zlib.hpp:361
format_t m_format
Format to be used for compressed data.
Definition zlib.hpp:348
params_t & reserve_buffer_size(std::size_t size) &
Set the size initially reserved for buffer.
Definition zlib.hpp:321
int level() const
Get compression level.
Definition zlib.hpp:127
std::size_t reserve_buffer_size() const
Get the size initially reserved for buffer.
Definition zlib.hpp:317
params_t && reserve_buffer_size(std::size_t size) &&
Set the size initially reserved for buffer.
Definition zlib.hpp:335
params_t(operation_t op, format_t f, int l=-1)
Init constructor.
Definition zlib.hpp:96
params_t & level(int level_value) &
Set compression level.
Definition zlib.hpp:136
params_t & reference_to_self()
Get the reference to self.
Definition zlib.hpp:342
params_t && mem_level(int mem_level_value) &&
Set compression mem_level.
Definition zlib.hpp:254
int strategy() const
Get compression strategy.
Definition zlib.hpp:263
operation_t
Types of transformation.
Definition zlib.hpp:68
operation_t operation() const
Get operation.
Definition zlib.hpp:118
params_t & strategy(int strategy_value) &
Set compression strategy.
Definition zlib.hpp:274
Base class for body appenders with restinio or user controlled output.
Definition zlib.hpp:1021
Descendant & append(string_view_t input)
Append a piece of data to response.
Definition zlib.hpp:1029
body_appender_base_t< X_Controlled_Output, Descendant > base_type_t
Definition zlib.hpp:1023
void complete()
Complete zlib transformation operation.
Definition zlib.hpp:1038
zlib_t(const zlib_t &)=delete
auto output_size() const
Get current output size.
Definition zlib.hpp:695
bool m_zlib_stream_initialized
Flag: was m_zlib_stream initialized properly.
Definition zlib.hpp:859
void inc_buffer()
Increment internal buffer for receiving output.
Definition zlib.hpp:727
std::size_t m_write_pos
Next write pos in out buffer.
Definition zlib.hpp:867
bool is_completed() const
Is operation complete?
Definition zlib.hpp:698
void write(string_view_t input)
Append input data.
Definition zlib.hpp:570
void write_compress_impl(int flush)
Handle incoming data for compression operation.
Definition zlib.hpp:755
void flush()
Flush the zlib stream.
Definition zlib.hpp:617
const char * get_error_msg() const
Get zlib error message if it exists.
Definition zlib.hpp:708
void ensure_operation_in_not_completed() const
Checks completion flag and throws if operation is is already completed.
Definition zlib.hpp:719
auto prepare_out_buffer()
Prepare out buffer for receiving data.
Definition zlib.hpp:735
const params_t m_params
Parameters for zlib.
Definition zlib.hpp:852
std::string m_out_buffer
Output buffer.
Definition zlib.hpp:865
zlib_t & operator=(zlib_t &&)=delete
zlib_t(const params_t &transformation_params)
Definition zlib.hpp:481
std::string giveaway_output()
Get current accumulated output data.
Definition zlib.hpp:684
void write_decompress_impl(int flush)
Handle incoming data for decompression operation.
Definition zlib.hpp:805
void complete()
Complete the stream.
Definition zlib.hpp:639
zlib_t & operator=(const zlib_t &)=delete
const params_t & params() const
Get parameters of current transformation.
Definition zlib.hpp:562
z_stream m_zlib_stream
zlib stream.
Definition zlib.hpp:862
#define RESTINIO_FMT_FORMAT_STRING(s)
std::string content_encoding_token(params_t::format_t f)
Get token for copression format.
Definition zlib.hpp:959
void ensure_is_compression_operation(params_t::operation_t op)
Check if operation is a copression, and throw if it is not.
Definition zlib.hpp:941
void ensure_valid_transforator(zlib_t *ztransformator)
Check if a pointer to zlib transformator is valid.
Definition zlib.hpp:950
params_t make_gzip_compress_params(int compression_level=-1)
Definition zlib.hpp:401
params_t make_gzip_decompress_params()
Definition zlib.hpp:410
std::string gzip_decompress(string_view_t input)
Definition zlib.hpp:921
constexpr int default_mem_level
Definition zlib.hpp:47
std::string transform(string_view_t input, const params_t &params)
Do a specified zlib transformation.
Definition zlib.hpp:893
params_t make_deflate_compress_params(int compression_level=-1)
Definition zlib.hpp:384
constexpr int default_strategy
Definition zlib.hpp:48
std::string deflate_decompress(string_view_t input)
Definition zlib.hpp:909
decltype(auto) handle_body(const generic_request_t< Extra_Data > &req, Handler &&handler)
Call a handler over a request body.
Definition zlib.hpp:1332
std::string deflate_compress(string_view_t input, int compression_level=-1)
Definition zlib.hpp:903
body_appender_t< Response_Output_Strategy > deflate_body_appender(response_builder_t< Response_Output_Strategy > &resp, int compression_level=-1)
Create body appender with deflate transformation and a given compression level.
Definition zlib.hpp:1266
body_appender_t< Response_Output_Strategy > gzip_body_appender(response_builder_t< Response_Output_Strategy > &resp, int compression_level=-1)
Create body appender with gzip transformation and a given compression level.
Definition zlib.hpp:1277
constexpr std::size_t default_output_reserve_buffer_size
Default reserve buffer size for zlib transformator.
Definition zlib.hpp:38
body_appender_t< Response_Output_Strategy > identity_body_appender(response_builder_t< Response_Output_Strategy > &resp, int=-1)
Create body appender with gzip transformation and a given compression level.
Definition zlib.hpp:1288
params_t make_identity_params()
Definition zlib.hpp:418
std::string gzip_compress(string_view_t input, int compression_level=-1)
Definition zlib.hpp:915
body_appender_t< Response_Output_Strategy > body_appender(response_builder_t< Response_Output_Strategy > &resp, const params_t &params)
Create body appender with given zlib transformation parameters.
Definition zlib.hpp:1255
params_t make_deflate_decompress_params()
Definition zlib.hpp:393
constexpr int default_window_bits
Definition zlib.hpp:46
http_field_t http_field
Helper alies to omitt _t suffix.
Tag type for chunked output response builder.
Tag type for RESTinio controlled output response builder.
Tag type for user controlled output response builder.