
Show HN: Drogon – A C++14/17 based high performance HTTP application framework - an-tao
https://github.com/an-tao/drogon
======
shakna
2 small things.

a) Show me the code to start with, don't send me digging for it. A
SimpleController example as the very first thing would help give a feel for
the project, and makes me more likely to consider the project.

b) If there's an easier way (like the drogon_ctl utility at the start of
Quickstart [0]), show that first, and the more detailed way second.

Other than that, it looks great. I've used libmicrohttpd a few times, so a bit
less of an overhead always looks great.

[0] [https://github.com/an-tao/drogon/wiki/quick-start](https://github.com/an-
tao/drogon/wiki/quick-start)

~~~
an-tao
Your suggestion is very good, I have made some modifications.

------
KirinDave
This is cool, and I like it. Very Haskell like, which is a compliment in my
book.

But one thing that surprises me is that folks are essentially sleeping on
HTTP/2\. HTTP/2 is just a hell of a lot better in most every dimension. It's
better for handshake latency, it's better for bandwidth in most cases, it's
better for eliminating excess SSL overhead and also, it's kinda easier to
write client libraries for, because it's so much simpler (although the
parallel and concurrent nature of connections will challenge a lot of
programmers).

It's not bad to see a new contender in this space, but it's surprising that it
isn't http/2 first. Is there a good reason for this? It's busted through 90%
support on caniuse, so it's hard to make an argument that adoption holds it
back.

~~~
ilovecaching
By that argument, they should get cracking on HTTP/3 already. :)

Even though HTTP/2 is usable, adoption is still low (last I checked it was
around 25%), and any server that supports 2 needs to support 1 for backwards
compatability. It's also more complicated to implement, so I can see why you'd
want to start with HTTP/1.

~~~
KirinDave
HTTP/2 has support on over 90% on caniuse, which is a decent approximation of
what most folks selling products here will see. The browsers that don't
support it aren't the sort we really care that much about anyways unless we're
building a bank or something, in which case suffering is your mandate anyways.

As for client libraries for APIs, it's a non-issue. There's plenty of
flexibility on backends.

I'm not saying you'd skip "HTTP/1" but I certainly wouldn't put a lot of
effort into it.

------
utopcell
I've used Crow [1], a C++ web micro framework, in the past and it was
delightful. Glad to see more competition in this space.

