aleph 2024-02-06

How do I return a Buffered (or ByteArray) OutputStream as a response body in aleph?

I generate a zip archive in the handler and I'd need to return the results, but aleph can't seem to handle either BufferedOutputStream or ByteArrayOutputStream

I was kinda expecting aleph to handle this automagically, considering the existence of byte-streams and all

Maybe. But we own neither the protocol nor the impl, and that's liable to cause issues eventually (https://github.com/funcool/promesa/issues/151). We could add a fallback condition to handle StreamableResponseBody if we can't identify the :body type, and leave it up to users to add types for their convenience. (We don't generally want to use StreamableResponseBody internally, since most of the time it'll just be overhead. It's not a very useful protocol IMO. We have better data types than java.io.*Stream for the server, and clients want to read, not write.)

👍 2

Doesn't seem weird to me, tbh, it's an output stream after all.

I'm supposed to write a response body, not read it

Yeah, I suppose you can frame it that way, but Aleph reads bytes from :body to transfer them over the network.

The problem with BufferedOutputStream interface is that it doesn't guarantee that the bytes can be read back

BAOS is another story, it can be converted back into something readable, but it's reasonable to expect user to do it manually.

Which is what I ended up doing (calling toByteArray), but it feels like a weird extra step when aleph can handle even async streams as response body

But those are async input streams, still 🙂

I've just tried, byte-streams doesn't define a path from BAOS to input-stream

And neither to byte array which is a bit weird, I have to agree

Yeah, the Input/Output distinction is a bit weird, but as Alex points out, what happens is, whoever gets the response map needs to read the body, not write it, so it has to be an *Input*Stream. Also see the bottom of https://github.com/ring-clojure/ring/blob/2.0/SPEC-1 > byte-streams doesn't define a path from BAOS to input-stream byte-streams predates me, but my guess is Zach wasn't thinking about having byte-streams convert between input, output, and non-stream formats, but only convert within them.

👍 2

> whoever gets the response map needs to read the body, not write it Is it just me or that doesn't make sense? The one who gets (passes on) the response is aleph (netty). The recipient is on the other end of the network connection (and request maps do have InputStream :body's). Saying that it should be an InputStream because aleph receives it is like saying every OutputStream should be an InputStream because the JVM "receives" (passes on) the data. From the user-facing API's point of view it should be an OutputStream

Not if you're getting response maps from the Aleph client

Yes obviously on the client side it should be the other way around, because that's the user facing direction

Think of it more like producer/consumer

The producer preps the body as an InputStream for the next link in the chain (Aleph on the server side, the user on the client-side) to read from

I get that there's multiple ways of looking at it, since the server is outputting a body to be sent over the wire, I'm just trying to describe a way that helps me keep it straight

Okay I get that point but it still feels extremely weird that I have to think in reverse when I create a server-side response

You could design the API in a way that :body is always an output stream that is given to the user, and the user is supposed to write their data into it.

But that's not how Ring spec is designed

In the end, every output is the next thing's input...

Exactly that's why it feels weird that I have to think about the next thing instead of what I'm trying to do

I understand it's ring and it's been like that for idk 20 years but it still feels wrong as API design

Guess it's all about the ancient limitations on java iostreams

It's not wrong

You can't treat every output stream as an input stream, on an abstraction level. So you have to choose.

Yeah, they kinda suck. But there weren't any good alternatives at the time, if you needed something potentially unbounded, or being generated on the fly.

👍 2

If Ring had the same design as, say, Java's default HttpServer (where the API is exactly what you want, it gives you an outputstream to write into), you wouldn't be able to "return" anything as body anymore. Any string or byte array or a map would have to be statefully written into body manually.

👍 2

I don't see how stitching output stream bodies into a streamablebody implementation would make the already streamablebody implementations suddenly not work, but this isn't a hill I'm going to climb any higher on tonight 🤷

Times like this I wish java had some streaming API like Dart's generic streams

Could we consider extend-protocol on ByteArrayOutputStream for StreamableResponseBody? Basically to make this transparent?

Is there some way to financially support aleph (and related library) development? Since all our jvm clojure services use aleph it feels appropriate, but I can't seem to find a sponsorship link anywhere

I'm contributing on company time since we're also using Aleph a lot, so I don't currently need sponsorship 🙂

My time has gotten crunched, I'm afraid. I started working a new job this week, so I'm a bit busier.

🙏

Arnaud and Moritz do a lot, too, but I don't know if they're set up for sponsorships

I'll try to get it on this year's budget 💪

That's very kind of you.

On my side, my contribution to Aleph doesn't depend on financial support at all. I would say the limiting factor is time. The best investment is Matthew today since he both brings support and fixes within 24 hours. He also has some ideas to push Zach's ecosystem further as well. 🙂