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