All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.quarkiverse.quinoa.QuinoaDevProxyHandler Maven / Gradle / Ivy
package io.quarkiverse.quinoa;
import static io.quarkiverse.quinoa.QuinoaRecorder.compressIfNeeded;
import static io.quarkiverse.quinoa.QuinoaRecorder.isIgnored;
import static io.quarkiverse.quinoa.QuinoaRecorder.next;
import static io.quarkiverse.quinoa.QuinoaRecorder.shouldHandleMethod;
import java.util.List;
import org.jboss.logging.Logger;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
class QuinoaDevProxyHandler implements Handler {
private static final Logger LOG = Logger.getLogger(QuinoaDevProxyHandler.class);
private final List HEADERS_TO_FORWARD = List.of(
HttpHeaders.ACCEPT_RANGES.toString(),
HttpHeaders.CONTENT_RANGE.toString(),
HttpHeaders.CONTENT_LENGTH.toString(),
HttpHeaders.CONTENT_TYPE.toString());
private final QuinoaNetworkConfiguration networkConfiguration;
private final WebClient client;
private final QuinoaDevWebSocketProxyHandler wsUpgradeHandler;
private final ClassLoader currentClassLoader;
private final QuinoaDevProxyHandlerConfig config;
QuinoaDevProxyHandler(final QuinoaDevProxyHandlerConfig config, final Vertx vertx, QuinoaNetworkConfiguration network) {
WebClientOptions options = new WebClientOptions();
options.setSsl(network.isTls());
options.setTrustAll(network.isTlsAllowInsecure());
options.setVerifyHost(!network.isTlsAllowInsecure());
this.client = WebClient.create(vertx, options);
this.wsUpgradeHandler = network.isWebsocket() ? new QuinoaDevWebSocketProxyHandler(vertx, network) : null;
this.config = config;
this.networkConfiguration = network;
currentClassLoader = Thread.currentThread().getContextClassLoader();
}
@Override
public void handle(final RoutingContext ctx) {
if (!shouldHandleMethod(ctx)) {
next(currentClassLoader, ctx);
return;
}
String path = ctx.normalizedPath();
if (isIgnored(path, config.ignoredPathPrefixes)) {
next(currentClassLoader, ctx);
return;
}
final String resourcePath = path.endsWith("/") ? path + config.indexPage : path;
if (isIgnored(resourcePath, config.ignoredPathPrefixes)) {
next(currentClassLoader, ctx);
return;
}
if (isUpgradeToWebSocket(ctx)) {
if (this.wsUpgradeHandler != null) {
wsUpgradeHandler.handle(ctx);
} else {
next(currentClassLoader, ctx);
}
} else {
handleHttpRequest(ctx, resourcePath);
}
}
private static boolean isUpgradeToWebSocket(RoutingContext ctx) {
return ctx.request().headers().contains("Upgrade")
&& "websocket".equalsIgnoreCase(ctx.request().headers().get("Upgrade"));
}
private void handleHttpRequest(final RoutingContext ctx, final String resourcePath) {
final HttpServerRequest request = ctx.request();
final MultiMap headers = request.headers();
final String uri = computeResourceURI(resourcePath, request);
// Disable compression in the forwarded request
headers.remove("Accept-Encoding");
client.request(request.method(), networkConfiguration.getPort(), networkConfiguration.getHost(), uri)
.putHeaders(headers)
.send(event -> {
if (event.succeeded()) {
final int statusCode = event.result().statusCode();
switch (statusCode) {
case 200:
if (config.devServerDirectForwarding || shouldForward(ctx, event.result())) {
forwardResponse(event, request, ctx, resourcePath);
} else {
next(currentClassLoader, ctx);
}
break;
case 404:
next(currentClassLoader, ctx);
break;
default:
forwardError(event, statusCode, ctx);
}
} else {
error(event, ctx);
}
});
}
private boolean shouldForward(RoutingContext ctx, HttpResponse result) {
final List contentType = result.headers().getAll(HttpHeaders.CONTENT_TYPE);
if (contentType != null && contentType.stream().anyMatch(s -> s.contains("text/html"))) {
final String path = QuinoaRecorder.resolvePath(ctx);
// We forward if the server returns a html, and it was intended:
// - if the path ends with .html
// - if the path is empty (root)
return path.endsWith(".html") || path.equals("/") || path.isEmpty();
}
return true;
}
private String computeResourceURI(String path, HttpServerRequest request) {
String uri = path;
final String query = request.query();
if (query != null) {
uri += "?" + query;
}
return uri;
}
private void forwardError(AsyncResult> event, int statusCode, RoutingContext ctx) {
final Buffer body = event.result().body();
final HttpServerResponse response = ctx.response().setStatusCode(statusCode);
if (body != null) {
response.send(body);
} else {
response.send();
}
}
private void forwardResponse(AsyncResult> event, HttpServerRequest request, RoutingContext ctx,
String resourcePath) {
LOG.debugf("Quinoa is forwarding: '%s'", request.uri());
final HttpServerResponse response = ctx.response();
for (String header : HEADERS_TO_FORWARD) {
response.headers().add(header, event.result().headers().getAll(header));
}
compressIfNeeded(config, ctx, resourcePath);
final Buffer body = event.result().body();
if (body != null) {
response.send(body);
} else {
response.send();
}
}
private void error(AsyncResult> event, RoutingContext ctx) {
final String error = String.format("Quinoa failed to forward request '%s', see logs.", ctx.request().uri());
ctx.response().setStatusCode(500);
ctx.response().send(error);
LOG.error(error, event.cause());
}
}