New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support AsyncFile, Path. Added PathPart and FilePart #16286
Conversation
I think this is a reasonable way to start out |
public void handle(Void event) { | ||
response.end(); | ||
// Not sure if I need to resume, actually | ||
ctx.resume(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likely not, since there should be no more handlers in the chain, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, there's always cleanup, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What cleanup? I mean if this is the last Handler in the chain, resuming should do nothing but push the context into the event loop and just return when the event actually gets processed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the context.close we do things like call async listeners and tear down the CDI scope and stuff, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, good point
ctx.suspend(); | ||
ServerHttpResponse response = context.serverResponse(); | ||
response.setChunked(true); | ||
file.handler(buffer -> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we also need an exception handler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good point.
import javax.ws.rs.ext.MessageBodyWriter; | ||
import org.jboss.resteasy.reactive.FilePart; | ||
|
||
public class FilePartBodyHandler implements MessageBodyWriter<FilePart> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be best to suffix these with BodyWriter
instead of BodyHandler
which is rather overloaded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied the other class names, but yeah, in this case they're write-only, so good point.
|
||
@Provider | ||
@Produces("*/*") | ||
@Consumes("*/*") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consumes shouldn't be needed, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, yes!
Also, do we really need both |
I sorta agree, but given that we support both |
Yeah, I don't mind either way |
So, I thought about this a bit more, and it turns out that I don't think this type can work with writer interceptors at all, in fact.
I'm just going to disallow But also, I'm now wondering if our architecture really works if we have any WDYT @stuartwdouglas @geoand ? |
Also, I fucking hate IO and in particular blocking and async IO. BOTH. |
+1
Yeah, I've wondered about this myself at times. |
Yea, this is complex, the blocking API's that JAX-RS provides don't work well with non-blocking. At the moment our output stream will just fully buffer if you are on the IO thread, which works fine for small responses but is not going to work for large responses such as files. Maybe switch to the worker thread whenever WriterInterceptors are involved, but introduce our own non-blocking version? In general though I think for safety maybe this whole block needs to be run in a worker thread: Line 201 in 76cd569
|
} | ||
|
||
@Override | ||
public boolean isWriteQueueFull() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isWriteQueueFull and addDrainHandler don't really make sense here. The interface really needs some Javadoc but at the moment write() is assuming you can write once, then you need to wait for a notification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I honestly didn't know how to implement those. Any hint? I did need them for the vertx async part.
|
||
@Override | ||
public ServerHttpResponse sendFile(String path, long offset, long length) { | ||
context.response().sendFile(path, offset, length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bypasses a lot of Servlet stuff, it really needs to write the file using the Servlet API's.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any specific APIs for servlet to write files?
.../java/io/quarkus/resteasy/reactive/server/runtime/ServerVertxAsyncFileMessageBodyWriter.java
Outdated
Show resolved
Hide resolved
.../main/java/org/jboss/resteasy/reactive/common/providers/serialisers/PathPartBodyHandler.java
Show resolved
Hide resolved
...c/main/java/io/quarkus/resteasy/reactive/common/runtime/VertxAsyncFileMessageBodyWriter.java
Outdated
Show resolved
Hide resolved
Well, yeah, perhaps. I had to do that for Async-IO in RESTEasy Classic. I was hoping not to have to do it again, but… |
3c6c576
to
6386fea
Compare
This workflow status is outdated as a new workflow run has been triggered. Failing Jobs - Building 6386fea
Full information is available in the Build summary check run. Test Failures⚙️ JVM Tests - JDK 11 #📦 integration-tests/kafka✖ ✖ ⚙️ JVM Tests - JDK 11 Windows #📦 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment✖ ⚙️ JVM Tests - JDK 15 #📦 integration-tests/kafka✖ ✖ ⚙️ MicroProfile TCKs Tests #📦 resteasy-reactive-testsuite/tests✖ |
OK, the kafka failures are not mine. But this Windows failure is very interesting:
So the file size is different on windows? That's… how do we explain that? |
If you rebase onto the current |
This is probably stupid, but line endings maybe? |
Well, but who would remove/add them? |
The TCK failure is also interesting: It's a This looks like a silly test: it should probably remove the Or should I drop the |
Well, the server reads the file from the filesystem, right? So in that case it would likely add the Windows line ending when sending the file back, while the test is looking for the string length having the string memory, no? |
Yeah, I would expect that.
I think you have done the right thing and the test is making a mess |
Also, are there 5 lines in the result? If not, we can simply ignore my comment :) |
There are. But I don't get it. I'm expecting a file of size 448, which is the size of the This isn't even about encoding or something adding line endings. I don't think the FS on Windows goes and reports file sizes with adjusted line endings. |
Because it changes the content, and the server is right in adding this header. See quarkusio/quarkus#16286
OK, adjusted for Windows, pushed new TCK, rebased on |
Failing Jobs - Building 8151c96
Full information is available in the Build summary check run. Test Failures⚙️ Gradle Tests - JDK 11 Windows #📦 integration-tests/gradle✖ |
I don't see how that gradle/windows/devmode failure can be related, so restarting. |
It's probably just a flake |
CI actually passed, but the |
Ah I know, the outdated status was probably not hidden properly. I'll take this case into account. You can safely merge this if all is good for you all. |
@FroMage I assume this is ready to go? |
Merged! |
(@FroMage told me it was ok) |
👍🏼 |
Famous Last Words™ Thanks ;) |
Because it changes the content, and the server is right in adding this header. See quarkusio/quarkus#16286
Support for new file types and partial versions.
I need guidance for how to handle AsyncFile properly though in the case that there are writer interceptors, because our current infra doesn't support suspending in the writer if there are writer interceptors, and if we're in the IO thread I can't very well block in the writer to read the file.
We could detect a return type of
AsyncFile
at build time, and throw if there are writer interceptors and force the user to use@Blocking
on the endpoint.Or we start supporting
@Blocking
on the writer, but that sounds like a lot of work.Any advice @stuartwdouglas ?