2
3
6
7
8
9
10
14#include <restinio/helpers/string_algo.hpp>
15#include <restinio/helpers/easy_parser.hpp>
16#include <restinio/helpers/http_field_parsers/basics.hpp>
17#include <restinio/helpers/http_field_parsers/content-type.hpp>
19#include <restinio/http_headers.hpp>
20#include <restinio/request_handler.hpp>
21#include <restinio/expected.hpp>
23#include <restinio/impl/string_caseless_compare.hpp>
25#include <restinio/utils/metaprogramming.hpp>
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
74 string_view_t boundary )
78 std::vector< string_view_t > result;
79 std::vector< string_view_t > tmp_result;
81 const string_view_t eol{
"\r\n" };
82 const string_view_t last_separator{
"--\r\n" };
85 auto boundary_pos = body.find( boundary );
86 if( string_view_t::npos == boundary_pos )
92 if( boundary_pos != 0u &&
93 (boundary_pos < eol.size() ||
94 body.substr( boundary_pos - eol.size(), eol.size() ) != eol) )
97 auto remaining_body = body.substr( boundary_pos + boundary.size() );
98 if( starts_with( remaining_body, last_separator ) )
102 while( starts_with( remaining_body, eol ) )
104 remaining_body = remaining_body.substr( eol.size() );
106 boundary_pos = remaining_body.find( boundary );
107 if( string_view_t::npos == boundary_pos )
111 if( boundary_pos < eol.size() ||
112 remaining_body.substr( boundary_pos - eol.size(), eol.size() ) != eol )
115 tmp_result.push_back(
116 remaining_body.substr( 0u, boundary_pos - eol.size() ) );
118 remaining_body = remaining_body.substr( boundary_pos + boundary.size() );
120 if( starts_with( remaining_body, last_separator ) )
123 swap( tmp_result, result );
139
140
141
142
165constexpr char CR =
'\r';
166constexpr char LF =
'\n';
172
173
174
175
176
177
178
179
180
181
182
191 return from.fragment( from.current_position() );
199
200
201
202
203
204
205
206
214 std::string accumulator;
218 accumulator += ch
.m_ch;
224 from.current_position(),
231 return { std::move(accumulator) };
241
242
243
244
245
246
247
248
249
250
251
258 return produce< parsed_part_t >(
259 produce< http_header_fields_t >(
261 produce< http_header_field_t >(
262 token_p() >> to_lower() >> custom_consumer(
263 [](
auto & f, std::string && v) {
264 f.name(std::move(v));
268 field_value_producer_t{} >> custom_consumer(
269 [](
auto & f, std::string && v) {
270 f.value(std::move(v));
272 symbol(CR), symbol(LF)
273 ) >> custom_consumer(
274 [](
auto & to, http_header_field_t && v) {
275 to.add_field( std::move(v) );
278 ) >> &parsed_part_t::fields,
279 symbol(CR), symbol(LF),
280 body_producer_t{} >> &parsed_part_t::body );
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
321 auto actual_producer = impl::make_parser();
323 return easy_parser::impl::top_level_clause_t<
decltype(actual_producer) >{
324 std::move(actual_producer)
325 }.try_process( source );
332
333
334
335
336
357
358
359
360
405 return (ch >=
'0' && ch <=
'9')
406 || ((ch >=
'A' && ch <=
'Z') || (ch >=
'a' && ch <=
'z'))
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
461 if( value.size() >= 1u && value.size() <= 70u )
463 const std::size_t last_index = value.size() - 1u;
464 for( std::size_t i = 0u; i != last_index; ++i )
465 if( !is_bchar( value[i] ) )
468 if( !is_bcharnospace( value[ last_index ] ) )
481
482
483
484
485
486
487
488
489
490
491
492
493
494template<
typename Extra_Data >
499 string_view_t expected_media_type,
500 std::optional< string_view_t > expected_media_subtype )
506 const auto content_type = req.header().opt_value_of(
509 return make_unexpected(
514 const auto parse_result = hfp::content_type_value_t::try_parse(
517 return make_unexpected(
520 const auto & media_type = parse_result->media_type;
521 if( !is_equal_caseless( expected_media_type, media_type.type ) )
523 return make_unexpected(
526 if( expected_media_subtype &&
527 !is_equal_caseless( *expected_media_subtype, media_type.subtype ) )
529 return make_unexpected(
534 const auto boundary = hfp::find_first(
535 parse_result->media_type.parameters,
538 return make_unexpected(
542 const auto boundary_check_result = check_boundary_value( *boundary );
543 if( boundary_check_result )
544 return make_unexpected( *boundary_check_result );
547 std::string actual_boundary_mark;
548 actual_boundary_mark.reserve( 2 + boundary->size() );
549 actual_boundary_mark.append(
"--" );
550 actual_boundary_mark.append( boundary->data(), boundary->size() );
552 return { std::move(actual_boundary_mark) };
559
560
561
562
563
564
565
566
567template<
typename Handler >
571 const std::vector< string_view_t > & parts,
574 std::size_t parts_processed{ 0u };
577 for(
auto current_part : parts )
580 auto part_parse_result = try_parse_part( current_part );
581 if( !part_parse_result )
582 return make_unexpected( enumeration_error_t::unexpected_error );
585 const handling_result_t handler_ret_code = handler(
586 std::move(*part_parse_result) );
588 if( handling_result_t::terminate_enumeration != handler_ret_code )
591 error = enumeration_error_t::terminated_by_handler;
593 if( handling_result_t::continue_enumeration != handler_ret_code )
598 return make_unexpected( *error );
600 return parts_processed;
609template<
typename T >
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683template<
typename User_Type,
typename Handler >
697 string_view_t expected_media_type =
string_view_t{
"multipart" },
704 std::optional< string_view_t > expected_media_subtype = std::nullopt )
707 impl::valid_handler_type< std::decay_t<Handler> >::value,
708 "Handler should be callable object, "
709 "should accept parsed_part_t by value, const or rvalue reference, "
710 "and should return handling_result_t" );
712 const auto boundary = detect_boundary_for_multipart_body(
715 expected_media_subtype );
718 const auto parts = split_multipart_body( req.body(), *boundary );
721 return make_unexpected(
724 return impl::enumerate_parts_of_request_body(
726 std::forward<Handler>(handler) );
729 return make_unexpected( boundary.error() );
The class that implements "input stream".
void putback() noexcept
Return one character back to the input stream.
character_t getch() noexcept
Get the next character from the input stream.
Information about parsing error.
error_reason_t
Reason of parsing error.
@ unexpected_eof
Unexpected end of input is encontered when some character expected.
constexpr bool is_bchar(char ch)
constexpr bool is_bcharnospace(char ch)
expected_t< std::size_t, enumeration_error_t > enumerate_parts_of_request_body(const std::vector< string_view_t > &parts, Handler &&handler)
A function that parses every part of a multipart body and calls a user-provided handler for every par...
auto make_parser()
A factory function for a parser of a part of multipart message.
handling_result_t
The result to be returned from user-provided handler of parts of multipart body.
@ stop_enumeration
Enumeration of parts should be stopped. All remaining parts of multipart body will be skipped....
@ terminate_enumeration
Enumeration of parts should be ignored. All remaining parts of multipart body will be skipped and the...
@ continue_enumeration
Enumeration of parts should be continued. If there is another part the user-provided handler will be ...
std::vector< string_view_t > split_multipart_body(string_view_t body, string_view_t boundary)
Helper function for spliting a multipart body into a serie of separate parts.
expected_t< parsed_part_t, restinio::easy_parser::parse_error_t > try_parse_part(string_view_t part)
Helper function for parsing content of one part of a multipart body.
std::optional< enumeration_error_t > check_boundary_value(string_view_t value)
A helper function for checking the validity of 'boundary' value.
enumeration_error_t
The result of an attempt to enumerate parts of a multipart body.
@ content_type_field_inappropriate_value
Content-Type field value parsed but doesn't contain an appropriate value. For example there can be me...
@ no_parts_found
No parts of a multipart body actually found.
@ illegal_boundary_value
Value of 'boundary' parameter is invalid (for example it contains some illegal characters).
@ content_type_field_not_found
Content-Type field is not found. If Content-Type is absent there is no way to detect 'boundary' param...
@ terminated_by_handler
Enumeration of parts was aborted by user-provided handler. This code is returned when user-provided h...
@ content_type_field_parse_error
Unable to parse Content-Type field value.
@ unexpected_error
Some unexpected error encountered during the enumeration.
expected_t< std::string, enumeration_error_t > detect_boundary_for_multipart_body(const generic_request_t< Extra_Data > &req, string_view_t expected_media_type, std::optional< string_view_t > expected_media_subtype)
Helper function for parsing Content-Type field and extracting the value of 'boundary' parameter.
expected_t< std::size_t, enumeration_error_t > enumerate_parts(const generic_request_t< User_Type > &req, Handler &&handler, string_view_t expected_media_type=string_view_t{ "multipart" }, std::optional< string_view_t > expected_media_subtype=std::nullopt)
A helper function for enumeration of parts of a multipart body.
http_field_t http_field
Helper alies to omitt _t suffix.
A special producer that consumes the whole remaining content from the input stream.
expected_t< string_view_t, easy_parser::parse_error_t > try_parse(easy_parser::impl::source_t &from) const noexcept
A special producer that consumes the rest of the current line in the input stream until CR/LF will be...
expected_t< std::string, easy_parser::parse_error_t > try_parse(easy_parser::impl::source_t &from) const
A description of parsed content of one part of a multipart body.
http_header_fields_t fields
HTTP-fields local for that part.
string_view_t body
The body of that part.