I've put together an experimental Ring adapter, https://github.com/weavejester/capra, that's written in Clojure and is reasonably performant. Currently HTTP/1.1 support is implemented, with WebSockets planned.
Do you anticipate the JavaNIO dependency to make this hard to achieve? > It can be more easily ported to Clojure-like environments that don't use the JVM.
That's one of the reasons why Capra is divided into two: Capra the HTTP server, and TeensyP the TCP server. TeensyP is very much tied to the JavaNIO classes, but provides a neutral interface that Capra sits on top of. This means that as long as a Clojure-like language can replicate TeensyP, they get Capra on top for relatively little effort.
I assume that language needs to be sync? Would you be open to an async interface to support squint etc. From the JS runtimes?
No, there's nothing in the TeensyP API that necessitates a synchronous interface. The only blocking IO used is the InputStream and OutputStream necessary to adhere to the Ring spec. If you were implementing that on a platform with purely asynchronous IO, such as JavaScript, you'd presumably use a slightly different interface.
So teensyp.server is purely asynchronous NIO, and teensyp.stream allows you to convert that interface into a blocking streaming one.
Very cool. I look forward to seeing the ports!
I let AI port teensyp to babashka, and the capra (and the http kit) benchmark both run.
Http Kit is 10x faster than Capra there for simple. Equal for hot. But it's neat that it's possible.
Babashka currently lacks some key classes that TeensyP uses, like Selector, so porting via AI might result in a very different architecture. However, adding those classes to Babashka isn't hard, so real support for bb shouldn't be far off.
Ah, you've looked into this. Yeah, it went for a naive implementation.
Re: the Java interfaces to avoid proxy, have you looked at implementing readable/writeable byte channels(interfaces) and using the stream adapting methods here https://docs.oracle.com/javase/8/docs/api/java/nio/channels/Channels.html
The channel interfaces look a lot like the custom interfaces https://docs.oracle.com/javase/8/docs/api/java/nio/channels/ReadableByteChannel.html
That was actually the first thing I tried. I can't remember the exact problem I had, but I think it had something to do with how the channels might block, and the async design turned out to be too complex. However, now that I look back on it with fresh eyes, I can't see where my original issue was. It might be worth checking again.
I took capra/teensyp for a spin today and found a few bugs.. I posted issues 🙂 very nice little lib, it was a pleasure to read.
Ah, thanks! I'll take a look.
#12 looks to be what you suspect. I'm not sure what's going on with #11 though. The control queue is for pausing and resuming reads, which is used by the streaming interface when the stream buffer gets full. My guess is that there's something wrong with that mechanism.
re #11 -> its a teensyp thing, I've just spent the last hour or so spelunking it
making a reply to #11 shortly
Oh, thank you!
done.. thanks for a fun post friday lunch activity haha
I'm glad you found it fun and not frustrating!
Indeed, it was a nice diversion. I'm actually frustrated with my own http server project at the moment.. chasing down perf problems across FFI<->clj has me demotivated at the moment
I know that feeling. Capra had very poor performance until I managed to track down the bottlenecks that were causing issues. And even then it wasn't quite as good as I wanted.
Are you sure the change at line 162 is needed? I see the issue with foreach! on line 25, but I'm unsure why setting the PAUSED flag directly is necessary (also I think it might introduce subtle ordering bugs for control events).
Hm, I don't have all that in my head anymore, but possibly not.. i was tweaking things in the queue-control fn before landing on foreach!
Looks like you were right: there's two separate issues, one with the foreach! (which I've fixed in 0.9.2), but there's a separate issue that you fixed with the change on 162. Specifically, there's no way of checking via TeensyP if a channel is already paused, so pauses just keep piling up from streams. What I can probably do is to check the head of the queue and filter out consecutive duplicates.
Or just ensure that if the stream is paused, it doesn't continually add to the control queue, which is probably the best solution and seems to work.
And thanks again for finding not one but two bugs in TeensyP, and also one extra bug in Capra.