diff --git a/maven-settings.xml b/maven-settings.xml index adce7ab..18a15cb 100644 --- a/maven-settings.xml +++ b/maven-settings.xml @@ -1,12 +1,12 @@ - - - - repo.gittr.ch - ci - ${env.REPO_TOKEN} - - + + + + repo.gittr.ch + ci + ${env.REPO_TOKEN} + + diff --git a/pom.xml b/pom.xml index 8259a2d..f99aeac 100644 --- a/pom.xml +++ b/pom.xml @@ -1,130 +1,132 @@ - - 4.0.0 + + 4.0.0 - - ch.fritteli - fritteli-build-parent - 2.0.4 - + + ch.fritteli + fritteli-build-parent + 2.0.4 + - ch.fritteli.labyrinth - labyrinth-server - 0.0.2-SNAPSHOT + ch.fritteli.labyrinth + labyrinth-server + 0.0.2-SNAPSHOT - - 1.4.6 - 1.18.26 - 2.0.5 - 17 - 17 - + + 1.4.6 + 1.18.26 + 2.0.5 + 17 + 17 + - - - ch.fritteli.labyrinth - labyrinth-generator - 0.0.2 - - - io.vavr - vavr - - - org.projectlombok - lombok - - - org.jetbrains - annotations - - - org.slf4j - slf4j-api - ${slf4j.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - io.undertow - undertow-core - 2.2.22.Final - - - org.junit.jupiter - junit-jupiter-api - - + + + ch.fritteli.labyrinth + labyrinth-generator + 0.0.2 + + + io.vavr + vavr + + + org.projectlombok + lombok + + + org.jetbrains + annotations + + + org.slf4j + slf4j-api + ${slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + io.undertow + undertow-core + 2.2.22.Final + + + org.junit.jupiter + junit-jupiter-api + + - - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.4 - - - - ch.fritteli.labyrinth.server.Main - - - - - - package - - shade - - - - - - - - scm:git:git://gittr.ch/java/labyrinth-server.git - scm:git:ssh://git@gittr.ch/java/labyrinth-server.git - https://gittr.ch/java/labyrinth-server - HEAD - - - - repo.gittr.ch - gittr.ch - https://repo.gittr.ch/releases/ - - - repo.gittr.ch - gittr.ch - https://repo.gittr.ch/snapshots/ - - - - - repo.gittr.ch.releases - https://repo.gittr.ch/releases/ - - true - never - - - false - never - - - - repo.gittr.ch.snapshots - https://repo.gittr.ch/snapshots/ - - false - never - - - true - always - - - + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + + ch.fritteli.labyrinth.server.Main + + + + + + package + + shade + + + + + + + + scm:git:git://gittr.ch/java/labyrinth-server.git + scm:git:ssh://git@gittr.ch/java/labyrinth-server.git + https://gittr.ch/java/labyrinth-server + HEAD + + + + repo.gittr.ch + gittr.ch + https://repo.gittr.ch/releases/ + + + repo.gittr.ch + gittr.ch + https://repo.gittr.ch/snapshots/ + + + + + repo.gittr.ch.releases + https://repo.gittr.ch/releases/ + + true + never + + + false + never + + + + repo.gittr.ch.snapshots + https://repo.gittr.ch/snapshots/ + + false + never + + + true + always + + + diff --git a/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java b/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java index aab20f6..f082742 100644 --- a/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java +++ b/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java @@ -119,15 +119,13 @@ public class LabyrinthServer { } responseHeaders.add("Content-type", output.getContentType()); if (output.isAttachment()) { - responseHeaders.add( - "Content-disposition", - String.format("attachment; filename=\"labyrinth-%dx%d-%d.%s\"", - width, - height, - id, - output.getFileExtension() - ) - ); + responseHeaders.add("Content-disposition", String.format( + "attachment; filename=\"labyrinth-%dx%d-%d.%s\"", + width, + height, + id, + output.getFileExtension() + )); } exchange.sendResponseHeaders(200, 0); final OutputStream responseBody = exchange.getResponseBody(); @@ -254,7 +252,7 @@ public class LabyrinthServer { } } - private enum OutputType { + public enum OutputType { TEXT_PLAIN("text/plain; charset=UTF-8", labyrinth -> TextRenderer.newInstance().render(labyrinth).getBytes(StandardCharsets.UTF_8), "txt", @@ -303,7 +301,7 @@ public class LabyrinthServer { this.names = List.of(names); } - static Option ofString(@Nullable final String name) { + public static Option ofString(@Nullable final String name) { if (name == null) { return Option.none(); } @@ -316,7 +314,7 @@ public class LabyrinthServer { return this.names.last(); } - byte[] render(@NonNull final Labyrinth labyrinth) { + public byte[] render(@NonNull final Labyrinth labyrinth) { return this.render.apply(labyrinth); } } diff --git a/src/main/java/ch/fritteli/labyrinth/server/ServerConfig.java b/src/main/java/ch/fritteli/labyrinth/server/ServerConfig.java index 58cee13..fc161dd 100644 --- a/src/main/java/ch/fritteli/labyrinth/server/ServerConfig.java +++ b/src/main/java/ch/fritteli/labyrinth/server/ServerConfig.java @@ -10,7 +10,6 @@ import org.jetbrains.annotations.Nullable; import java.net.InetAddress; - @AllArgsConstructor(access = AccessLevel.PRIVATE) @Slf4j @Value @@ -18,8 +17,7 @@ public class ServerConfig { public static final String SYSPROP_HOST = "fritteli.labyrinth.server.host"; public static final String SYSPROP_PORT = "fritteli.labyrinth.server.port"; - @NonNull - InetAddress address; + @NonNull InetAddress address; int port; public ServerConfig(@Nullable final String address, final int port) throws ConfigurationException { @@ -28,6 +26,19 @@ public class ServerConfig { log.debug("host={}, port={}", this.address, this.port); } + @NonNull + private static InetAddress validateAddress(@Nullable final String address) { + return Try.of(() -> InetAddress.getByName(address)) + .getOrElseThrow(cause -> new ConfigurationException("Invalid hostname/address: " + address, cause)); + } + + private static int validatePort(final int port) { + if (port < 0 || port > 0xFFFF) { + throw new ConfigurationException("Port out of range (0..65535): " + port); + } + return port; + } + @NonNull public static ServerConfig init() throws ConfigurationException { final String host = System.getProperty(SYSPROP_HOST); @@ -36,26 +47,16 @@ public class ServerConfig { return new ServerConfig(host, port); } - @NonNull - private static InetAddress validateAddress(@Nullable final String address) { - return Try.of(() -> InetAddress.getByName(address)) - .getOrElseThrow(cause -> new ConfigurationException("Invalid hostname/address: " + address, cause)); - } - private static int validatePort(@Nullable final String portString) { if (portString == null) { log.info("No port configured; using default."); return 0; } return Try.of(() -> Integer.valueOf(portString)) - .map(ServerConfig::validatePort) - .getOrElseThrow(cause -> new ConfigurationException("Failed to parse port specified in system property '" + SYSPROP_PORT + "': " + portString, cause)); - } - - private static int validatePort(final int port) { - if (port < 0 || port > 0xFFFF) { - throw new ConfigurationException("Port out of range (0..65535): " + port); - } - return port; + .map(ServerConfig::validatePort) + .getOrElseThrow(cause -> new ConfigurationException( + "Failed to parse port specified in system property '" + SYSPROP_PORT + "': " + portString, + cause + )); } } diff --git a/src/main/java/ch/fritteli/labyrinth/server/StaticResourcesFileHandler.java b/src/main/java/ch/fritteli/labyrinth/server/StaticResourcesFileHandler.java index d1cabfc..e6911e8 100644 --- a/src/main/java/ch/fritteli/labyrinth/server/StaticResourcesFileHandler.java +++ b/src/main/java/ch/fritteli/labyrinth/server/StaticResourcesFileHandler.java @@ -24,43 +24,6 @@ public class StaticResourcesFileHandler implements HttpHandler { log.debug("Created {}", this.getClass().getSimpleName()); } - private static void redirect(@NonNull final HttpExchange exchange, @NonNull final String target) throws IOException { - log.debug("Sending redirect to {}", target); - exchange.getResponseHeaders().add("Location", target); - exchange.sendResponseHeaders(302, -1); - } - - private static void notFound(@NonNull final HttpExchange exchange, @NonNull final String path) throws IOException { - log.debug("Resource '{}' not found, replying with HTTP 404", path); - exchange.getResponseHeaders().add("Content-type", "text/plain; charset=utf-8"); - exchange.sendResponseHeaders(404, 0); - exchange.getResponseBody().write("404 - Not found".getBytes(StandardCharsets.UTF_8)); - exchange.getResponseBody().flush(); - } - - @NonNull - private static byte[] getBytes(@NonNull final String path) throws IOException { - final InputStream stream = StaticResourcesFileHandler.class.getClassLoader().getResourceAsStream(WEBASSETS_DIRECTORY + path); - if (stream == null) { - log.debug("Resource '{}' not found in classpath.", path); - return new byte[0]; - } - final byte[] response = IOUtils.toByteArray(stream); - log.debug("Sending reply; {} bytes", response.length); - return response; - } - - @Nullable - private static String getMimeType(@NonNull final String path) { - if (path.endsWith(".html")) { - return "text/html"; - } - if (path.endsWith(".css")) { - return "text/css"; - } - return null; - } - @Override public void handle(@NonNull final HttpExchange exchange) throws IOException { this.executorService.submit(() -> { @@ -86,7 +49,13 @@ public class StaticResourcesFileHandler implements HttpHandler { notFound(exchange, path); return; } - log.debug("Serving {}{} with mimetype {}: {} bytes", WEBASSETS_DIRECTORY, path, mimeType, responseBytes.length); + log.debug( + "Serving {}{} with mimetype {}: {} bytes", + WEBASSETS_DIRECTORY, + path, + mimeType, + responseBytes.length + ); exchange.getResponseHeaders().add("Content-type", mimeType); exchange.sendResponseHeaders(200, 0); exchange.getResponseBody().write(responseBytes); @@ -98,4 +67,43 @@ public class StaticResourcesFileHandler implements HttpHandler { } }); } + + private static void redirect(@NonNull final HttpExchange exchange, @NonNull final String target) throws + IOException { + log.debug("Sending redirect to {}", target); + exchange.getResponseHeaders().add("Location", target); + exchange.sendResponseHeaders(302, -1); + } + + private static void notFound(@NonNull final HttpExchange exchange, @NonNull final String path) throws IOException { + log.debug("Resource '{}' not found, replying with HTTP 404", path); + exchange.getResponseHeaders().add("Content-type", "text/plain; charset=utf-8"); + exchange.sendResponseHeaders(404, 0); + exchange.getResponseBody().write("404 - Not found".getBytes(StandardCharsets.UTF_8)); + exchange.getResponseBody().flush(); + } + + @Nullable + private static String getMimeType(@NonNull final String path) { + if (path.endsWith(".html")) { + return "text/html"; + } + if (path.endsWith(".css")) { + return "text/css"; + } + return null; + } + + @NonNull + private static byte[] getBytes(@NonNull final String path) throws IOException { + final InputStream stream = StaticResourcesFileHandler.class.getClassLoader() + .getResourceAsStream(WEBASSETS_DIRECTORY + path); + if (stream == null) { + log.debug("Resource '{}' not found in classpath.", path); + return new byte[0]; + } + final byte[] response = IOUtils.toByteArray(stream); + log.debug("Sending reply; {} bytes", response.length); + return response; + } } diff --git a/src/main/java/ch/fritteli/labyrinth/server/undertow_playground/UndertowPlayground.java b/src/main/java/ch/fritteli/labyrinth/server/undertow_playground/UndertowPlayground.java index b64a343..8bb037f 100644 --- a/src/main/java/ch/fritteli/labyrinth/server/undertow_playground/UndertowPlayground.java +++ b/src/main/java/ch/fritteli/labyrinth/server/undertow_playground/UndertowPlayground.java @@ -1,5 +1,7 @@ package ch.fritteli.labyrinth.server.undertow_playground; +import ch.fritteli.labyrinth.generator.model.Labyrinth; +import ch.fritteli.labyrinth.server.LabyrinthServer; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.server.RoutingHandler; @@ -10,69 +12,69 @@ import io.vavr.control.Option; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import java.nio.ByteBuffer; import java.util.Deque; import java.util.Map; +import java.util.Random; @Slf4j public class UndertowPlayground { - public static final RoutingHandler r = new RoutingHandler() - .get("/create/{output}", new HttpHandler() { - @Override - public void handleRequest(HttpServerExchange exchange) throws Exception { - if (exchange.isInIoThread()) { - exchange.dispatch(this); - return; - } - final Map> queryParameters = exchange.getQueryParameters(); - final Option output = getFirstOption(queryParameters, "output"); - final Option width = getIntOption(queryParameters, "width"); - final Option height = getIntOption(queryParameters, "height"); - final Option id = getIntOption(queryParameters, "id"); + public static final RoutingHandler r = new RoutingHandler().get("/create/{output}", new HttpHandler() { + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + if (exchange.isInIoThread()) { + exchange.dispatch(this); + return; + } + final Map> queryParameters = exchange.getQueryParameters(); + final Option output = getFirstOption(queryParameters, "output"); + final Option width = getIntOption(queryParameters, "width"); + final Option height = getIntOption(queryParameters, "height"); + final Option id = getIntOption(queryParameters, "id"); - log.info("Output: {}", output); - log.info("Width: {}", width); - log.info("Height: {}", height); - log.info("Id: {}", id); - exchange.getResponseSender().send( - "Output: " + output + - ", Width: " + width + - ", Height: " + height + - ", Id: " + id - ); - } - }) - .post("/render", new HttpHandler() { - @Override - public void handleRequest(final HttpServerExchange exchange) { - if (exchange.isInIoThread()) { - exchange.dispatch(this); - return; - } - exchange.getResponseSender().send("TODO: read body, render stuff"); - } - }) - .setFallbackHandler(new HttpHandler() { - @Override - public void handleRequest(HttpServerExchange exchange) throws Exception { - exchange.getResponseSender().send("Request: " + exchange.getRequestURI()); - final HeaderValues strings = exchange.getRequestHeaders().get(Headers.ACCEPT); - strings.peekFirst(); - } - } - ); + log.info("Output: {}", output); + log.info("Width: {}", width); + log.info("Height: {}", height); + log.info("Id: {}", id); + final Integer theId = id.getOrElse(() -> new Random().nextInt()); + final Labyrinth labyrinth = new Labyrinth(width.get(), height.get(), theId); + final LabyrinthServer.OutputType outputType = output.flatMap(LabyrinthServer.OutputType::ofString).get(); + final byte[] result = outputType.render(labyrinth); + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, outputType.getContentType()); + exchange.getResponseHeaders().put(HttpString.tryFromString("X-Labyrinth-ID"), String.valueOf(theId)); + exchange.getResponseHeaders() + .put(HttpString.tryFromString("X-Labyrinth-Width"), String.valueOf(width.get())); + exchange.getResponseHeaders() + .put(HttpString.tryFromString("X-Labyrinth-Height"), String.valueOf(height.get())); + exchange.getResponseSender().send(ByteBuffer.wrap(result)); + } + }).post("/render", new HttpHandler() { + @Override + public void handleRequest(final HttpServerExchange exchange) { + if (exchange.isInIoThread()) { + exchange.dispatch(this); + return; + } + exchange.getResponseSender().send("TODO: read body, render stuff"); + } + }).setFallbackHandler(new HttpHandler() { + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + exchange.getResponseSender().send("Request: " + exchange.getRequestURI()); + final HeaderValues strings = exchange.getRequestHeaders().get(Headers.ACCEPT); + strings.peekFirst(); + } + }); @NonNull - private static Option getFirstOption(@NonNull final Map> queryParams, @NonNull final String paramName) { - return Option.of(queryParams.get(paramName)) - .map(Deque::peek) - .flatMap(Option::of); + private static Option getIntOption(@NonNull final Map> queryParams, + @NonNull final String paramName) { + return getFirstOption(queryParams, paramName).toTry().map(Integer::parseInt).toOption(); } @NonNull - private static Option getIntOption(@NonNull final Map> queryParams, @NonNull final String paramName) { - return getFirstOption(queryParams, paramName) - .toTry() - .map(Integer::parseInt) - .toOption(); + private static Option getFirstOption(@NonNull final Map> queryParams, + @NonNull final String paramName) { + return Option.of(queryParams.get(paramName)).map(Deque::peek).flatMap(Option::of); } } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 4bba3c1..f484fc8 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,17 +1,17 @@ - + - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + - - - - + + + + diff --git a/src/main/resources/webassets/index.html b/src/main/resources/webassets/index.html index 3444b42..b97b934 100644 --- a/src/main/resources/webassets/index.html +++ b/src/main/resources/webassets/index.html @@ -3,7 +3,7 @@ Labyrinth Generator - +
@@ -11,8 +11,8 @@

Enter some values, click the "Create!" button and see what happens!

- - + +
- +