All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.danielflower.apprunner.web.WebServer Maven / Gradle / Ivy

Go to download

A self-hosted platform-as-a-service that hosts web apps written in Java, Clojure, NodeJS, Python, golang and Scala.

There is a newer version: 2.4.6
Show newest version
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.ContentTypes;
import io.muserver.Method;
import io.muserver.MuServer;
import io.muserver.MuServerBuilder;
import io.muserver.openapi.OpenAPIObjectBuilder;
import io.muserver.rest.RestHandlerBuilder;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.util.HttpCookieStore;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.ServerSocket;

import static io.muserver.ContextHandlerBuilder.context;
import static io.muserver.openapi.InfoObjectBuilder.infoObject;

public class WebServer implements AutoCloseable {
    public static final Logger log = LoggerFactory.getLogger(WebServer.class);
    private final ProxyMap proxyMap;
    private final int httpPort;
    private final int httpsPort;
    private final SSLContext 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 HttpClient rpClient;

    public WebServer(int httpPort, int httpsPort, SSLContext sslContext, ProxyMap proxyMap, String defaultAppName, SystemResource systemResource, AppResource appResource, int idleTimeout, int totalTimeout) {
        this.httpPort = httpPort;
        this.httpsPort = httpsPort;
        this.sslContext = sslContext;
        this.proxyMap = proxyMap;
        this.defaultAppName = defaultAppName;
        this.systemResource = systemResource;
        this.appResource = appResource;
        this.idleTimeout = idleTimeout;
        this.totalTimeout = totalTimeout;
    }

    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 {
        rpClient = createClient();

        muServer = MuServerBuilder.muServer()
            .withHttpPort(httpPort)
            .withHttpsPort(httpsPort)
            .withHttpsConfig(sslContext)
            .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((request, response) -> {
                    response.headers().set("Access-Control-Allow-Origin", "*");
                    response.headers().set("Access-Control-Allow-Headers",
                        "origin, content-type, accept, authorization");
                    response.headers().set("Access-Control-Allow-Credentials", "true");
                    response.headers().set("Access-Control-Allow-Methods",
                        "GET, POST, PUT, DELETE, OPTIONS, HEAD");
                    return false;
                })
                .addHandler(context("v1")
                    .addHandler(
                        RestHandlerBuilder.restHandler(systemResource, appResource)
                            .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(null, "/{appName : [^/?]+}{ignored:(.*)}", new AppReverseProxy(rpClient, proxyMap, totalTimeout))
            .start();

        log.info("Started web server at " + muServer.httpsUri() + " / " + muServer.httpUri());
    }

    private HttpClient createClient() throws Exception {

        int selectors = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);

        HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(selectors), new SslContextFactory(true));
        client.setFollowRedirects(false);
        client.setCookieStore(new HttpCookieStore.Empty());
        client.setMaxConnectionsPerDestination(256);
        client.setAddressResolutionTimeout(15000);
        client.setConnectTimeout(15000);
        client.setIdleTimeout(idleTimeout);
        client.setUserAgentField(null);
        client.start();

        client.getContentDecoderFactories().clear();

        return client;
    }


    public void close() throws Exception {
        if (muServer != null) {
            muServer.stop();
            muServer = null;
        }
        if (rpClient != null) {
            rpClient.stop();
            rpClient = null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy