RESTinio
basics.hpp
Go to the documentation of this file.
1 /*
2  * RESTinio
3  */
4 
5 /*!
6  * @file
7  * @brief Utilities for parsing values of http-fields
8  *
9  * @since v.0.6.1
10  */
11 
12 #pragma once
13 
14 #include <restinio/impl/string_caseless_compare.hpp>
15 
16 #include <restinio/helpers/easy_parser.hpp>
17 
18 #include <restinio/expected.hpp>
19 
20 #include <algorithm>
21 
22 namespace restinio
23 {
24 
25 namespace http_field_parsers
26 {
27 
28 using namespace restinio::easy_parser;
29 
30 namespace meta = restinio::utils::metaprogramming;
31 
32 namespace qvalue_details
33 {
34 
35 //! A type to hold a qvalue.
37 
38 //! The maximal allowed value for a qvalue.
39 constexpr underlying_uint_t maximum = 1000u;
40 //! The minimal allowed value for a qvalue.
41 constexpr underlying_uint_t zero = 0u;
42 
43 // A part of workaround for problem with static constexpr members
44 // detected after release of v.0.6.1.
45 enum class extremum_min_t { v };
46 enum class extremum_max_t { v };
47 
48 //! A helper wrapper to indicate that value is already checked and
49 //! shouldn't be checked again.
50 class trusted
51 {
53 
54 public :
55  explicit constexpr
56  trusted( underlying_uint_t value ) noexcept : m_value{ value } {}
57 
58  constexpr auto get() const noexcept { return m_value; }
59 };
60 
61 //! A helper wrapper to indicate that value hasn't been checked yet
62 //! and should be checked in the constructor of qvalue.
63 class untrusted
64 {
66 
67 public :
68  explicit
69  untrusted( underlying_uint_t value ) : m_value{ value }
70  {
71  if( m_value > maximum )
72  throw exception_t( "invalid value for "
73  "http_field_parser::rfc::qvalue_t" );
74  }
75 
76  auto get() const noexcept { return m_value; }
77 };
78 
79 } /* namespace qvalue_details */
80 
81 //
82 // qvalue_t
83 //
84 /*!
85  * @brief A class for holding the parsed value of qvalue from RFC7231.
86  *
87  * An important note: qvalue in RFC7231 is defined as a number with
88  * fractional point. This number can have no more that three digits
89  * after a point. And the non-factional part can only be 0 or 1. If
90  * non-frational part is 1 then fractional part can only be 0. It means
91  * that qvalue can be in the range [0.000, 1.000].
92  *
93  * To simplify work with qvalue RESTinio holds qvalue as a small integer
94  * in the range [0, 1000] where 0 corresponds to 0.000 and 1000 corresponds
95  * to 1.000. In means, for example, that value 0.05 will be represented and 50,
96  * and the value 0.001 will be represented as 1, and the value 0.901 will
97  * be represented as 901.
98  *
99  * The integer representation of qvalue can be obtained via as_uint()
100  * method.
101  *
102  * Method as_string() returns std::string with number with fractional
103  * part inside. It means:
104  * @code
105  * assert("1.000" == qvalue_t{qvalue_t::maximum}.as_string());
106  * assert("0.901" == qvalue_t{qvalue_t::untrusted{901}}.as_string());
107  * @endcode
108  * Such representation is also used in `operator<<` for std::ostream.
109  *
110  * Instances of qvalue_t can be compared, operations `==`, `!=`, `<` and `<=`
111  * are supported.
112  *
113  * There are two ways to construct a new qvalue object.
114  *
115  * The first and recommended way is intended for cases when the source
116  * value for a new qvalue object isn't known at the compile-time and
117  * produced somehow at the run-time. It's the usage of qvalue_t::untrusted
118  * wrapper:
119  * @code
120  * qvalue_t weight{qvalue_t::untrusted{config.default_weigh() * 10}};
121  * @endcode
122  * In that case the value of `untrusted` will be checked in
123  * qvalue_t::untrusted's constructor and an exception will be thrown if that
124  * value isn't in [0, 1000] range.
125  *
126  * The second way is intended to be used with compile-time constants:
127  * @code
128  * qvalue_t weight{qvalue_t::trusted{250}};
129  * @endcode
130  * In the case of `trusted` the value is not checked in qvalue_t's
131  * constructor. Therefore that way should be used with additional care.
132  *
133  * @since v.0.6.1
134  */
135 class qvalue_t
136 {
137 public :
138  //! The type of underlying small integer.
140  //! The type that doesn't check a value to be used for qvalue.
141  using trusted = qvalue_details::trusted;
142  //! The type that checks a value to be used for qvalue.
143  using untrusted = qvalue_details::untrusted;
144 
145  //! The indicator that tells that new qvalue object should have
146  //! the maximal allowed value.
149  //! The indicator that tells that new qvalue object should have
150  //! the minimal allowed value.
151  static constexpr qvalue_details::extremum_min_t zero =
153 
154 private :
155  // Note: with the terminal 0-symbol.
156  using underlying_char_array_t = std::array<char, 6>;
157 
159 
161  make_char_array() const noexcept
162  {
163  underlying_char_array_t result;
164  if( qvalue_details::maximum == m_value )
165  {
166  std::strcpy( &result[0], "1.000" );
167  }
168  else
169  {
170  result[0] = '0';
171  result[1] = '.';
172 
173  result[2] = '0' + static_cast<char>(m_value / 100u);
174  const auto d2 = m_value % 100u;
175  result[3] = '0' + static_cast<char>(d2 / 10u);
176  const auto d3 = d2 % 10u;
177  result[4] = '0' + static_cast<char>(d3);
178  result[5] = 0;
179  }
180 
181  return result;
182  }
183 
184 public :
185 
186  constexpr qvalue_t() = default;
187 
188  qvalue_t( untrusted val ) noexcept
189  : m_value{ val.get() }
190  {}
191 
192  constexpr qvalue_t( trusted val ) noexcept
193  : m_value{ val.get() }
194  {}
195 
196  constexpr qvalue_t( qvalue_details::extremum_min_t ) noexcept
198  {}
199 
200  constexpr qvalue_t( qvalue_details::extremum_max_t ) noexcept
202  {}
203 
204  constexpr auto as_uint() const noexcept { return m_value; }
205 
206  auto as_string() const
207  {
208  return std::string{ &make_char_array().front() };
209  }
210 
211  friend std::ostream &
212  operator<<( std::ostream & to, const qvalue_t & what )
213  {
214  return (to << &what.make_char_array().front());
215  }
216 };
217 
219 inline bool
220 operator==( const qvalue_t & a, const qvalue_t & b ) noexcept
221 {
222  return a.as_uint() == b.as_uint();
223 }
224 
226 inline bool
227 operator!=( const qvalue_t & a, const qvalue_t & b ) noexcept
228 {
229  return a.as_uint() != b.as_uint();
230 }
231 
233 inline bool
234 operator<( const qvalue_t & a, const qvalue_t & b ) noexcept
235 {
236  return a.as_uint() < b.as_uint();
237 }
238 
240 inline bool
241 operator<=( const qvalue_t & a, const qvalue_t & b ) noexcept
242 {
243  return a.as_uint() <= b.as_uint();
244 }
245 
246 namespace impl
247 {
248 
249 using namespace restinio::easy_parser::impl;
250 
251 //
252 // is_alpha
253 //
254 /*!
255  * @brief Is a character an ALPHA?
256  *
257  * See: https://tools.ietf.org/html/rfc5234#appendix-B.1
258  *
259  * @since v.0.6.1
260  */
262 inline constexpr bool
263 is_alpha( const char ch ) noexcept
264 {
265  return (ch >= '\x41' && ch <= '\x5A') ||
266  (ch >= '\x61' && ch <= '\x7A');
267 }
268 
269 //
270 // is_alpha_predicate_t
271 //
272 /*!
273  * @brief A preducate for symbol_producer_template that checks that
274  * a symbol is an alpha.
275  *
276  * @since v.0.6.2
277  */
279 {
281  bool
282  operator()( const char actual ) const noexcept
283  {
284  return is_alpha(actual);
285  }
286 };
287 
288 //
289 // is_alphanum_predicate_t
290 //
291 /*!
292  * @brief A preducate for symbol_producer_template that checks that
293  * a symbol is an alpha or numeric.
294  *
295  * @since v.0.6.2
296  */
298 {
300  bool
301  operator()( const char actual ) const noexcept
302  {
303  return is_alpha(actual) || is_digit(actual);
304  }
305 };
306 
307 //
308 // is_vchar
309 //
310 /*!
311  * @brief Is a character a VCHAR?
312  *
313  * See: https://tools.ietf.org/html/rfc5234#appendix-B.1
314  *
315  * @since v.0.6.1
316  */
318 inline constexpr bool
319 is_vchar( const char ch ) noexcept
320 {
321  return (ch >= '\x21' && ch <= '\x7E');
322 }
323 
324 //
325 // is_vchar_predicate_t
326 //
327 /*!
328  * @brief A preducate for symbol_producer_template that checks that
329  * a symbol is a VCHAR.
330  *
331  * @since v.0.6.2
332  */
334 {
336  bool
337  operator()( const char actual ) const noexcept
338  {
339  return is_vchar(actual);
340  }
341 };
342 
343 //
344 // is_obs_text
345 //
346 /*!
347  * @brief Is a character an obs_text?
348  *
349  * See: https://tools.ietf.org/html/rfc7230
350  *
351  * @since v.0.6.1
352  */
354 inline constexpr bool
355 is_obs_text( const char ch ) noexcept
356 {
357  constexpr unsigned short left = 0x80u;
358  constexpr unsigned short right = 0xFFu;
359 
360  const unsigned short t = static_cast<unsigned short>(
361  static_cast<unsigned char>(ch));
362 
363  return (t >= left && t <= right);
364 }
365 
366 //
367 // is_qdtext
368 //
369 /*!
370  * @brief Is a character a qdtext?
371  *
372  * See: https://tools.ietf.org/html/rfc7230
373  *
374  * @since v.0.6.1
375  */
377 inline constexpr bool
378 is_qdtext( const char ch ) noexcept
379 {
380  return ch == SP ||
381  ch == HTAB ||
382  ch == '!' ||
383  (ch >= '\x23' && ch <= '\x5B') ||
384  (ch >= '\x5D' && ch <= '\x7E') ||
385  is_obs_text( ch );
386 }
387 
388 //
389 // is_ctext
390 //
391 /*!
392  * @brief Is a character a ctext?
393  *
394  * See: https://tools.ietf.org/html/rfc7230
395  *
396  * @since v.0.6.4
397  */
399 inline constexpr bool
400 is_ctext( const char ch ) noexcept
401 {
402  return ch == SP ||
403  ch == HTAB ||
404  (ch >= '\x21' && ch <= '\x27') ||
405  (ch >= '\x2A' && ch <= '\x5B') ||
406  (ch >= '\x5D' && ch <= '\x7E') ||
407  is_obs_text( ch );
408 }
409 
410 //
411 // is_ctext_predicate_t
412 //
413 /*!
414  * @brief A preducate for symbol_producer_template that checks that
415  * a symbol is a ctext.
416  *
417  * @since v.0.6.4
418  */
420 {
422  bool
423  operator()( const char actual ) const noexcept
424  {
425  return is_ctext(actual);
426  }
427 };
428 
429 //
430 // is_token_char_predicate_t
431 //
432 /*!
433  * @brief A predicate for symbol_producer_template that checks that
434  * a symbol can be used inside a token.
435  */
437 {
439  static constexpr bool
440  is_token_char( const char ch ) noexcept
441  {
442  return is_alpha(ch) || is_digit(ch) ||
443  ch == '!' ||
444  ch == '#' ||
445  ch == '$' ||
446  ch == '%' ||
447  ch == '&' ||
448  ch == '\'' ||
449  ch == '*' ||
450  ch == '+' ||
451  ch == '-' ||
452  ch == '.' ||
453  ch == '^' ||
454  ch == '_' ||
455  ch == '`' ||
456  ch == '|' ||
457  ch == '~';
458  }
459 
461  bool
462  operator()( const char actual ) const noexcept
463  {
464  return is_token_char(actual);
465  }
466 };
467 
468 //
469 // ows_producer_t
470 //
471 /*!
472  * @brief A producer for OWS.
473  *
474  * If an OWS found in the input stream it produces non-empty
475  * optional_t<char> with SP as the value.
476  *
477  * See: https://tools.ietf.org/html/rfc7230
478  *
479  * @since v.0.6.1
480  */
482 {
483 public :
484  RESTINIO_NODISCARD
485  expected_t< result_type, parse_error_t >
487  source_t & from ) const noexcept
488  {
490  character_t ch;
491  for( ch = from.getch();
492  !ch.m_eof && is_space(ch.m_ch);
493  ch = from.getch() )
494  {
496  }
497 
498  if( !ch.m_eof )
499  // The first non-space char should be returned back.
500  from.putback();
501 
502  if( extracted_spaces > 0u )
503  return result_type{ ' ' };
504 
505  return result_type{ nullopt };
506  }
507 };
508 
509 //
510 // token_producer_t
511 //
512 /*!
513  * @brief A producer for token.
514  *
515  * If a token is found in the input stream it produces std::string
516  * with the value of that token.
517  *
518  * See: https://tools.ietf.org/html/rfc7230
519  *
520  * @since v.0.6.1
521  */
523 {
525  static optional_t< parse_error_t >
527  {
529 
530  do
531  {
532  const auto ch = from.getch();
533  if( ch.m_eof )
534  {
536  break;
537  }
538 
539  if( !is_token_char(ch.m_ch) )
540  {
541  from.putback();
543  break;
544  }
545 
546  accumulator += ch.m_ch;
547  }
548  while( true );
549 
550  if( accumulator.empty() )
551  {
553  }
554 
555  return nullopt;
556  }
557 
559  static constexpr bool
560  is_token_char( const char ch ) noexcept
561  {
563  }
564 
565 public :
569  {
570  std::string value;
571  const auto try_result = try_parse_value( from, value );
572  if( !try_result )
573  return { std::move(value) };
574  else
575  return make_unexpected( *try_result );
576  }
577 };
578 
579 //
580 // quoted_string_producer_t
581 //
582 /*!
583  * @brief A producer for quoted_string.
584  *
585  * If a quoted_string is found in the input stream it produces std::string
586  * with the value of that token.
587  *
588  * See: https://tools.ietf.org/html/rfc7230
589  *
590  * @since v.0.6.1
591  */
593 {
595  static optional_t< parse_error_t >
597  {
599 
600  bool second_quote_extracted{ false };
601  do
602  {
603  const auto ch = from.getch();
604  if( ch.m_eof )
605  {
607  break;
608  }
609 
610  if( '"' == ch.m_ch )
611  second_quote_extracted = true;
612  else if( '\\' == ch.m_ch )
613  {
614  const auto next = from.getch();
615  if( next.m_eof )
616  {
618  break;
619  }
620  else if( SP == next.m_ch || HTAB == next.m_ch ||
621  is_vchar( next.m_ch ) ||
622  is_obs_text( next.m_ch ) )
623  {
624  accumulator += next.m_ch;
625  }
626  else
627  {
629  from.putback();
630  break;
631  }
632  }
633  else if( is_qdtext( ch.m_ch ) )
634  accumulator += ch.m_ch;
635  else
636  {
638  from.putback();
639  break;
640  }
641  }
642  while( !second_quote_extracted );
643 
646  else
647  return nullopt;
648  }
649 
650 public :
654  {
656 
657  const auto ch = from.getch();
658  if( !ch.m_eof )
659  {
660  if( '"' == ch.m_ch )
661  {
662  std::string value;
663  const auto try_result = try_parse_value( from, value );
664  if( !try_result )
665  {
666  consumer.commit();
667  return { std::move(value) };
668  }
669  else
670  return make_unexpected( *try_result );
671  }
672  else
673  {
677  } );
678  }
679  }
680  else
684  } );
685  }
686 };
687 
688 //
689 // quoted_pair_producer_t
690 //
691 /*!
692  * @brief A producer for quoted_pair.
693  *
694  * If a quoted_pair is found in the input stream it produces char
695  * with the value of that token.
696  *
697  * See: https://tools.ietf.org/html/rfc7230
698  *
699  * @since v.0.6.4
700  */
701 class quoted_pair_producer_t : public producer_tag< char >
702 {
703 public :
704  RESTINIO_NODISCARD
705  expected_t< result_type, parse_error_t >
707  {
709 
711 
712  const auto ch = from.getch();
713  if( !ch.m_eof )
714  {
715  if( '\\' == ch.m_ch )
716  {
717  const auto next = from.getch();
718  if( !next.m_eof )
719  {
720  if( SP == next.m_ch || HTAB == next.m_ch ||
721  is_vchar( next.m_ch ) ||
722  is_obs_text( next.m_ch ) )
723  {
724  consumer.commit();
725  return next.m_ch;
726  }
727 
729  }
730  }
731  else
733  }
734 
737  reason
738  } );
739  }
740 };
741 
742 //
743 // comment_producer_t
744 //
745 /*!
746  * @brief A producer for comment.
747  *
748  * If a comment is found in the input stream it produces std::string
749  * with the value of that token.
750  *
751  * Comment is defined that way:
752 @verbatim
753 comment = "(" *( ctext / quoted-pair / comment ) ")"
754 ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
755 quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
756 @endverbatim
757  *
758  * See: https://tools.ietf.org/html/rfc7230
759  *
760  * @since v.0.6.4
761  */
763 {
764 public :
765  RESTINIO_NODISCARD
766  expected_t< result_type, parse_error_t >
767  try_parse( source_t & from ) const; // NOTE: implementation below.
768 };
769 
770 } /* namespace impl */
771 
772 //
773 // alpha_symbol_producer
774 //
775 /*!
776  * @brief A factory for producer of ALPHA symbols.
777  *
778  * Usage example:
779  * @code
780  produce<std::string>(
781  repeat(1, 20, alpha_symbol_p() >> to_container());
782  * @endcode
783  *
784  * @since v.0.6.2
785  */
787 inline auto
789 {
792 }
793 
794 //
795 // alphanum_symbol_producer
796 //
797 /*!
798  * @brief A factory for producer of symbol that an ALPHA or DIGIT.
799  *
800  * Usage example:
801  * @code
802  produce<std::string>(
803  repeat(1, 20, alphanum_symbol_p() >> to_container());
804  * @endcode
805  *
806  * @since v.0.6.2
807  */
809 inline auto
811 {
814 }
815 
816 //
817 // vchar_symbol_producer
818 //
819 /*!
820  * @brief A factory for producer of VCHAR symbols.
821  *
822  * Usage example:
823  * @code
824  produce<std::string>(
825  repeat(1, 20, vchar_symbol_p() >> to_container());
826  * @endcode
827  *
828  * @since v.0.6.2
829  */
831 inline auto
833 {
836 }
837 
838 //
839 // ctext_symbol_producer
840 //
841 /*!
842  * @brief A factory for producer of ctext symbols.
843  *
844  * Usage example:
845  * @code
846  produce<std::string>(
847  repeat(1, 20, ctext_symbol_p() >> to_container());
848  * @endcode
849  *
850  * @since v.0.6.4
851  */
853 inline auto
855 {
858 }
859 
860 //
861 // comment_producer
862 //
863 /*!
864  * @brief A factory for producer of comment token.
865  *
866  * Usage example:
867  * @code
868  produce<std::string>(
869  alternatives(
870  token_p() >> as_result(),
871  comment_p() >> as_result()
872  )
873  );
874  * @endcode
875  *
876  * @since v.0.6.4
877  */
879 inline auto
881 {
882  return impl::comment_producer_t{};
883 }
884 
885 //
886 // ows_producer
887 //
888 /*!
889  * @brief A factory function to create an ows_producer.
890  *
891  * Usage example:
892  * @code
893  * produce<std::string>(
894  * ows_p() >> skip(),
895  * symbol('v'),
896  * symbol('='),
897  * ows_p() >> skip(),
898  * token_p() >> as_result()
899  * );
900  * @endcode
901  *
902  * @note
903  * Factory function ows() can be used instead of expression `ows_p() >> skip()`.
904  *
905  * @since v.0.6.1
906  */
908 inline auto
909 ows_p() noexcept { return impl::ows_producer_t{}; }
910 
911 //
912 // ows
913 //
914 /*!
915  * @brief A factory function to create an OWS clause.
916  *
917  * This clause handles an optional sequence of spaces in the input stream and
918  * skips the value of that sequence.
919  *
920  * Usage example:
921  * @code
922  * produce<std::string>(
923  * ows(),
924  * symbol('v'),
925  * symbol('='),
926  * ows(),
927  * token_p() >> as_result()
928  * );
929  * @endcode
930  * This expression corresponds the following rule:
931  @verbatim
932  T := OWS 'v' '=' OWS token
933  @endverbatim
934  *
935  * @since v.0.6.1
936  */
938 inline auto
939 ows() noexcept { return ows_p() >> skip(); }
940 
941 //
942 // token_symbol_producer
943 //
944 /*!
945  * @brief A factory for producer of symbols than can be used in tokens.
946  *
947  * Usage example:
948  * @code
949  produce<std::string>(
950  repeat(1, 20, token_symbol_p() >> to_container());
951  * @endcode
952  *
953  * @since v.0.6.9
954  */
956 inline auto
957 token_symbol_p() noexcept
958 {
961 }
962 
963 //
964 // token_producer
965 //
966 /*!
967  * @brief A factory function to create a token_producer.
968  *
969  * Usage example:
970  * @code
971  * using parameter = std::pair<std::string, std::string>;
972  * produce<parameter>(
973  * ows(),
974  * token_p() >> &parameter::first,
975  * symbol('='),
976  * ows(),
977  * token_p() >> &parameter::second
978  * );
979  * @endcode
980  *
981  * @since v.0.6.1
982  */
984 inline auto
985 token_p() noexcept { return impl::token_producer_t{}; }
986 
987 //
988 // quoted_string_producer
989 //
990 /*!
991  * @brief A factory function to create a quoted_string_producer.
992  *
993  * Usage example:
994  * @code
995  * using parameter = std::pair<std::string, std::string>;
996  * produce<parameter>(
997  * ows(),
998  * token_p() >> &parameter::first,
999  * symbol('='),
1000  * ows(),
1001  * alternatives(
1002  * token_p() >> &parameter::second,
1003  * quoted_string_p() >> &parameter::second
1004  * )
1005  * );
1006  * @endcode
1007  *
1008  * @since v.0.6.1
1009  */
1011 inline auto
1013 {
1014  return impl::quoted_string_producer_t{};
1015 }
1016 
1017 //
1018 // quoted_pair_producer
1019 //
1020 /*!
1021  * @brief A factory function to create a quoted_pair_producer.
1022  *
1023  * Usage example:
1024  * @code
1025  * produce<std::string>(
1026  * repeat(1, N,
1027  * alternatives(
1028  * ctext_symbol_p() >> to_container(),
1029  * quoted_pair_p() >> to_container()
1030  * )
1031  * );
1032  * @endcode
1033  *
1034  * @since v.0.6.4
1035  */
1037 inline auto
1038 quoted_pair_p() noexcept
1039 {
1040  return impl::quoted_pair_producer_t{};
1041 }
1042 
1043 //
1044 // expected_token_p
1045 //
1046 /*!
1047  * @brief A factory function to create a producer that expect a
1048  * token with specific value.
1049  *
1050  * If the expected token is successfully parsed then boolean value
1051  * is produced.
1052  *
1053  * Usage example:
1054  * @code
1055  * enum class compression { zlib, bz2, xz };
1056  * produce<compression>(
1057  * expected_token_p("compression") >> skip(),
1058  * ows(),
1059  * symbol('='),
1060  * ows(),
1061  * alternatives(
1062  * expected_token_p("zlib") >> just_result(compression::zlib),
1063  * expected_token_p("bz2") >> just_result(compression::bz2),
1064  * expected_token_p("xz") >> just_result(compression::xz)
1065  * )
1066  * );
1067  * @endcode
1068  *
1069  * @since v.0.6.9
1070  */
1072 inline auto
1074 {
1075  return produce< bool >(
1076  exact_p( token ) >> as_result(),
1077  not_clause( token_symbol_p() >> skip() ) );
1078 }
1079 
1080 //
1081 // expected_caseless_token_p
1082 //
1083 /*!
1084  * @brief A factory function to create a producer that expect a
1085  * token with specific value.
1086  *
1087  * This processer uses case-insensitive comparison.
1088  *
1089  * If the expected token is successfully parsed then boolean value
1090  * is produced.
1091  *
1092  * Usage example:
1093  * @code
1094  * enum class compression { zlib, bz2, xz };
1095  * produce<compression>(
1096  * caseless_expected_token_p("Compression") >> skip(),
1097  * ows(),
1098  * symbol('='),
1099  * ows(),
1100  * alternatives(
1101  * caseless_expected_token_p("ZLib") >> just_result(compression::zlib),
1102  * caseless_expected_token_p("BZ2") >> just_result(compression::bz2),
1103  * caseless_expected_token_p("xz") >> just_result(compression::xz)
1104  * )
1105  * );
1106  * @endcode
1107  *
1108  * @since v.0.6.9
1109  */
1111 inline auto
1113 {
1114  return produce< bool >(
1116  not_clause( token_symbol_p() >> skip() ) );
1117 }
1118 
1119 namespace impl
1120 {
1121 
1122 //
1123 // comment_producer_t implementation
1124 //
1128 {
1129  return produce< std::string >(
1130  sequence(
1131  symbol('('),
1132  repeat(0, N,
1133  alternatives(
1135  quoted_pair_p() >> to_container(),
1137  []( std::string & dest, std::string && what ) {
1138  dest += what;
1139  } )
1140  ) ),
1141  symbol(')') )
1142  ).try_parse( from );
1143 }
1144 
1145 //
1146 // qvalue_producer_t
1147 //
1148 /*!
1149  * @brief An implementation of producer of qvalue.
1150  *
1151  * Handles the following rule:
1152  @verbatim
1153  qvalue = ( "0" [ "." 0*3DIGIT ] )
1154  / ( "1" [ "." 0*3("0") ] )
1155  @endverbatim
1156  * and produces an instance of qvalue_t.
1157  *
1158  * See: https://tools.ietf.org/html/rfc7231
1159  *
1160  * @since v.0.6.1
1161  */
1163  : public producer_tag< qvalue_t >
1164 {
1165  // This type has to be used as type parameter for produce().
1167  {
1169  };
1170 
1171  //! A helper class to be used to accumulate actual integer
1172  //! while when the next digit is extracted from the input stream.
1174  {
1176 
1177  public :
1178  constexpr digit_consumer_t( qvalue_t::underlying_uint_t m )
1179  : m_multiplier{ m }
1180  {}
1181 
1182  void
1183  consume( zero_initialized_unit_t & dest, char && digit )
1184  {
1185  dest.m_value += m_multiplier *
1186  static_cast< qvalue_t::underlying_uint_t >(digit - '0');
1187  }
1188  };
1189 
1190 public :
1191  RESTINIO_NODISCARD
1192  expected_t< result_type, parse_error_t >
1193  try_parse( source_t & from ) const noexcept
1194  {
1196  alternatives(
1197  sequence(
1198  symbol('0'),
1199  maybe(
1200  symbol('.'),
1201  maybe( digit_p() >> digit_consumer_t{100},
1202  maybe( digit_p() >> digit_consumer_t{10},
1203  maybe( digit_p() >> digit_consumer_t{1} )
1204  )
1205  )
1206  )
1207  ),
1208  sequence(
1209  symbol_p('1') >> digit_consumer_t{1000},
1210  maybe(
1211  symbol('.'),
1212  maybe( symbol('0'),
1213  maybe( symbol('0'),
1214  maybe( symbol('0') )
1215  )
1216  )
1217  )
1218  )
1219  )
1220  ).try_parse( from );
1221 
1222  if( parse_result )
1223  return qvalue_t{ qvalue_t::trusted{ parse_result->m_value } };
1224  else
1225  return make_unexpected( parse_result.error() );
1226  }
1227 };
1228 
1229 } /* namespace impl */
1230 
1231 //
1232 // qvalue_producer
1233 //
1234 /*!
1235  * @brief A factory function to create a qvalue_producer.
1236  *
1237  * Usage example:
1238  * @code
1239  * produce<qvalue_t>(
1240  * alternatives(symbol('r'), symbol('R')),
1241  * symbol('='),
1242  * qvalue_p() >> as_result()
1243  * );
1244  * @endcode
1245  *
1246  * @since v.0.6.1
1247  */
1249 inline auto
1250 qvalue_p() noexcept
1251 {
1252  return impl::qvalue_producer_t{};
1253 }
1254 
1255 //
1256 // weight_producer
1257 //
1258 /*!
1259  * @brief A factory function to create a producer for weight parameter.
1260  *
1261  * Returns a producer that handles the following rules:
1262  @verbatim
1263  weight = OWS ';' OWS ('q' / 'Q') '=' qvalue
1264 
1265  qvalue = ( "0" [ "." 0*3DIGIT ] )
1266  / ( "1" [ "." 0*3("0") ] )
1267  @endverbatim
1268  *
1269  * See: https://tools.ietf.org/html/rfc7231
1270  *
1271  * That producer produces a value of type qvalue_t.
1272  *
1273  * @since v.0.6.1
1274  */
1276 inline auto
1277 weight_p() noexcept
1278 {
1279  return produce< qvalue_t >(
1280  ows(),
1281  symbol(';'),
1282  ows(),
1283  caseless_symbol('q'),
1284  symbol('='),
1285  qvalue_p() >> as_result()
1286  );
1287 }
1288 
1289 namespace impl
1290 {
1291 
1292 //
1293 // non_empty_comma_separated_list_producer_t
1294 //
1295 /*!
1296  * @brief A template for a producer that handles non-empty list of
1297  * comma-separated values.
1298  *
1299  * That producer handles the following rule:
1300 @verbatim
1301 1#element => *( "," OWS ) element *( OWS "," [ OWS element ] )
1302 @endverbatim
1303  *
1304  * See: https://tools.ietf.org/html/rfc7230
1305  * (section "7. ABNF List Extension: #rule")
1306  *
1307  * @tparam Container the type of container to be produced.
1308  *
1309  * @tparam Element_Producer the type of the producer of a single item.
1310  *
1311  * @since v.0.6.1
1312  */
1313 template<
1314  typename Container,
1315  typename Element_Producer >
1317  : public producer_tag< Container >
1318 {
1319  static_assert( impl::is_producer_v<Element_Producer>,
1320  "Element_Producer should be a value producer type" );
1321 
1322  Element_Producer m_element;
1323 
1324 public :
1325  using typename producer_tag< Container >::result_type;
1326 
1328  Element_Producer && element )
1329  : m_element{ std::move(element) }
1330  {}
1331 
1332  RESTINIO_NODISCARD
1333  expected_t< result_type, parse_error_t >
1335  {
1337 
1338  const auto appender = to_container();
1339 
1340  const auto process_result = sequence(
1341  repeat( 0, N, symbol(','), ows() ),
1342  m_element >> appender,
1343  repeat( 0, N,
1344  ows(), symbol(','),
1345  maybe( ows(), m_element >> appender )
1346  )
1347  ).try_process( from, tmp_value );
1348 
1349  if( !process_result )
1350  return { std::move(tmp_value) };
1351  else
1352  return make_unexpected( *process_result );
1353  }
1354 };
1355 
1356 //
1357 // maybe_empty_comma_separated_list_producer_t
1358 //
1359 /*!
1360  * @brief A template for a producer that handles possibly empty list of
1361  * comma-separated values.
1362  *
1363  * That producer handles the following rule:
1364 @verbatim
1365 #element => [ ( "," / element ) *( OWS "," [ OWS element ] ) ]
1366 @endverbatim
1367  *
1368  * See: https://tools.ietf.org/html/rfc7230
1369  * (section "7. ABNF List Extension: #rule")
1370  *
1371  * @tparam Container the type of container to be produced.
1372  *
1373  * @tparam Element_Producer the type of the producer of a single item.
1374  *
1375  * @since v.0.6.1
1376  */
1377 template<
1378  typename Container,
1379  typename Element_Producer >
1381  : public producer_tag< Container >
1382 {
1383  static_assert( impl::is_producer_v<Element_Producer>,
1384  "Element_Producer should be a value producer type" );
1385 
1386  Element_Producer m_element;
1387 
1388 public :
1389  using typename producer_tag< Container >::result_type;
1390 
1392  Element_Producer && element )
1393  : m_element{ std::move(element) }
1394  {}
1395 
1396  RESTINIO_NODISCARD
1397  expected_t< result_type, parse_error_t >
1399  {
1401 
1402  const auto appender = to_container();
1403 
1404  const auto process_result = maybe(
1405  alternatives( symbol(','), m_element >> appender ),
1406  repeat( 0, N,
1407  ows(), symbol(','),
1408  maybe( ows(), m_element >> appender )
1409  )
1410  ).try_process( from, tmp_value );
1411 
1412  if( !process_result )
1413  return { std::move(tmp_value) };
1414  else
1415  return make_unexpected( *process_result );
1416  }
1417 };
1418 
1419 } /* namespace impl */
1420 
1421 //
1422 // non_empty_comma_separated_list_producer
1423 //
1424 /*!
1425  * @brief A factory for a producer that handles non-empty list of
1426  * comma-separated values.
1427  *
1428  * That producer handles the following rule:
1429 @verbatim
1430 1#element => *( "," OWS ) element *( OWS "," [ OWS element ] )
1431 @endverbatim
1432  *
1433  * See: https://tools.ietf.org/html/rfc7230
1434  * (section "7. ABNF List Extension: #rule")
1435  *
1436  * Usage example:
1437  * @code
1438  auto parse = produce< byte_ranges_data >(
1439  make_bytes_prefix_parser(),
1440  non_empty_comma_separated_list_p< std::vector< byte_range > >(
1441  make_byte_range_parser()
1442  ) >> &byte_ranges_data::ranges
1443  );
1444  * @endcode
1445  *
1446  * @tparam Container the type of container to be produced.
1447  *
1448  * @tparam Element_Producer the type of the producer of a single item.
1449  *
1450  * @since v.0.6.1
1451  */
1452 template<
1453  typename Container,
1454  typename Element_Producer >
1456 auto
1458 {
1459  static_assert( impl::is_producer_v<Element_Producer>,
1460  "Element_Producer should be a value producer type" );
1461 
1463  Container,
1465 }
1466 
1467 //
1468 // maybe_empty_comma_separated_list_producer
1469 //
1470 /*!
1471  * @brief A factory for a producer that handles possibly empty list of
1472  * comma-separated values.
1473  *
1474  * That producer handles the following rule:
1475 @verbatim
1476 #element => [ ( "," / element ) *( OWS "," [ OWS element ] ) ]
1477 @endverbatim
1478  *
1479  * See: https://tools.ietf.org/html/rfc7230
1480  * (section "7. ABNF List Extension: #rule")
1481  *
1482  * Usage example:
1483  * @code
1484  auto parse = produce< byte_ranges_data >(
1485  make_bytes_prefix_parser(),
1486  maybe_empty_comma_separated_list_p< std::vector< byte_range > >(
1487  make_byte_range_parser()
1488  ) >> &byte_ranges_data::ranges
1489  );
1490  * @endcode
1491  *
1492  * @tparam Container the type of container to be produced.
1493  *
1494  * @tparam Element_Producer the type of the producer of a single item.
1495  *
1496  * @since v.0.6.1
1497  */
1498 template<
1499  typename Container,
1500  typename Element_Producer >
1502 auto
1504 {
1505  static_assert( impl::is_producer_v<Element_Producer>,
1506  "Element_Producer should be a value producer type" );
1507 
1509  Container,
1511 }
1512 
1513 //
1514 // parameter_with_mandatory_value_t
1515 //
1516 /*!
1517  * @brief A type that describes a parameter with mandatory value.
1518  *
1519  * @since v.0.6.1
1520  */
1522 
1523 //
1524 // parameter_with_mandatory_value_container_t
1525 //
1526 /*!
1527  * @brief A type of container for parameters with mandatory values.
1528  *
1529  * @since v.0.6.1
1530  */
1533 
1534 //
1535 // not_found_t
1536 //
1537 /*!
1538  * @brief An empty type to be used as indicator of negative search result.
1539  *
1540  * @since v.0.6.1
1541  */
1542 struct not_found_t {};
1543 
1544 //
1545 // find_first
1546 //
1547 /*!
1548  * @brief A helper function to find the first occurence of a parameter
1549  * with the specified value.
1550  *
1551  * @note
1552  * The caseless (case-insentive) search is used. It means that
1553  * search with value "charset" will returns items "CharSet", "charset",
1554  * "CHARSET" and so on.
1555  *
1556  * Usage example:
1557  * @code
1558  * const auto charset = find_first(content_type_params, "charset");
1559  * if(charset) {
1560  * ... // Handle the value of charset parameter.
1561  * }
1562  * @endcode
1563  *
1564  * @since v.0.6.1
1565  */
1571 {
1572  const auto it = std::find_if( where.begin(), where.end(),
1573  [&what]( const auto & pair ) {
1575  } );
1576  if( it != where.end() )
1577  return string_view_t{ it->second };
1578  else
1579  return make_unexpected( not_found_t{} );
1580 }
1581 
1582 namespace impl
1583 {
1584 
1586 {
1587 
1588 //
1589 // make_parser
1590 //
1591 /*!
1592  * @brief Helper function that creates an instance of producer
1593  * of parameter_with_mandatory_value_container.
1594  *
1595  * @since v.0.6.1
1596  */
1598 inline auto
1600 {
1602  repeat( 0, N,
1604  ows(),
1605  symbol(';'),
1606  ows(),
1607  token_p() >> to_lower()
1609  symbol('='),
1610  alternatives(
1611  token_p()
1613  quoted_string_p()
1615  )
1616  ) >> to_container()
1617  )
1618  );
1619 }
1620 
1621 } /* namespace params_with_value_producer_details */
1622 
1623 //
1624 // params_with_value_producer_t
1625 //
1626 /*!
1627  * @brief A type of producer that produces instances of
1628  * parameter_with_mandatory_value_container.
1629  *
1630  * @since v.0.6.1
1631  */
1634 {
1635  using actual_producer_t = std::decay_t<
1637 
1640 
1641 public :
1642  params_with_value_producer_t() = default;
1643 
1645  auto
1647  {
1648  return m_producer.try_parse( from );
1649  }
1650 };
1651 
1652 } /* namespace impl */
1653 
1654 //
1655 // params_with_value_producer
1656 //
1657 /*!
1658  * @brief A factory of producer of parameter_with_mandatory_value_container.
1659  *
1660  * Creates a produces that handles the following rule:
1661 @verbatim
1662 T := *( OWS ';' OWS token '=' OWS (token / quoted_string))
1663 @endverbatim
1664  *
1665  * Usage example:
1666  * @code
1667  * struct my_field {
1668  * std::string value;
1669  * parameter_with_mandatory_value_container_t params;
1670  * };
1671  * produce<my_field>(
1672  * token_p() >> to_lower() >> &my_field::value,
1673  * params_with_value_p() >> &my_field::params
1674  * );
1675  * @endcode
1676  *
1677  * @note
1678  * Parameters names are converted to lower case. Parameters' values
1679  * are not changed and stored as they are.
1680  *
1681  * @since v.0.6.1
1682  */
1685 params_with_value_p() { return {}; }
1686 
1687 //
1688 // parameter_with_optional_value_t
1689 //
1690 /*!
1691  * @brief A type that describes a parameter with optional value.
1692  *
1693  * @since v.0.6.1
1694  */
1697 
1698 //
1699 // parameter_with_optional_value_container_t
1700 //
1701 /*!
1702  * @brief A type of container for parameters with optional values.
1703  *
1704  * @since v.0.6.1
1705  */
1708 
1709 //
1710 // find_first
1711 //
1712 /*!
1713  * @brief A helper function to find the first occurence of a parameter
1714  * with the specified value.
1715  *
1716  * @note
1717  * The caseless (case-insentive) search is used. It means that
1718  * search with value "charset" will returns items "CharSet", "charset",
1719  * "CHARSET" and so on.
1720  *
1721  * Usage example:
1722  * @code
1723  * const auto max_age = find_first(cache_control_params, "max-age");
1724  * if(max_age) {
1725  * if(*max_age) {
1726  * ... // Handle the value of max-age parameter.
1727  * }
1728  * else {
1729  * ... // Handle the case where max-age specified but without a value.
1730  * }
1731  * }
1732  * @endcode
1733  *
1734  * @since v.0.6.1
1735  */
1741 {
1742  const auto it = std::find_if( where.begin(), where.end(),
1743  [&what]( const auto & pair ) {
1745  } );
1746  if( it != where.end() )
1747  {
1748  const auto opt = it->second;
1749  if( opt )
1750  return string_view_t{ *opt };
1751  else
1752  return restinio::optional_t< string_view_t >{ nullopt };
1753  }
1754  else
1755  return make_unexpected( not_found_t{} );
1756 }
1757 
1758 namespace impl
1759 {
1760 
1762 {
1763 
1764 //
1765 // make_parser
1766 //
1767 /*!
1768  * @brief Helper function that creates an instance of producer
1769  * of parameter_with_optional_value_container.
1770  *
1771  * @since v.0.6.1
1772  */
1774 inline auto
1776 {
1778  repeat( 0, N,
1780  ows(),
1781  symbol(';'),
1782  ows(),
1783  token_p() >> to_lower()
1785  maybe(
1786  symbol('='),
1787  alternatives(
1788  token_p()
1790  quoted_string_p()
1792  )
1793  )
1794  ) >> to_container()
1795  )
1796  );
1797 }
1798 
1799 } /* namespace params_with_opt_value_producer_details */
1800 
1801 //
1802 // params_with_opt_value_producer_t
1803 //
1804 /*!
1805  * @brief A type of producer that produces instances of
1806  * parameter_with_optional_value_container.
1807  *
1808  * @since v.0.6.1
1809  */
1812 {
1813  using actual_producer_t = std::decay_t<
1815 
1818 
1819 public :
1821 
1823  auto
1825  {
1826  return m_producer.try_parse( from );
1827  }
1828 };
1829 
1830 } /* namespace impl */
1831 
1832 //
1833 // params_with_opt_value_producer
1834 //
1835 /*!
1836  * @brief A factory of producer of parameter_with_optional_value_container.
1837  *
1838  * Creates a produces that handles the following rule:
1839 @verbatim
1840 T := *( OWS ';' OWS token ['=' OWS (token / quoted_string)] )
1841 @endverbatim
1842  *
1843  * Usage example:
1844  * @code
1845  * struct my_field {
1846  * std::string value;
1847  * parameter_with_optional_value_container_t params;
1848  * };
1849  * produce<my_field>(
1850  * token_p() >> to_lower() >> &my_field::value,
1851  * params_with_opt_value_p() >> &my_field::params
1852  * );
1853  * @endcode
1854  *
1855  * @note
1856  * Parameters names are converted to lower case. Parameters' values
1857  * are not changed and stored as they are.
1858  *
1859  * @since v.0.6.1
1860  */
1863 params_with_opt_value_p() { return {}; }
1864 
1865 } /* namespace http_field_parser */
1866 
1867 } /* namespace restinio */
RESTINIO_NODISCARD bool operator()(const char actual) const noexcept
Definition: basics.hpp:301
RESTINIO_NODISCARD constexpr bool is_ctext(const char ch) noexcept
Is a character a ctext?
Definition: basics.hpp:400
A helper wrapper to indicate that value hasn&#39;t been checked yet and should be checked in the construc...
Definition: basics.hpp:63
constexpr underlying_uint_t zero
The minimal allowed value for a qvalue.
Definition: basics.hpp:41
RESTINIO_NODISCARD expected_t< result_type, parse_error_t > try_parse(source_t &from) const
Definition: basics.hpp:706
RESTINIO_NODISCARD expected_t< result_type, parse_error_t > try_parse(source_t &from) const noexcept
Definition: basics.hpp:1193
constexpr underlying_uint_t maximum
The maximal allowed value for a qvalue.
Definition: basics.hpp:39
RESTINIO_NODISCARD bool operator()(const char actual) const noexcept
Definition: basics.hpp:282
A class for holding the parsed value of qvalue from RFC7231.
Definition: basics.hpp:135
RESTINIO_NODISCARD auto make_parser()
Helper function that creates an instance of producer of parameter_with_optional_value_container.
Definition: basics.hpp:1775
RESTINIO_NODISCARD bool operator()(const char actual) const noexcept
Definition: basics.hpp:337
constexpr trusted(underlying_uint_t value) noexcept
Definition: basics.hpp:56
constexpr qvalue_t(trusted val) noexcept
Definition: basics.hpp:192
RESTINIO_NODISCARD expected_t< result_type, parse_error_t > try_parse(source_t &from)
Definition: basics.hpp:1334
static constexpr qvalue_details::extremum_max_t maximum
The indicator that tells that new qvalue object should have the maximal allowed value.
Definition: basics.hpp:147
constexpr qvalue_t(qvalue_details::extremum_min_t) noexcept
Definition: basics.hpp:196
RESTINIO_NODISCARD bool operator()(const char actual) const noexcept
Definition: basics.hpp:462
RESTINIO_NODISCARD bool operator()(const char actual) const noexcept
Definition: basics.hpp:423
A helper class to be used to accumulate actual integer while when the next digit is extracted from th...
Definition: basics.hpp:1173
qvalue_t(untrusted val) noexcept
Definition: basics.hpp:188
constexpr qvalue_t(qvalue_details::extremum_max_t) noexcept
Definition: basics.hpp:200
friend std::ostream & operator<<(std::ostream &to, const qvalue_t &what)
Definition: basics.hpp:212
RESTINIO_NODISCARD expected_t< result_type, parse_error_t > try_parse(source_t &from)
Definition: basics.hpp:1398
RESTINIO_NODISCARD expected_t< result_type, parse_error_t > try_parse(source_t &from) const
Definition: basics.hpp:1127
RESTINIO_NODISCARD expected_t< result_type, parse_error_t > try_parse(source_t &from) const
Definition: basics.hpp:653
underlying_char_array_t make_char_array() const noexcept
Definition: basics.hpp:161
An empty type to be used as indicator of negative search result.
Definition: basics.hpp:1542
RESTINIO_NODISCARD expected_t< result_type, parse_error_t > try_parse(source_t &from) const
Definition: basics.hpp:568
RESTINIO_NODISCARD auto try_parse_field(const generic_request_t< Extra_Data > &req, http_field_t field_id, string_view_t default_value=string_view_t{})
A helper function for extraction and parsing a value of HTTP-field.
RESTINIO_NODISCARD expected_t< result_type, parse_error_t > try_parse(source_t &from) const noexcept
Definition: basics.hpp:486
void consume(zero_initialized_unit_t &dest, char &&digit)
Definition: basics.hpp:1183
static constexpr qvalue_details::extremum_min_t zero
The indicator that tells that new qvalue object should have the minimal allowed value.
Definition: basics.hpp:151
constexpr auto as_uint() const noexcept
Definition: basics.hpp:204
RESTINIO_NODISCARD auto make_parser()
Helper function that creates an instance of producer of parameter_with_mandatory_value_container.
Definition: basics.hpp:1599
A helper wrapper to indicate that value is already checked and shouldn&#39;t be checked again...
Definition: basics.hpp:50
RESTINIO_NODISCARD auto try_parse(source_t &from)
Definition: basics.hpp:1646
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