RESTinio
Loading...
Searching...
No Matches
zlib.hpp
Go to the documentation of this file.
1/*
2 restinio
3*/
4
11#pragma once
12
14
16
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
38constexpr std::size_t default_output_reserve_buffer_size = 256 * 1024;
39
50
51//
52// params_t
53//
54
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:
67 enum class operation_t
68 {
73 };
74
76 enum class format_t
77 {
79 deflate,
81 gzip,
83 /*
84 Means that no compression will be used and no header/trailer will be applied.
85 */
87 };
88
90
100 format_t f,
102 int l = -1 )
103 : m_operation{ op }
104 , m_format{ f }
105 {
106 level( l );
107 }
108
113 {
114 level( -1 );
115 }
116
119
121 format_t format() const { return m_format; }
122
124
127 int level() const { return m_level; }
128
130
135 params_t &
137 {
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
149
150 return reference_to_self();
151 }
152
154 params_t &&
156 {
157 return std::move( this->level( level_value ) );
158 }
159
161 int window_bits() const { return m_window_bits; }
162
164
171 params_t &
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
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" ),
199 MAX_WBITS ) };
200 }
201
202 if( 8 == window_bits_value )
204
206
207 return reference_to_self();
208 }
209
211 params_t &&
213 {
214 return std::move( this->window_bits( window_bits_value ) );
215 }
216
218
221 int mem_level() const { return m_mem_level; }
222
224
233 params_t &
235 {
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 {}" ),
244 MAX_MEM_LEVEL ) };
245 }
246
248
249 return reference_to_self();
250 }
251
253 params_t &&
255 {
256 return std::move( this->mem_level( mem_level_value ) );
257 }
258
260
263 int strategy() const { return m_strategy; }
264
266
273 params_t &
275 {
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({})" ),
294 Z_RLE ) };
295 }
296
298
299 return reference_to_self();
300 }
301
303 params_t &&
305 {
306 return std::move( this->strategy( strategy_value ) );
307 }
308
310
317 std::size_t reserve_buffer_size() const { return m_reserve_buffer_size; }
318
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
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:
342 params_t & reference_to_self() { return *this; }
343
346
349
351
355
359
362};
363
383inline params_t
391
392inline params_t
399
400inline params_t
408
409inline params_t
416
417inline params_t
419{
420 return params_t{};
421}
423
424//
425// zlib_t
426//
427
429
479{
480 public:
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.
497
499 {
501 }
502
504 {
505 // zlib format.
509 m_params.level(),
513 m_params.strategy() );
514 }
515 else
516 {
521 }
522
523 if( Z_OK != init_result )
524 {
525 throw exception_t{
526 fmt::format(
528 "Failed to initialize zlib stream: {}, {}" ),
530 get_error_msg() ) };
531 }
532
534
535 // Reserve initial buffer.
536 inc_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 {
553 }
554 else
555 {
557 }
558 }
559 }
560
562 const params_t & params() const { return m_params; }
563
565
569 void
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 {
602 }
603 else
604 {
606 }
607 }
608 }
609 }
610
612
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 {
629 }
630 else
631 {
633 }
634 }
635 }
636
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 {
651 }
652 else
653 {
655 }
656 }
657
659 }
660
662
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
695 auto output_size() const { return m_write_pos; }
696
698 bool is_completed() const { return m_operation_is_complete; }
699
700 private:
701 bool is_identity() const
702 {
704 }
705
707 const char *
709 {
710 const char * err_msg = "<no zlib error description>";
711 if( m_zlib_stream.msg )
713
714 return err_msg;
715 }
716
718 void
720 {
721 if( is_completed() )
722 throw exception_t{ "zlib operation is already completed" };
723 }
724
726 void
728 {
729 m_out_buffer.resize(
731 }
732
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
747 }
748
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 {
760
761 int operation_result = deflate( &m_zlib_stream, flush );
762
763 if( !( Z_OK == operation_result ||
766 {
767 const char * err_msg = "<no error desc>";
768 if( m_zlib_stream.msg )
770
771 throw exception_t{
772 fmt::format(
774 "unexpected result of deflate() (zlib): {}, {}" ),
776 err_msg ) };
777 }
778
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.
787 inc_buffer();
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
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 {
810
812 if( !( Z_OK == operation_result ||
815 {
816 throw exception_t{
817 fmt::format(
819 "unexpected result of inflate() (zlib): {}, {}" ),
821 get_error_msg() ) };
822 }
823
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.
832 inc_buffer();
833 continue;
834 }
835
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
853
855
860
863
865 std::string m_out_buffer;
867 std::size_t m_write_pos{ 0 };
868
870};
871
890
892inline std::string
894{
895 zlib_t z{ params };
896 z.write( input );
897 z.complete();
898
899 return z.giveaway_output();
900}
901
902inline std::string
907
908inline std::string
913
914inline std::string
919
920inline std::string
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
942{
944 {
945 throw exception_t{ "operation is not copress" };
946 }
947}
948
951{
952 if( nullptr == ztransformator )
953 {
954 throw exception_t{ "invalid body appender" };
955 }
956}
957
960{
961 std::string result{ "identity" };
962
964 {
965 result.assign( "deflate" );
966 }
968 {
969 result.assign( "gzip" );
970 }
971
972 return result;
973}
974
975} /* namespace impl */
976
977//
978// body_appender_base_t
979//
980
982template < typename Response_Output_Strategy, typename Descendant >
984{
985 public:
987
989 : m_ztransformator{ std::make_unique< zlib_t >( params ) }
990 , m_resp{ resp }
991 {
993 m_ztransformator->params().operation() );
994
995 m_resp.append_header(
996 restinio::http_field::content_encoding,
998 m_ztransformator->params().format() ) );
999 }
1000
1004
1006 : m_ztransformator{ std::move( ba.m_ztransformator ) }
1007 , m_resp{ ba.m_resp }
1008 {}
1009
1011
1012 protected:
1013 std::unique_ptr< zlib_t > m_ztransformator;
1015};
1016
1018template < typename X_Controlled_Output, typename Descendant >
1020 : public body_appender_base_t< X_Controlled_Output, Descendant >
1021{
1022 public:
1024
1025 using base_type_t::base_type_t;
1026
1028 Descendant &
1030 {
1032 this->m_ztransformator->write( input );
1033 return static_cast< Descendant & >( *this );
1034 }
1035
1037 void
1039 {
1041
1042 this->m_ztransformator->complete();
1043
1044 this->m_resp.append_body( this->m_ztransformator->giveaway_output() );
1045 }
1046};
1047
1070template <>
1073 restinio_controlled_output_t,
1074 body_appender_t< restinio_controlled_output_t > >
1075{
1076 public:
1081
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
1117template <>
1120 user_controlled_output_t,
1121 body_appender_t< user_controlled_output_t > >
1122{
1123 public:
1128
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
1177template <>
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
1192
1196 auto &
1198 {
1199 impl::ensure_valid_transforator( m_ztransformator.get() );
1200
1201 m_ztransformator->write( input );
1202 return *this;
1203 }
1204
1207
1212 auto &
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
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
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
1253template < typename Response_Output_Strategy >
1261
1264template < typename Response_Output_Strategy >
1272
1275template < typename Response_Output_Strategy >
1283
1286template < typename Response_Output_Strategy >
1294
1296
1330template < typename Extra_Data, typename Handler >
1331decltype(auto)
1334 Handler && handler )
1335{
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" ),
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
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(const body_appender_base_t &)=delete
body_appender_base_t(const params_t &params, resp_t &resp)
Definition zlib.hpp:988
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
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...
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
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(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
A special wrapper around fmtlib include files.
#define RESTINIO_FMT_FORMAT_STRING(s)
bool is_equal_caseless(const char *a, const char *b, std::size_t size) noexcept
Comparator for fields names.
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
run_on_this_thread_settings_t< Traits > on_this_thread()
A special marker for the case when http_server must be run on the context of the current thread.
std::string_view string_view_t
Helpers for caseless comparison of strings.
Tag type for chunked output response builder.
Tag type for RESTinio controlled output response builder.
Tag type for user controlled output response builder.