Skip to content

Commit c47872a

Browse files
committed
feat: multipart_form_sink
1 parent 2e893d9 commit c47872a

File tree

9 files changed

+738
-3
lines changed

9 files changed

+738
-3
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//
2+
// Copyright (c) 2025 Mohammad Nejati
3+
//
4+
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
//
7+
// Official repository: https://github.com/cppalliance/http_proto
8+
//
9+
10+
#ifndef BOOST_HTTP_PROTO_MULTIPART_FORM_SINK_HPP
11+
#define BOOST_HTTP_PROTO_MULTIPART_FORM_SINK_HPP
12+
13+
#include <boost/capy/file.hpp>
14+
#include <boost/http_proto/sink.hpp>
15+
16+
#include <boost/optional.hpp>
17+
#include <boost/variant2.hpp>
18+
19+
namespace boost {
20+
namespace http_proto {
21+
22+
class BOOST_HTTP_PROTO_SYMBOL_VISIBLE multipart_form_sink
23+
: public http_proto::sink
24+
{
25+
public:
26+
struct file_field
27+
{
28+
std::string name;
29+
std::string path;
30+
};
31+
32+
struct text_field
33+
{
34+
std::string data;
35+
};
36+
37+
struct part
38+
{
39+
std::string name;
40+
variant2::variant<text_field, file_field> content;
41+
boost::optional<std::string> content_type;
42+
};
43+
44+
BOOST_HTTP_PROTO_DECL
45+
explicit
46+
multipart_form_sink(
47+
core::string_view boundary);
48+
49+
BOOST_HTTP_PROTO_DECL
50+
boost::span<part const>
51+
parts() const noexcept;
52+
53+
private:
54+
BOOST_HTTP_PROTO_DECL
55+
results
56+
on_write(
57+
buffers::const_buffer b,
58+
bool more) override;
59+
60+
void
61+
parse(
62+
bool match,
63+
core::string_view b,
64+
system::error_code& ec);
65+
66+
enum class state
67+
{
68+
preamble,
69+
post_boundary0,
70+
post_boundary1,
71+
post_boundary2,
72+
header,
73+
content,
74+
finished
75+
};
76+
77+
state state_ = state::preamble;
78+
std::string needle_;
79+
std::string leftover_;
80+
std::string header_;
81+
capy::file file_;
82+
std::vector<part> parts_;
83+
};
84+
85+
} // http_proto
86+
} // boost
87+
88+
#endif
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// Copyright (c) 2025 Mohammad Nejati
3+
//
4+
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5+
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6+
//
7+
// Official repository: https://github.com/cppalliance/http_proto
8+
//
9+
10+
#ifndef BOOST_HTTP_PROTO_RFC_CONTENT_DISPOSITION_RULE_HPP
11+
#define BOOST_HTTP_PROTO_RFC_CONTENT_DISPOSITION_RULE_HPP
12+
13+
#include <boost/http_proto/detail/config.hpp>
14+
#include <boost/http_proto/rfc/parameter.hpp>
15+
#include <boost/url/grammar/range_rule.hpp>
16+
17+
namespace boost {
18+
namespace http_proto {
19+
20+
namespace implementation_defined {
21+
struct content_disposition_rule_t
22+
{
23+
struct value_type
24+
{
25+
core::string_view type;
26+
grammar::range<parameter> params;
27+
};
28+
29+
BOOST_HTTP_PROTO_DECL
30+
system::result<value_type>
31+
parse(
32+
char const*& it,
33+
char const* end) const noexcept;
34+
};
35+
} // implementation_defined
36+
37+
/** Rule matching content-disposition
38+
39+
@par Value Type
40+
@code
41+
struct value_type
42+
{
43+
core::string_view type;
44+
grammar::range< parameter > params;
45+
};
46+
@endcode
47+
48+
@par Example
49+
@code
50+
@endcode
51+
52+
@par BNF
53+
@code
54+
content-disposition = disposition-type *( OWS ";" OWS disposition-parm )
55+
56+
disposition-type = token
57+
disposition-parm = token "=" ( token / quoted-string )
58+
@endcode
59+
60+
@par Specification
61+
@li <a href="https://www.rfc-editor.org/rfc/rfc6266#section-4.1"
62+
>4.1. Grammar (rfc6266)</a>
63+
@li <a href="https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6"
64+
>3.2.6. Field Value Components (rfc7230)</a>
65+
66+
@see
67+
@ref quoted_token_view.
68+
*/
69+
BOOST_INLINE_CONSTEXPR implementation_defined::content_disposition_rule_t content_disposition_rule{};
70+
71+
} // http_proto
72+
} // boost
73+
74+
#endif

include/boost/http_proto/rfc/parameter.hpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <boost/http_proto/detail/config.hpp>
1414
#include <boost/http_proto/rfc/quoted_token_view.hpp>
1515
#include <boost/system/result.hpp>
16+
#include <boost/url/grammar/range_rule.hpp>
1617

1718
namespace boost {
1819
namespace http_proto {
@@ -48,6 +49,18 @@ struct parameter_rule_t
4849
char const*) const noexcept ->
4950
system::result<value_type>;
5051
};
52+
53+
struct parameters_rule_t
54+
{
55+
using value_type = grammar::range<parameter>;
56+
57+
BOOST_HTTP_PROTO_DECL
58+
auto
59+
parse(
60+
char const*&,
61+
char const*) const noexcept ->
62+
system::result<value_type>;
63+
};
5164
} // implementation_defined
5265

5366
/** Rule matching parameter
@@ -75,6 +88,35 @@ struct parameter_rule_t
7588
*/
7689
BOOST_INLINE_CONSTEXPR implementation_defined::parameter_rule_t parameter_rule{};
7790

91+
//------------------------------------------------
92+
93+
/** Rule matching parameters
94+
95+
@par Value Type
96+
@code
97+
using value_type = grammar::range< parameter >;
98+
@endcode
99+
100+
@par Example
101+
@code
102+
@endcode
103+
104+
@par BNF
105+
@code
106+
parameters = *( OWS ";" OWS parameter )
107+
parameter = token "=" ( token / quoted-string )
108+
@endcode
109+
110+
@par Specification
111+
@li <a href="https://www.rfc-editor.org/rfc/rfc7231#section-3.1.1.1"
112+
>3.1.1.1. Media Type (rfc7231)</a>
113+
114+
@see
115+
@ref parameter,
116+
@ref parameter_rule.
117+
*/
118+
BOOST_INLINE_CONSTEXPR implementation_defined::parameters_rule_t parameters_rule{};
119+
78120
} // http_proto
79121
} // boost
80122

include/boost/http_proto/rfc/quoted_token_view.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class quoted_token_view final
5050
: string_view_base(s)
5151
, n_(n)
5252
{
53+
// TODO: trim leading and trailing '"' characters?
5354
BOOST_ASSERT(s.size() >= 2);
5455
BOOST_ASSERT(s.front() == '\"');
5556
BOOST_ASSERT(s.back() == '\"');

0 commit comments

Comments
 (0)