saghul / txiki.js

A tiny JavaScript runtime
MIT License
2.5k stars 167 forks source link

Performant HTTP server #44

Open brandonros opened 5 years ago

brandonros commented 5 years ago

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?

saghul commented 5 years ago

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 :-)

brandonros commented 5 years ago

libwebsockets is a good idea

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

saghul commented 5 years ago

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!

brandonros commented 5 years ago

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.

saghul commented 5 years ago

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.

brandonros commented 5 years ago

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

saghul commented 5 years ago

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

s0kil commented 4 years ago

Another option could be to use uWebSockets

saghul commented 4 years ago

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

s0kil commented 4 years ago

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

srdjan commented 4 years ago

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!!!

lal12 commented 5 months ago

@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?

saghul commented 5 months ago

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 😅

lal12 commented 5 months ago

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.

saghul commented 5 months ago

Thanks for sharing!

lal12 commented 5 months ago

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.

saghul commented 5 months ago

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

lal12 commented 5 months ago

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.

saghul commented 5 months ago

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.

lal12 commented 5 months ago

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

anggape commented 5 months ago

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

saghul commented 5 months ago

Thanks for sharing!

KaruroChori commented 3 months ago

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.

I repackaged your code as an external module here: https://github.com/KaruroChori/http-txiki-module.
However, there are no examples on how to use it so I have not really tested it.

I will probably work a bit more on it to provide full ts types and some tests. The overall slowness of your benchmark aligns with the general gap in performance I also observed in other tasks.