3

I am trying to allow my game client to download the cache needed for the client to run. Inside of my game web server I am doing this:

@RouteManifest(template="/cache", method="GET")
public class APICacheRoute extends RouteHttpHandler<JadePreprocessor> {
    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        Path path = Paths.get("./cache/Alterscape.zip");
        File file = path.toFile();
        exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/octet-stream");
        exchange.getResponseSender().send(file);
    }
}

However, I am receiving the error that the file must be a ByteBuffer. How can I return a file to download?

My web server looks like this:

public static void initialize() {
    ServerController server = new ServerController("localhost", 8080, 8443);
    server.register(new APIVirtualHost());
    server.inititialize();
}

My APIVirtualHost looks like this:

public APIVirtualHost() {
    super(127.0.0.1);
    setDirectoryListingEnabled(false);
}
Jaquarh
  • 6,493
  • 7
  • 34
  • 86

2 Answers2

6

With Undertow, you need to dispatch the handler off the I/O thread to use a blocking operation. You'll need to do this any time you need to perform blocking operations, such as reading the a request body, accepting a file upload stream, and of course, sending a file.

At the top of your handle() method, use this to dispatch the request to a blocking thread, and off the non-blocking I/O thread:

if (serverExchange.isInIoThread()) {
    serverExchange.dispatch(this);
    return;
}

Then start blocking when you're ready to send the buffer:

serverExchange.startBlocking();

Then to send a file, you can use a Buffered stream:

serverExchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/octet-stream");

final File file = new File("/location/to/your/file");
final OutputStream outputStream = serverExchange.getOutputStream();
final InputStream inputStream = new FileInputStream(file);

byte[] buf = new byte[8192];
int c;
while ((c = inputStream.read(buf, 0, buf.length)) > 0) {
    outputStream.write(buf, 0, c);
    outputStream.flush();
}

outputStream.close();
inputStream.close();

The send method on the ServerExchange object is for sending response data using the non-blocking method - good for text or JSON. The ByteBuffer argument overload is for sending raw byte data, which is what Undertow converts strings to when sending. It can be a bit misleading.

Happy Coding.

mwieczorek
  • 2,107
  • 6
  • 31
  • 37
1

Try to wrap your own handler with this io.undertow.server.handlers.BlockingHandler. It already contains the necessary checks before performing a blocking operation.

public final class BlockingHandler implements HttpHandler {
    ...
    @Override
    public void handleRequest(final HttpServerExchange exchange) throws Exception {

        exchange.startBlocking();
        if (exchange.isInIoThread()) {
            exchange.dispatch(handler);
        } else {
            handler.handleRequest(exchange);
        }
    }
    ...
}
quadrix
  • 56
  • 4