I'm writing a boost::beast
server, and I'm having trouble figuring out how to use the different request flavours.
When a connection is accepted, I do this to read the request:
async_read()
, which populates my request<string_body>
using namespace boost::beast::http;
using namespace boost::beast;
class Session : public enable_shared_from_this<Session>
{
tcp_steam stream;
flat_buffer buf;
request<string_body> req;
public:
Session(boost::asio::ip::tcp::socket&& socket)
: stream(std::move(socket) ) {}
void Read() {
req = request<string_body>{}
async_read(stream, buf, req,
bind_front_handler(&Session::OnRead, shared_from_this() )
);
}
void OnRead(error_code ec, size_t)
{
auto method = GetRoute( req.target(), req.method() );
method( std::move(req) );
}
};
However, some of those methods (like when a user wants to upload/POST
a binary file) I suspect would work better if they received a request<file_body>
Can I convert the request<string_body> to a request<file_body> in those methods?
If not, how can I know the request's method/target before creating the request object? It doesn't seem like there is a way to call async_read
without knowing the Body
in request<Body>
ahead of time.
The idea here is to read the headers first, then decide on the body type, and switch.
There is a body-type-switching conversion constructor specifically for this purpose, and it is documented here Change Body Type. It comes with an example, relevant excerpt:
// Start with an empty_body parser
request_parser<empty_body> req0;
// Read just the header. Otherwise, the empty_body
// would generate an error if body octets were received.
read_header(stream, buffer, req0);
// Choose a body depending on the method verb
switch(req0.get().method())
{
case verb::post:
{
// If this is not a form upload then use a string_body
if( req0.get()[field::content_type] != "application/x-www-form-urlencoded" &&
req0.get()[field::content_type] != "multipart/form-data")
goto do_dynamic_body;
// Commit to string_body as the body type.
// As long as there are no body octets in the parser
// we are constructing from, no exception is thrown.
request_parser<string_body> req{std::move(req0)};
// Finish reading the message
read(stream, buffer, req);
// Call the handler. It can take ownership
// if desired, since we are calling release()
handler(req.release());
break;
}
do_dynamic_body:
default:
{
// Commit to dynamic_body as the body type.
// As long as there are no body octets in the parser
// we are constructing from, no exception is thrown.
request_parser<dynamic_body> req{std::move(req0)};
// Finish reading the message
read(stream, buffer, req);
// Call the handler. It can take ownership
// if desired, since we are calling release()
handler(req.release());
break;
}