Stewart
Stewart

Reputation: 5052

How to convert a Boost.Beast request<string_body> to a request<file_body>?

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:

  1. I call async_read(), which populates my request<string_body>
  2. I look at the request's method and target to dispatch it to whoever can handle that request
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.

Upvotes: 1

Views: 1256

Answers (1)

sehe
sehe

Reputation: 393769

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;
}

Upvotes: 3

Related Questions