[1] [https://github.com/ipkn/crow](https://github.com/ipkn/crow)

------
StreamBright
It should be submitted to Techempower benchmarks.

[https://www.techempower.com/benchmarks/](https://www.techempower.com/benchmarks/)

~~~
an-tao
It has recently been submitted to TFB.

[https://github.com/TechEmpower/FrameworkBenchmarks/tree/mast...](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/C%2B%2B/drogon)

~~~
StreamBright
Good to know thanks!

------
WalterGR
What’s the security story with this framework?

~~~
ilovecaching
If you really want a security story, you shouldn't be looking into a C++
server. That's a very false sense of security, there is no way to guarantee
memory safety.

~~~
otabdeveloper2
> there is no way to guarantee memory safety.

Absolutely not true for C++. The language was invented to guarantee memory
safety, like Rust was. (The fact that people ignore the safety features when
coding is irrelevant, people liberally throw 'unsafes' around in Rust and
Haskell too.)

~~~
Thorrez
When a simple

    
    
        int foo(int x) {
          return x+1;
        }
    

leads to undefined behavior (aka the standard says the program can delete your
hard drive) if x is too big, that's not a language that guarantees memory
safety.

~~~
dearrifling
That has nothing to do with memory safety, that's just plain UB.

~~~
Thorrez
According to the spec, it can lead to all the same problems that any UB leads
to, including all the problems that any memory unsafety leads to. The code is
allowed by the spec to cause memory unsafety.

But to list some direct memory unsafety possibilities: indexing off the end of
an array, indexing off the end of a vector, dereferencing a bad pointer,
dereferencing a null pointer, dereferencing a bad iterator, double delete.

------
vivekbernard1
Not that it's super important, but it'll be interesting to see this in the
next techempower benchmark round.

------
keyle
I wish it told me why it's better than nginx/others rather than why the author
picked that name.

~~~
securityfreak
Drogon is an application framework. Nginx is a web server. You can’t write a
custom application in nginx. You can write almost anything in Drogon.

Both handle HTTP requests, but nginx mostly forwards requests to other stacks.

~~~
dagw
_You can’t write a custom application in nginx._

Using Nginx modules and LuaJIT, you kind of can. OpenResty is a web framework
built using this principle.

------
umvi
I like it, but the API for defining HTTP endpoints seems messy compared to,
say, Flask or ExpressJS. Is there any particular reason it is done this way vs
something syntactically cleaner, like:

    
    
        app.get("/test", [](drogon::Request & req, drogon::Response & res) {
            // Business Logic Goes Here?
        });
    

I'm going to be honest, I'm turned off by the macros and general API style of
endpoint definition. I know, I know...

> Don't be scared by the code.

I can't help it!

~~~
an-tao
You can register handler like this in Drogon:

    
    
       drogon::app.registerHttpMethod("/test",
                                       [=](const HttpRequestPtr& req,
                                           const std::function<void (const HttpResponsePtr &)> & callback)
                                       {
                                           Json::Value json;
                                           json["result"]="ok";
                                           auto resp=HttpResponse::newHttpJsonResponse(json);
                                           callback(resp);
                                       },
                                       {Get,"LoginFilter"});
    
    

The method in the readme file is for decoupling. Imagine a scene with dozens
of path handlers. Isn't it better to disperse them in their respective
classes?

Of course, for very simple logic, the interface you are talking about is
really more intuitive, I will consider adding such an interface. Thank you for
your comment.

------
twic
> Use a NIO network lib based on epoll (kqueue under MacOS/FreeBSD) to provide
> high-concurrency, high-performance network IO

What is "NIO" here? I am familiar with NIO as New Input-Output from Java,
which was the extension of the standard library to cover asynchronous I/O. Is
the author using "NIO" to mean asynchronous IO? Their use of '"JSP-like" CSP
file' elsewhere hints at a Java background.

~~~
an-tao
Maybe I misused the proper nouns, what I mean is 'non-blocking I/O'. Thanks
for your reminder.

------
agallego
cool project.

However, the callback has an odd interface.

why not pass a universal ref T&& vs a const T&

most of your code is like this.

``` auto resp = HttpResponse::newHttpJsonResponse(json); callback(resp); ```

which means callback can reuse the buffer if it wanted to rather than making a
copy because it's const ref.

PS: did you benchmark it against Folly/Proxygen (facebook) and Seastar

~~~
an-tao
For benchmarking I have submitted it to the famous TFB and I am waiting for
the result of testing round 18.
[https://github.com/TechEmpower/FrameworkBenchmarks/tree/mast...](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/C%2B%2B/drogon)

------
rlopezcc
Just wanted you to know "Drogon" means "Stoner" in spanish.

~~~
an-tao
It's an embarrassing mistake, thank you for your reminder, but it seems that
it's too late to change the name.

------
gitgud
How big does this hello-world server binary compile down to?

~~~
an-tao
About 5M bytes in size, considering that the drogon application is statically
linked, I am afraid it has no advantage in binary size.

------
rienbdj
Shame this is not built on libuv or even boost.

~~~
eao197
There is a lightweight alternative that is built on top of standalone Asio
(but Boost.Asio can also be used):
[https://github.com/Stiffstream/restinio](https://github.com/Stiffstream/restinio)

------
hawkingrip
It's really awesome

------
ilovecaching
Why not just wrap libevent or some other battle tested network poller?

Also, this would be super cool if it were in Rust. As it stands writing web
services in C++ is basically programming malpractice at this point. Would love
to see the author contributing to tokio or hyper and helping us get rid of all
this legacy inheritancey impl junk.

~~~
oconnor663
> As it stands writing web services in C++ is basically programming
> malpractice at this point.

This is the kind of extremism that makes people hate Rust. Please don't make
people hate Rust.

~~~
ilovecaching
I am unapologetic about the truth. The calculus engineers employee that leads
them to defer the safety of user facing software for the familiarity of C++
has to stop.

~~~
jchw
Safety is one thing, but let's not pretend Rust has no downsides compared to
modern C++. That's just inane.

~~~
ilovecaching
I don't claim that Rust is bulletproof. You still have to verify the axiomatic
unsafe sections of your code. There can still be bugs in Rust itself, or
logical errors in your programs. What I do claim is Rust is a giant leap
forward over C++, which is an inadequate tool to write software end users will
use. As society continues to rely more and more on software, as it becomes the
thing that we stake our lives on, we have a moral responsibility to modernize
our tools.

~~~
jchw
Programming languages are not judged on a single gauge labeled "safety."

~~~
ilovecaching
Rust is designed with the hindsight of C++ and many other languages, it
frankly sweeps the floor in every way other than compile times and certain
efficiencies, which will inevitably improve. The only legitimate reason to use
C++ is to access legacy C++ libraries that Rust has not yet developed and
maintain existing C++ projects.

~~~
jchw
That is not the only legitimate reason, at all; adoption of new technology
takes time for good reasons. Rust is still pretty new, a lot of things are
still only in nightly, and while impressive the ecosystem has a long way to go
in terms of robust libraries.

Open source software is only one facet; there are plenty of companies with
millions of lines of C++ internally, some of which making up parts of their
infrastructure.

I'm being intentionally very non exhaustive for two reasons:

1, I don't feel I should have to explain this. It's self explanatory that
these two extremely complicated things can't be compared like integers

2, because it's very much subjective. If you are starting a completely
greenfield project Rust might be a good idea. I am a huge fan of what Rust has
to offer. But it has it's kludges and kinks to work out, whereas companies
have had _decades_ to work out and improve their usage of C++. In particular,
linting and coding standards for modern C++ projects generally go very far in
improving memory safety even if they don't provide strong guarantees like Rust
can. With enforced coding standards you can virtually eliminate risks like
buffer overflow and discourage risks like data races. In addition to the
ecosystem of libraries, there's a lot of excellent tooling for C++, for static
analysis and debugging.

I fear now that I have elaborated the first instinct you will have is to poke
holes in individual points. Please don't. All I'm trying to illustrate is that
switching to Rust costs time+effort, and it is seriously not better than C++
in every way, in the same way that Go isn't.

~~~
ilovecaching
Then we generally agree. Like I said, the one use case for C++ is if you're
already using it, or require a library that is only available in C++.

My general point is that people should make a commitment to upgrade, and set a
course in that direction. The opposite approach is the wait and see mentality,
which leads companies waiting in perpetuity because they collectively never
adopt better languages due to the sunk cost fallacy.

It's exactly the big companies that need to commit, because they have the
resources to invest in sanding down the kludges and kinks and making Rust
_really_ better than C++ in every way, and it's in the early stages of Rust's
development were senior C++ talent could really help set the direction of Rust
based on what we've learned from C++.

~~~
mempko
C++ is updated every 3 years as an ISO standard. Rust is updated at the whim
of the developers. C++ templates blow Rust's generics out of the water in
terms of writing smaller code.

~~~
steveklabnik
(Rust is updated every six weeks, and we have a consensus based process, it’s
at nobody’s whim.)

~~~
mempko
Thanks for correcting my ignorance! Definitely a stupid statement on my part.
The process looks pretty solid. What is confusing is who the Rust Team is and
how the project is sponsored. Is it all volunteer like C++ or is there some
sponsorship involved?

~~~
steveklabnik
It’s cool!

The team is largely volunteers, though several companies sponsor people as
well. Some people who are sponsored are only part time, or only on the bits
relevant to that company. Mozilla sponsors the most full-time people at the
moment. We’re working on getting more full-time people, there are some
challenges there.

