RESTinio

file_upload.hpp
Go to the documentation of this file.
1/*
2 * RESTinio
3 */
4
12#pragma once
13
14#include <restinio/helpers/http_field_parsers/content-type.hpp>
15#include <restinio/helpers/http_field_parsers/content-disposition.hpp>
16#include <restinio/helpers/multipart_body.hpp>
17
18#include <restinio/http_headers.hpp>
19#include <restinio/request_handler.hpp>
20#include <restinio/expected.hpp>
21
22#include <iostream>
23
24 namespace restinio
25{
26
27 namespace file_upload
28{
29
30//
31// enumeration_error_t
32//
39 enum class enumeration_error_t
40{
44 content_type_field_not_found,
46 content_type_field_parse_error,
50 content_type_field_inappropriate_value,
53 illegal_boundary_value,
55 content_disposition_field_parse_error,
58 content_disposition_field_inappropriate_value,
60 no_parts_found,
65 no_files_found,
69 terminated_by_handler,
71 unexpected_error
72};
73
74 namespace impl
75{
76
83 RESTINIO_NODISCARD
84 constexpr enumeration_error_t
85 translate_enumeration_error(
86 restinio::multipart_body::enumeration_error_t original )
87{
88 using source = restinio::multipart_body::enumeration_error_t;
89 using dest = enumeration_error_t;
90
91 dest result = dest::unexpected_error;
92
93 switch( original )
94 {
95 case source::content_type_field_not_found:
96 result = dest::content_type_field_not_found; break;
97
98 case source::content_type_field_parse_error:
99 result = dest::content_type_field_parse_error; break;
100
101 case source::content_type_field_inappropriate_value:
102 result = dest::content_type_field_inappropriate_value; break;
103
104 case source::illegal_boundary_value:
105 result = dest::illegal_boundary_value; break;
106
107 case source::no_parts_found:
108 result = dest::no_parts_found; break;
109
110 case source::terminated_by_handler:
111 result = dest::terminated_by_handler; break;
112
113 case source::unexpected_error:
114 /* nothing to do */ break;
115 }
116
117 return result;
118}
119
120} /* namespace impl */
121
122//
123// handling_result_t
124//
131 using handling_result_t = restinio::multipart_body::handling_result_t;
132
133//
134// part_description_t
135//
146 struct part_description_t
147{
149
153 http_header_fields_t fields;
155 string_view_t body;
157 std::string name;
159
169 optional_t< std::string > filename_star;
171
175 optional_t< std::string > filename;
176};
177
178//
179// analyze_part
180//
219 RESTINIO_NODISCARD
220 inline expected_t< part_description_t, enumeration_error_t >
221 analyze_part( restinio::multipart_body::parsed_part_t parsed_part )
222{
223 namespace hfp = restinio::http_field_parsers;
224
225 // Content-Disposition field should be present.
226 const auto disposition_field = parsed_part.fields.opt_value_of(
227 restinio::http_field::content_disposition );
228 if( !disposition_field )
229 return make_unexpected( enumeration_error_t::no_files_found );
230
231 // Content-Disposition should have value `form-data` with
232 // `name` and `filename*`/`filename` parameters.
233 const auto parsed_disposition = hfp::content_disposition_value_t::
234 try_parse( *disposition_field );
235 if( !parsed_disposition )
236 return make_unexpected(
237 enumeration_error_t::content_disposition_field_parse_error );
238 if( "form-data" != parsed_disposition->value )
239 return make_unexpected( enumeration_error_t::no_files_found );
240
241 const auto name = hfp::find_first(
242 parsed_disposition->parameters, "name" );
243 if( !name )
244 return make_unexpected(
245 enumeration_error_t::content_disposition_field_inappropriate_value );
246 const auto expected_to_optional = []( auto expected ) {
247 return expected ?
248 optional_t< std::string >{ std::string{
249 expected->data(),
250 expected->size()
251 } }
252 : optional_t< std::string >{};
253 };
254
255 auto filename_star = expected_to_optional( hfp::find_first(
256 parsed_disposition->parameters, "filename*" ) );
257 auto filename = expected_to_optional( hfp::find_first(
258 parsed_disposition->parameters, "filename" ) );
259
260 // If there is no `filename*` nor `filename` then there is no file.
261 if( !filename_star && !filename )
262 return make_unexpected( enumeration_error_t::no_files_found );
263
264 return part_description_t{
265 std::move( parsed_part.fields ),
266 parsed_part.body,
267 std::string{ name->data(), name->size() },
268 std::move(filename_star),
269 std::move(filename)
270 };
271}
272
273 namespace impl
274{
275
276//
277// valid_handler_type
278//
279 template< typename, typename = restinio::utils::metaprogramming::void_t<> >
280 struct valid_handler_type : public std::false_type {};
281
282 template< typename T >
283 struct valid_handler_type<
284 T,
285 restinio::utils::metaprogramming::void_t<
286 std::enable_if_t<
287 std::is_same<
288 handling_result_t,
289 decltype(std::declval<T>()(std::declval<part_description_t>()))
290 >::value,
291 bool
292 >
293 >
294 > : public std::true_type
295{};
296
297} /* namespace impl */
298
358 template< typename Extra_Data, typename Handler >
359 expected_t< std::size_t, enumeration_error_t >
360 enumerate_parts_with_files(
362 const generic_request_t< Extra_Data > & req,
364 Handler && handler,
367 string_view_t expected_media_type = string_view_t{"multipart"},
369 string_view_t expected_media_subtype = string_view_t{"form-data"} )
370{
371 static_assert(
372 impl::valid_handler_type< std::decay_t<Handler> >::value,
373 "Handler should be callable object, "
374 "should accept part_description_t by value, const or rvalue reference, "
375 "and should return handling_result_t" );
376
377 std::size_t files_found{ 0u };
378 optional_t< enumeration_error_t > error;
379
380 const auto result = restinio::multipart_body::enumerate_parts( req,
381 [&handler, &files_found, &error]
382 ( restinio::multipart_body::parsed_part_t part )
383 {
384 auto part_description = analyze_part( std::move(part) );
385 if( part_description )
386 {
387 ++files_found;
388
389 return handler( std::move(*part_description) );
390 }
391 else if( enumeration_error_t::no_files_found ==
392 part_description.error() )
393 {
394 return handling_result_t::continue_enumeration;
395 }
396 else
397 {
398 error = part_description.error();
399 return handling_result_t::terminate_enumeration;
400 }
401 },
402 expected_media_type,
403 expected_media_subtype );
404
405 if( error )
406 return make_unexpected( *error );
407 else if( !result )
408 return make_unexpected(
409 impl::translate_enumeration_error( result.error() ) );
410 else
411 return files_found;
412}
413
414} /* namespace file_upload */
415
416} /* namespace restinio */
417
nonstd::optional_lite::optional
class optional
Definition: optional.hpp:919
restinio::generic_request_t
HTTP Request data.
Definition: request_handler.hpp:201
restinio::http_header_fields_t
Header fields map.
Definition: http_headers.hpp:703
restinio::http_header_fields_t::opt_value_of
optional_t< string_view_t > opt_value_of(string_view_t name) const noexcept
Get optional value of a field.
Definition: http_headers.hpp:1300
RESTINIO_NODISCARD
#define RESTINIO_NODISCARD
Definition: compiler_features.hpp:33
content-disposition.hpp
Stuff related to value of Content-Disposition HTTP-field.
content-type.hpp
Stuff related to value of Content-Type HTTP-field.
expected.hpp
http_headers.hpp
multipart_body.hpp
Various tools for working with multipart bodies.
nonstd::optional_lite::std11::false_type
bool_constant< false > false_type
Definition: optional.hpp:460
nonstd::optional_lite::std11::move
T & move(T &t)
Definition: optional.hpp:465
nonstd::optional_lite::std11::true_type
bool_constant< true > true_type
Definition: optional.hpp:459
restinio::easy_parser::try_parse
RESTINIO_NODISCARD expected_t< typename Producer::result_type, parse_error_t > try_parse(string_view_t from, Producer producer)
Perform the parsing of the specified content by using specified value producer.
Definition: easy_parser.hpp:5042
restinio::file_upload::impl::translate_enumeration_error
RESTINIO_NODISCARD constexpr enumeration_error_t translate_enumeration_error(restinio::multipart_body::enumeration_error_t original)
Helper function for conversion from one enumeration_error to another.
Definition: file_upload.hpp:85
restinio::file_upload::analyze_part
RESTINIO_NODISCARD expected_t< part_description_t, enumeration_error_t > analyze_part(restinio::multipart_body::parsed_part_t parsed_part)
Helper function for analyzing an already parsed part of a multipart body for presence of an uploaded ...
Definition: file_upload.hpp:221
restinio::file_upload::enumerate_parts_with_files
expected_t< std::size_t, enumeration_error_t > enumerate_parts_with_files(const generic_request_t< Extra_Data > &req, Handler &&handler, string_view_t expected_media_type=string_view_t{"multipart"}, string_view_t expected_media_subtype=string_view_t{"form-data"})
A helper function for enumeration of parts of a multipart body those contain uploaded files.
Definition: file_upload.hpp:360
restinio::file_upload::enumeration_error_t
enumeration_error_t
The result of an attempt to enumerate parts of a multipart body that contains uploaded file.
Definition: file_upload.hpp:40
restinio::file_upload::enumeration_error_t::content_type_field_inappropriate_value
@ content_type_field_inappropriate_value
Content-Type field value parsed but doesn't contain an appropriate value. For example there can be me...
restinio::file_upload::enumeration_error_t::no_files_found
@ no_files_found
No files found in the current part. For example, there is no Content-Disposition field for that part,...
restinio::file_upload::enumeration_error_t::no_parts_found
@ no_parts_found
No parts of a multipart body actually found.
restinio::file_upload::enumeration_error_t::illegal_boundary_value
@ illegal_boundary_value
Value of 'boundary' parameter is invalid (for example it contains some illegal characters).
restinio::file_upload::enumeration_error_t::content_type_field_not_found
@ content_type_field_not_found
Content-Type field is not found. If Content-Type is absent there is no way to detect 'boundary' param...
restinio::file_upload::enumeration_error_t::terminated_by_handler
@ terminated_by_handler
Enumeration of parts was aborted by user-provided handler. This code is returned when user-provided h...
restinio::file_upload::enumeration_error_t::content_type_field_parse_error
@ content_type_field_parse_error
Unable to parse Content-Type field value.
restinio::file_upload::enumeration_error_t::content_disposition_field_inappropriate_value
@ content_disposition_field_inappropriate_value
Content-Disposition field value parsed but doesn't contain an appropriate value. For example,...
restinio::file_upload::enumeration_error_t::unexpected_error
@ unexpected_error
Some unexpected error encountered during the enumeration.
restinio::file_upload::enumeration_error_t::content_disposition_field_parse_error
@ content_disposition_field_parse_error
Unable to parse Content-Disposition field.
restinio::http_field_parsers
Definition: accept-charset.hpp:20
restinio::http_field_parsers::find_first
RESTINIO_NODISCARD expected_t< string_view_t, not_found_t > find_first(const parameter_with_mandatory_value_container_t &where, string_view_t what)
A helper function to find the first occurence of a parameter with the specified value.
Definition: basics.hpp:1568
restinio::multipart_body::handling_result_t
handling_result_t
The result to be returned from user-provided handler of parts of multipart body.
Definition: multipart_body.hpp:338
restinio::multipart_body::handling_result_t::continue_enumeration
@ continue_enumeration
Enumeration of parts should be continued. If there is another part the user-provided handler will be ...
restinio::multipart_body::enumerate_parts
RESTINIO_NODISCARD 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" }, optional_t< string_view_t > expected_media_subtype=nullopt)
A helper function for enumeration of parts of a multipart body.
Definition: multipart_body.hpp:686
restinio::multipart_body::enumeration_error_t
enumeration_error_t
The result of an attempt to enumerate parts of a multipart body.
Definition: multipart_body.hpp:362
restinio::utils::metaprogramming::void_t
typename make_void< Ts... >::type void_t
Definition: metaprogramming.hpp:28
restinio
Definition: asio_include.hpp:21
restinio::string_view_t
nonstd::string_view string_view_t
Definition: string_view.hpp:19
restinio::expected_t
nonstd::expected< T, E > expected_t
Definition: expected.hpp:22
request_handler.hpp
restinio::file_upload::impl::valid_handler_type
Definition: file_upload.hpp:280
restinio::file_upload::part_description_t
A description of one part with an uploaded file.
Definition: file_upload.hpp:147
restinio::file_upload::part_description_t::fields
http_header_fields_t fields
HTTP-fields local for that part.
Definition: file_upload.hpp:153
restinio::file_upload::part_description_t::filename
optional_t< std::string > filename
The value of Content-Disposition's 'filename' parameter.
Definition: file_upload.hpp:175
restinio::file_upload::part_description_t::name
std::string name
The value of Content-Disposition's 'name' parameter.
Definition: file_upload.hpp:157
restinio::file_upload::part_description_t::filename_star
optional_t< std::string > filename_star
The value of Content-Disposition's 'filename*' parameter.
Definition: file_upload.hpp:169
restinio::file_upload::part_description_t::body
string_view_t body
The body of that part.
Definition: file_upload.hpp:155
restinio::multipart_body::parsed_part_t
A description of parsed content of one part of a multipart body.
Definition: multipart_body.hpp:144
restinio::multipart_body::parsed_part_t::fields
http_header_fields_t fields
HTTP-fields local for that part.
Definition: multipart_body.hpp:150
restinio::multipart_body::parsed_part_t::body
string_view_t body
The body of that part.
Definition: multipart_body.hpp:152