1 /*
2 restinio
3 */
4
5 /*!
6 helpers for http communication.
7 */
8
9 #pragma once
10
11 #include <restinio/impl/include_fmtlib.hpp>
12
13 #include <restinio/impl/string_caseless_compare.hpp>
14
15 #include <restinio/exception.hpp>
16 #include <restinio/string_view.hpp>
17 #include <restinio/optional.hpp>
18 #include <restinio/common_types.hpp>
19
20 #include <http_parser.h>
21
22 #include <iosfwd>
23 #include <string>
24 #include <vector>
25 #include <algorithm>
26
27 namespace restinio
28 {
29
30
31 // Adopted header fields
32 // (https://www.iana.org/assignments/message-headers/message-headers.xml#perm-headers).
33 // Fields `Connection` and `Content-Length` are specieal cases, thus they are excluded from the list.
34 #define RESTINIO_HTTP_FIELDS_MAP( RESTINIO_GEN ) \
35 RESTINIO_GEN( a_im, A-IM ) \
36 RESTINIO_GEN( accept, Accept ) \
37 RESTINIO_GEN( accept_additions, Accept-Additions ) \
38 RESTINIO_GEN( accept_charset, Accept-Charset ) \
39 RESTINIO_GEN( accept_datetime, Accept-Datetime ) \
40 RESTINIO_GEN( accept_encoding, Accept-Encoding ) \
41 RESTINIO_GEN( accept_features, Accept-Features ) \
42 RESTINIO_GEN( accept_language, Accept-Language ) \
43 RESTINIO_GEN( accept_patch, Accept-Patch ) \
44 RESTINIO_GEN( accept_post, Accept-Post ) \
45 RESTINIO_GEN( accept_ranges, Accept-Ranges ) \
46 RESTINIO_GEN( age, Age ) \
47 RESTINIO_GEN( allow, Allow ) \
48 RESTINIO_GEN( alpn, ALPN ) \
49 RESTINIO_GEN( alt_svc, Alt-Svc ) \
50 RESTINIO_GEN( alt_used, Alt-Used ) \
51 RESTINIO_GEN( alternates, Alternates ) \
52 RESTINIO_GEN( apply_to_redirect_ref, Apply-To-Redirect-Ref ) \
53 RESTINIO_GEN( authentication_control, Authentication-Control ) \
54 RESTINIO_GEN( authentication_info, Authentication-Info ) \
55 RESTINIO_GEN( authorization, Authorization ) \
56 RESTINIO_GEN( c_ext, C-Ext ) \
57 RESTINIO_GEN( c_man, C-Man ) \
58 RESTINIO_GEN( c_opt, C-Opt ) \
59 RESTINIO_GEN( c_pep, C-PEP ) \
60 RESTINIO_GEN( c_pep_info, C-PEP-Info ) \
61 RESTINIO_GEN( cache_control, Cache-Control ) \
62 RESTINIO_GEN( caldav_timezones, CalDAV-Timezones ) \
63 RESTINIO_GEN( close, Close ) \
64 RESTINIO_GEN( content_base, Content-Base ) \
65 RESTINIO_GEN( content_disposition, Content-Disposition ) \
66 RESTINIO_GEN( content_encoding, Content-Encoding ) \
67 RESTINIO_GEN( content_id, Content-ID ) \
68 RESTINIO_GEN( content_language, Content-Language ) \
69 RESTINIO_GEN( content_location, Content-Location ) \
70 RESTINIO_GEN( content_md5, Content-MD5 ) \
71 RESTINIO_GEN( content_range, Content-Range ) \
72 RESTINIO_GEN( content_script_type, Content-Script-Type ) \
73 RESTINIO_GEN( content_style_type, Content-Style-Type ) \
74 RESTINIO_GEN( content_type, Content-Type ) \
75 RESTINIO_GEN( content_version, Content-Version ) \
76 RESTINIO_GEN( cookie, Cookie ) \
77 RESTINIO_GEN( cookie2, Cookie2 ) \
78 RESTINIO_GEN( dasl, DASL ) \
79 RESTINIO_GEN( dav, DAV ) \
80 RESTINIO_GEN( date, Date ) \
81 RESTINIO_GEN( default_style, Default-Style ) \
82 RESTINIO_GEN( delta_base, Delta-Base ) \
83 RESTINIO_GEN( depth, Depth ) \
84 RESTINIO_GEN( derived_from, Derived-From ) \
85 RESTINIO_GEN( destination, Destination ) \
86 RESTINIO_GEN( differential_id, Differential-ID ) \
87 RESTINIO_GEN( digest, Digest ) \
88 RESTINIO_GEN( etag, ETag ) \
89 RESTINIO_GEN( expect, Expect ) \
90 RESTINIO_GEN( expires, Expires ) \
91 RESTINIO_GEN( ext, Ext ) \
92 RESTINIO_GEN( forwarded, Forwarded ) \
93 RESTINIO_GEN( from, From ) \
94 RESTINIO_GEN( getprofile, GetProfile ) \
95 RESTINIO_GEN( hobareg, Hobareg ) \
96 RESTINIO_GEN( host, Host ) \
97 RESTINIO_GEN( http2_settings, HTTP2-Settings ) \
98 RESTINIO_GEN( im, IM ) \
99 RESTINIO_GEN( if_, If ) \
100 RESTINIO_GEN( if_match, If-Match ) \
101 RESTINIO_GEN( if_modified_since, If-Modified-Since ) \
102 RESTINIO_GEN( if_none_match, If-None-Match ) \
103 RESTINIO_GEN( if_range, If-Range ) \
104 RESTINIO_GEN( if_schedule_tag_match, If-Schedule-Tag-Match ) \
105 RESTINIO_GEN( if_unmodified_since, If-Unmodified-Since ) \
106 RESTINIO_GEN( keep_alive, Keep-Alive ) \
107 RESTINIO_GEN( label, Label ) \
108 RESTINIO_GEN( last_modified, Last-Modified ) \
109 RESTINIO_GEN( link, Link ) \
110 RESTINIO_GEN( location, Location ) \
111 RESTINIO_GEN( lock_token, Lock-Token ) \
112 RESTINIO_GEN( man, Man ) \
113 RESTINIO_GEN( max_forwards, Max-Forwards ) \
114 RESTINIO_GEN( memento_datetime, Memento-Datetime ) \
115 RESTINIO_GEN( meter, Meter ) \
116 RESTINIO_GEN( mime_version, MIME-Version ) \
117 RESTINIO_GEN( negotiate, Negotiate ) \
118 RESTINIO_GEN( opt, Opt ) \
119 RESTINIO_GEN( optional_www_authenticate, Optional-WWW-Authenticate ) \
120 RESTINIO_GEN( ordering_type, Ordering-Type ) \
121 RESTINIO_GEN( origin, Origin ) \
122 RESTINIO_GEN( overwrite, Overwrite ) \
123 RESTINIO_GEN( p3p, P3P ) \
124 RESTINIO_GEN( pep, PEP ) \
125 RESTINIO_GEN( pics_label, PICS-Label ) \
126 RESTINIO_GEN( pep_info, Pep-Info ) \
127 RESTINIO_GEN( position, Position ) \
128 RESTINIO_GEN( pragma, Pragma ) \
129 RESTINIO_GEN( prefer, Prefer ) \
130 RESTINIO_GEN( preference_applied, Preference-Applied ) \
131 RESTINIO_GEN( profileobject, ProfileObject ) \
132 RESTINIO_GEN( protocol, Protocol ) \
133 RESTINIO_GEN( protocol_info, Protocol-Info ) \
134 RESTINIO_GEN( protocol_query, Protocol-Query ) \
135 RESTINIO_GEN( protocol_request, Protocol-Request ) \
136 RESTINIO_GEN( proxy_authenticate, Proxy-Authenticate ) \
137 RESTINIO_GEN( proxy_authentication_info, Proxy-Authentication-Info ) \
138 RESTINIO_GEN( proxy_authorization, Proxy-Authorization ) \
139 RESTINIO_GEN( proxy_features, Proxy-Features ) \
140 RESTINIO_GEN( proxy_instruction, Proxy-Instruction ) \
141 RESTINIO_GEN( public_, Public ) \
142 RESTINIO_GEN( public_key_pins, Public-Key-Pins ) \
143 RESTINIO_GEN( public_key_pins_report_only, Public-Key-Pins-Report-Only ) \
144 RESTINIO_GEN( range, Range ) \
145 RESTINIO_GEN( redirect_ref, Redirect-Ref ) \
146 RESTINIO_GEN( referer, Referer ) \
147 RESTINIO_GEN( retry_after, Retry-After ) \
148 RESTINIO_GEN( safe, Safe ) \
149 RESTINIO_GEN( schedule_reply, Schedule-Reply ) \
150 RESTINIO_GEN( schedule_tag, Schedule-Tag ) \
151 RESTINIO_GEN( sec_websocket_accept, Sec-WebSocket-Accept ) \
152 RESTINIO_GEN( sec_websocket_extensions, Sec-WebSocket-Extensions ) \
153 RESTINIO_GEN( sec_websocket_key, Sec-WebSocket-Key ) \
154 RESTINIO_GEN( sec_websocket_protocol, Sec-WebSocket-Protocol ) \
155 RESTINIO_GEN( sec_websocket_version, Sec-WebSocket-Version ) \
156 RESTINIO_GEN( security_scheme, Security-Scheme ) \
157 RESTINIO_GEN( server, Server ) \
158 RESTINIO_GEN( set_cookie, Set-Cookie ) \
159 RESTINIO_GEN( set_cookie2, Set-Cookie2 ) \
160 RESTINIO_GEN( setprofile, SetProfile ) \
161 RESTINIO_GEN( slug, SLUG ) \
162 RESTINIO_GEN( soapaction, SoapAction ) \
163 RESTINIO_GEN( status_uri, Status-URI ) \
164 RESTINIO_GEN( strict_transport_security, Strict-Transport-Security ) \
165 RESTINIO_GEN( surrogate_capability, Surrogate-Capability ) \
166 RESTINIO_GEN( surrogate_control, Surrogate-Control ) \
167 RESTINIO_GEN( tcn, TCN ) \
168 RESTINIO_GEN( te, TE ) \
169 RESTINIO_GEN( timeout, Timeout ) \
170 RESTINIO_GEN( topic, Topic ) \
171 RESTINIO_GEN( trailer, Trailer ) \
172 RESTINIO_GEN( transfer_encoding, Transfer-Encoding ) \
173 RESTINIO_GEN( ttl, TTL ) \
174 RESTINIO_GEN( urgency, Urgency ) \
175 RESTINIO_GEN( uri, URI ) \
176 RESTINIO_GEN( upgrade, Upgrade ) \
177 RESTINIO_GEN( user_agent, User-Agent ) \
178 RESTINIO_GEN( variant_vary, Variant-Vary ) \
179 RESTINIO_GEN( vary, Vary ) \
180 RESTINIO_GEN( via, Via ) \
181 RESTINIO_GEN( www_authenticate, WWW-Authenticate ) \
182 RESTINIO_GEN( want_digest, Want-Digest ) \
183 RESTINIO_GEN( warning, Warning ) \
184 RESTINIO_GEN( x_frame_options, X-Frame-Options ) \
185 \
186 RESTINIO_GEN( access_control, Access-Control ) \
187 RESTINIO_GEN( access_control_allow_credentials, Access-Control-Allow-Credentials ) \
188 RESTINIO_GEN( access_control_allow_headers, Access-Control-Allow-Headers )\
189 RESTINIO_GEN( access_control_allow_methods, Access-Control-Allow-Methods )\
190 RESTINIO_GEN( access_control_allow_origin, Access-Control-Allow-Origin ) \
191 RESTINIO_GEN( access_control_max_age, Access-Control-Max-Age ) \
192 RESTINIO_GEN( access_control_request_method, Access-Control-Request-Method ) \
193 RESTINIO_GEN( access_control_request_headers, Access-Control-Request-Headers ) \
194 RESTINIO_GEN( compliance, Compliance ) \
195 RESTINIO_GEN( content_transfer_encoding, Content-Transfer-Encoding ) \
196 RESTINIO_GEN( cost, Cost ) \
197 RESTINIO_GEN( ediint_features, EDIINT-Features ) \
198 RESTINIO_GEN( message_id, Message-ID ) \
199 RESTINIO_GEN( method_check, Method-Check ) \
200 RESTINIO_GEN( method_check_expires, Method-Check-Expires ) \
201 RESTINIO_GEN( non_compliance, Non-Compliance ) \
202 RESTINIO_GEN( optional, Optional ) \
203 RESTINIO_GEN( referer_root, Referer-Root ) \
204 RESTINIO_GEN( resolution_hint, Resolution-Hint ) \
205 RESTINIO_GEN( resolver_location, Resolver-Location ) \
206 RESTINIO_GEN( subok, SubOK ) \
207 RESTINIO_GEN( subst, Subst ) \
208 RESTINIO_GEN( title, Title ) \
209 RESTINIO_GEN( ua_color, UA-Color ) \
210 RESTINIO_GEN( ua_media, UA-Media ) \
211 RESTINIO_GEN( ua_pixels, UA-Pixels ) \
212 RESTINIO_GEN( ua_resolution, UA-Resolution ) \
213 RESTINIO_GEN( ua_windowpixels, UA-Windowpixels ) \
214 RESTINIO_GEN( version, Version ) \
215 RESTINIO_GEN( x_device_accept, X-Device-Accept ) \
216 RESTINIO_GEN( x_device_accept_charset, X-Device-Accept-Charset ) \
217 RESTINIO_GEN( x_device_accept_encoding, X-Device-Accept-Encoding ) \
218 RESTINIO_GEN( x_device_accept_language, X-Device-Accept-Language ) \
219 RESTINIO_GEN( x_device_user_agent, X-Device-User-Agent )
220 // SPECIAL CASE: RESTINIO_GEN( connection, Connection )
221 // SPECIAL CASE: RESTINIO_GEN( content_length, Content-Length )
222
223 //
224 // http_field_t
225 //
226
227 //! C++ enum that repeats nodejs c-style enum.
228 /*!
229 \note Fields `Connection` and `Content-Length` are specieal cases,
230 thus they are not present in the list.
231 */
232 enum class http_field_t : std::uint8_t //By now 152 + 34 + 1 items fits to uint8_t
233 {
234 #define RESTINIO_HTTP_FIELD_GEN( name, ignored ) name,
235 RESTINIO_HTTP_FIELDS_MAP( RESTINIO_HTTP_FIELD_GEN )
236 #undef RESTINIO_HTTP_FIELD_GEN
237 // Unspecified field.
238 field_unspecified
239 };
240
241 //! Helper alies to omitt `_t` suffix.
242 using http_field = http_field_t;
243
244 //
245 // string_to_field()
246 //
247
248 //! Helper function to get method string name.
249 //! \{
250 inline http_field_t
string_to_field(string_view_t field)251 string_to_field( string_view_t field ) noexcept
252 {
253 const char * field_name = field.data();
254 const std::size_t field_name_size = field.size();
255
256 #define RESTINIO_HTTP_CHECK_FOR_FIELD( field_id, candidate_field_name ) \
257 if( impl::is_equal_caseless(field_name, #candidate_field_name , field_name_size ) ) \
258 return http_field_t:: field_id;
259
260 // TODO: make most popular fields to be checked first.
261
262 switch( field_name_size )
263 {
264 case 2:
265 RESTINIO_HTTP_CHECK_FOR_FIELD( if_, If )
266 RESTINIO_HTTP_CHECK_FOR_FIELD( im, IM )
267 RESTINIO_HTTP_CHECK_FOR_FIELD( te, TE )
268 break;
269
270 case 3:
271 RESTINIO_HTTP_CHECK_FOR_FIELD( age, Age )
272 RESTINIO_HTTP_CHECK_FOR_FIELD( dav, DAV )
273 RESTINIO_HTTP_CHECK_FOR_FIELD( ext, Ext )
274 RESTINIO_HTTP_CHECK_FOR_FIELD( man, Man )
275 RESTINIO_HTTP_CHECK_FOR_FIELD( opt, Opt )
276 RESTINIO_HTTP_CHECK_FOR_FIELD( p3p, P3P )
277 RESTINIO_HTTP_CHECK_FOR_FIELD( pep, PEP )
278 RESTINIO_HTTP_CHECK_FOR_FIELD( tcn, TCN )
279 RESTINIO_HTTP_CHECK_FOR_FIELD( ttl, TTL )
280 RESTINIO_HTTP_CHECK_FOR_FIELD( uri, URI )
281 RESTINIO_HTTP_CHECK_FOR_FIELD( via, Via )
282 break;
283
284 case 4:
285 // Known to be more used first:
286 RESTINIO_HTTP_CHECK_FOR_FIELD( host, Host )
287
288 RESTINIO_HTTP_CHECK_FOR_FIELD( a_im, A-IM )
289 RESTINIO_HTTP_CHECK_FOR_FIELD( alpn, ALPN )
290 RESTINIO_HTTP_CHECK_FOR_FIELD( dasl, DASL )
291 RESTINIO_HTTP_CHECK_FOR_FIELD( date, Date )
292 RESTINIO_HTTP_CHECK_FOR_FIELD( etag, ETag )
293 RESTINIO_HTTP_CHECK_FOR_FIELD( from, From )
294 RESTINIO_HTTP_CHECK_FOR_FIELD( link, Link )
295 RESTINIO_HTTP_CHECK_FOR_FIELD( safe, Safe )
296 RESTINIO_HTTP_CHECK_FOR_FIELD( slug, SLUG )
297 RESTINIO_HTTP_CHECK_FOR_FIELD( vary, Vary )
298 RESTINIO_HTTP_CHECK_FOR_FIELD( cost, Cost )
299 break;
300
301 case 5:
302 RESTINIO_HTTP_CHECK_FOR_FIELD( allow, Allow )
303 RESTINIO_HTTP_CHECK_FOR_FIELD( c_ext, C-Ext )
304 RESTINIO_HTTP_CHECK_FOR_FIELD( c_man, C-Man )
305 RESTINIO_HTTP_CHECK_FOR_FIELD( c_opt, C-Opt )
306 RESTINIO_HTTP_CHECK_FOR_FIELD( c_pep, C-PEP )
307 RESTINIO_HTTP_CHECK_FOR_FIELD( close, Close )
308 RESTINIO_HTTP_CHECK_FOR_FIELD( depth, Depth )
309 RESTINIO_HTTP_CHECK_FOR_FIELD( label, Label )
310 RESTINIO_HTTP_CHECK_FOR_FIELD( meter, Meter )
311 RESTINIO_HTTP_CHECK_FOR_FIELD( range, Range )
312 RESTINIO_HTTP_CHECK_FOR_FIELD( topic, Topic )
313 RESTINIO_HTTP_CHECK_FOR_FIELD( subok, SubOK )
314 RESTINIO_HTTP_CHECK_FOR_FIELD( subst, Subst )
315 RESTINIO_HTTP_CHECK_FOR_FIELD( title, Title )
316 break;
317
318 case 6:
319 // Known to be more used first:
320 RESTINIO_HTTP_CHECK_FOR_FIELD( accept, Accept )
321 RESTINIO_HTTP_CHECK_FOR_FIELD( cookie, Cookie )
322 RESTINIO_HTTP_CHECK_FOR_FIELD( server, Server )
323
324 RESTINIO_HTTP_CHECK_FOR_FIELD( digest, Digest )
325 RESTINIO_HTTP_CHECK_FOR_FIELD( expect, Expect )
326 RESTINIO_HTTP_CHECK_FOR_FIELD( origin, Origin )
327 RESTINIO_HTTP_CHECK_FOR_FIELD( pragma, Pragma )
328 RESTINIO_HTTP_CHECK_FOR_FIELD( prefer, Prefer )
329 RESTINIO_HTTP_CHECK_FOR_FIELD( public_, Public )
330 break;
331
332 case 7:
333 RESTINIO_HTTP_CHECK_FOR_FIELD( alt_svc, Alt-Svc )
334 RESTINIO_HTTP_CHECK_FOR_FIELD( cookie2, Cookie2 )
335 RESTINIO_HTTP_CHECK_FOR_FIELD( expires, Expires )
336 RESTINIO_HTTP_CHECK_FOR_FIELD( hobareg, Hobareg )
337 RESTINIO_HTTP_CHECK_FOR_FIELD( referer, Referer )
338 RESTINIO_HTTP_CHECK_FOR_FIELD( timeout, Timeout )
339 RESTINIO_HTTP_CHECK_FOR_FIELD( trailer, Trailer )
340 RESTINIO_HTTP_CHECK_FOR_FIELD( urgency, Urgency )
341 RESTINIO_HTTP_CHECK_FOR_FIELD( upgrade, Upgrade )
342 RESTINIO_HTTP_CHECK_FOR_FIELD( warning, Warning )
343 RESTINIO_HTTP_CHECK_FOR_FIELD( version, Version )
344 break;
345
346 case 8:
347 RESTINIO_HTTP_CHECK_FOR_FIELD( alt_used, Alt-Used )
348 RESTINIO_HTTP_CHECK_FOR_FIELD( if_match, If-Match )
349 RESTINIO_HTTP_CHECK_FOR_FIELD( if_range, If-Range )
350 RESTINIO_HTTP_CHECK_FOR_FIELD( location, Location )
351 RESTINIO_HTTP_CHECK_FOR_FIELD( pep_info, Pep-Info )
352 RESTINIO_HTTP_CHECK_FOR_FIELD( position, Position )
353 RESTINIO_HTTP_CHECK_FOR_FIELD( protocol, Protocol )
354 RESTINIO_HTTP_CHECK_FOR_FIELD( optional, Optional )
355 RESTINIO_HTTP_CHECK_FOR_FIELD( ua_color, UA-Color )
356 RESTINIO_HTTP_CHECK_FOR_FIELD( ua_media, UA-Media )
357 break;
358
359 case 9:
360 RESTINIO_HTTP_CHECK_FOR_FIELD( forwarded, Forwarded )
361 RESTINIO_HTTP_CHECK_FOR_FIELD( negotiate, Negotiate )
362 RESTINIO_HTTP_CHECK_FOR_FIELD( overwrite, Overwrite )
363 RESTINIO_HTTP_CHECK_FOR_FIELD( ua_pixels, UA-Pixels )
364 break;
365
366 case 10:
367 RESTINIO_HTTP_CHECK_FOR_FIELD( alternates, Alternates )
368 RESTINIO_HTTP_CHECK_FOR_FIELD( c_pep_info, C-PEP-Info )
369 RESTINIO_HTTP_CHECK_FOR_FIELD( content_id, Content-ID )
370 RESTINIO_HTTP_CHECK_FOR_FIELD( delta_base, Delta-Base )
371 RESTINIO_HTTP_CHECK_FOR_FIELD( getprofile, GetProfile )
372 RESTINIO_HTTP_CHECK_FOR_FIELD( keep_alive, Keep-Alive )
373 RESTINIO_HTTP_CHECK_FOR_FIELD( lock_token, Lock-Token )
374 RESTINIO_HTTP_CHECK_FOR_FIELD( pics_label, PICS-Label )
375 RESTINIO_HTTP_CHECK_FOR_FIELD( set_cookie, Set-Cookie )
376 RESTINIO_HTTP_CHECK_FOR_FIELD( setprofile, SetProfile )
377 RESTINIO_HTTP_CHECK_FOR_FIELD( soapaction, SoapAction )
378 RESTINIO_HTTP_CHECK_FOR_FIELD( status_uri, Status-URI )
379 RESTINIO_HTTP_CHECK_FOR_FIELD( user_agent, User-Agent )
380 RESTINIO_HTTP_CHECK_FOR_FIELD( compliance, Compliance )
381 RESTINIO_HTTP_CHECK_FOR_FIELD( message_id, Message-ID )
382 break;
383
384 case 11:
385 RESTINIO_HTTP_CHECK_FOR_FIELD( accept_post, Accept-Post )
386 RESTINIO_HTTP_CHECK_FOR_FIELD( content_md5, Content-MD5 )
387 RESTINIO_HTTP_CHECK_FOR_FIELD( destination, Destination )
388 RESTINIO_HTTP_CHECK_FOR_FIELD( retry_after, Retry-After )
389 RESTINIO_HTTP_CHECK_FOR_FIELD( set_cookie2, Set-Cookie2 )
390 RESTINIO_HTTP_CHECK_FOR_FIELD( want_digest, Want-Digest )
391 break;
392
393 case 12:
394 // Known to be more used first:
395 RESTINIO_HTTP_CHECK_FOR_FIELD( content_type, Content-Type )
396
397 RESTINIO_HTTP_CHECK_FOR_FIELD( accept_patch, Accept-Patch )
398 RESTINIO_HTTP_CHECK_FOR_FIELD( content_base, Content-Base )
399 RESTINIO_HTTP_CHECK_FOR_FIELD( derived_from, Derived-From )
400 RESTINIO_HTTP_CHECK_FOR_FIELD( max_forwards, Max-Forwards )
401 RESTINIO_HTTP_CHECK_FOR_FIELD( mime_version, MIME-Version )
402 RESTINIO_HTTP_CHECK_FOR_FIELD( schedule_tag, Schedule-Tag )
403 RESTINIO_HTTP_CHECK_FOR_FIELD( redirect_ref, Redirect-Ref )
404 RESTINIO_HTTP_CHECK_FOR_FIELD( variant_vary, Variant-Vary )
405 RESTINIO_HTTP_CHECK_FOR_FIELD( method_check, Method-Check )
406 RESTINIO_HTTP_CHECK_FOR_FIELD( referer_root, Referer-Root )
407 break;
408
409 case 13:
410 RESTINIO_HTTP_CHECK_FOR_FIELD( accept_ranges, Accept-Ranges )
411 RESTINIO_HTTP_CHECK_FOR_FIELD( authorization, Authorization )
412 RESTINIO_HTTP_CHECK_FOR_FIELD( cache_control, Cache-Control )
413 RESTINIO_HTTP_CHECK_FOR_FIELD( content_range, Content-Range )
414 RESTINIO_HTTP_CHECK_FOR_FIELD( default_style, Default-Style )
415 RESTINIO_HTTP_CHECK_FOR_FIELD( if_none_match, If-None-Match )
416 RESTINIO_HTTP_CHECK_FOR_FIELD( last_modified, Last-Modified )
417 RESTINIO_HTTP_CHECK_FOR_FIELD( ordering_type, Ordering-Type )
418 RESTINIO_HTTP_CHECK_FOR_FIELD( profileobject, ProfileObject )
419 RESTINIO_HTTP_CHECK_FOR_FIELD( protocol_info, Protocol-Info )
420 RESTINIO_HTTP_CHECK_FOR_FIELD( ua_resolution, UA-Resolution )
421 break;
422
423 case 14:
424 RESTINIO_HTTP_CHECK_FOR_FIELD( accept_charset, Accept-Charset )
425 RESTINIO_HTTP_CHECK_FOR_FIELD( http2_settings, HTTP2-Settings )
426 RESTINIO_HTTP_CHECK_FOR_FIELD( protocol_query, Protocol-Query )
427 RESTINIO_HTTP_CHECK_FOR_FIELD( proxy_features, Proxy-Features )
428 RESTINIO_HTTP_CHECK_FOR_FIELD( schedule_reply, Schedule-Reply )
429 RESTINIO_HTTP_CHECK_FOR_FIELD( non_compliance, Non-Compliance )
430 RESTINIO_HTTP_CHECK_FOR_FIELD( access_control, Access-Control )
431 break;
432
433 case 15:
434 RESTINIO_HTTP_CHECK_FOR_FIELD( accept_encoding, Accept-Encoding )
435 RESTINIO_HTTP_CHECK_FOR_FIELD( accept_features, Accept-Features )
436 RESTINIO_HTTP_CHECK_FOR_FIELD( accept_language, Accept-Language )
437 RESTINIO_HTTP_CHECK_FOR_FIELD( accept_datetime, Accept-Datetime )
438 RESTINIO_HTTP_CHECK_FOR_FIELD( content_version, Content-Version )
439 RESTINIO_HTTP_CHECK_FOR_FIELD( differential_id, Differential-ID )
440 RESTINIO_HTTP_CHECK_FOR_FIELD( public_key_pins, Public-Key-Pins )
441 RESTINIO_HTTP_CHECK_FOR_FIELD( security_scheme, Security-Scheme )
442 RESTINIO_HTTP_CHECK_FOR_FIELD( x_frame_options, X-Frame-Options )
443 RESTINIO_HTTP_CHECK_FOR_FIELD( x_device_accept, X-Device-Accept )
444 RESTINIO_HTTP_CHECK_FOR_FIELD( resolution_hint, Resolution-Hint )
445 RESTINIO_HTTP_CHECK_FOR_FIELD( ediint_features, EDIINT-Features )
446 RESTINIO_HTTP_CHECK_FOR_FIELD( ua_windowpixels, UA-Windowpixels )
447 break;
448
449 case 16:
450 RESTINIO_HTTP_CHECK_FOR_FIELD( accept_additions, Accept-Additions )
451 RESTINIO_HTTP_CHECK_FOR_FIELD( caldav_timezones, CalDAV-Timezones )
452 RESTINIO_HTTP_CHECK_FOR_FIELD( content_encoding, Content-Encoding )
453 RESTINIO_HTTP_CHECK_FOR_FIELD( content_language, Content-Language )
454 RESTINIO_HTTP_CHECK_FOR_FIELD( content_location, Content-Location )
455 RESTINIO_HTTP_CHECK_FOR_FIELD( memento_datetime, Memento-Datetime )
456 RESTINIO_HTTP_CHECK_FOR_FIELD( protocol_request, Protocol-Request )
457 RESTINIO_HTTP_CHECK_FOR_FIELD( www_authenticate, WWW-Authenticate )
458 break;
459
460 case 17:
461 RESTINIO_HTTP_CHECK_FOR_FIELD( if_modified_since, If-Modified-Since )
462 RESTINIO_HTTP_CHECK_FOR_FIELD( proxy_instruction, Proxy-Instruction )
463 RESTINIO_HTTP_CHECK_FOR_FIELD( sec_websocket_key, Sec-WebSocket-Key )
464 RESTINIO_HTTP_CHECK_FOR_FIELD( surrogate_control, Surrogate-Control )
465 RESTINIO_HTTP_CHECK_FOR_FIELD( transfer_encoding, Transfer-Encoding )
466 RESTINIO_HTTP_CHECK_FOR_FIELD( resolver_location, Resolver-Location )
467 break;
468
469 case 18:
470 RESTINIO_HTTP_CHECK_FOR_FIELD( content_style_type, Content-Style-Type )
471 RESTINIO_HTTP_CHECK_FOR_FIELD( preference_applied, Preference-Applied )
472 RESTINIO_HTTP_CHECK_FOR_FIELD( proxy_authenticate, Proxy-Authenticate )
473 break;
474
475 case 19:
476 RESTINIO_HTTP_CHECK_FOR_FIELD( authentication_info, Authentication-Info )
477 RESTINIO_HTTP_CHECK_FOR_FIELD( content_disposition, Content-Disposition )
478 RESTINIO_HTTP_CHECK_FOR_FIELD( content_script_type, Content-Script-Type )
479 RESTINIO_HTTP_CHECK_FOR_FIELD( if_unmodified_since, If-Unmodified-Since )
480 RESTINIO_HTTP_CHECK_FOR_FIELD( proxy_authorization, Proxy-Authorization )
481 RESTINIO_HTTP_CHECK_FOR_FIELD( x_device_user_agent, X-Device-User-Agent )
482 break;
483
484 case 20:
485 RESTINIO_HTTP_CHECK_FOR_FIELD( sec_websocket_accept, Sec-WebSocket-Accept )
486 RESTINIO_HTTP_CHECK_FOR_FIELD( surrogate_capability, Surrogate-Capability )
487 RESTINIO_HTTP_CHECK_FOR_FIELD( method_check_expires, Method-Check-Expires )
488 break;
489
490 case 21:
491 RESTINIO_HTTP_CHECK_FOR_FIELD( apply_to_redirect_ref, Apply-To-Redirect-Ref )
492 RESTINIO_HTTP_CHECK_FOR_FIELD( if_schedule_tag_match, If-Schedule-Tag-Match )
493 RESTINIO_HTTP_CHECK_FOR_FIELD( sec_websocket_version, Sec-WebSocket-Version )
494 break;
495
496 case 22:
497 RESTINIO_HTTP_CHECK_FOR_FIELD( authentication_control, Authentication-Control )
498 RESTINIO_HTTP_CHECK_FOR_FIELD( sec_websocket_protocol, Sec-WebSocket-Protocol )
499 RESTINIO_HTTP_CHECK_FOR_FIELD( access_control_max_age, Access-Control-Max-Age )
500 break;
501
502 case 23:
503 RESTINIO_HTTP_CHECK_FOR_FIELD( x_device_accept_charset, X-Device-Accept-Charset )
504 break;
505
506 case 24:
507 RESTINIO_HTTP_CHECK_FOR_FIELD( sec_websocket_extensions, Sec-WebSocket-Extensions )
508 RESTINIO_HTTP_CHECK_FOR_FIELD( x_device_accept_encoding, X-Device-Accept-Encoding )
509 RESTINIO_HTTP_CHECK_FOR_FIELD( x_device_accept_language, X-Device-Accept-Language )
510 break;
511
512 case 25:
513 RESTINIO_HTTP_CHECK_FOR_FIELD( optional_www_authenticate, Optional-WWW-Authenticate )
514 RESTINIO_HTTP_CHECK_FOR_FIELD( proxy_authentication_info, Proxy-Authentication-Info )
515 RESTINIO_HTTP_CHECK_FOR_FIELD( strict_transport_security, Strict-Transport-Security )
516 RESTINIO_HTTP_CHECK_FOR_FIELD( content_transfer_encoding, Content-Transfer-Encoding )
517 break;
518
519 case 27:
520 RESTINIO_HTTP_CHECK_FOR_FIELD( public_key_pins_report_only, Public-Key-Pins-Report-Only )
521 RESTINIO_HTTP_CHECK_FOR_FIELD( access_control_allow_origin, Access-Control-Allow-Origin )
522 break;
523
524 case 28:
525 RESTINIO_HTTP_CHECK_FOR_FIELD( access_control_allow_headers, Access-Control-Allow-Headers )
526 RESTINIO_HTTP_CHECK_FOR_FIELD( access_control_allow_methods, Access-Control-Allow-Methods )
527 break;
528
529 case 29:
530 RESTINIO_HTTP_CHECK_FOR_FIELD( access_control_request_method, Access-Control-Request-Method )
531 break;
532
533 case 30:
534 RESTINIO_HTTP_CHECK_FOR_FIELD( access_control_request_headers, Access-Control-Request-Headers )
535 break;
536
537 case 32:
538 RESTINIO_HTTP_CHECK_FOR_FIELD( access_control_allow_credentials, Access-Control-Allow-Credentials )
539 break;
540 }
541
542 #undef RESTINIO_HTTP_CHECK_FOR_FIELD
543
544 return http_field_t::field_unspecified;
545 }
546
547 //
548 // field_to_string()
549 //
550
551 //! Helper sunction to get method string name.
552 inline const char *
field_to_string(http_field_t f)553 field_to_string( http_field_t f ) noexcept
554 {
555 const char * result = "";
556 switch( f )
557 {
558 #define RESTINIO_HTTP_FIELD_STR_GEN( name, string_name ) \
559 case http_field_t::name: result = #string_name; break;
560
561 RESTINIO_HTTP_FIELDS_MAP( RESTINIO_HTTP_FIELD_STR_GEN )
562 #undef RESTINIO_HTTP_FIELD_STR_GEN
563
564 case http_field_t::field_unspecified: break; // Ignore.
565 }
566
567 return result;
568 }
569
570 //
571 // http_header_field_t
572 //
573
574 //! A single header field.
575 /*!
576 Fields m_name and m_field_id are kind of having the same meaning,
577 and m_name field seems like can be omitted, but
578 for the cases of custom header fields it is important to
579 rely on the name only. And as the names of almoust all speified fields
580 fits in SSO it doesn't involve much overhead on standard fields.
581 */
582 class http_header_field_t
583 {
584 public:
http_header_field_t()585 http_header_field_t()
586 : m_field_id{ http_field_t::field_unspecified }
587 {}
588
http_header_field_t(std::string name,std::string value)589 http_header_field_t(
590 std::string name,
591 std::string value )
592 : m_name{ std::move( name ) }
593 , m_value{ std::move( value ) }
594 , m_field_id{ string_to_field( m_name ) }
595 {}
596
http_header_field_t(string_view_t name,string_view_t value)597 http_header_field_t(
598 string_view_t name,
599 string_view_t value )
600 : m_name{ name.data(), name.size() }
601 , m_value{ value.data(), value.size() }
602 , m_field_id{ string_to_field( m_name ) }
603 {}
604
http_header_field_t(http_field_t field_id,std::string value)605 http_header_field_t(
606 http_field_t field_id,
607 std::string value )
608 : m_name{ field_to_string( field_id ) }
609 , m_value{ std::move( value ) }
610 , m_field_id{ field_id }
611 {}
612
http_header_field_t(http_field_t field_id,string_view_t value)613 http_header_field_t(
614 http_field_t field_id,
615 string_view_t value )
616 : m_name{ field_to_string( field_id ) }
617 , m_value{ std::move( value ) }
618 , m_field_id{ field_id }
619 {}
620
name() const621 const std::string & name() const noexcept { return m_name; }
value() const622 const std::string & value() const noexcept { return m_value; }
field_id() const623 http_field_t field_id() const noexcept { return m_field_id; }
624
625 void
name(std::string n)626 name( std::string n )
627 {
628 m_name = std::move( n );
629 m_field_id = string_to_field( m_name );
630 }
631
632 void
value(std::string v)633 value( std::string v )
634 {
635 m_value = std::move( v );
636 }
637
638 void
append_value(string_view_t v)639 append_value( string_view_t v )
640 {
641 m_value.append( v.data(), v.size() );
642 }
643
644 void
field_id(http_field_t field_id)645 field_id( http_field_t field_id )
646 {
647 m_field_id = field_id;
648 m_name = field_to_string( m_field_id );
649 }
650
651 private:
652 std::string m_name;
653 std::string m_value;
654 http_field_t m_field_id;
655 };
656
657 // Make neccessary forward declarations.
658 class http_header_fields_t;
659 namespace impl
660 {
661
662 void
663 append_last_field_accessor( http_header_fields_t &, string_view_t );
664
665 } /* namespace impl */
666
667 #if !defined( RESTINIO_HEADER_FIELDS_DEFAULT_RESERVE_COUNT )
668 #define RESTINIO_HEADER_FIELDS_DEFAULT_RESERVE_COUNT 4
669 #endif
670
671 //
672 // http_header_fields_t
673 //
674
675 //! Header fields map.
676 /*!
677 This class holds a collection of header fields.
678
679 There are 2 special cases for fields: `Connection` and `Content-Length`
680 This cases are handled separetely from the rest of the fields.
681 And as the implementation of http_header_fields_t doesn't
682 have checks on each field manipulation checking whether
683 field name is `Connection` or `Content-Length` it is important
684 to use proper member functions in derived classes for manipulating them.
685
686 @par Getting values of fields
687
688 Since v.0.4.9 there are two groups of methods for accessing values of
689 fields. The first group returns `std::string` (or references/pointers
690 to `std::string`). This group includes the following methods: get_field(),
691 get_field_or(), try_get_field().
692
693 The second group returns `string_view_t` or `optional_t<string_view_t>`.
694 This group includes the following methods: value_of() and opt_value_of().
695
696 The first group was created in early versions of RESTinio and is present
697 here for historical and compatibility reasons. They are not deprecated
698 yet but they could be deprecated in newer versions of RESTinio.
699 Because of that the usage of value_of() and opt_value_of() is more
700 preferable.
701 */
702 class http_header_fields_t
703 {
704 friend void
705 impl::append_last_field_accessor( http_header_fields_t &, string_view_t );
706
707 public:
708 using fields_container_t = std::vector< http_header_field_t >;
709
710 //! Type of const_iterator for enumeration of fields.
711 using const_iterator = fields_container_t::const_iterator;
712
713 //! The result of handling yet another field value.
714 /*!
715 * A value of that enumeration should be returned by a lambda-function
716 * passed to for_each_value_of() method.
717 *
718 * @since v.0.6.9
719 */
720 enum class handling_result_t
721 {
722 //! Next value of field should be found and passed to the next
723 //! invocation of handler.
724 continue_enumeration,
725 //! The loop on field values should be stopped.
726 stop_enumeration
727 };
728
continue_enumeration()729 constexpr static handling_result_t continue_enumeration() noexcept
730 { return handling_result_t::continue_enumeration; }
731
stop_enumeration()732 constexpr static handling_result_t stop_enumeration() noexcept
733 { return handling_result_t::stop_enumeration; }
734
http_header_fields_t()735 http_header_fields_t()
736 {
737 m_fields.reserve( RESTINIO_HEADER_FIELDS_DEFAULT_RESERVE_COUNT );
738 }
739 http_header_fields_t(const http_header_fields_t &) = default;
740 http_header_fields_t(http_header_fields_t &&) = default;
~http_header_fields_t()741 virtual ~http_header_fields_t() {}
742
743 http_header_fields_t & operator=(const http_header_fields_t &) = default;
744 http_header_fields_t & operator=(http_header_fields_t &&) = default;
745
746 void
swap_fields(http_header_fields_t & http_header_fields)747 swap_fields( http_header_fields_t & http_header_fields )
748 {
749 std::swap( m_fields, http_header_fields.m_fields );
750 }
751
752 //! Check field by name.
753 bool
has_field(string_view_t field_name) const754 has_field( string_view_t field_name ) const noexcept
755 {
756 return m_fields.cend() != cfind( field_name );
757 }
758
759 //! Check field by field-id.
760 /*!
761 \note If `field_id=http_field_t::field_unspecified`
762 then function returns not more than just a fact
763 whether there is at least one unspecified field.
764 */
765 bool
has_field(http_field_t field_id) const766 has_field( http_field_t field_id ) const noexcept
767 {
768 return m_fields.cend() != cfind( field_id );
769 }
770
771 //! Set header field via http_header_field_t.
772 void
set_field(http_header_field_t http_header_field)773 set_field( http_header_field_t http_header_field )
774 {
775 fields_container_t::iterator it;
776 if( http_field_t::field_unspecified != http_header_field.field_id() )
777 {
778 // Field has a standard name.
779 // Search it by id.
780 it = find( http_header_field.field_id() );
781 }
782 else
783 {
784 // Field has a non standard name.
785 // Search it by name.
786 it = find( http_header_field.name() );
787 }
788
789 if( m_fields.end() != it )
790 {
791 *it = std::move( http_header_field );
792 }
793 else
794 {
795 m_fields.emplace_back( std::move( http_header_field ) );
796 }
797 }
798
799 //! Set field with string pair.
800 void
set_field(std::string field_name,std::string field_value)801 set_field(
802 std::string field_name,
803 std::string field_value )
804 {
805 const auto it = find( field_name );
806
807 if( m_fields.end() != it )
808 {
809 it->name( std::move( field_name ) );
810 it->value( std::move( field_value ) );
811 }
812 else
813 {
814 m_fields.emplace_back(
815 std::move( field_name ),
816 std::move( field_value ) );
817 }
818 }
819
820 //! Set field with id-value pair.
821 /*!
822 If `field_id=http_field_t::field_unspecified`
823 then function does nothing.
824 */
825 void
set_field(http_field_t field_id,std::string field_value)826 set_field(
827 http_field_t field_id,
828 std::string field_value )
829 {
830 if( http_field_t::field_unspecified != field_id )
831 {
832 const auto it = find( field_id );
833
834 if( m_fields.end() != it )
835 {
836 it->value( std::move( field_value ) );
837 }
838 else
839 {
840 m_fields.emplace_back(
841 field_id,
842 std::move( field_value ) );
843 }
844 }
845 }
846
847 /*!
848 * @brief Add a field in the form of id-value pair.
849 *
850 * If `field_id=http_field_t::field_unspecified` then function
851 * does nothing.
852 *
853 * @note
854 * This method doesn't check the presence of the field.
855 * So it can be used for storing of several values of HTTP-field.
856 *
857 * @since v.0.6.9
858 */
859 void
add_field(http_field_t field_id,std::string field_value)860 add_field(
861 http_field_t field_id,
862 std::string field_value )
863 {
864 if( http_field_t::field_unspecified != field_id )
865 {
866 m_fields.emplace_back(
867 field_id,
868 std::move( field_value ) );
869 }
870 }
871
872 /*!
873 * @brief Add a field in the form of name-value pair.
874 *
875 * @note
876 * This method doesn't check the presence of the field.
877 * So it can be used for storing of several values of HTTP-field.
878 *
879 * @since v.0.6.9
880 */
881 void
add_field(std::string field_name,std::string field_value)882 add_field(
883 std::string field_name,
884 std::string field_value )
885 {
886 m_fields.emplace_back(
887 std::move( field_name ),
888 std::move( field_value ) );
889 }
890
891 /*!
892 * @brief Add a field in the form of http_header_field object.
893 *
894 * @note
895 * This method doesn't check the presence of the field.
896 * So it can be used for storing of several values of HTTP-field.
897 *
898 * @since v.0.6.9
899 */
900 void
add_field(http_header_field_t http_header_field)901 add_field( http_header_field_t http_header_field )
902 {
903 m_fields.push_back( std::move(http_header_field) );
904 }
905
906 //! Append field with name.
907 void
append_field(string_view_t field_name,string_view_t field_value)908 append_field(
909 string_view_t field_name,
910 string_view_t field_value )
911 {
912 const auto it = find( field_name );
913
914 if( m_fields.end() != it )
915 {
916 it->append_value( field_value );
917 }
918 else
919 {
920 m_fields.emplace_back( field_name, field_value );
921 }
922 }
923
924 //! Append field with id.
925 /*!
926 If `field_id=http_field_t::field_unspecified`
927 then function does nothing.
928 */
929 void
append_field(http_field_t field_id,string_view_t field_value)930 append_field(
931 http_field_t field_id,
932 string_view_t field_value )
933 {
934 if( http_field_t::field_unspecified != field_id )
935 {
936 const auto it = find( field_id );
937
938 if( m_fields.end() != it )
939 {
940 it->append_value( field_value );
941 }
942 else
943 {
944 m_fields.emplace_back( field_id, field_value );
945 }
946 }
947 }
948
949 //! Get field by name.
950 const std::string &
get_field(string_view_t field_name) const951 get_field( string_view_t field_name ) const
952 {
953 const auto it = cfind( field_name );
954
955 if( m_fields.end() == it )
956 throw exception_t{
957 fmt::format( "field '{}' doesn't exist", field_name ) };
958
959 return it->value();
960 }
961
962 //! Try to get the value of a field by field name.
963 /*!
964 @note
965 Returns nullptr if the field is not found.
966
967 Usage example:
968 \code
969 auto f = headers().try_get_field("Content-Type");
970 if(f && *f == "text/plain")
971 ...
972 \endcode
973 */
974 nullable_pointer_t<const std::string>
try_get_field(string_view_t field_name) const975 try_get_field( string_view_t field_name ) const noexcept
976 {
977 const auto it = cfind( field_name );
978 if( m_fields.end() == it )
979 return nullptr;
980 else
981 return std::addressof(it->value());
982 }
983
984 //! Get field by id.
985 const std::string &
get_field(http_field_t field_id) const986 get_field( http_field_t field_id ) const
987 {
988 if( http_field_t::field_unspecified == field_id )
989 {
990 throw exception_t{
991 fmt::format(
992 "unspecified fields cannot be searched by id" ) };
993 }
994
995 const auto it = cfind( field_id );
996
997 if( m_fields.end() == it )
998 {
999 throw exception_t{
1000 fmt::format(
1001 "field '{}' doesn't exist",
1002 field_to_string( field_id ) ) };
1003 }
1004
1005 return it->value();
1006 }
1007
1008 //! Try to get the value of a field by field ID.
1009 /*!
1010 @note
1011 Returns nullptr if the field is not found.
1012
1013 Usage example:
1014 \code
1015 auto f = headers().try_get_field(restinio::http_field::content_type);
1016 if(f && *f == "text/plain")
1017 ...
1018 \endcode
1019 */
1020 nullable_pointer_t<const std::string>
try_get_field(http_field_t field_id) const1021 try_get_field( http_field_t field_id ) const noexcept
1022 {
1023 if( http_field_t::field_unspecified != field_id )
1024 {
1025 const auto it = cfind( field_id );
1026 if( m_fields.end() != it )
1027 return std::addressof(it->value());
1028 }
1029
1030 return nullptr;
1031 }
1032
1033 //! Get field value by field name or default value if the field not found.
1034 /*!
1035 @note
1036 This method returns field value as a new std::string instance,
1037 not a const reference to std::string.
1038 */
1039 std::string
get_field_or(string_view_t field_name,string_view_t default_value) const1040 get_field_or(
1041 string_view_t field_name,
1042 string_view_t default_value ) const
1043 {
1044 const auto it = cfind( field_name );
1045
1046 if( m_fields.end() == it )
1047 return std::string( default_value.data(), default_value.size() );
1048
1049 return it->value();
1050 }
1051
1052 //! Get field value by field name or default value if the field not found.
1053 /*!
1054 @note
1055 This method returns field value as a new std::string instance,
1056 not a const reference to std::string.
1057 */
1058 std::string
get_field_or(string_view_t field_name,std::string && default_value) const1059 get_field_or(
1060 string_view_t field_name,
1061 std::string && default_value ) const
1062 {
1063 const auto it = cfind( field_name );
1064
1065 if( m_fields.end() == it )
1066 return std::move(default_value);
1067
1068 return it->value();
1069 }
1070
1071 //! Get field by name or default value if the field not found.
1072 /*!
1073 This is just overload for get_field_or(string_view_t,string_view_t);
1074 */
1075 auto
get_field_or(string_view_t field_name,const char * default_value) const1076 get_field_or(
1077 string_view_t field_name,
1078 const char * default_value ) const
1079 {
1080 return this->get_field_or( field_name, string_view_t{ default_value } );
1081 }
1082
1083 //! Get field by name or default value if the field not found.
1084 /*!
1085 This is just overload for get_field_or(string_view_t,string_view_t);
1086 */
1087 auto
get_field_or(string_view_t field_name,const std::string & default_value) const1088 get_field_or(
1089 string_view_t field_name,
1090 const std::string & default_value ) const
1091 {
1092 return this->get_field_or( field_name, string_view_t{ default_value } );
1093 }
1094
1095 //! Get field by id or default value if the field not found.
1096 /*!
1097 @note
1098 This method returns field value as a new std::string instance,
1099 not a const reference to std::string.
1100 */
1101 std::string
get_field_or(http_field_t field_id,string_view_t default_value) const1102 get_field_or(
1103 http_field_t field_id,
1104 string_view_t default_value ) const
1105 {
1106 if( http_field_t::field_unspecified != field_id )
1107 {
1108 const auto it = cfind( field_id );
1109
1110 if( m_fields.end() != it )
1111 return it->value();
1112 }
1113
1114 return std::string( default_value.data(), default_value.size() );
1115 }
1116
1117 //! Get field by id or default value if the field not found.
1118 /*!
1119 This is just overload for get_field_or(http_field_t,string_view_t);
1120 */
1121 auto
get_field_or(http_field_t field_id,const char * default_value) const1122 get_field_or(
1123 http_field_t field_id,
1124 const char * default_value ) const
1125 {
1126 return this->get_field_or( field_id, string_view_t{ default_value } );
1127 }
1128
1129 //! Get field by id or default value if the field not found.
1130 /*!
1131 This is just overload for get_field_or(http_field_t,string_view_t);
1132 */
1133 auto
get_field_or(http_field_t field_id,const std::string & default_value) const1134 get_field_or(
1135 http_field_t field_id,
1136 const std::string & default_value ) const
1137 {
1138 return this->get_field_or( field_id, string_view_t{ default_value } );
1139 }
1140
1141 //! Get field by id or default value if the field not found.
1142 /*!
1143 @note
1144 This method returns field value as a new std::string instance,
1145 not a const reference to std::string.
1146 */
1147 std::string
get_field_or(http_field_t field_id,std::string && default_value) const1148 get_field_or(
1149 http_field_t field_id,
1150 std::string && default_value ) const
1151 {
1152 if( http_field_t::field_unspecified != field_id )
1153 {
1154 const auto it = cfind( field_id );
1155
1156 if( m_fields.end() != it )
1157 return it->value();
1158 }
1159
1160 return std::move( default_value );
1161 }
1162
1163 //! Remove field by name.
1164 /*!
1165 * If there are several occurences of @a field_name only the first
1166 * one will be removed.
1167 *
1168 * @note
1169 * Since v.0.6.9 returns `true` if an occurence of a field
1170 * with name @a field_name has been removed. The value `false`
1171 * returned if there is no field with name @a field_name.
1172 */
1173 bool
remove_field(string_view_t field_name)1174 remove_field( string_view_t field_name ) noexcept
1175 {
1176 const auto it = find( field_name );
1177
1178 if( m_fields.end() != it )
1179 {
1180 m_fields.erase( it );
1181 return true;
1182 }
1183
1184 return false;
1185 }
1186
1187 //! Remove field by id.
1188 /*!
1189 * If there are several occurences of @a field_id only the first
1190 * one will be removed.
1191 *
1192 * @note
1193 * Since v.0.6.9 returns `true` if an occurence of a field
1194 * with id @a field_id has been removed. The value `false`
1195 * returned if there is no field with id @a field_id.
1196 */
1197 bool
remove_field(http_field_t field_id)1198 remove_field( http_field_t field_id ) noexcept
1199 {
1200 if( http_field_t::field_unspecified != field_id )
1201 {
1202 const auto it = find( field_id );
1203
1204 if( m_fields.end() != it )
1205 {
1206 m_fields.erase( it );
1207 return true;
1208 }
1209 }
1210
1211 return false;
1212 }
1213
1214 //! Remove all occurences of a field with specified name.
1215 /*!
1216 * @return the count of removed occurences.
1217 *
1218 * @since v.0.6.9
1219 */
1220 std::size_t
remove_all_of(string_view_t field_name)1221 remove_all_of( string_view_t field_name ) noexcept
1222 {
1223 std::size_t count{};
1224 for( auto it = m_fields.begin(); it != m_fields.end(); )
1225 {
1226 if( impl::is_equal_caseless( it->name(), field_name ) )
1227 {
1228 it = m_fields.erase( it );
1229 ++count;
1230 }
1231 else
1232 ++it;
1233 }
1234
1235 return count;
1236 }
1237
1238 //! Remove all occurences of a field with specified id.
1239 /*!
1240 * @return the count of removed occurences.
1241 *
1242 * @since v.0.6.9
1243 */
1244 std::size_t
remove_all_of(http_field_t field_id)1245 remove_all_of( http_field_t field_id ) noexcept
1246 {
1247 std::size_t count{};
1248 if( http_field_t::field_unspecified != field_id )
1249 {
1250 for( auto it = m_fields.begin(); it != m_fields.end(); )
1251 {
1252 if( it->field_id() == field_id )
1253 {
1254 it = m_fields.erase( it );
1255 ++count;
1256 }
1257 else
1258 ++it;
1259 }
1260 }
1261
1262 return count;
1263 }
1264
1265 /*!
1266 * @name Getters of field value which return string_view.
1267 * @{
1268 */
1269 //! Get the value of a field or throw if the field not found.
1270 string_view_t
value_of(string_view_t name) const1271 value_of(
1272 //! Name of a field.
1273 string_view_t name ) const
1274 {
1275 return { this->get_field(name) };
1276 }
1277
1278 //! Get the value of a field or throw if the field not found.
1279 string_view_t
value_of(http_field_t field_id) const1280 value_of(
1281 //! ID of a field.
1282 http_field_t field_id ) const
1283 {
1284 return { this->get_field(field_id) };
1285 }
1286
1287 //! Get optional value of a field.
1288 /*!
1289 Doesn't throw exception if the field is not found. Empty optional
1290 will be returned instead.
1291
1292 Usage example:
1293 \code
1294 auto f = headers().opt_value_of("Content-Type");
1295 if(f && *f == "text/plain")
1296 ...
1297 \endcode
1298 */
1299 optional_t< string_view_t >
opt_value_of(string_view_t name) const1300 opt_value_of(
1301 //! Name of a field.
1302 string_view_t name ) const noexcept
1303 {
1304 optional_t< string_view_t > result;
1305
1306 if( auto * ptr = this->try_get_field(name) )
1307 result = string_view_t{ *ptr };
1308
1309 return result;
1310 }
1311
1312 //! Get optional value of a field.
1313 /*!
1314 Doesn't throw exception if the field is not found. Empty optional
1315 will be returned instead.
1316
1317 Usage example:
1318 \code
1319 auto f = headers().opt_value_of(restinio::http_field::content_type);
1320 if(f && *f == "text/plain")
1321 ...
1322 \endcode
1323 */
1324 optional_t< string_view_t >
opt_value_of(http_field_t field_id) const1325 opt_value_of(
1326 //! ID of a field.
1327 http_field_t field_id ) const noexcept
1328 {
1329 optional_t< string_view_t > result;
1330
1331 if( auto * ptr = this->try_get_field(field_id) )
1332 result = string_view_t{ *ptr };
1333
1334 return result;
1335 }
1336 /*!
1337 * @}
1338 */
1339
1340 //! Enumeration of fields.
1341 /*!
1342 Calls \a lambda for each field in the container.
1343
1344 Lambda should have one of the following formats:
1345 \code
1346 void(const http_header_field_t &);
1347 void(http_header_field_t);
1348 \endcode
1349
1350 This method is `noexcept` if \a lambda is `noexcept`.
1351
1352 Usage example:
1353 \code
1354 headers().for_each_field( [](const auto & f) {
1355 std::cout << f.name() << ": " << f.value() << std::endl;
1356 } );
1357 \endcode
1358 */
1359 template< typename Lambda >
1360 void
for_each_field(Lambda && lambda) const1361 for_each_field( Lambda && lambda ) const
1362 noexcept(noexcept(lambda(
1363 std::declval<const http_header_field_t &>())))
1364 {
1365 for( const auto & f : m_fields )
1366 lambda( f );
1367 }
1368
1369 //! Enumeration of each value of a field.
1370 /*!
1371 * Calls @a lambda for each value of a field @a field_id.
1372 *
1373 * Lambda should has one of the following formats:
1374 * @code
1375 * restinio::http_header_fields_t::handling_result_t
1376 * (const restinio::string_view_t &);
1377 *
1378 * restinio::http_header_fields_t::handling_result_t
1379 * (restinio::string_view_t);
1380 * @endcode
1381 *
1382 * @note
1383 * The @a lambda can throw.
1384 *
1385 * @attention
1386 * The content of this http_header_fields_t shouldn't be changed
1387 * during the enumeration (it means that fields can't be removed and
1388 * new fields can't be added).
1389 *
1390 * Usage example:
1391 * @code
1392 * headers().for_each_value_of(restinio::http_field_t::transfer_encoding,
1393 * [](auto value) {
1394 * std::cout << "encoding: " << value << std::endl;
1395 * return restinio::http_header_fields_t::continue_enumeration();
1396 * } );
1397 * @endcode
1398 */
1399 template< typename Lambda >
1400 void
for_each_value_of(http_field_t field_id,Lambda && lambda) const1401 for_each_value_of(
1402 http_field_t field_id,
1403 Lambda && lambda ) const
1404 noexcept(noexcept(lambda(
1405 std::declval<const string_view_t &>())))
1406 {
1407 static_assert(
1408 std::is_same<
1409 handling_result_t,
1410 decltype(lambda(std::declval<const string_view_t &>()))
1411 >::value,
1412 "lambda should return restinio::http_header_fields_t::handling_result_t" );
1413
1414 for( const auto & f : m_fields )
1415 {
1416 if( field_id == f.field_id() )
1417 {
1418 const handling_result_t r = lambda( f.value() );
1419 if( stop_enumeration() == r )
1420 break;
1421 }
1422 }
1423 }
1424
1425 //! Enumeration of each value of a field.
1426 /*!
1427 * Calls @a lambda for each value of a field @a field_name.
1428 *
1429 * Lambda should has one of the following formats:
1430 * @code
1431 * restinio::http_header_fields_t::handling_result_t
1432 * (const restinio::string_view_t &);
1433 *
1434 * restinio::http_header_fields_t::handling_result_t
1435 * (restinio::string_view_t);
1436 * @endcode
1437 *
1438 * @note
1439 * The @a lambda can throw.
1440 *
1441 * @attention
1442 * The content of this http_header_fields_t shouldn't be changed
1443 * during the enumeration (it means that fields can't be removed and
1444 * new fields can't be added).
1445 *
1446 * Usage example:
1447 * @code
1448 * headers().for_each_value_of("Transfer-Encoding",
1449 * [](auto value) {
1450 * std::cout << "encoding: " << value << std::endl;
1451 * return restinio::http_header_fields_t::continue_enumeration();
1452 * } );
1453 * @endcode
1454 */
1455 template< typename Lambda >
1456 void
for_each_value_of(string_view_t field_name,Lambda && lambda) const1457 for_each_value_of(
1458 string_view_t field_name,
1459 Lambda && lambda ) const
1460 noexcept(noexcept(lambda(
1461 std::declval<const string_view_t &>())))
1462 {
1463 static_assert(
1464 std::is_same<
1465 handling_result_t,
1466 decltype(lambda(std::declval<const string_view_t &>()))
1467 >::value,
1468 "lambda should return restinio::http_header_fields_t::handling_result_t" );
1469
1470 for( const auto & f : m_fields )
1471 {
1472 if( impl::is_equal_caseless( f.name(), field_name ) )
1473 {
1474 const handling_result_t r = lambda( f.value() );
1475 if( stop_enumeration() == r )
1476 break;
1477 }
1478 }
1479 }
1480
1481 const_iterator
begin() const1482 begin() const noexcept
1483 {
1484 return m_fields.cbegin();
1485 }
1486
1487 const_iterator
end() const1488 end() const noexcept
1489 {
1490 return m_fields.cend();
1491 }
1492
fields_count() const1493 auto fields_count() const noexcept
1494 {
1495 return m_fields.size();
1496 }
1497
1498 private:
1499 //! Appends last added field.
1500 /*!
1501 This is function is used by http-parser when
1502 field value is created by 2 separate
1503 invocation of on-header-field-value callback
1504
1505 Function doesn't check if at least one field exists,
1506 so it is not in the public interface.
1507 */
1508 void
append_last_field(string_view_t field_value)1509 append_last_field( string_view_t field_value )
1510 {
1511 m_fields.back().append_value( field_value );
1512 }
1513
1514 fields_container_t::iterator
find(string_view_t field_name)1515 find( string_view_t field_name ) noexcept
1516 {
1517 return std::find_if(
1518 m_fields.begin(),
1519 m_fields.end(),
1520 [&]( const auto & f ){
1521 return impl::is_equal_caseless( f.name(), field_name );
1522 } );
1523 }
1524
1525 fields_container_t::const_iterator
cfind(string_view_t field_name) const1526 cfind( string_view_t field_name ) const noexcept
1527 {
1528 return std::find_if(
1529 m_fields.cbegin(),
1530 m_fields.cend(),
1531 [&]( const auto & f ){
1532 return impl::is_equal_caseless( f.name(), field_name );
1533 } );
1534 }
1535
1536 fields_container_t::iterator
find(http_field_t field_id)1537 find( http_field_t field_id ) noexcept
1538 {
1539 return std::find_if(
1540 m_fields.begin(),
1541 m_fields.end(),
1542 [&]( const auto & f ){
1543 return f.field_id() == field_id;
1544 } );
1545 }
1546
1547 fields_container_t::const_iterator
cfind(http_field_t field_id) const1548 cfind( http_field_t field_id ) const noexcept
1549 {
1550 return std::find_if(
1551 m_fields.cbegin(),
1552 m_fields.cend(),
1553 [&]( const auto & f ){
1554 return f.field_id() == field_id;
1555 } );
1556 }
1557
1558 fields_container_t m_fields;
1559 };
1560
1561 //
1562 // http_connection_header_t
1563 //
1564
1565 //! Values for conection header field.
1566 enum class http_connection_header_t : std::uint8_t
1567 {
1568 keep_alive,
1569 close,
1570 upgrade
1571 };
1572
1573 //
1574 // http_header_common_t
1575 //
1576
1577 //! Req/Resp headers common data.
1578 struct http_header_common_t
1579 : public http_header_fields_t
1580 {
1581 public:
1582 //! Http version.
1583 //! \{
1584 std::uint16_t
http_majorrestinio::http_header_common_t1585 http_major() const noexcept
1586 { return m_http_major; }
1587
1588 void
http_majorrestinio::http_header_common_t1589 http_major( std::uint16_t v ) noexcept
1590 { m_http_major = v; }
1591
1592 std::uint16_t
http_minorrestinio::http_header_common_t1593 http_minor() const noexcept
1594 { return m_http_minor; }
1595
1596 void
http_minorrestinio::http_header_common_t1597 http_minor( std::uint16_t v ) noexcept
1598 { m_http_minor = v; }
1599 //! \}
1600
1601 //! Length of body of an http-message.
1602 std::uint64_t
content_lengthrestinio::http_header_common_t1603 content_length() const noexcept
1604 { return m_content_length; }
1605
1606 void
content_lengthrestinio::http_header_common_t1607 content_length( std::uint64_t l ) noexcept
1608 { m_content_length = l; }
1609
1610 bool
should_keep_aliverestinio::http_header_common_t1611 should_keep_alive() const noexcept
1612 {
1613 return http_connection_header_t::keep_alive == m_http_connection_header_field_value;
1614 }
1615
1616 void
should_keep_aliverestinio::http_header_common_t1617 should_keep_alive( bool keep_alive ) noexcept
1618 {
1619 connection( keep_alive?
1620 http_connection_header_t::keep_alive :
1621 http_connection_header_t::close );
1622 }
1623
1624 //! Get the value of 'connection' header field.
1625 http_connection_header_t
connectionrestinio::http_header_common_t1626 connection() const
1627 {
1628 return m_http_connection_header_field_value;
1629 }
1630
1631 //! Set the value of 'connection' header field.
1632 void
connectionrestinio::http_header_common_t1633 connection( http_connection_header_t ch ) noexcept
1634 {
1635 m_http_connection_header_field_value = ch;
1636 }
1637
1638 private:
1639 //! Http version.
1640 //! \{
1641 std::uint16_t m_http_major{1};
1642 std::uint16_t m_http_minor{1};
1643 //! \}
1644
1645 //! Length of body of an http-message.
1646 std::uint64_t m_content_length{ 0 };
1647
1648 http_connection_header_t m_http_connection_header_field_value{ http_connection_header_t::close };
1649 };
1650
1651 //! HTTP methods mapping with nodejs http methods
1652 #define RESTINIO_HTTP_METHOD_MAP(RESTINIO_GEN) \
1653 RESTINIO_GEN( http_method_delete, HTTP_DELETE, DELETE ) \
1654 RESTINIO_GEN( http_method_get, HTTP_GET, GET ) \
1655 RESTINIO_GEN( http_method_head, HTTP_HEAD, HEAD ) \
1656 RESTINIO_GEN( http_method_post, HTTP_POST, POST ) \
1657 RESTINIO_GEN( http_method_put, HTTP_PUT, PUT ) \
1658 /* pathological */ \
1659 RESTINIO_GEN( http_method_connect, HTTP_CONNECT, CONNECT ) \
1660 RESTINIO_GEN( http_method_options, HTTP_OPTIONS, OPTIONS ) \
1661 RESTINIO_GEN( http_method_trace, HTTP_TRACE, TRACE ) \
1662 /* WebDAV */ \
1663 RESTINIO_GEN( http_method_copy, HTTP_COPY, COPY ) \
1664 RESTINIO_GEN( http_method_lock, HTTP_LOCK, LOCK ) \
1665 RESTINIO_GEN( http_method_mkcol, HTTP_MKCOL, MKCOL ) \
1666 RESTINIO_GEN( http_method_move, HTTP_MOVE, MOVE ) \
1667 RESTINIO_GEN( http_method_propfind, HTTP_PROPFIND, PROPFIND ) \
1668 RESTINIO_GEN( http_method_proppatch, HTTP_PROPPATCH, PROPPATCH ) \
1669 RESTINIO_GEN( http_method_search, HTTP_SEARCH, SEARCH ) \
1670 RESTINIO_GEN( http_method_unlock, HTTP_UNLOCK, UNLOCK ) \
1671 RESTINIO_GEN( http_method_bind, HTTP_BIND, BIND ) \
1672 RESTINIO_GEN( http_method_rebind, HTTP_REBIND, REBIND ) \
1673 RESTINIO_GEN( http_method_unbind, HTTP_UNBIND, UNBIND ) \
1674 RESTINIO_GEN( http_method_acl, HTTP_ACL, ACL ) \
1675 /* subversion */ \
1676 RESTINIO_GEN( http_method_report, HTTP_REPORT, REPORT ) \
1677 RESTINIO_GEN( http_method_mkactivity, HTTP_MKACTIVITY, MKACTIVITY ) \
1678 RESTINIO_GEN( http_method_checkout, HTTP_CHECKOUT, CHECKOUT ) \
1679 RESTINIO_GEN( http_method_merge, HTTP_MERGE, MERGE ) \
1680 /* upnp */ \
1681 RESTINIO_GEN( http_method_msearch, HTTP_MSEARCH, M-SEARCH) \
1682 RESTINIO_GEN( http_method_notify, HTTP_NOTIFY, NOTIFY ) \
1683 RESTINIO_GEN( http_method_subscribe, HTTP_SUBSCRIBE, SUBSCRIBE ) \
1684 RESTINIO_GEN( http_method_unsubscribe, HTTP_UNSUBSCRIBE, UNSUBSCRIBE ) \
1685 /* RFC-5789 */ \
1686 RESTINIO_GEN( http_method_patch, HTTP_PATCH, PATCH ) \
1687 RESTINIO_GEN( http_method_purge, HTTP_PURGE, PURGE ) \
1688 /* CalDAV */ \
1689 RESTINIO_GEN( http_method_mkcalendar, HTTP_MKCALENDAR, MKCALENDAR ) \
1690 /* RFC-2068, section 19.6.1.2 */ \
1691 RESTINIO_GEN( http_method_link, HTTP_LINK, LINK ) \
1692 RESTINIO_GEN( http_method_unlink, HTTP_UNLINK, UNLINK )
1693
1694 //
1695 // http_method_id_t
1696 //
1697 /*!
1698 * @brief A type for representation of HTTP method ID.
1699 *
1700 * RESTinio uses http_parser for working with HTTP-protocol.
1701 * HTTP-methods in http_parser are identified by `int`s like
1702 * HTTP_GET, HTTP_POST and so on.
1703 *
1704 * Usage of plain `int` is error prone. So since v.0.5.0 RESTinio contain
1705 * type http_method_id_t as type for ID of HTTP method.
1706 *
1707 * An instance of http_method_id_t contains two values:
1708 * * integer identifier from http_parser (like HTTP_GET, HTTP_POST and so on);
1709 * * a string representation of HTTP method ID (like "GET", "POST", "DELETE"
1710 * and so on).
1711 *
1712 * There is an important requirement for user-defined HTTP method IDs:
1713 * a pointer to string representation of HTTP method ID must outlive
1714 * the instance of http_method_id_t. It means that is safe to use string
1715 * literals or static strings, for example:
1716 * @code
1717 * constexpr const restinio::http_method_id_t my_http_method(255, "MY-METHOD");
1718 * @endcode
1719 *
1720 * @note
1721 * Instances of http_method_id_t can't be used in switch() operator.
1722 * For example, you can't write that way:
1723 * @code
1724 * const int method_id = ...;
1725 * switch(method_id) {
1726 * case restinio::http_method_get(): ...; break;
1727 * case restinio::http_method_post(): ...; break;
1728 * case restinio::http_method_delete(): ...; break;
1729 * }
1730 * @endcode
1731 * In that case raw_id() method can be used:
1732 * @code
1733 * const int method_id = ...;
1734 * switch(method_id) {
1735 * case restinio::http_method_get().raw_id(): ...; break;
1736 * case restinio::http_method_post().raw_id(): ...; break;
1737 * case restinio::http_method_delete().raw_id(): ...; break;
1738 * }
1739 * @endcode
1740 *
1741 * @since v.0.5.0
1742 */
1743 class http_method_id_t
1744 {
1745 int m_value;
1746 const char * m_name;
1747
1748 public:
1749 static constexpr const int unknown_method = -1;
1750
http_method_id_t()1751 constexpr http_method_id_t() noexcept
1752 : m_value{ unknown_method }
1753 , m_name{ "<undefined>" }
1754 {}
http_method_id_t(int value,const char * name)1755 constexpr http_method_id_t(
1756 int value,
1757 const char * name ) noexcept
1758 : m_value{ value }
1759 , m_name{ name }
1760 {}
1761
1762 constexpr http_method_id_t( const http_method_id_t & ) noexcept = default;
1763 constexpr http_method_id_t &
1764 operator=( const http_method_id_t & ) noexcept = default;
1765
1766 constexpr http_method_id_t( http_method_id_t && ) noexcept = default;
1767 constexpr http_method_id_t &
1768 operator=( http_method_id_t && ) noexcept = default;
1769
1770 constexpr auto
raw_id() const1771 raw_id() const noexcept { return m_value; }
1772
1773 constexpr const char *
c_str() const1774 c_str() const noexcept { return m_name; }
1775
1776 friend constexpr bool
operator ==(const http_method_id_t & a,const http_method_id_t & b)1777 operator==( const http_method_id_t & a, const http_method_id_t & b ) noexcept {
1778 return a.raw_id() == b.raw_id();
1779 }
1780
1781 friend constexpr bool
operator !=(const http_method_id_t & a,const http_method_id_t & b)1782 operator!=( const http_method_id_t & a, const http_method_id_t & b ) noexcept {
1783 return a.raw_id() != b.raw_id();
1784 }
1785
1786 friend constexpr bool
operator <(const http_method_id_t & a,const http_method_id_t & b)1787 operator<( const http_method_id_t & a, const http_method_id_t & b ) noexcept {
1788 return a.raw_id() < b.raw_id();
1789 }
1790 };
1791
1792 inline std::ostream &
operator <<(std::ostream & to,const http_method_id_t & m)1793 operator<<( std::ostream & to, const http_method_id_t & m )
1794 {
1795 return to << m.c_str();
1796 }
1797
1798 // Generate helper funcs.
1799 #define RESTINIO_HTTP_METHOD_FUNC_GEN( func_name, nodejs_code, method_name ) \
1800 inline constexpr http_method_id_t func_name() { \
1801 return { nodejs_code, #method_name }; \
1802 }
1803
RESTINIO_HTTP_METHOD_MAP(RESTINIO_HTTP_METHOD_FUNC_GEN)1804 RESTINIO_HTTP_METHOD_MAP( RESTINIO_HTTP_METHOD_FUNC_GEN )
1805 #undef RESTINIO_HTTP_METHOD_FUNC_GEN
1806
1807 inline constexpr http_method_id_t
1808 http_method_unknown()
1809 {
1810 return http_method_id_t{};
1811 }
1812
1813 //
1814 // default_http_methods_t
1815 //
1816 /*!
1817 * @brief The default implementation for http_method_mapper.
1818 *
1819 * Since v.0.5.0 RESTinio allows to use modified versions of http_parser
1820 * libraries. Such modified versions can handle non-standard HTTP methods.
1821 * In that case a user should define its own http_method_mapper-type.
1822 * That http_method_mapper must contain static method from_nodejs for
1823 * mapping the http_parser's ID of HTTP method to an instance of
1824 * http_method_id_t.
1825 *
1826 * Class default_http_methods_t is the default implementation of
1827 * http_method_mapper-type for vanila version of http_parser.
1828 *
1829 * @since v.0.5.0
1830 */
1831 class default_http_methods_t
1832 {
1833 public :
1834 inline static constexpr http_method_id_t
from_nodejs(int value)1835 from_nodejs( int value ) noexcept
1836 {
1837 http_method_id_t result;
1838 switch( value )
1839 {
1840 #define RESTINIO_HTTP_METHOD_FUNC_GEN( func_name, nodejs_code, method_name ) \
1841 case nodejs_code : result = func_name(); break;
1842
1843 RESTINIO_HTTP_METHOD_MAP( RESTINIO_HTTP_METHOD_FUNC_GEN )
1844 #undef RESTINIO_HTTP_METHOD_FUNC_GEN
1845 default : ; // Nothing to do.
1846 }
1847
1848 return result;
1849 }
1850 };
1851
1852 //
1853 // http_request_header
1854 //
1855
1856 //! Req header.
1857 struct http_request_header_t final
1858 : public http_header_common_t
1859 {
1860 static std::size_t
memchr_helperrestinio::http_request_header_t1861 memchr_helper( int chr , const char * from, std::size_t size )
1862 {
1863 const char * result = static_cast< const char * >(
1864 std::memchr( from, chr, size ) );
1865
1866 return result ? static_cast< std::size_t >( result - from ) : size;
1867 }
1868
1869 public:
1870 http_request_header_t() = default;
1871
http_request_header_trestinio::http_request_header_t1872 http_request_header_t(
1873 http_method_id_t method,
1874 std::string request_target_ )
1875 : m_method{ method }
1876 {
1877 request_target( std::move( request_target_ ) );
1878 }
1879
1880 http_method_id_t
methodrestinio::http_request_header_t1881 method() const noexcept
1882 { return m_method; }
1883
1884 void
methodrestinio::http_request_header_t1885 method( http_method_id_t m ) noexcept
1886 { m_method = m; }
1887
1888 const std::string &
request_targetrestinio::http_request_header_t1889 request_target() const noexcept
1890 { return m_request_target; }
1891
1892 void
request_targetrestinio::http_request_header_t1893 request_target( std::string t )
1894 {
1895 m_request_target.assign( std::move( t ) );
1896
1897 m_fragment_separator_pos =
1898 memchr_helper( '#', m_request_target.data(), m_request_target.size() );
1899
1900 m_query_separator_pos =
1901 memchr_helper( '?', m_request_target.data(), m_fragment_separator_pos );
1902 }
1903
1904 //! Request URL-structure.
1905 //! \{
1906
1907 //! Get the path part of the request URL.
1908 /*!
1909 If request target is `/weather/temperature?from=2012-01-01&to=2012-01-10`,
1910 then function returns string view on '/weather/temperature' part.
1911 */
1912 string_view_t
pathrestinio::http_request_header_t1913 path() const noexcept
1914 {
1915 return string_view_t{ m_request_target.data(), m_query_separator_pos };
1916 }
1917
1918 //! Get the query part of the request URL.
1919 /*!
1920 If request target is `/weather/temperature?from=2012-01-01&to=2012-01-10`,
1921 then function returns string view on 'from=2012-01-01&to=2012-01-10' part.
1922 */
1923 string_view_t
queryrestinio::http_request_header_t1924 query() const noexcept
1925 {
1926 return
1927 m_fragment_separator_pos == m_query_separator_pos ?
1928 string_view_t{ nullptr, 0 } :
1929 string_view_t{
1930 m_request_target.data() + m_query_separator_pos + 1,
1931 m_fragment_separator_pos - m_query_separator_pos - 1 };
1932 }
1933
1934
1935 //! Get the fragment part of the request URL.
1936 /*!
1937 If request target is `/sobjectizerteam/json_dto-0.2#markdown-header-what-is-json_dto`,
1938 then function returns string view on 'markdown-header-what-is-json_dto' part.
1939 */
1940 string_view_t
fragmentrestinio::http_request_header_t1941 fragment() const
1942 {
1943 return
1944 m_request_target.size() == m_fragment_separator_pos ?
1945 string_view_t{ nullptr, 0 } :
1946 string_view_t{
1947 m_request_target.data() + m_fragment_separator_pos + 1,
1948 m_request_target.size() - m_fragment_separator_pos - 1 };
1949 }
1950 //! \}
1951
1952 //! Helpfull function for using in parser callback.
1953 void
append_request_targetrestinio::http_request_header_t1954 append_request_target( const char * at, size_t length )
1955 {
1956 if( m_request_target.size() == m_fragment_separator_pos )
1957 {
1958 // If fragment separator hadn't already appeared,
1959 // search for it in a new block.
1960
1961 const auto fragment_separator_pos_inc =
1962 memchr_helper( '#', at, length );
1963
1964 m_fragment_separator_pos += fragment_separator_pos_inc;
1965
1966 if( m_request_target.size() == m_query_separator_pos )
1967 {
1968 // If request separator hadn't already appeared,
1969 // search for it in a new block.
1970 m_query_separator_pos +=
1971 memchr_helper( '?', at, fragment_separator_pos_inc );
1972 }
1973 }
1974 // Else fragment separator appeared
1975 // (req separator is either already defined or does not exist)
1976
1977 m_request_target.append( at, length );
1978 }
1979
1980 private:
1981 http_method_id_t m_method{ http_method_get() };
1982 std::string m_request_target;
1983 std::size_t m_query_separator_pos{ 0 };
1984 std::size_t m_fragment_separator_pos{ 0 };
1985 };
1986
1987 //
1988 // http_status_code_t
1989 //
1990
1991 //! A handy wrapper for HTTP response status code.
1992 class http_status_code_t
1993 {
1994 public:
http_status_code_t()1995 constexpr http_status_code_t() noexcept
1996 {}
1997
http_status_code_t(std::uint16_t status_code)1998 constexpr explicit http_status_code_t( std::uint16_t status_code ) noexcept
1999 : m_status_code{ status_code }
2000 {}
2001
2002 constexpr auto
raw_code() const2003 raw_code() const noexcept
2004 {
2005 return m_status_code;
2006 }
2007
2008 constexpr bool
operator ==(const http_status_code_t & sc) const2009 operator == ( const http_status_code_t & sc ) const noexcept
2010 {
2011 return raw_code() == sc.raw_code();
2012 }
2013
2014 constexpr bool
operator !=(const http_status_code_t & sc) const2015 operator != ( const http_status_code_t & sc ) const noexcept
2016 {
2017 return sc.raw_code() != sc.raw_code();
2018 }
2019
2020 constexpr bool
operator <(const http_status_code_t & sc) const2021 operator < ( const http_status_code_t & sc ) const noexcept
2022 {
2023 return sc.raw_code() < sc.raw_code();
2024 }
2025
2026 private:
2027 //! Status code value.
2028 std::uint16_t m_status_code{ 0 };
2029 };
2030
2031 namespace status_code
2032 {
2033
2034 /** @name RFC 2616 status code list.
2035 * @brief Codes defined by RFC 2616: https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1.
2036 */
2037 ///@{
2038
2039 // Add '_', because 'continue is reserved word.'
2040 constexpr http_status_code_t continue_{ 100 };
2041
2042 constexpr http_status_code_t switching_protocols{ 101 };
2043 constexpr http_status_code_t ok{ 200 };
2044 constexpr http_status_code_t created{ 201 };
2045 constexpr http_status_code_t accepted{ 202 };
2046 constexpr http_status_code_t non_authoritative_information{ 203 };
2047 constexpr http_status_code_t no_content{ 204 };
2048 constexpr http_status_code_t reset_content{ 205 };
2049 constexpr http_status_code_t partial_content{ 206 };
2050 constexpr http_status_code_t multiple_choices{ 300 };
2051 constexpr http_status_code_t moved_permanently{ 301 };
2052 constexpr http_status_code_t found{ 302 };
2053 constexpr http_status_code_t see_other{ 303 };
2054 constexpr http_status_code_t not_modified{ 304 };
2055 constexpr http_status_code_t use_proxy{ 305 };
2056 constexpr http_status_code_t temporary_redirect{ 307 };
2057 constexpr http_status_code_t bad_request{ 400 };
2058 constexpr http_status_code_t unauthorized{ 401 };
2059 constexpr http_status_code_t payment_required{ 402 };
2060 constexpr http_status_code_t forbidden{ 403 };
2061 constexpr http_status_code_t not_found{ 404 };
2062 constexpr http_status_code_t method_not_allowed{ 405 };
2063 constexpr http_status_code_t not_acceptable{ 406 };
2064 constexpr http_status_code_t proxy_authentication_required{ 407 };
2065 constexpr http_status_code_t request_time_out{ 408 };
2066 constexpr http_status_code_t conflict{ 409 };
2067 constexpr http_status_code_t gone{ 410 };
2068 constexpr http_status_code_t length_required{ 411 };
2069 constexpr http_status_code_t precondition_failed{ 412 };
2070
2071 //413 Payload Too Large (RFC 7231)
2072 // The request is larger than the server is willing or able to process.
2073 // Previously called "Request Entity Too Large".[44]
2074 constexpr http_status_code_t payload_too_large{ 413 };
2075
2076 // 414 URI Too Long (RFC 7231)
2077 // The URI provided was too long for the server to process.
2078 // Often the result of too much data being encoded as a query-string of a GET request,
2079 // in which case it should be converted to a POST request.
2080 // Called "Request-URI Too Long" previously.[46]
2081 constexpr http_status_code_t uri_too_long{ 414 };
2082
2083 constexpr http_status_code_t unsupported_media_type{ 415 };
2084 constexpr http_status_code_t requested_range_not_satisfiable{ 416 };
2085 constexpr http_status_code_t expectation_failed{ 417 };
2086 constexpr http_status_code_t internal_server_error{ 500 };
2087 constexpr http_status_code_t not_implemented{ 501 };
2088 constexpr http_status_code_t bad_gateway{ 502 };
2089 constexpr http_status_code_t service_unavailable{ 503 };
2090 constexpr http_status_code_t gateway_time_out{ 504 };
2091 constexpr http_status_code_t http_version_not_supported{ 505 };
2092 ///@}
2093
2094 /** @name Additional status codes.
2095 * @brief Codes not covered with RFC 2616.
2096 */
2097 ///@{
2098 // RFC 7538
2099 constexpr http_status_code_t permanent_redirect{ 308 };
2100
2101 // RFC 2518
2102 constexpr http_status_code_t processing{ 102 };
2103 constexpr http_status_code_t multi_status{ 207 };
2104 constexpr http_status_code_t unprocessable_entity{ 422 };
2105 constexpr http_status_code_t locked{ 423 };
2106 constexpr http_status_code_t failed_dependency{ 424 };
2107 constexpr http_status_code_t insufficient_storage{ 507 };
2108
2109 // RFC 6585
2110 constexpr http_status_code_t precondition_required{ 428 };
2111 constexpr http_status_code_t too_many_requests{ 429 };
2112 constexpr http_status_code_t request_header_fields_too_large{ 431 };
2113 constexpr http_status_code_t network_authentication_required{ 511 };
2114 ///@}
2115
2116 } /* namespace status_code */
2117
2118 //
2119 // http_status_line_t
2120 //
2121
2122 //! HTTP response header status line.
2123 class http_status_line_t
2124 {
2125 public:
http_status_line_t()2126 http_status_line_t()
2127 {}
2128
http_status_line_t(http_status_code_t sc,std::string reason_phrase)2129 http_status_line_t(
2130 http_status_code_t sc,
2131 std::string reason_phrase )
2132 : m_status_code{ sc }
2133 , m_reason_phrase{ std::move( reason_phrase ) }
2134 {}
2135
2136 http_status_code_t
status_code() const2137 status_code() const noexcept
2138 { return m_status_code; }
2139
2140 void
status_code(http_status_code_t c)2141 status_code( http_status_code_t c ) noexcept
2142 { m_status_code = c; }
2143
2144 const std::string &
reason_phrase() const2145 reason_phrase() const noexcept
2146 { return m_reason_phrase; }
2147
2148 void
reason_phrase(std::string r)2149 reason_phrase( std::string r )
2150 { m_reason_phrase.assign( std::move( r ) ); }
2151
2152 private:
2153 http_status_code_t m_status_code{ status_code::ok };
2154 std::string m_reason_phrase;
2155 };
2156
2157 inline std::ostream &
operator <<(std::ostream & o,const http_status_line_t & status_line)2158 operator << ( std::ostream & o, const http_status_line_t & status_line )
2159 {
2160 return o << "{" << status_line.status_code().raw_code() << ", "
2161 << status_line.reason_phrase() << "}";
2162 }
2163
2164 /** @name RFC 2616 statuses.
2165 * @brief Codes defined by RFC 2616: https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1.
2166 */
2167 ///@{
2168
status_continue()2169 inline http_status_line_t status_continue()
2170 { return http_status_line_t{ status_code::continue_, "Continue" }; }
2171
status_switching_protocols()2172 inline http_status_line_t status_switching_protocols()
2173 { return http_status_line_t{ status_code::switching_protocols, "Switching Protocols" }; }
2174
status_ok()2175 inline http_status_line_t status_ok()
2176 { return http_status_line_t{ status_code::ok, "OK" }; }
2177
status_created()2178 inline http_status_line_t status_created()
2179 { return http_status_line_t{ status_code::created, "Created" }; }
2180
status_accepted()2181 inline http_status_line_t status_accepted()
2182 { return http_status_line_t{ status_code::accepted, "Accepted" }; }
2183
status_non_authoritative_information()2184 inline http_status_line_t status_non_authoritative_information()
2185 { return http_status_line_t{ status_code::non_authoritative_information, "Non-Authoritative Information" }; }
2186
status_no_content()2187 inline http_status_line_t status_no_content()
2188 { return http_status_line_t{ status_code::no_content, "No Content" }; }
2189
status_reset_content()2190 inline http_status_line_t status_reset_content()
2191 { return http_status_line_t{ status_code::reset_content, "Reset Content" }; }
2192
status_partial_content()2193 inline http_status_line_t status_partial_content()
2194 { return http_status_line_t{ status_code::partial_content, "Partial Content" }; }
2195
status_multiple_choices()2196 inline http_status_line_t status_multiple_choices()
2197 { return http_status_line_t{ status_code::multiple_choices, "Multiple Choices" }; }
2198
status_moved_permanently()2199 inline http_status_line_t status_moved_permanently()
2200 { return http_status_line_t{ status_code::moved_permanently, "Moved Permanently" }; }
2201
status_found()2202 inline http_status_line_t status_found()
2203 { return http_status_line_t{ status_code::found, "Found" }; }
2204
status_see_other()2205 inline http_status_line_t status_see_other()
2206 { return http_status_line_t{ status_code::see_other, "See Other" }; }
2207
status_not_modified()2208 inline http_status_line_t status_not_modified()
2209 { return http_status_line_t{ status_code::not_modified, "Not Modified" }; }
2210
status_use_proxy()2211 inline http_status_line_t status_use_proxy()
2212 { return http_status_line_t{ status_code::use_proxy, "Use Proxy" }; }
2213
status_temporary_redirect()2214 inline http_status_line_t status_temporary_redirect()
2215 { return http_status_line_t{ status_code::temporary_redirect, "Temporary Redirect" }; }
2216
status_bad_request()2217 inline http_status_line_t status_bad_request()
2218 { return http_status_line_t{ status_code::bad_request, "Bad Request" }; }
2219
status_unauthorized()2220 inline http_status_line_t status_unauthorized()
2221 { return http_status_line_t{ status_code::unauthorized, "Unauthorized" }; }
2222
status_payment_required()2223 inline http_status_line_t status_payment_required()
2224 { return http_status_line_t{ status_code::payment_required, "Payment Required" }; }
2225
status_forbidden()2226 inline http_status_line_t status_forbidden()
2227 { return http_status_line_t{ status_code::forbidden, "Forbidden" }; }
2228
status_not_found()2229 inline http_status_line_t status_not_found()
2230 { return http_status_line_t{ status_code::not_found, "Not Found" }; }
2231
status_method_not_allowed()2232 inline http_status_line_t status_method_not_allowed()
2233 { return http_status_line_t{ status_code::method_not_allowed, "Method Not Allowed" }; }
2234
status_not_acceptable()2235 inline http_status_line_t status_not_acceptable()
2236 { return http_status_line_t{ status_code::not_acceptable, "Not Acceptable" }; }
2237
status_proxy_authentication_required()2238 inline http_status_line_t status_proxy_authentication_required()
2239 { return http_status_line_t{status_code::proxy_authentication_required, "Proxy Authentication Required" }; }
2240
status_request_time_out()2241 inline http_status_line_t status_request_time_out()
2242 { return http_status_line_t{ status_code::request_time_out, "Request Timeout" }; }
2243
status_conflict()2244 inline http_status_line_t status_conflict()
2245 { return http_status_line_t{ status_code::conflict, "Conflict" }; }
2246
status_gone()2247 inline http_status_line_t status_gone()
2248 { return http_status_line_t{ status_code::gone, "Gone" }; }
2249
status_length_required()2250 inline http_status_line_t status_length_required()
2251 { return http_status_line_t{ status_code::length_required, "Length Required" }; }
2252
status_precondition_failed()2253 inline http_status_line_t status_precondition_failed()
2254 { return http_status_line_t{ status_code::precondition_failed, "Precondition Failed" }; }
2255
status_payload_too_large()2256 inline http_status_line_t status_payload_too_large()
2257 { return http_status_line_t{ status_code::payload_too_large, "Payload Too Large" }; }
2258
status_uri_too_long()2259 inline http_status_line_t status_uri_too_long()
2260 { return http_status_line_t{ status_code::uri_too_long, "URI Too Long" }; }
2261
status_unsupported_media_type()2262 inline http_status_line_t status_unsupported_media_type()
2263 { return http_status_line_t{ status_code::unsupported_media_type, "Unsupported Media Type" }; }
2264
status_requested_range_not_satisfiable()2265 inline http_status_line_t status_requested_range_not_satisfiable()
2266 { return http_status_line_t{ status_code::requested_range_not_satisfiable, "Requested Range Not Satisfiable" }; }
2267
status_expectation_failed()2268 inline http_status_line_t status_expectation_failed()
2269 { return http_status_line_t{ status_code::expectation_failed, "Expectation Failed" }; }
2270
status_internal_server_error()2271 inline http_status_line_t status_internal_server_error()
2272 { return http_status_line_t{ status_code::internal_server_error, "Internal Server Error" }; }
2273
status_not_implemented()2274 inline http_status_line_t status_not_implemented()
2275 { return http_status_line_t{ status_code::not_implemented, "Not Implemented" }; }
2276
status_bad_gateway()2277 inline http_status_line_t status_bad_gateway()
2278 { return http_status_line_t{ status_code::bad_gateway, "Bad Gateway" }; }
2279
status_service_unavailable()2280 inline http_status_line_t status_service_unavailable()
2281 { return http_status_line_t{ status_code::service_unavailable, "Service Unavailable" }; }
2282
status_gateway_time_out()2283 inline http_status_line_t status_gateway_time_out()
2284 { return http_status_line_t{ status_code::gateway_time_out, "Gateway Timeout" }; }
2285
status_http_version_not_supported()2286 inline http_status_line_t status_http_version_not_supported()
2287 { return http_status_line_t{ status_code::http_version_not_supported, "HTTP Version not supported" }; }
2288 ///@}
2289
2290 /** @name Additional statuses.
2291 * @brief Not covered with RFC 2616.
2292 */
2293 ///@{
2294 // RFC 7538
status_permanent_redirect()2295 inline http_status_line_t status_permanent_redirect()
2296 { return http_status_line_t{ status_code::permanent_redirect, "Permanent Redirect" }; }
2297
2298 // RFC 2518
status_processing()2299 inline http_status_line_t status_processing()
2300 { return http_status_line_t{ status_code::processing, "Processing" }; }
2301
status_multi_status()2302 inline http_status_line_t status_multi_status()
2303 { return http_status_line_t{ status_code::multi_status, "Multi-Status" }; }
2304
status_unprocessable_entity()2305 inline http_status_line_t status_unprocessable_entity()
2306 { return http_status_line_t{ status_code::unprocessable_entity, "Unprocessable Entity" }; }
2307
status_locked()2308 inline http_status_line_t status_locked()
2309 { return http_status_line_t{ status_code::locked, "Locked" }; }
2310
status_failed_dependency()2311 inline http_status_line_t status_failed_dependency()
2312 { return http_status_line_t{ status_code::failed_dependency, "Failed Dependency" }; }
2313
status_insufficient_storage()2314 inline http_status_line_t status_insufficient_storage()
2315 { return http_status_line_t{ status_code::insufficient_storage, "Insufficient Storage" }; }
2316
2317 // RFC 6585
status_precondition_required()2318 inline http_status_line_t status_precondition_required()
2319 { return http_status_line_t{ status_code::precondition_required, "Precondition Required" }; }
2320
status_too_many_requests()2321 inline http_status_line_t status_too_many_requests()
2322 { return http_status_line_t{ status_code::too_many_requests, "Too Many Requests" }; }
2323
status_request_header_fields_too_large()2324 inline http_status_line_t status_request_header_fields_too_large()
2325 { return http_status_line_t{ status_code::request_header_fields_too_large, "Request Header Fields Too Large" }; }
2326
status_network_authentication_required()2327 inline http_status_line_t status_network_authentication_required()
2328 { return http_status_line_t{ status_code::network_authentication_required, "Network Authentication Required" }; }
2329 ///@}
2330
2331 //
2332 // http_response_header_t
2333 //
2334
2335 //! Resp header.
2336 struct http_response_header_t final
2337 : public http_header_common_t
2338 {
2339 public:
http_response_header_trestinio::http_response_header_t2340 http_response_header_t()
2341 {}
2342
http_response_header_trestinio::http_response_header_t2343 http_response_header_t( http_status_line_t status_line )
2344 : m_status_line{ std::move( status_line ) }
2345 {}
2346
2347 http_status_code_t
status_coderestinio::http_response_header_t2348 status_code() const noexcept
2349 { return m_status_line.status_code(); }
2350
2351 void
status_coderestinio::http_response_header_t2352 status_code( http_status_code_t c ) noexcept
2353 { m_status_line.status_code( c ); }
2354
2355 const std::string &
reason_phraserestinio::http_response_header_t2356 reason_phrase() const noexcept
2357 { return m_status_line.reason_phrase(); }
2358
2359 void
reason_phraserestinio::http_response_header_t2360 reason_phrase( std::string r )
2361 { m_status_line.reason_phrase( std::move( r ) ); }
2362
2363 const http_status_line_t &
status_linerestinio::http_response_header_t2364 status_line() const noexcept
2365 {
2366 return m_status_line;
2367 }
2368
2369 void
status_linerestinio::http_response_header_t2370 status_line( http_status_line_t sl )
2371 {
2372 m_status_line = std::move( sl );
2373 }
2374
2375 private:
2376 http_status_line_t m_status_line;
2377 };
2378
2379 } /* namespace restinio */
2380