RESTinio
uri_helpers.hpp
Go to the documentation of this file.
1 /*
2  restinio
3 */
4 
5 /*!
6  escape functions.
7 */
8 
9 #pragma once
10 
11 #include <string>
12 #include <unordered_map>
13 
14 #include <fmt/format.h>
15 #include <fmt/ostream.h>
16 
17 #include <restinio/exception.hpp>
18 #include <restinio/utils/percent_encoding.hpp>
19 #include <restinio/optional.hpp>
20 
21 namespace restinio
22 {
23 
24 namespace impl
25 {
26 
27 inline const char *
28 modified_memchr( int chr , const char * from, const char * to )
29 {
30  const char * result = static_cast< const char * >(
31  std::memchr( from, chr, static_cast<std::size_t>(to - from) ) );
32 
33  return result ? result : to;
34 }
35 
36 } /* namespace impl */
37 
38 //
39 // query_string_params_t
40 //
41 
42 //! Parameters container for query strings parameters.
43 class query_string_params_t final
44 {
45  public:
46  using parameters_container_t = std::vector< std::pair< string_view_t, string_view_t > >;
47 
48  //! Constructor for the case when query string empty of
49  //! contains a set of key-value pairs.
51  std::unique_ptr< char[] > data_buffer,
52  parameters_container_t parameters )
55  {}
56 
57  //! Constructor for the case when query string contains only tag
58  //! (web beacon).
60  std::unique_ptr< char[] > data_buffer,
61  optional_t< string_view_t > tag )
63  , m_tag{ tag }
64  {}
65 
66  query_string_params_t( query_string_params_t && ) = default;
67  query_string_params_t & operator = ( query_string_params_t && ) = default;
68 
69  query_string_params_t( const query_string_params_t & ) = delete;
70  query_string_params_t & operator = ( const query_string_params_t & ) = delete;
71 
72  //! Get parameter.
74  operator [] ( string_view_t key ) const
75  {
76  return find_parameter_with_check( key ).second;
77  }
78 
79  //! Check parameter.
80  bool
81  has( string_view_t key ) const noexcept
82  {
83  return m_parameters.end() != find_parameter( key );
84  }
85 
86  //! Get the value of a parameter if it exists.
87  //! @since v.0.4.4
89  get_param( string_view_t key ) const noexcept
90  {
91  const auto it = find_parameter( key );
92 
93  return m_parameters.end() != it ?
94  optional_t< string_view_t >{ it->second } :
95  optional_t< string_view_t >{ nullopt };
96  }
97 
98  //! Get the size of parameters.
99  auto size() const noexcept { return m_parameters.size(); }
100 
101  //! Is there any parameters?
102  //! @since v.0.4.8
103  bool empty() const noexcept { return m_parameters.empty(); }
104 
105  //! @name Iterate parameters.
106  //! @{
108  begin() const noexcept
109  {
110  return m_parameters.begin();
111  }
112 
114  end() const noexcept
115  {
116  return m_parameters.end();
117  }
118  //! @}
119 
120  //! Get the tag (web beacon) part.
121  /*!
122  A value of "tag" (also known as web beacon) is available only
123  if URI looks like that:
124  \verbatim
125  http://example.com/resource?value
126  \endverbatim
127  In that case tag will contain `value`. For URI with different
128  formats tag() will return empty optional.
129 
130  @since v.0.4.9
131  */
132  auto tag() const noexcept { return m_tag; }
133 
134  private:
136  find_parameter( string_view_t key ) const noexcept
137  {
138  return
139  std::find_if(
140  m_parameters.begin(),
141  m_parameters.end(),
142  [&]( const auto p ){
143  return key == p.first;
144  } );
145  }
146 
149  {
150  auto it = find_parameter( key );
151 
152  if( m_parameters.end() == it )
153  {
154  throw exception_t{
155  fmt::format(
156  "unable to find parameter \"{}\"",
157  std::string{ key.data(), key.size() } ) };
158  }
159 
160  return *it;
161  }
162 
163  //! Shared buffer for string_view of named parameterts names.
166 
167  //! Tag (or web beacon) part.
168  /*! @since v.0.4.9 */
170 };
171 
172 //! Cast query string parameter to a given type.
173 template < typename Value_Type >
174 Value_Type
175 get( const query_string_params_t & params, string_view_t key )
176 {
177  return get< Value_Type >( params[ key ] );
178 }
179 
181 {
182 
183 /*!
184  * @brief Traits for the default RESTinio parser for query string.
185  *
186  * The default RESTinio parser prohibit usage of unexcaped asterisk.
187  *
188  * @note
189  * This traits type is used by default. It means that a call:
190  * @code
191  * auto result = restinio::parse_query<restinio::parse_query_traits::restinio_defaults>("name=value");
192  * @endcode
193  * is equivalent to:
194  * @code
195  * auto result = restinio::parse_query("name=value");
196  * @endcode
197  *
198  * @since v.0.4.9.1
199  */
201 
202 /*!
203  * @brief Traits for parsing a query string in JavaScript-compatible mode.
204  *
205  * In that mode unexcaped asterisk is alowed.
206  *
207  * Usage example:
208  * @code
209  * auto result = restinio::parse_query<restinio::parse_query_traits::javascript_compatible>("name=A*");
210  * @endcode
211  *
212  * @since v.0.4.9.1
213  */
215 
216 } /* namespace parse_query_traits */
217 
218 //! Parse query key-value parts.
219 /*!
220  Since v.0.4.9 this function correctly handles the following cases:
221 
222  - presence of tag (web beacon) in URI. For example, when URI looks like
223  `http://example.com/resource?tag`. In that case value of the tag (web
224  beacon) can be obtained via query_string_params_t::tag() method.
225  References: [web beacon](https://en.wikipedia.org/wiki/Web_beacon) and
226  [query-string-tracking](https://en.wikipedia.org/wiki/Query_string#Tracking);
227  - usage of `;` instead of `&` as parameter separator.
228 
229  Since v.0.4.9.1 this function can be parametrized by parser traits. For
230  example:
231  @code
232  auto result = restinio::parse_query<restinio::parse_query_traits::javascript_compatible>("name=A*");
233  @endcode
234 */
235 template< typename Parse_Traits = parse_query_traits::restinio_defaults >
236 inline query_string_params_t
238  //! Query part of the request target.
239  string_view_t original_query_string )
240 {
241  constexpr const string_view_t separators{ "&;", 2u };
242 
243  std::unique_ptr< char[] > data_buffer;
245 
247  {
248  // Because query string is not empty a new buffer should be
249  // allocated and query string should be copied to it.
250  data_buffer.reset( new char[ original_query_string.size() ] );
251  std::memcpy(
252  data_buffer.get(),
255 
256  // Work with created buffer:
258  data_buffer.get(),
260  };
263 
264  while( pos < end_pos )
265  {
266  const auto eq_pos = work_query_string.find_first_of( '=', pos );
267 
268  if( string_view_t::npos == eq_pos )
269  {
270  // Since v.0.4.9 we should check the presence of tag (web beacon)
271  // in query string.
272  // Tag can be the only item in query string.
273  if( pos != 0u )
274  // The query string has illegal format.
275  throw exception_t{
276  fmt::format(
277  "invalid format of key-value pairs in query_string: {}, "
278  "no '=' symbol starting from position {}",
280  pos ) };
281  else
282  {
283  // Query string contains only tag (web beacon).
284  const auto tag_size =
286  &data_buffer[ pos ],
287  end_pos - pos );
288 
290  pos, tag_size );
291 
293  }
294  }
295 
296  const auto eq_pos_next = eq_pos + 1u;
301 
302  // Handle next pair of parameters found.
304  &data_buffer[ pos ],
306  &data_buffer[ pos ],
307  eq_pos - pos )
308  };
309 
315  };
316 
318 
319  pos = separator_pos + 1u;
320  }
321  }
322 
323  return query_string_params_t{
324  std::move( data_buffer ),
325  std::move( parameters )
326  };
327 }
328 
329 } /* namespace restinio */
auto tag() const noexcept
Get the tag (web beacon) part.
optional_t< string_view_t > get_param(string_view_t key) const noexcept
Get the value of a parameter if it exists.
Definition: uri_helpers.hpp:89
parameters_container_t::const_iterator find_parameter(string_view_t key) const noexcept
parameters_container_t::const_reference find_parameter_with_check(string_view_t key) const
query_string_params_t & operator=(const query_string_params_t &)=delete
std::unique_ptr< char[] > m_data_buffer
Shared buffer for string_view of named parameterts names.
parameters_container_t m_parameters
query_string_params_t(const query_string_params_t &)=delete
parameters_container_t::const_iterator begin() const noexcept
auto size() const noexcept
Get the size of parameters.
Definition: uri_helpers.hpp:99
bool empty() const noexcept
Is there any parameters?
query_string_params_t parse_query(string_view_t original_query_string)
Parse query key-value parts.
bool has(string_view_t key) const noexcept
Check parameter.
Definition: uri_helpers.hpp:81
query_string_params_t(std::unique_ptr< char[] > data_buffer, optional_t< string_view_t > tag)
Constructor for the case when query string contains only tag (web beacon).
Definition: uri_helpers.hpp:59
const char * modified_memchr(int chr, const char *from, const char *to)
Definition: uri_helpers.hpp:28
Value_Type get(const query_string_params_t &params, string_view_t key)
Cast query string parameter to a given type.
query_string_params_t(query_string_params_t &&)=default
string_view_t operator[](string_view_t key) const
Get parameter.
Definition: uri_helpers.hpp:74
query_string_params_t & operator=(query_string_params_t &&)=default
parameters_container_t::const_iterator end() const noexcept
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
optional_t< string_view_t > m_tag
Tag (or web beacon) part.