Boost.HTTPKit, a new library from the makers of Beast!
During the formal review of Beast the desire was expressed to have parsing and serialization available independently of Asio. Unfortunately the obstacles to this are the concrete functions and types required for interoperability with the buffer sequence concepts defined in Asio. I have drawn up a solution to this problem which involves creating two brand new libraries. The first new library is called Boost.Buffers, and I described it partially in an earlier list posting. This library will contain copies of just the header files from Asio needed to implement the buffer sequence concepts. A preprocessor switch will allow users of Boost.Buffers to decide whether to use the copies of the headers, or to just include the Asio headers directly: <https://github.com/vinniefalco/buffers/blob/30ef7031ec0909972a720c0cdd8d6c6e1cc9e37b/include/boost/buffers/asio.hpp#L13> If an author wants to develop a library which uses just the buffer sequence concepts from Asio, then instead of writing: #include <boost/asio/buffer.hpp> they will instead write: #include <boost/buffers/asio.hpp> in header files, and in their build scripts set: #define BOOST_NET_BUFFER_NO_ASIO 1 This way, their tests and example programs will use the copies of the asio headers found in the Boost.Buffers libraries and everything will work. Boost.Asio will not be a dependency. However, if a consumer of that library wants to also use Boost.Asio, they can do so by just not setting the macro in their build scripts (or explicitly setting it to 0). Now that we have a way to write a library that uses Asio buffer concepts without depending on all of Asio, we can move the Beast dynamic buffer implementations and buffer adapters into the Boost.Buffers library. Boost.Buffers will contain these public includes: Boost.Buffers <boost/buffers.hpp> <boost/buffers/asio.hpp> boost::asio::const_buffer boost::asio::const_buffers_1 boost::asio::mutable_buffer boost::asio::mutable_buffers_1 boost::asio::buffer_copy boost::asio::buffer_size boost::asio::buffer_cast boost::asio:is_const_buffer_sequence boost::asio:is_mutable_buffer_sequence <boost/buffers/buffers_adapter.hpp> <boost/buffers/buffers_cat.hpp> <boost/buffers/buffers_prefix.hpp> <boost/buffers/buffers_suffix.hpp> <boost/buffers/buffers_to_string.hpp> <boost/buffers/flat_buffer.hpp> <boost/buffers/flat_static_buffer.hpp> <boost/buffers/multi_buffer.hpp> <boost/buffers/ostream.hpp> <boost/buffers/read_size.hpp> <boost/buffers/static_buffer.hpp> Boost.Buffers would have dependencies on these Boost libraries: Array Assert Config Core Exception Intrusive StaticAssert ThrowException TypeTraits Beast will then be modified to take a dependency on Boost.Buffers to have access to all the buffer adapters and dynamic buffer implementations. Now that we have a new library which offers Asio buffer concepts and Beast's collection of useful dynamic buffers and buffer adapters, we can extract the HTTP parsing and serialization algorithms from Beast to form a new library: Boost.HTTPKit will contain these public includes: Boost.HTTPKit <boost/http/basic_file_body.hpp> <boost/http/basic_dynamic_body.hpp> <boost/http/basic_parser.hpp> <boost/http/buffer_body.hpp> <boost/http/chunk_encode.hpp> <boost/http/dynamic_body.hpp> <boost/http/empty_body.hpp> <boost/http/parse_error.hpp> <boost/http/field.hpp> <boost/http/fields.hpp> <boost/http/message.hpp> <boost/http/parser.hpp> <boost/http/rfc7230.hpp> <boost/http/serializer.hpp> <boost/http/span_body.hpp> <boost/http/status.hpp> <boost/http/string_body.hpp> <boost/http/string_param.hpp> <boost/http/type_traits.hpp> <boost/http/vector_body.hpp> <boost/http/verb.hpp> Boost.HTTPKit would have dependencies on these Boost libraries: Array Assert Config Core Exception Intrusive StaticAssert ThrowException TypeTraits This new library provides serialization and parsing of HTTP/1 messages to and from Asio buffer sequences, without requiring the full Boost.Asio dependency. The library also provides Beast's universal HTTP message container. Beast will be modified to take an additional dependency on Boost.HTTPKit. This solution satisfies long-stated needs of users to have HTTP parsing and serialization without Boost.Asio. However, there is one problem and that is the documentation. All of Beast's documentation related to buffer-oriented parsing and serialization, message containers, Body types, Fields concept, buffer sequences, and dynamic buffers will now be moved into two other libraries. This leaves the Beast documentation objectively worse off for users, as it is impossible to cross-link to other library documentation from inside a Javadoc comment extracted by Doxygen. I would like to hear ideas for how to smooth this out. Questions: How does the community feel about: * Boost.Buffers as a solution to accessing buffer concepts without Asio? * Boost.Buffers offering Beast's buffer sequence adapters and dynamic buffers? * Boost.HTTPKit depending on Boost.Buffers? * Boost.HTTPKit offering serialization and parsing without Asio? Any feedback is appreciated Thanks
2017-10-04 12:22 GMT-03:00 Vinnie Falco via Boost <boost@lists.boost.org>:
This solution satisfies long-stated needs of users to have HTTP parsing and serialization without Boost.Asio.
Could you compare this to my parser[1]? I've been writing this project since the last year[2]. It could be that I'm slow, but I'm starting to think you like to rush design decisions. [1] https://vinipsmaker.github.io/asiohttpserver/ [2] https://gist.github.com/vinipsmaker/4998ccfacb971a0dc1bd -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/
On Wed, Oct 4, 2017 at 9:44 AM, Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com> wrote:
Could you compare this to my parser[1]?
HTTP parsing is a pretty well defined problem and if you want to have decent performance, there aren't a whole lot of different ways to write it. Looking over your code I don't see any obvious implementation mistakes although as far as the interface goes I would have made different choices, as you can see by looking at the declaration: <https://github.com/boostorg/beast/blob/7fe74b1bf544a64ecf8985fde44abf88f9902251/include/boost/beast/http/basic_parser.hpp#L171> The biggest, obvious difference is that beast::http::basic_parser and beast::http::serializer are already in Boost, and have gotten extensive testing and vetting from stakeholders. It is also running on production servers that handle hundreds of millions of dollars worth of financial transactions per month.
It could be that I'm slow
I don't know if I would say that you're slow as I have not observed your workflow but a comparison is probably unfair, as I have the opportunity to work on Beast and related projects such as Boost.Buffers and Boost.HTTPKit full-time.
but I'm starting to think you like to rush design decisions.
Now that is really unfair, and ignores the enormous amount of hours not just from me but from all of the other people who participated in the design of Beast (note that I invited you to be one of those participants). Nothing about Beast was rushed, in fact for 3 months in 2016 I was blocked on the parser design because I could not figure out an elegant interface that worked with the stream algorithms and allowed the users to supply their own buffer for the body. I tried several different designs, if you look in the commit log you can find those alternate designs. Here are just a few of the design collaborations that helped to get Boost.Beast where it is at today: Progressive body reading (122 comments!) <https://github.com/boostorg/beast/issues/154> Split parsing / headers first <https://github.com/boostorg/beast/issues/132> Expect: 100-continue design <https://github.com/boostorg/beast/issues/123> Message container constructors <https://github.com/boostorg/beast/issues/13> Fields concept and allocator support <https://github.com/boostorg/beast/issues/124> Refactor Body types <https://github.com/boostorg/beast/issues/580> Asio deallocate-before-invoke contract <https://github.com/boostorg/beast/issues/215> And this does not include over a hundred hours working with engineers from Ripple (Beast was originally designed for them) on shared code pads to create the initial container and stream algorithm ideas. Quite a lot of time went into the design of Beast in fact there was far more time spent designing than actual coding. And I couldn't have done it without the very helpful interaction with actual users of the library; Beast was shaped by the needs of stakeholders. As a reminder to everyone, the Beast pull request / issues queue is open to all and I welcome anyone who wants to participate in the design of the library to jump right in and comment on any of the issues or pull requests. You can see that there are a number of open issues that have unresolved design questions: <https://github.com/boostorg/beast/issues?q=is%3Aissue+is%3Aopen+label%3ADesign> Thanks!
2017-10-04 15:20 GMT-03:00 Vinnie Falco <vinnie.falco@gmail.com>:
Now that is really unfair, and ignores the enormous amount of hours not just from me but from all of the other people who participated in the design of Beast
If anything, it was my response that was rushed. Sorry about that. Now, moving on... given that you have __not__ answered how your parser's design[1] compares to the parser I've developed, I wrote an “implementing Boost.Beast parser interface” in my documentation: https://vinipsmaker.github.io/asiohttpserver/#_implementing_ boost_beast_parser_interface This tutorial is full of “design implications” blocks where I take the time to dissect what it means to go with each choice. (note that I invited
you to be one of those participants).
Yes, just bad timing. [1] at most it hides behind a “multiple people/stakeholders agree with me” __shield__ -- Vinícius dos Santos Oliveira https://vinipsmaker.github.io/
How does the community feel about:
* Boost.Buffers as a solution to accessing buffer concepts without Asio?
* Boost.Buffers offering Beast's buffer sequence adapters and dynamic buffers?
I reiterate from my Beast review that the best design for these above is to use: * span<T> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0122r5.pdf * Ranges TS http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4685.pdf Both of these can integrate with the Networking TS, but not the other way round due to ASIO's legacy design. You chose, against my advice, to base Beast on the outdated and hard coded buffer sequence design in ASIO. It was not a showstopper for Beast because Beast was so closely tied into ASIO, so I recommended acceptance for Beast despite this design choice. But for a standalone library, things are different. I would advise strongly against accepting a Boost.Buffers library unless it is based on span<T> and Ranges and is forward looking into C++ 20 design patterns, not backwards at C++ 98 era design patterns like ASIO's buffer sequences. Regarding HTTPKit etc, I haven't looked into it much, but if I remember I had some issues with your implementation and design there too specifically the lack of use of zero copy views. Again, as part of Beast wholly dependent on ASIO and thus limited by ASIO's limits, that's acceptable. As a standalone library where a much wider use case applies, I think I would need to set a much higher bar in any review. But I haven't looked into it, you may have completely changed the design and implementation since I last looked. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
On Wed, Oct 4, 2017 at 4:18 PM, Niall Douglas via Boost <boost@lists.boost.org> wrote:
I reiterate from my Beast review that the best design for these above is to use:
* span<T> * Ranges TS
First of all, thanks for investing the time to respond, your feedback is appreciated. I think this might be one of those rare cases where I am in partial agreement. A design that uses standard library types is better than one which requires `std::experimental::net::const_buffer` or `boost::asio::const_buffer`. However, a couple of points: * Ranges TS is unnecessary, as ConstBufferSequence and MutableBufferSequence are already modeled after ForwardRange. * `span<byte>` and `span<byte const>` are not great choices because `span` and `byte` are not part of Boost and not part of the standard library. The closest we can get and still have something compatible with C++11 is `pair<void const*, size_t>` and `pair<void*, size_t>`. * Paul's suggestion to change the requirements of ConvertibleToConstBuffer is even better. Change the definition of `const_buffer` to include conversion constructors that can accept a more general concept, such as any class which implements data() and size(). If I was to re-implement Beast's buffer adapters, dynamic buffers, parser, and serializer to use these ideas then they would no longer be compatible with Asio. Using them would be more cumbersome and less composable with other standard components which use Asio buffer concepts. Feedback from users is that they overwhelmingly prefer solutions that work out of the box over adherance to some model of buffer abstraction. I have no control over what happens in Boost.Asio and I do not control the evolution of the Networking TS although I have filed some additional issues against it. For better or worse, Boost.Asio is the model that we have and what people are coding against. Therefore, as with Beast my design choice here is pragmatic - use what exists, and what works. The approach offered in Boost.Buffers allows libraries to be written which do not depend on all of Boost.Asio, but yet offer compatibility with Asio's buffer concepts. There's a light at the end of the tunnel though, if you can convince the Asio author and influence the evolution of Networking-TS to make changes to the buffer model to eliminate the custom types, then my proposed Boost.Buffers library could be updated to reflect those changes. The beneficiaries will at all times be the users - they get something that works today, and the possibility of something that will work even better tomorrow. One thing you might consider, is that span<T> does not have the "buffer debugging" feature found in `boost::asio::const_buffer`: <https://github.com/boostorg/asio/blob/b002097359f246b7b1478775251dfb153ab3ff4b/include/boost/asio/buffer.hpp#L109> I don't see a sane way to add that feature to span<> in a way that doesn't burden everyone using span instead of just the people using it for networking buffers. The buffer debugging feature is really useful. Asynchronous programming is already hard enough, having every additional advantage helps.
You chose, against my advice, to base Beast on the outdated and hard coded buffer sequence design in ASIO.
I'll say the same thing I said the last time you brought this up. If you feel strongly about it, then you need to write a paper. Otherwise, your advice to me effectively becomes "ignore existing and emerging standards and invent a new, incompatible concept." That's way too much risk for me to take on, and I have no evidence that my users want such a thing. All the feedback I have received thus far cites compatibility with Asio as a primary motivator for adoption of Beast. Ignoring this seems...reckless.
But for a standalone library, things are different. I would advise strongly against accepting a Boost.Buffers library unless it is based on span<T> and Ranges and is forward looking into C++ 20 design patterns, not backwards at C++ 98 era design patterns like ASIO's buffer sequences.
If you feel that span<T>, Ranges, and C++20 design patterns are important then you should be enthusiastic about my Boost.Buffers proposal, because it physically separates the buffer concepts from the networking algorithms. It effectively "factors out the buffers from Asio." In other words it completes the first step of achieving your goal - it separates buffers from the rest of Asio where it can be acted on independently. Note that I am in favor of Paul's proposal to change the requirements of ConvertibleToConstBuffer, in order to relax the dependency on a concrete type. I believe this is in line with your goals as well.
Regarding HTTPKit etc, I haven't looked into it much, but if I remember I had some issues with your implementation and design there too specifically the lack of use of zero copy views.
In the version of Beast that was accepted into Boost, beast::http::basic_parser was already "zero-copy" (when presented with input as a single contiguous buffer) and beast::http::serializer was "zero-copy". They still are.
As a standalone library where a much wider use case applies, I think I would need to set a much higher bar in any review.
So, I think my comments earlier apply here as well. The primary consumers of beast::http::basic_parser and beast::http::serializer are using Asio buffer concepts (Beast stream algorithms being the best examples). Factoring out the buffer concepts from Asio into a new library Boost.Buffers, and then factoring out the parser and serializer from Beast into a new library Boost.HTTPKit which uses Boost.Buffers without needing Boost.Asio as a dependency seems like a very pragmatic way forward which gives stakeholders something they've been asking for, doesn't reinvent established buffer concepts, doesn't force Boost.Asio as a dependency, and yet is compatible with Boost.Asio for the users that want it. Thanks
participants (3)
-
Niall Douglas
-
Vinnie Falco
-
Vinícius dos Santos Oliveira