Boost logo

Boost :

Subject: Re: [boost] [http] Formal review of Boost.Http
From: Darren Cook (darren_at_[hidden])
Date: 2015-08-15 15:18:48


I'm out of time to actually try the library, so will give my review
based on just the study of the documentation/mailing list comments.

> 1. Should Boost.Http be accepted into Boost? Please state all
> conditions for acceptance explicitly.

No, sorry.

> 2. What is your evaluation of the design?
> 3. What is your evaluation of the implementation?
> 4. What is your evaluation of the documentation?
> 5. What is your evaluation of the potential usefulness of the library?

The documentation did not allow me to tell if this is a weak library, or
a good library that is being marketed badly.

It contains a single example which is very long and off-putting. The
initial page gives a list of features, and the design rationale repeats
them, but not how to use any of them.

The first example in the documentation should be no more than 10 lines.
Then there must be examples that show: SSL (using a self-signed
certificate), a file server, a routing application with 2-3 routers
doing distinct things and an example of streaming data.

>From what I can tell, the design is an ambitious attempt to be all
things to all people. IMHO this is a fatal mistake.

Naming and Scope: It should be called Boost.HttpServer; and then there
should be another library called Boost.HttpClient. Reasons:

   1. They will share very little public API code

   2. The majority of projects that need an http server will not also
need an http client; the huge majority of projects that need an http
client will not need an http server. (The 1% that need both can just
include both libraries.)

   3. There is not actually a great amount of implementation code they
share.

   4. Http is a protocol. Libraries shouldn't map to protocols, they
should map to the work people need to do.

API Level: What C++ badly needs is a high-level, easy to use http server
library. It needs a high-level http client library even more badly.

The proposed library looks like some building blocks towards this. I
suggest it either stay as a github project that an
Boost.HttpServer can build upon, or be merged into asio as an extension.

Here is the kind of http server api I was hoping to see up for review
(this is the short motivating example I expected to see as the first
thing in the tutorial): (...moved to [1])

My opinion on some other topics the review has brought up:

  Header-only: Yes, important.

  Pre-C++11 support: No need. C++11 is more pleasant, and building in
reliance on lambda or any other C++11 feature is acceptable if it makes
the design nicer. I could even accept it being C++14-only if there was a
good reason.

  Asio Integration: No need. I have used boost::asio on some projects. I
do not enjoy using it, it is easy to get things wrong, code tends to
become long and complicated and/or it is under-documented. (While I wait
for my ideal C++ http-server I will now use posix socket's C functions
directly. And while waiting for my ideal C++ http-client implementation,
I am using curl.)
  In other words, if it is built on top of asio I want that as an
implementation detail I don't have to care about.

  Http/2: Not essential at this stage. However it has to be clear that
the design and API supports moving to it transparently. As that may not
be trivial, I think a library offered for review does have to show
support for http/2.

 Streaming: I would like to see examples of sending a chunked response,
and of sending a streaming (SSE or old-fashioned comet) response that
never closes. This may reveal bugs or design flaws.

  Routing: A built-in router should be part of the version for review.
It should be a distinct component so that alternatives can be used, but
`boost::httpserver::server` should be using a
"pleases-most-people-most-of-the-time" implementation.
   If routing is not included, everyone will end up rolling their own,
and that is bad.

  Benchmarks: I would like to see CPU and memory usage benchmarks;
ideally on reference hardware such as an Amazon EC2 instance. Realistic
examples such as: a simple file server, a file-server that is doing
substitution/modification of the files being read, and a chat
server. This is because artificial benchmarks often encourage making the
design harder to use for the sake of a few microseconds. I'd rather have
a server, doing real work, that can support 499 clients/second than one
that can support 500 clients/second but requires me to write twice as
much code.

> 6. Did you try to use the library? With what compiler? Did you have
> any problems?

No.

> 7. How much effort did you put into your evaluation? A glance? A quick
> reading? In-depth study?

Maybe 10 hours reading the documentation, following the discussion, and
forming my thoughts here. I only glanced at the code on github (mainly
in the hope of finding more examples or documentation).

> 8. Are you knowledgeable about the problem domain?

Reasonably so. I've written socket clients and socket servers in C++,
C#, PHP and Node.js. I have used asio, and looked at various C++ http
libraries (usually ending up rejecting them all). I have a lot of
experience with LAMP stacks, and with http scaling issues. For an
http server API my favourite design is node.js's http module (though it
lacks routing). For an http client I like the power and simplicity of
PHP's file_get_contents().

Darren

-- 
Darren Cook, Software Researcher/Developer
My new book: Data Push Apps with HTML5 SSE
Published by O'Reilly: (ask me for a discount code!)
  http://shop.oreilly.com/product/0636920030928.do
Also on Amazon and at all good booksellers!
[1]:
  #include <boost/http_server.h>
  using namespace boost::httpserver;
  int main(){
  server server;
  server.add_handler([](request request,response response){
    response.send("Hello client");
    });
  server.run();
  return 0;	//Never reach here
  }
The constructor defaults options, in this case defaulting to http/1.1,
listening on 127.0.0.1, listening on port 80, and no SSL. [2]
add_handler() with just one parameter takes a function that is the
default handler. With two parameters, the first is a router pattern
match, and the second is the function to handle it.
The function takes request and response objects. The request tells me
the URL and the headers and the cookies. The response is what I talk
with. Beyond send(), there are functions to send cookies and other
headers. It defaults to status code 200 if one not given before the
first send().
server.run() is (conceptually) implemented as:
   server.bind();
   server.listen();
They throw exceptions for errors. server.listen() is an infinite loop.
By default it maintains a thread pool, and each request runs on a
dedicated thread. [3]
[2]: For implementation I think I'd go with separate classes for each of
the combinations of SSL/no-SSL and http/1.0, http/1.1 and http/2, and
boost::httpserver::server would just be a typedef. (A scaled-up web
server would usually put the global IP and SSL certificate on the load
balancer, not on the individual clients.)
An SSL version might look like:
  http11_SSL_server server;
  server.options['key'] = "/path/to/my-key.pem";
  server.options['cert'] = "/path/to/my-cert.pem";
  server.add_handler(...);
  server.run();
I prefer using an std::map<std::string, std::string> options, as it can
be passed around to helper classes, i.e. compared to this:
  server.key = "/path/to/my-key.pem";
  server.cert = "/path/to/my-cert.pem";
[3]: I would have the request handler object as a templated parameter on
the server. Then it can be replaced with one using fibres, external cgi
processes, etc. However it shares the same options map as the server object.

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