标签: C

  • Web development in C

    davidmoreno/onion

    kore

    klone

    nxweb

     

    fast C HTTP server library comparison & wishlist

    Hi,

    Trying to choose an embeddable HTTP server library for a project, and
    also considering writing my own special-purpose code, I came up with
    the following comparison of libonion vs. other C libraries that include
    high-performance HTTP support and are currently maintained.

    Licenses:

    libevhtp+libevent – 3-clause BSD
    libmicrohttpd – LGPL 2.1
    libonion – Apache 2 (except for some examples) or GPLv2+
    mongoose – GPLv2 (and commercial)

    Build environment:

    libevhtp+libevent – cmake+autotools
    libmicrohttpd – autotools
    libonion – cmake
    mongoose – none (one large file, like SQLite)

    Code size (“text” as reported by the size(1) command on the library or
    on a tiny sample program if statically linked, on Scientific Linux 6.6
    on x86_64):

    libevhtp+libevent – ~500 KB, or ~200 KB without unicode.c.o and reg*.c.o
    libmicrohttpd – ~100 KB default, ~55 KB with most ./configure –disable-*
    libonion – ~100 KB with most ONION_USE_* set to false
    mongoose – ~100 KB including JSON-RPC

    For the smaller builds of libmicrohttpd and libonion, I kept threads
    support enabled, but disabled pretty much everything else that could be
    disabled without patching the code.  It looks like libmicrohttpd wins
    this test.  Maybe there’s more code in libonion to disable (make into
    compile-time options) – I haven’t checked yet.

    Built-in JSON support:

    libevhtp+libevent – none
    libmicrohttpd – none
    libonion – JSON builtin, JSON-RPC in Apache 2 licensed example
    mongoose – JSON-RPC builtin (simple JSON parser not exported?)

    All of this is for current versions on GitHub or in recent release
    tarballs as of a few days ago.

    Maybe someone else will find this useful.  I’d appreciate corrections.
    It is very likely that I overlooked something.

    On a related note, I found the list of alternate implementations on the
    libmicrohttpd homepage very helpful.  That’s classy.  Thanks.

    My wishlist:

    A processes (pre-fork) + [e]poll mode, like nginx has.  Processes have
    pros and cons vs. threads: more reliable, faster malloc/free (no lock
    contention risk), but OTOH slower context switches (if running process
    count exceeds number of logical CPUs).  I would likely prefer this mode,
    but all four libraries appear to be missing it.

    Ability to accept not only HTTP, but also raw TCP connections, and
    handle them in application code along with the library-handled HTTP.
    Such as for implementing JSON-RPC directly over TCP, while also having
    it over TCP+HTTP, and without having to manage an own/separate
    threads/processes pool.  Do any of the four have this?  I found no such
    examples with any of them.

    Easily and cleanly embeddable into an application’s source tree, while
    also allowing easy updates to new upstream versions.  mongoose almost
    achieves this, but at the expense of sacrificing meaningful separation
    into multiple translation units within the library itself.  I think we
    don’t have to pay this price.  We could have multiple files (10 or so?),
    in a subdirectory, which are also easy to list in a project’s Makefile.
    Maybe I’d do that for libonion, freeing it from cmake, but then updating
    to new upstream versions would be harder.  Do I really have to bite the
    cmake or/and autotools bullet for something as simple as accepting HTTP?

    I’d prefer a more permissive license like 2-clause BSD or MIT.  But I
    guess I’ll have to settle on Apache 2 or such.  mongoose’ use of GPLv2
    is understandable – need to make money – but is otherwise a disadvantage
    (even for a commercial project that could pay, and even when publishing
    any source code changes is not a problem and would be planned anyway; we
    just don’t want to put our time into something that we would not always
    be able to reuse in other projects).

    Optional JSON from the same upstream is a plus, ideally exported both as
    a generic JSON parser and as JSON-RPC support.  Looks like only libonion
    sort of delivers both (but the code might not be production quality).

    Ability to exclude more of the functionality – for example, to include
    only the POST method (and not compile in code for the rest).  I am
    concerned not so much about code size per se, as I am about attack
    surface, and about ease of code reviews (not having to determine if some
    compiled-in code is actually dead code in a given case, but to know
    reliably that it’s not compiled in).

    On a related note, David’s use of Coverity for libonion is commendable,
    but it looks abandoned since 2014, and many “defects” (even if false
    positives) remained unfixed back then.

    Mark’s use of Coverity for libevhtp is also commendable… and looks
    abandoned since May 10, 2015.  It shows “48,919 Lines of Code Analyzed”,
    only “4 Total defects” and “0 Outstanding” – I guess it means that
    everything detected by Coverity before (which must have been many more
    “defects”) had been eliminated prior to that run.  That’s impressive.
    But we don’t know how many new “defects” may have appeared in the 9
    months that passed.  Also, I haven’t looked into whether libevent has
    been subjected to similar static analysis or not (although being
    initially written by Niels Provos speaks in its favor, given Niels’
    other work), and accepting TCP connections isn’t as much risk as parsing
    HTTP and JSON.

    I don’t give a lot of weight to the Coverity results for my
    decision-making, but it shows whether the maintainers care, and there
    are few other somewhat-meaningful metrics I could use before having
    spent time to analyze and try to use the code myself.

    Why am I posting this to the onion mailing list specifically?  I find it
    likely that libonion wins for me, although not by a large margin (and
    there’s a lot that I dislike about it).  This is not a final decision
    yet.  I might as well end up reverting to writing special-purpose code
    from scratch.

    Thanks,

    Alexander