txiki.js
txiki.js copied to clipboard
Performant HTTP server
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?
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 :-)
libwebsockets is a good idea
could you outline what that would take and I’ll see if I can take a crack at it?
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!
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.
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.
Compared to node.js, how bad do you think quv + libwebsocket performance would be for an HTTP server?
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
Another option could be to use uWebSockets
Good one indeed! Does it also provide a C API or is it just C++?
I'm not sure, but it is built somehow into a JavaScript wrapper: https://github.com/uNetworking/uWebSockets.js
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!!!
@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?
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 😅
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.
Thanks for sharing!
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.
Exciting! I'll take a look shortly! Going low level is also something I considered but wanted to avoid...
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.
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.
Yeah forgot that it was GPL, that's why I never looked into microhttpd in detail in the first place.
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
Thanks for sharing!