com.danielflower.apprunner.web.WebServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of app-runner Show documentation
Show all versions of app-runner Show documentation
A self-hosted platform-as-a-service that hosts web apps written in Java, Clojure, NodeJS, Python, golang and Scala.
package com.danielflower.apprunner.web;
import com.danielflower.apprunner.Config;
import com.danielflower.apprunner.problems.AppRunnerException;
import com.danielflower.apprunner.web.v1.AppResource;
import com.danielflower.apprunner.web.v1.SystemResource;
import io.muserver.*;
import io.muserver.acme.AcmeCertManager;
import io.muserver.handlers.HttpsRedirectorBuilder;
import io.muserver.openapi.OpenAPIObjectBuilder;
import io.muserver.rest.RestHandlerBuilder;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.client.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.TimeUnit;
import static io.muserver.ContextHandlerBuilder.context;
import static io.muserver.murp.HttpClientBuilder.httpClient;
import static io.muserver.murp.ReverseProxyBuilder.reverseProxy;
import static io.muserver.openapi.InfoObjectBuilder.infoObject;
import static io.muserver.rest.CORSConfigBuilder.corsConfig;
public class WebServer implements AutoCloseable {
public static final Logger log = LoggerFactory.getLogger(WebServer.class);
private final AcmeCertManager acmeCertManager;
private final int redirectToHttps;
private final ProxyMap proxyMap;
private final int httpPort;
private final int httpsPort;
private final HttpsConfigBuilder sslContext;
private MuServer muServer;
private final String defaultAppName;
private final SystemResource systemResource;
private final AppResource appResource;
private final int idleTimeout;
private final int totalTimeout;
private final String viaName;
private HttpClient rpClient;
private final long maxRequestSize;
public WebServer(int httpPort, int httpsPort, HttpsConfigBuilder sslContext, AcmeCertManager acmeCertManager, int redirectToHttps, ProxyMap proxyMap, String defaultAppName, SystemResource systemResource, AppResource appResource, int idleTimeout, int totalTimeout, String viaName, long maxRequestSize) {
this.httpPort = httpPort;
this.httpsPort = httpsPort;
this.sslContext = sslContext;
this.acmeCertManager = acmeCertManager;
this.redirectToHttps = redirectToHttps;
this.proxyMap = proxyMap;
this.defaultAppName = defaultAppName;
this.systemResource = systemResource;
this.appResource = appResource;
this.idleTimeout = idleTimeout;
this.totalTimeout = totalTimeout;
this.viaName = viaName;
this.maxRequestSize = maxRequestSize;
}
public static int getAFreePort() {
try {
try (ServerSocket serverSocket = new ServerSocket(0)) {
return serverSocket.getLocalPort();
}
} catch (IOException e) {
throw new AppRunnerException("Unable to get a port", e);
}
}
public void start() throws Exception {
int maxRequestHeadersSize = 24 * 1024;
rpClient = httpClient()
.withIdleTimeoutMillis(idleTimeout)
.withMaxRequestHeadersSize(maxRequestHeadersSize)
.build();
muServer = MuServerBuilder.muServer()
.withHttpPort(httpPort)
.withHttpsPort(httpsPort)
.withMaxRequestSize(maxRequestSize)
.withIdleTimeout(Math.max(idleTimeout + 5000, 10 /* minutes */ * 60 * 1000), TimeUnit.MILLISECONDS) // timeout is at least a little longer than configured timeout, or 5 minutes to account for slow API responses
.withHttpsConfig(acmeCertManager != null ? acmeCertManager.createHttpsConfig() : sslContext)
.withMaxHeadersSize(maxRequestHeadersSize)
.addHandler(acmeCertManager == null ? null : acmeCertManager.createHandler())
.addHandler(redirectToHttps < 1 ? null : HttpsRedirectorBuilder.toHttpsPort(redirectToHttps))
.addHandler(Method.GET, "/", (request, response, pathParams) -> {
if (StringUtils.isNotEmpty(defaultAppName)) {
response.redirect("/" + defaultAppName);
} else {
response.status(400);
response.contentType(ContentTypes.TEXT_PLAIN);
response.write("You can set a default app by setting the " + Config.DEFAULT_APP_NAME + " property.");
}
})
.addHandler(context("api")
.addHandler(context("v1")
.addHandler(
RestHandlerBuilder.restHandler(appResource, systemResource)
.withCORS(corsConfig()
.withAllowedOriginRegex(".*")
.withAllowCredentials(true)
.withExposedHeaders("content-type", "accept", "authorization")
)
.withOpenApiJsonUrl("swagger.json")
.withOpenApiHtmlUrl("api.html")
.withOpenApiDocument(OpenAPIObjectBuilder.openAPIObject()
.withInfo(infoObject()
.withTitle("App Runner")
.withDescription("The REST API for App Runner which is used for registering apps, deploying apps, viewing logs etc.")
.withVersion("1.0")
.build())
)
)
))
.addHandler(reverseProxy()
.withUriMapper(new AppUriMapper(proxyMap))
.withTotalTimeout(totalTimeout)
.withViaName(viaName)
.sendLegacyForwardedHeaders(true)
.discardClientForwardedHeaders(false)
.withHttpClient(rpClient)
)
.start();
if (acmeCertManager != null) {
acmeCertManager.start(muServer);
}
log.info("Started web server at " + muServer.httpsUri() + " / " + muServer.httpUri());
}
public void close() throws Exception {
if (acmeCertManager != null) {
acmeCertManager.stop();
}
if (muServer != null) {
muServer.stop();
muServer = null;
}
if (rpClient != null) {
rpClient.stop();
rpClient = null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy