Boost logo

Boost :

Subject: Re: [boost] [beast] Request for Discussion
From: Vinnie Falco (vinnie.falco_at_[hidden])
Date: 2016-09-30 11:23:11


On Fri, Sep 30, 2016 at 10:34 AM, Christof Donat <cd_at_[hidden]> wrote:
>> I'm proposing that there is a server layer that is even lower than the
>> items listed above.
>
> So that would be between the low level HTTP stuff and the request router.

Right. My approach is to work from the bottom up, since the number of
degrees of freedom with respect to designing an interface increases as
you go to higher levels (some would disagree with that statement). No
one can dispute that an HTTP application needs to send and receive
HTTP messages (the lowest level). But there would be debate on how to
handle cookies or how they should be persisted (high level functions).
Once you have read/write HTTP message primitives available, then on
the server side the next logical step up is to have a low-level server
framework.

> We could try define a kind of concept for the session store and the session
> objects, so that the session could also be persistent on disk

Oops, I think perhaps I miscommunicated. When I say session, I mean it
in the lowest level sense. Pretty much the
boost::asio::ip::tcp::socket and a few state variables (such as a
reference to the server, and a reference to the acceptor). I wasn't
talking about a high level HTTP session such as persistent data that
survives an individual TCP/IP connection.

If it seems that I'm trying to steer the discussion towards more
low-level concepts, well... I am :) For me to get from point A
(Beast's interfaces) to point B (what real users want, such as a
robust client or server template) I think of small steps that build on
what's already there.

> One important thing, I think is, that we should also be able to have no
> session tracking at all without having to pay for that

I agree with the general principle of "pay for what you use."

> So am I correct to assume, that the idea is to have a more or less arbitrary
> body object.

I'm not exactly sure what you mean by an arbitrary body object.

> In the end that will have to serialized to a string of bytes.

Yes, in order for an HTTP message instantiated with a specified Body
type to be Serializable, the Body must provide the nested type which
meets the requirements of Writer:
http://vinniefalco.github.io/beast/beast/ref/Writer.html

> Did you intend to have that implemented in the body object?

If I understand your question correctly, yes the Body type provides
both the container used to represent the body (this becomes a data
member in the message object) and the algorithms used to serialize and
parse the body. The choice of container is entirely up to you and
doesn't need to actually hold the data. In the Beast HTTP server
example, the file_body uses a std::string representing the path to the
file:
https://github.com/vinniefalco/Beast/blob/70b8555cdca69b9c5777db02115d30d1c1aad631/examples/file_body.hpp#L22
https://github.com/vinniefalco/Beast/blob/70b8555cdca69b9c5777db02115d30d1c1aad631/examples/file_body.hpp#L52

> My current idea was to call the template inside the handler
> and write the result to the body.

If you're serving a static file and anticipate that there will be
large files, I think you will get better results if you implement your
own custom Body type. Remember that the calculation of HTTP responses
happens on an io_service thread. Things work better if you block on
O(1) instead of O(n). So rather than reading in the entire file into
memory when building the response, its better to just send fixed size
pieces of the file from a custom writer. This also consumes less
memory, allowing the server to scale to more connections with the same
amount of resources.

The Beast file_body sends in chunks of 4K:
https://github.com/vinniefalco/Beast/blob/70b8555cdca69b9c5777db02115d30d1c1aad631/examples/file_body.hpp#L30

> I think, the router can be made to handle different return types

I don't know about that...I wouldn't use the return value.

> I also have some thoughts to make it
> more flexible on the parameters. For some handlers you'll be happy to just
> get the URL match, others will maybe need a stream for websocket
> interaction. I think, that can be achieved using boost call traits,
> std::result_of, std::is_callable, or similar things, and a little bit of not
> too difficult meta programming magic.

A code sample would greatly help in visualizing these ideas!

>> Another approach, instead of using the return type, is to give the
>> handler an object which when invoked with a message, queues or sends
>> the message.
> ...
> I also thought about that. For the time being, I chose to return the
> response object for semantic reasons. For me that is the result of the
> operation. With the approach of passing in a kind of response object, the
> operation would, as a side effect, respond to the request, and return
> something else, like an error code, or even void.

Adhering to language "purity" certainly has its merits but when
building general purpose code I think we need to consider all the
possibilities.

For returning the http::response_v1, using the indirect interface I
described above will result in smaller, easier to understand code.
Creating a system where functions return response objects of different
types (varying by their Body parameter) only creates complexity for
calling code. And the only advantage is that it is emotionally
satisfying - this will be hard to justify to users of the library.

For error_code, passing it as a non-const reference is a clear winner.
If you use error_code as the return value you are paying for a default
construction in every function on the call chain.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk