
io.quarkus.vertx.http.deployment.devmode.console.DevConsoleHttpHandler Maven / Gradle / Ivy
package io.quarkus.vertx.http.deployment.devmode.console;
import java.io.ByteArrayOutputStream;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import org.jboss.logging.Logger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.FileRegion;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.DefaultLastHttpContent;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.util.ReferenceCountUtil;
import io.quarkus.dev.console.DevConsoleRequest;
import io.quarkus.dev.console.DevConsoleResponse;
import io.quarkus.netty.runtime.virtual.VirtualAddress;
import io.quarkus.netty.runtime.virtual.VirtualClientConnection;
import io.quarkus.netty.runtime.virtual.VirtualResponseHandler;
import io.quarkus.vertx.http.runtime.QuarkusHttpHeaders;
/**
* The idea here is to dispatch the DevConsoleRequest into the Netty event loop that powers the Dev Vert.x instance.
*
* Although this is loaded by the Augmentation ClassLoader at build time,
* it however works because all the classes part its signature are loaded by the System ClassLoader
* which is the same for build steps and runtime code (DevConsoleRequest is a part of the devmode-spi which
* is a parent first dependency thus also loaded by the System ClassLoader).
*/
@SuppressWarnings("unused")
public class DevConsoleHttpHandler implements Consumer {
private static final Logger log = Logger.getLogger(DevConsoleHttpHandler.class);
public static VirtualAddress QUARKUS_DEV_CONSOLE = new VirtualAddress("quarkus-dev-console");
private static final int BUFFER_SIZE = 8096;
@Override
public void accept(DevConsoleRequest request) {
try {
nettyDispatch(request);
} catch (Exception e) {
request.getResponse().completeExceptionally(e);
}
}
private class NettyResponseHandler implements VirtualResponseHandler {
ByteArrayOutputStream baos;
WritableByteChannel byteChannel;
final DevConsoleRequest request;
final DevConsoleResponse responseBuilder = new DevConsoleResponse();
private volatile VirtualClientConnection connection;
public NettyResponseHandler(DevConsoleRequest request) {
this.request = request;
}
public CompletableFuture getFuture() {
return request.getResponse();
}
@Override
public void handleMessage(Object msg) {
try {
//log.info("Got message: " + msg.getClass().getName());
if (msg instanceof HttpResponse) {
HttpResponse res = (HttpResponse) msg;
responseBuilder.setStatus(res.status().code());
for (String name : res.headers().names()) {
responseBuilder.getHeaders().put(name, res.headers().getAll(name));
}
}
if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
int readable = content.content().readableBytes();
if (baos == null && readable > 0) {
baos = createByteStream();
}
for (int i = 0; i < readable; i++) {
baos.write(content.content().readByte());
}
}
if (msg instanceof FileRegion) {
FileRegion file = (FileRegion) msg;
if (file.count() > 0 && file.transferred() < file.count()) {
if (baos == null)
baos = createByteStream();
if (byteChannel == null)
byteChannel = Channels.newChannel(baos);
file.transferTo(byteChannel, file.transferred());
}
}
if (msg instanceof LastHttpContent) {
if (baos != null) {
responseBuilder.setBody(baos.toByteArray());
}
getFuture().complete(responseBuilder);
if (connection != null) {
connection.close();
}
}
} catch (Throwable ex) {
getFuture().completeExceptionally(ex);
} finally {
if (msg != null) {
ReferenceCountUtil.release(msg);
}
}
}
@Override
public void close() {
if (!getFuture().isDone()) {
getFuture().completeExceptionally(new RuntimeException("Connection closed"));
}
}
public void setConnection(VirtualClientConnection connection) {
this.connection = connection;
}
public VirtualClientConnection getConnection() {
return connection;
}
}
private void nettyDispatch(DevConsoleRequest request)
throws Exception {
String uri = request.getUri();
QuarkusHttpHeaders quarkusHeaders = new QuarkusHttpHeaders();
DefaultHttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.valueOf(request.getMethod()), uri, quarkusHeaders);
for (Map.Entry> i : request.getHeaders().entrySet()) {
nettyRequest.headers().add(i.getKey(), i.getValue());
}
if (!nettyRequest.headers().contains(HttpHeaderNames.HOST)) {
nettyRequest.headers().add(HttpHeaderNames.HOST, "localhost");
}
HttpContent requestContent = LastHttpContent.EMPTY_LAST_CONTENT;
if (request.getBody() != null) {
ByteBuf body = Unpooled.wrappedBuffer(request.getBody());
requestContent = new DefaultLastHttpContent(body);
}
NettyResponseHandler handler = new NettyResponseHandler(request);
VirtualClientConnection connection = VirtualClientConnection.connect(handler, QUARKUS_DEV_CONSOLE,
null);
handler.setConnection(connection);
connection.sendMessage(nettyRequest);
connection.sendMessage(requestContent);
}
private ByteArrayOutputStream createByteStream() {
ByteArrayOutputStream baos;
baos = new ByteArrayOutputStream(BUFFER_SIZE);
return baos;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy