> I'm open to collaboration but also cautious.
>
That's good to know.
From the rest of your reply, I assume Boost.Http isn't ready to be
integrated right now.
I wish you could put more details in the documentation, so I can understand
Beast better without delving into the code.
Maybe I'll bug you more later once Boost.Http advances a little more. And,
like Bjorn already stated, nice library.
There's a strong need for simple free functions
> to send and receive HTTP messages on a socket in as few lines of code
> as possible. Beast.HTTP achieves this. Example code:
>
> using namespace beast::http;
> boost::asio::ip::tcp::socket sock(ios);
> ...
> request<empty_body> req({method_t::http_get, "/", 11});
> req.headers.replace("Host", "boost.org:80");
> req.headers.replace("User-Agent", "Beast.HTTP");
> write(sock, req);
>
You explained your point quite well.
Could you please write an example of how do you imagine the ideal
server-side API?
"Boost.Http currently only provides a server-side API, but the
> reviewers felt that a client-side API would be usable to more users."
>
> Beast.HTTP is completely role-agnostic, and works for building clients
> and servers.
>
We hope to develop the client-side API after the parser project is
finished. The same abstractions should be used too. However, it may be
useful to add client-side only useful functions where we can have
specialized get or post methods.
Bjorn is wrapping libcurl into a Boost-like interface to gain knowledge:
https://github.com/breese/trial.http.curl
"There was also a recurring request for Boost.Http to be a header-only
> library. The HTTP parser currently used is not header-only and that
> is the main obstacle towards a header-only Boost.Http library."
>
> Agree 100%. Beast.HTTP also uses the NodeJS parser, and that's the
> only bit of code that is not header only. Thursday I started on a
> header-only parser, here's what we have so far (please keep in mind,
> this is a work in progress):
>
> https://github.com/vinniefalco/rippled/blob/my-parser/src/beast/test/http/my_parser.cpp
>
I wasn't aware of this effort before.
I took a look now that you mentioned. From what I've seen, I assume that
this parser has a SAX-like interface where you interact through callbacks.
The Boost.Http parser that will be developed within this summer will be a
pull parser. It has a less intrusive design and can be used to build parser
with SAX-like interfaces or DOM-like interfaces. It should be useful enough
to also be used as is (i.e. without wrapping in another interface). It
should also be easy to expose iterators using this parser and allow us to
reuse all STL algorithms.
You can take a look at this parser's proposal at
https://gist.github.com/vinipsmaker/4998ccfacb971a0dc1bd .
You can see an example of a pull parser at
https://github.com/google/pulldown-cmark .
--
VinÃcius dos Santos Oliveira
https://vinipsmaker.github.io/
Agreed. The HTTP side can definitely use more documentation. Since the
announcement I added a new page which explains things step by step:
http://vinniefalco.github.io/beast/beast/http/usage.html
> like Bjorn already stated, nice library.
Thanks, it is appreciated!
> Could you please write an example of how do you imagine the ideal
> server-side API?
Well, being role-agnostic means the same functions are used by both
clients and servers. The only meaningful difference is that a server
will receive request objects and send response objects instead of the
other way around. The message class template distinguishes requests
and responses using a bool "isRequest" template argument. These
statements each declare a response:
http::message<false, http::string_body> resp;
http::response<http::string_body> resp2;
Both of these declarations are identical, http::response is merely a
type alias for http::message with the bool "isRequest" set to true.
The same functions are used by both clients and servers to read and
write messages:
// synchronous
http::write(sock, resp);
// asynchronous
void handle_write(boost::system::error_code);
..
http::async_write(sock, resp,
std::bind(&handle_write, std::placeholders::_1));
Beast comes with an example program that implements a basic web
server, capable of serving simple HTML, in both synchronous and
asynchronous forms:
https://github.com/vinniefalco/Beast/blob/master/examples/http_sync_server.h
https://github.com/vinniefalco/Beast/blob/master/examples/http_async_server.h
> ...it may be useful to add client-side only useful functions where we can
> have specialized get or post methods.
These definitely sound like useful operations, and they should be
available at some interface level. But its not clear that they they
are sufficiently general purpose as to merit inclusion in a library
that tries to satisfy everyone. No matter how specialized the get or
post method there still needs to be a way to package the message up in
a first-class type and send or receive it; Beast provides the means to
do that.
>> Thursday I started on a header-only parser,
>
> I wasn't aware of this effort before.
Its a minor effort, likely not worthy of fanfare.
> I took a look now that you mentioned. From what I've seen, I assume that
> this parser has a SAX-like interface where you interact through callbacks.
I'm writing something that functions very similarly in style to the
nodejs-http-parser, but updated for C++. The goal here is to eliminate
a blemish on Beast, that it is not completely header-only. It inherits
the zero-memory / zero-copy design of nodejs-http-parse to retain as
much of its performance as possible (although, it does away with
architecture-specific branch prediction hints). Some of the design
improvements I'm making:
* Users derive from the parser base class using CRTP (Curiously
Recurring Template Pattern)
* Callbacks are optional, detected through SFINAE
* Callbacks are made to the derived class
* The callbacks are transparent to the compiler (i.e. no function
pointers), allowing inlining
* No macros or dependence on preprocessor directives
* No dependencies except for boost::string_ref; easily reused in other projects
* Random HTTP-message generator for fuzzing
> The Boost.Http parser that will be developed within this summer will be a
> pull parser. It has a less intrusive design and can be used to build parser
> with SAX-like interfaces or DOM-like interfaces. It should be useful enough
> to also be used as is (i.e. without wrapping in another interface).
Beast doesn't try to offer a universal or flexible parser, it just
offers a parser that gets users reading messages right out of the box,
and is sufficiently robust and performant as to make it a competitive
choice for implementing production-class servers.
Beast's HTTP message reading implementation is general purpose,
callers can provide their own Parser template argument that meets the
type requirements (*), permitting alternate implementation strategies.
For example, keeping only the headers you care about. Or using a
perfect hash function to decode the field name to an enum. This works
hand in hand with customizing the Headers parameter in the message
class template argument.
(*) planned feature
> It should also be easy to expose iterators using this parser and allow us to
> reuse all STL algorithms.
Since the whole thing is now templated it might be practical to revise
the interface to accept a Boost.Range of chars and work with iterators
in the fashion you described. That could be the subject of a future
improvement.
> > Could you please write an example of how do you imagine the ideal
> > server-side API?
>
> Well, being role-agnostic means the same functions are used by both
> clients and servers. The only meaningful difference is that a server
> will receive request objects and send response objects instead of the
> other way around. The message class template distinguishes requests
> and responses using a bool "isRequest" template argument. These
> statements each declare a response: [...]
>
This makes me think that the two projects are more alike than you might
think.
The main difference here is just that Boost.Http's Message don't carry
request-exclusive members (HTTP verb, uri) or response-exclusive members
(status code, status messages) and methods that work on them take them
separately. I'm open to follow a design more similar to yours (just need to
discuss with Bjorn and other members first as I remember this decision was
taken to follow a more fundamental message-based model).
Another main difference is that Boost.Http server side design is really
concerned about different HTTP backends. However, this is only observed in
details and I really doubt they'll bother you. At most, I need to implement
more convenient functions to remove the amount of boilerplate needed today
(already on the TODO list and on the GitHub issue tracker).
I believe the rest of the differences are really minor.
Of course I might be wrong and I'll know once more information about the
project is exposed (documentation maybe).
"Some reviewers also felt that HTTP/2 should be part of Boost.Http,
> partly because that would demonstrate the extensibility of the current
> design, and partly because the library would be in a stronger
> position to attract users if it offers more than its competitors."
>
> The IETF adopted as a goal for HTTP/2, to leave the message the same
> (while changing its wire format). Therefore, Beast.HTTP's message
> model is already HTTP/2-friendly.
>
> As for the extensibility of the design, free functions to send and
> receive HTTP messages are fundamentally incompatible with HTTP/2,
> which requires ongoing
> state to decompress headers and multiplex streams. And yet, we know
> that free functions to send and receive HTTP/1 messages are useful and
> sorely needed.
>
> Furthermore HTTP/2 adds features that don't make sense for HTTP/1.
> What would you do with the stream ID? It is not part of the message
> model, because it describes a transport level property (and what
> meaning would it have for a HTTP/1 message? or someone who is using
> the message object but not using any sockets at all?) What about the
> interface to inform the peer of a new receive window size? What about
> setting stream priorities/weights? How do we sort all this out?
>
> The interface for HTTP/1 should consist of a universal message model,
> plus functions to send and receive those messages on sockets. The
> interface for HTTP/2 should be a class template that wraps a socket
> (similar in style to beast::websocket::stream), deals in the same
> universal message model, and offers member functions to send and
> receive those messages with associated stream IDs as well as adjust
> HTTP/2-specific session parameters.
>
> We respectfully disagree with those reviewers who feel that a single
> interface should serve the needs of sending and receiving both HTTP/1
> and HTTP/2 messages.
>
But the two interfaces could be very similar. We also don't need force the
user to mess with stream IDs.
Server push is another story and indeed needs new APIs.
--
VinÃcius dos Santos Oliveira
https://vinipsmaker.github.io/
_______________________________________________
The discussion began with defining the role of Beast. Based on
feedback from IRC and VinÃcius, I realized that Beast is very much a
low level library and does not try to satisfy end users with
convenient one liners for doing common tasks like fetching a webpage
from a server. We formalized that role for Beast, defining these areas
of responsibility for the library:
1. Define a universal HTTP message model
2. Parse messages in HTTP/1.* wire format
3. Read HTTP/1.* messages from a socket/stream
4. Serialize messages in HTTP/1.* wire format
5. Write HTTP/1.* message to a socket/stream
While these interfaces are essential building blocks they don't
directly satisfy the use-cases that most users want. Boost.Http aims
to fill those gaps by providing higher level abstractions. For
example, its http server offering manages common tasks such as setting
ETag on replies, serving files asynchronously, and managing connection
state.
I introduced a new class, beast::http::basic_parser which is a
header-only HTTP parser in the style of the nodejs parser written in C
commonly used by these libraries, but updated to use C++ features like
CRTP and templating. This now-complete implementation allows existing
and new libraries to migrate off the nodejs parser and become
header-only offerings.
Discussions then moved to refinement of the BasicParser concept and
its role in deserializing messages. We discussed the Body concept and
its associated types permitting customization of serialization and
deserialization strategies.
Finally we talked about what a collaboration might look like. We
discussed the pros and cons of merging the projects, its effect on
development, documentation, tests, and the disposition of the
WebSocket portion of Beast. The conclusion for now is to explore Beast
integration in Boost.Http, where Beast's role is to provide the low
level operations for serializing, deserializing HTTP/1.1 to sockets
and streams. Beast could be incorporated as a submodule for now, with
tighter integration choices considered later.
We ended with a few calls to action:
* Changes to Beast message interfaces to facilitate integration
* Explore integration of beast::http::basic_parser into Boost.Http
Thanks
On Fri, Apr 22, 2016 at 1:01 PM, Vinnie Falco <vinnie...@gmail.com> wrote:
> "Beast"
>
> This is a new library that implements the HTTP and WebSocket protocols
> using Boost.Asio and a few other Boost tidbits. Here's a quick
> synopsis of what's in it:
_______________________________________________