I've been using Nim for several years now. I mostly use it for small utilities and experimentation on things like microcontrollers. However, most of my time goes into other projects, mostly web-related, and thus I've wanted to integrate Nim into some of my web endeavors. To start working on this goal, almost a month ago I sat down to begin writing a web framework for use within a group of sites that my friend and I work on. The decision to write my own framework was made after playing around with Prologue and realizing that it crashes with concurrent requests under ORC. I wanted to work on an existing framework to avoid further fragmentation of the ecosystem (already a huge issue), but I had no idea where to start with fixing the crash since it had to do with shared memory between threads. Eventually, I gave up and decided to write my own framework. If only I knew just how much else was not ready to be used.
I decided to build my framework on top of httpx since it had Windows support, and I didn't want to lock Windows users out. And so I began learning the httpx API. This was when I discovered that something very important was missing: request body streaming. This may not seem like a big issue to some people, but for me, this causes loads of issues. One of the sites I wanted to rewrite in my framework is a video site. It accepts file uploads which are streamed to disk, and when you watch videos, those are pulled from some storage source, and again streamed to the browser to watch them. With this in mind, the lack of streaming I/O was a massive problem. I come from Vert.x (Java/Kotlin) and Node.js, both non-blocking, and both supporting async streaming I/O. A missing feature like this is a complete deal-breaker for me. After that, I began researching other HTTP servers for Nim. This made me realize how woefully unfit the current Nim ecosystem is for production HTTP applications, other than blocking APIs (mostly thanks to Mummy). Finally, I bit the bullet and decided to try my hand at implementing async streaming I/O in httpx, since I figured it would be the best way to introduce it to the ecosystem. Since that point, I've been working with @ringabout recently to bring async streaming I/O to httpx. I would like to thank him for working with me, and for his contributions to the Nim language and ecosystem in general. I've got a working implementation of streaming I/O now, but it's far from stable and I'm not entirely sure if it will be usable given its poor performance at the moment. * * * If/until async streaming I/O comes to httpx or some other HTTP server, there are only a handful of options available to developers who want to write a web application in Nim: (Note that these were tested using ORC with threads on) **1\. AsyncHttpServer** Built into the stdlib, you have it if you have Nim. I've used this server in production for _very small_ projects that either don't get much traffic, or don't interact with the Internet at all. It has no support for request streaming, and response streaming uses FutureStream. FutureStream currently has no limit to its internal queue, which means that if you send too fast, you will use excess memory. It also doesn't perform well. **2\. HttpBeast** High performance, powers Jester, but leaks memory and crashes when you overload it with connections. It also is fundamentally incompatible with I/O streaming since both requests and responses are written entirely to memory. It also has serious performance issues that cause it to grind to a halt if you overload it with concurrent large request bodies. This makes it pretty easy to launch DoS attacks against it. Its event loop also has issues with sleepAsync, though that's a minor problem. **3\. httpx** Fork of HttpBeast, lower performance but with Windows support. Has most of the problems of HttpBeast. I fixed the crash issue in it, but the large request DoS issue still exists, and I'm not sure how to fix it. I'm currently working on streaming I/O, but it's slow. I'm not an I/O or async expert, although I've read large chunks of asyncdispatch's source code and have a fairly good grasp on it. Most of the memory issues are fixed with the streaming I/O implementation, but I've gotten it to leak in some edge cases that I'm not very qualified to diagnose. **4\. Chronos** Everyone knows about Chronos. It has its own HTTP server which looked promising, but after some prodding I was able to get it to leak memory when you try to read in request bodies with the `post` proc (reads and parses form data, performs unnecessary copies). The memory there is never freed. It's also not particularly performant once you start trying to read requests. The biggest kicker for most people is the fact that it's incompatible with asyncdispatch. If it wasn't for the memory leaks, I'd be using this right now. It's also the only one that supports HTTPS. **5\. Scorper** Scorper is built with Chronos and therefore is also incompatible with asyncdispatch. It implements its own HTTP server using Chronos' I/O facilities. Under ORC, it crashes with concurrent connections. That alone is enough to turn me away from it. **6\. Mummy** I need streaming I/O for my applications, so I can't use Mummy. It has the same crashing issue as HttpBeast, though this can easily be fixed by checking if the newly returned file descriptor int is higher than the max file descriptors count when accepting a socket, and if so, ignore it. With that said, I can't see any major memory leaks. This is probably due to the fact that it limits the body size. However, I need to be able to read large files for file uploads. Without that, it's very limited and can't be used for any sort of website with file uploads, static files, media streaming, etc. It really makes Mummy's range of use-cases very narrow. * * * With all of this said, it's no wonder that nobody is building large web projects with Nim. I understand that most users are interested in building interesting toys, but as someone who wants to use Nim in production for real, working web services, it is incredibly frustrating. I think a lot of it has to do with the poor async implementation and the lack of consensus on that. What we have now, asyncdispatch, is slow and has very few useful tools outside of bare I/O and timers. For example, FutureStream is nearly impossible to use safely because it doesn't enforce a maximum queue size. I had to implement my own async stream type for this purpose while working on httpx. On the other hand, Chronos is much better but is barely documented and is incompatible with everything else. I've read a bit about CPS but I'm not too familiar with the paradigm and therefore can't build anything useful with it at the moment. Most people's solution to the HTTP server problem seems to be "put it behind Nginx." Not only is this ignoring the problem (why should your application server NEED to be behind a reverse proxy?), but it's also admitting that none of them are production-ready. Lack of a production-ready HTTP server makes it hard for a very significant portion of programmers to build things they need with Nim. I would like more people to use Nim, and at the very least use it personally, but every single time I've tried to implement some web application, I've been hit with roadblock after roadblock. Asyncnet isn't particularly amazing either, but it's more generic and doesn't have as many places for it to go wrong in.