txiki.js icon indicating copy to clipboard operation
txiki.js copied to clipboard

Performant HTTP server

Open brandonros opened this issue 5 years ago • 23 comments

While I doubt nobody is rushing to run this in production, I am also going to bet that:

let conn;
while (true) {
    conn = await t.accept();
    handleConnection(conn);
    conn = undefined;
}

won't lead to the most performant approach to serving HTTP requests.

What are your thoughts on putting epoll or kqueue or select or something? How does libuv achieve this for servers?

brandonros avatar Sep 13 '19 19:09 brandonros

Hey there!

libuv uses epoll on Linux, kqueue on macOS and BSDs, IOCP on Windows, etc. What you see there is not a blocking call, everything continues to be async under the hood.

That said, I'm not (yet) focusing on servers. I was considering maybe using libwebsockets to do the heavy lifting, since it would provide HTTP (also HTTP/2) and WS so I could avoid having to do it myself, at least as a start :-)

saghul avatar Sep 13 '19 21:09 saghul

libwebsockets is a good idea

could you outline what that would take and I’ll see if I can take a crack at it?

brandonros avatar Sep 14 '19 17:09 brandonros

Sure thing! So I think the approach would be similar to curl: use a bit submodule and make using the system version optional.

Then there would be one lws “context” per QUVRuntime, and poll handlers would be used. I took a quick look and lws supports libuv integration and “external loop” already so that’s pretty cool.

As for the JS API, I don’t know, but that can be iterated!

Cheers!

saghul avatar Sep 14 '19 18:09 saghul

https://github.com/warmcat/libwebsockets/blob/master/test-apps/test-server.c

Are you able to mainly use that code?

I'm curious to hear what your usecase for quv is? You are clearly doing a great job fleshing it out and putting a bit of effort into it.

brandonros avatar Sep 23 '19 01:09 brandonros

https://github.com/warmcat/libwebsockets/blob/master/test-apps/test-server.c

Are you able to mainly use that code?

I'd say mostly yes. The important part is to use uv_poll_t handles to make sure the polling is handled by libuv. In addition, We probably want one per-runtime lws_server thing, see how we do it for the CURLM handle.

I'm curious to hear what your usecase for quv is? You are clearly doing a great job fleshing it out and putting a bit of effort into it.

I'm still figuring that out :-) For now I'm just having fun in the evenings and figuring things out as I go.

saghul avatar Sep 23 '19 14:09 saghul

Compared to node.js, how bad do you think quv + libwebsocket performance would be for an HTTP server?

brandonros avatar Sep 27 '19 02:09 brandonros

It’s impossible to estimate because everything is different across the 2. QuickJS is a lot slower than V8 to begin with: https://bellard.org/quickjs/bench.html

saghul avatar Sep 27 '19 18:09 saghul

Another option could be to use uWebSockets

s0kil avatar Nov 10 '19 01:11 s0kil

Good one indeed! Does it also provide a C API or is it just C++?

saghul avatar Nov 10 '19 09:11 saghul

I'm not sure, but it is built somehow into a JavaScript wrapper: https://github.com/uNetworking/uWebSockets.js

s0kil avatar Nov 10 '19 15:11 s0kil

in case you didn't look it up... uWebSockets is built on top of uSockets - which is a C API

txiki.js is a great, fun project - btw!!!

srdjan avatar May 20 '20 16:05 srdjan

@saghul I currently thinking about implementing a HTTP server for txiki. Though there are many different possibilities on how to approach this. E.g. libwebsockets is pretty large in itself and there would be different ways to integrate it. So did you have given this any thought already?

lal12 avatar Apr 13 '24 10:04 lal12

Hey there! Yes, I have given it quite a bit of thought lately.

My current state of mind is that of going with libwebsockets + mbedtls.

Yes, lws is somewhat large, but it has built time knobs for almost everything, and since we'd vendor it, we can only compile what we need.

That can start with HTTP and WS servers.

I'm also choosing mbedtls so I can implement TLS sockets and the subtle crypto web API.

Ultimately it would probably be nice to also replace the HTTP and WS client code with lws, so use a single dependency, and to get rid of the mess that WS currently is 😅

saghul avatar Apr 13 '24 11:04 saghul

So I have a minimal prototypic implementation with libwebsockets running. I don't think the libwebsockets API is a great fit. It's a bit dependent on the use case, but libwebsockets probably works great if you want basically a out of the box webserver for your project and just want to configure/adjust a few knobs. But my aim was more of a NodeJS like API where you have a basic API and to do the more advanced handling by yourself (or a library). This latter option however doesn't seem to be a good application for libwebsockets. Generally it is quite rigid, which is probably good for most cases, but not necessarily for a generic JS API. It's maybe a bit more framework than library.

Will give it a few thoughts, whether I continue with it or look at something else.

lal12 avatar Apr 16 '24 17:04 lal12

Thanks for sharing!

saghul avatar Apr 16 '24 17:04 saghul

Currently working on a very minimal implementation based on llhttp (nodejs http parser) here: https://github.com/lal12/txiki.js/tree/add-http-server.

Performance wasn't the main focus, I generally think if that's really the focus, nodejs or an nginx reverse proxy (e.g. delivering assets and caching) is the way to go.

My first benchmarks (GET route returning 'hello world') showing 4-11k requests/s, compared to nodejs with 87k requests/s.

lal12 avatar Apr 19 '24 12:04 lal12

Exciting! I'll take a look shortly! Going low level is also something I considered but wanted to avoid...

saghul avatar Apr 19 '24 12:04 saghul

Exciting! I'll take a look shortly! Going low level is also something I considered but wanted to avoid...

Tend to agree. uWebsockets actually looks good, though it would be C++. Another one on my list ist microhttpd though I haven't looked closer at it yet.

lal12 avatar Apr 20 '24 10:04 lal12

IIRC uWS only supports OpenSSL, and I think it would be nice to use something smaller like mbedtls.

I have never used microhttpd. I see there are 2 projects with that name, one part of GNU and thus GPL which we can't use.

saghul avatar Apr 20 '24 11:04 saghul

Yeah forgot that it was GPL, that's why I never looked into microhttpd in detail in the first place.

lal12 avatar Apr 20 '24 14:04 lal12

i think https://github.com/civetweb/civetweb is a good fit for this project. it meets all the requirements discussed earlier: MIT license, websocket client + server, mbedtls support, and small size

anggape avatar Apr 22 '24 17:04 anggape

Thanks for sharing!

saghul avatar Apr 22 '24 18:04 saghul