Boost logo

Boost :

Subject: Re: [boost] [review][beast] Review of Beast starts today : July 1 - July 10
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2017-07-03 12:33:57


On 03/07/2017 03:07, Vinnie Falco via Boost wrote:
> On Sun, Jul 2, 2017 at 6:43 PM, Niall Douglas via Boost
> <boost_at_[hidden]> wrote:
>> It's nearly 3am here...
>
> I looked once again at http::basic_parser and http::serializer. It
> might be possible to refactor http::basic_parser into a separate class
> and with some mess in http::parser support two versions. One which
> allows ConstBufferSequence and the other which allows just pair<void*,
> std::size>.

I've always felt concern about pair<void*, size_t>. AFIO v2 uses it, but
I am uncomfortable with it. In your case you can't use it because if you
declare it as a buffer type to ASIO and another piece of code does the
same, you get an ODR violation. So probably a struct beast::buffer {
void *data; size_t length; }; or something is safer.

> http::serializer is another story entirely. It contains and is tied to
> adapters which contain Asio buffer types. And it uses Asio buffer
> algorithms.
>
> You've taken the position that this is an easy matter, that you have
> extensive knowledge and experience in the domain, so I would kindly
> ask that you provide a working prototype of http::serializer which
> does not use Asio buffer types and yet remains compatible with the
> rest of Beast.

Your http::serializer claims it is for:

1. Send the header first, and the body later.
2. Set chunk extensions or trailers using a chunk decorator.
3. Send a message incrementally: bounded work in each I/O cycle.
4. Use a series of caller-provided buffers to represent the body.

The way I've always implemented HTTP serialisers in the recent past is
as a collection of open ranges of contiguous byte buffers for ASIO to
gather send from.

Generally I've kept a headers() collection and a body() collection.
Iterators for the whole serialiser iterate all the headers first, then
the body. But if the end user wanted to send just the headers first,
they simply by hand feed the headers range to ASIO first, then the body
range. So that's Item 1 taken care of.

Item 2, as I mentioned before I've never needed more than basic HTTP in
my career to date, so never needed to implement chunked encoding. But I
can tell you what my first instinct is: body() is a FIFO drain, so as
the user appends new body buffers, they get drained and sent. So, the
end iterator returned by body() is able to stop pointing after the last
item in body() if new items are added to body().

Item 3 is very straightforward because the end user is the person
creating the iterator range to hand to ASIO. So they simply choose a
range which is a subset of the total instead of the total.

Item 4 is even easier, because this entire design already is a series of
caller provided buffers from start to finish. Beast might provide
convenience functions for generating those buffers, but essentially the
caller supplies the buffers.

Finally, trailers I have never had need to implement either, but I'd
suggest a trailers() collection for those as well. Exactly the same
semantics as earlier.

I appreciate Vinnie that you envisioned Beast as doing all this stuff
for the end user and hiding all this complexity by wrapping up ASIO with
an i/o API which hides how ASIO is being used underneath.

I think that makes sense in a high level HTTP library. For a low level
HTTP library I think it a design mistake, much simpler is much better.
Less is more. I've used the above serialiser design on a number of
occasions now, it's very efficient, composable, and flexible. It *does*
push understanding of HTTP onto the end user, but then if the end user
doesn't understand HTTP, they wouldn't be able to use a low level
library anyway.

Niall

-- 
ned Productions Limited Consulting
http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/

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