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.

Reply via email to