[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Feasibility of an async HTTP server engine in Gambas (uvicorn-style) + question about JIT
[Thread Prev] | [Thread Next]
- Subject: Re: Feasibility of an async HTTP server engine in Gambas (uvicorn-style) + question about JIT
- From: alarch@xxxxxxxxx
- Date: Sun, 09 Aug 2026 10:43:54 +0200
- To: user@xxxxxxxxxxxxxxxxxxxxxx
Le 07.08.2026 19:56, Benoît Minisini a écrit :
Le 07/08/2026 à 00:05, alarch@xxxxxxxxx a écrit :Hello everyone,I've been using Gambas for years for internal tools and server administration scripts, and I'm currently thinking about a project idea I'd like your opinion on.Many Gambas users (myself included) end up relying on PHP for anything web-related, mostly by default rather than by choice. I was wondering whether it would be feasible to build a Gambas-native equivalent of Python's uvicorn — i.e. a standalone application server capable of handling HTTP connections directly (not through CGI/FastCGI), using an event-driven model built on gb.net's Socket class and its ReadyForRead/ ReadyForWrite events.I think that implementing an HTTP server in Gambas can be a great project to do.Note that you already have the 'gb.httpd' component that transforms your project into an HTTP server as soon as you check it.The rough architecture I have in mind has three layers:1. Transport layer: a Socket-based listener handling multiple connections, doing HTTP/1.1 parsing (request line, headers, Content- Length/chunked body, keep-alive) by hand. This is clearly the hardest part to get right, and the one I'd want to validate first.OK.2. Application contract: a stable interface (Request/Response objects) that application code implements — something like a minimal ASGI/WSGI equivalent, so that once it's defined, application developers don't need to touch the transport layer at all.3. Routing + middleware: routes table, static file serving, basic logging/auth hooks — the layer that would make it feel like an actual framework rather than a raw socket server.As all these things an internal API of your HTTP server or an external one? I mean: is the application logic implemented inside the HTTP server by extending it, or by external processes?For concurrency, my current thinking is a single event-loop process per core (à la Node.js/asyncio single worker), with multiple processes behind a reverse proxy (HAProxy/nginx) for multi-core scaling, since Gambas doesn't have true lightweight coroutines.Before going further, I have two questions for people more familiar with the internals:Has anyone already attempted something like this (a persistent event- driven HTTP server in pure Gambas, not CGI-based)? I know gb.web exists but as far as I understand it's oriented towards the CGI/FastCGI model rather than a standalone server process.Its name is 'gb.web.gui', and I admit it's a curious implementation of web applications that I have never seen anywhere else. But I think it makes its job.About JIT compilation: I recall reading that Benoît implemented some form of JIT in the Gambas runtime. Is this JIT exposed or usable from application code at all, specifically, is there any way to generate and execute code dynamically at runtime (similar to what Smarty/Jinja2 do when compiling templates into native code)?Or is it strictly an internal optimization of gbx3, with no way to leverage it for dynamic codegen from a Gambas application?If dynamic execution isn't possible, I'll go with a classic interpreted template engine (walking a parsed node tree), which works fine but is obviously slower.I'm not sure to understand what you mean. But 'gb.highlight' implements syntax highlighging by transforming a syntax description file into a Gambas component that is loaded dynamically, and that is JIT optimized.Any feedback — even "this is a bad idea because X" — would be very welcome. I'd rather know the real constraints now than discover them halfway through.Thanks in advance, Marc
Dear Claus, dear Benoît,I wanted to follow up with what I found after digging into gb.httpd and gb.web sources (thanks again for pointing me to both).
gb.httpd turns out to be a Gambas binding around thttpd (Jef Poskanzer's small HTTP server). For any request that isn't a static file already on disk, it forks a real child process and re-enters the same Gambas executable through a longjmp, populating standard CGI environment variables (PATH_INFO, QUERY_STRING, etc.) beforehand. So every dynamic request truly runs in its own isolated OS process — there is no risk of shared in-memory state between concurrent requests at that level, since each one starts a fresh process with its own memory space.
gb.web is built entirely around that assumption. Request reads everything from the CGI environment of the current process, and SessionManager (with both a file-based and an SQLite-based implementation available) explicitly serializes session state to disk between requests — FileSessionManager uses file locking with retries, and SqliteSessionManager opens its database in WAL mode, precisely to allow several independent processes to access the same session safely. So the question I originally raised about shared state (inspired by Claus's HttpServer.class, where properties live on the server instance itself) turns out to already have a well-thought-out answer in the official framework — just solved by process isolation plus on-disk persistence rather than by isolating a Request object in memory within a persistent process.
The trade-off, as I understand it now: this is closer to a classic CGI/PHP-FPM concurrency model (one process per dynamic request) than to an async event-loop server (uvicorn-style). For low-traffic personal use this is very likely a non-issue in practice, but it's worth knowing which model you're actually getting.
One confirmed limitation, unchanged in the current Gambas source tree: thttpd's own code still contains the explicit comment that keep-alive isn't implemented, even though the header is parsed. So each request (including every small asset like CSS/JS/images) opens its own TCP connection. As discussed, compression is absent as well. Both would need to be handled by the application or a reverse proxy in front, if needed.
Claus, to loop back to your original code: given all this, I think the state-sharing concern I raised initially would mostly apply if HttpServer.class is reused as-is in a context where the same process instance genuinely serves multiple simultaneous connections (which was not your original use case). It was a very useful starting point regardless, and it's what got me curious enough to dig into the "official" model.
Thanks again to both of you, this was a great learning exercise. I'll keep experimenting with a small proof-of-concept for my own project and will share anything worth reporting back.
Best regards, Marc
| Feasibility of an async HTTP server engine in Gambas (uvicorn-style) + question about JIT | alarch@xxxxxxxxx |
| Re: Feasibility of an async HTTP server engine in Gambas (uvicorn-style) + question about JIT | Benoît Minisini <benoit.minisini@xxxxxxxxxxxxxxxx> |