feature/undertow #4
8 changed files with 281 additions and 270 deletions
|
@ -1,12 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"
|
<settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xmlns="http://maven.apache.org/SETTINGS/1.1.0"
|
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
xmlns="http://maven.apache.org/SETTINGS/1.1.0">
|
||||||
<servers>
|
<servers>
|
||||||
<server>
|
<server>
|
||||||
<id>repo.gittr.ch</id>
|
<id>repo.gittr.ch</id>
|
||||||
<username>ci</username>
|
<username>ci</username>
|
||||||
<password>${env.REPO_TOKEN}</password>
|
<password>${env.REPO_TOKEN}</password>
|
||||||
</server>
|
</server>
|
||||||
</servers>
|
</servers>
|
||||||
</settings>
|
</settings>
|
||||||
|
|
248
pom.xml
248
pom.xml
|
@ -1,130 +1,132 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
<modelVersion>4.0.0</modelVersion>
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ch.fritteli</groupId>
|
<groupId>ch.fritteli</groupId>
|
||||||
<artifactId>fritteli-build-parent</artifactId>
|
<artifactId>fritteli-build-parent</artifactId>
|
||||||
<version>2.0.4</version>
|
<version>2.0.4</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>ch.fritteli.labyrinth</groupId>
|
<groupId>ch.fritteli.labyrinth</groupId>
|
||||||
<artifactId>labyrinth-server</artifactId>
|
<artifactId>labyrinth-server</artifactId>
|
||||||
<version>0.0.2-SNAPSHOT</version>
|
<version>0.0.2-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<logback.version>1.4.6</logback.version>
|
<logback.version>1.4.6</logback.version>
|
||||||
<lombok.version>1.18.26</lombok.version>
|
<lombok.version>1.18.26</lombok.version>
|
||||||
<slf4j.version>2.0.5</slf4j.version>
|
<slf4j.version>2.0.5</slf4j.version>
|
||||||
<java.source.version>17</java.source.version>
|
<java.source.version>17</java.source.version>
|
||||||
<java.target.version>17</java.target.version>
|
<java.target.version>17</java.target.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.fritteli.labyrinth</groupId>
|
<groupId>ch.fritteli.labyrinth</groupId>
|
||||||
<artifactId>labyrinth-generator</artifactId>
|
<artifactId>labyrinth-generator</artifactId>
|
||||||
<version>0.0.2</version>
|
<version>0.0.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.vavr</groupId>
|
<groupId>io.vavr</groupId>
|
||||||
<artifactId>vavr</artifactId>
|
<artifactId>vavr</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jetbrains</groupId>
|
<groupId>org.jetbrains</groupId>
|
||||||
<artifactId>annotations</artifactId>
|
<artifactId>annotations</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<version>${slf4j.version}</version>
|
<version>${slf4j.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>ch.qos.logback</groupId>
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>logback-classic</artifactId>
|
||||||
<version>${logback.version}</version>
|
<version>${logback.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.undertow</groupId>
|
<groupId>io.undertow</groupId>
|
||||||
<artifactId>undertow-core</artifactId>
|
<artifactId>undertow-core</artifactId>
|
||||||
<version>2.2.22.Final</version>
|
<version>2.2.22.Final</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<version>3.2.4</version>
|
<version>3.2.4</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<transformers>
|
<transformers>
|
||||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
<transformer
|
||||||
<mainClass>ch.fritteli.labyrinth.server.Main</mainClass>
|
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
</transformer>
|
<mainClass>ch.fritteli.labyrinth.server.Main</mainClass>
|
||||||
</transformers>
|
</transformer>
|
||||||
</configuration>
|
</transformers>
|
||||||
<executions>
|
</configuration>
|
||||||
<execution>
|
<executions>
|
||||||
<phase>package</phase>
|
<execution>
|
||||||
<goals>
|
<phase>package</phase>
|
||||||
<goal>shade</goal>
|
<goals>
|
||||||
</goals>
|
<goal>shade</goal>
|
||||||
</execution>
|
</goals>
|
||||||
</executions>
|
</execution>
|
||||||
</plugin>
|
</executions>
|
||||||
</plugins>
|
</plugin>
|
||||||
</build>
|
</plugins>
|
||||||
<scm>
|
</build>
|
||||||
<connection>scm:git:git://gittr.ch/java/labyrinth-server.git</connection>
|
<scm>
|
||||||
<developerConnection>scm:git:ssh://git@gittr.ch/java/labyrinth-server.git</developerConnection>
|
<connection>scm:git:git://gittr.ch/java/labyrinth-server.git</connection>
|
||||||
<url>https://gittr.ch/java/labyrinth-server</url>
|
<developerConnection>scm:git:ssh://git@gittr.ch/java/labyrinth-server.git</developerConnection>
|
||||||
<tag>HEAD</tag>
|
<url>https://gittr.ch/java/labyrinth-server</url>
|
||||||
</scm>
|
<tag>HEAD</tag>
|
||||||
<distributionManagement>
|
</scm>
|
||||||
<repository>
|
<distributionManagement>
|
||||||
<id>repo.gittr.ch</id>
|
<repository>
|
||||||
<name>gittr.ch</name>
|
<id>repo.gittr.ch</id>
|
||||||
<url>https://repo.gittr.ch/releases/</url>
|
<name>gittr.ch</name>
|
||||||
</repository>
|
<url>https://repo.gittr.ch/releases/</url>
|
||||||
<snapshotRepository>
|
</repository>
|
||||||
<id>repo.gittr.ch</id>
|
<snapshotRepository>
|
||||||
<name>gittr.ch</name>
|
<id>repo.gittr.ch</id>
|
||||||
<url>https://repo.gittr.ch/snapshots/</url>
|
<name>gittr.ch</name>
|
||||||
</snapshotRepository>
|
<url>https://repo.gittr.ch/snapshots/</url>
|
||||||
</distributionManagement>
|
</snapshotRepository>
|
||||||
<repositories>
|
</distributionManagement>
|
||||||
<repository>
|
<repositories>
|
||||||
<id>repo.gittr.ch.releases</id>
|
<repository>
|
||||||
<url>https://repo.gittr.ch/releases/</url>
|
<id>repo.gittr.ch.releases</id>
|
||||||
<releases>
|
<url>https://repo.gittr.ch/releases/</url>
|
||||||
<enabled>true</enabled>
|
<releases>
|
||||||
<updatePolicy>never</updatePolicy>
|
<enabled>true</enabled>
|
||||||
</releases>
|
<updatePolicy>never</updatePolicy>
|
||||||
<snapshots>
|
</releases>
|
||||||
<enabled>false</enabled>
|
<snapshots>
|
||||||
<updatePolicy>never</updatePolicy>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
<updatePolicy>never</updatePolicy>
|
||||||
</repository>
|
</snapshots>
|
||||||
<repository>
|
</repository>
|
||||||
<id>repo.gittr.ch.snapshots</id>
|
<repository>
|
||||||
<url>https://repo.gittr.ch/snapshots/</url>
|
<id>repo.gittr.ch.snapshots</id>
|
||||||
<releases>
|
<url>https://repo.gittr.ch/snapshots/</url>
|
||||||
<enabled>false</enabled>
|
<releases>
|
||||||
<updatePolicy>never</updatePolicy>
|
<enabled>false</enabled>
|
||||||
</releases>
|
<updatePolicy>never</updatePolicy>
|
||||||
<snapshots>
|
</releases>
|
||||||
<enabled>true</enabled>
|
<snapshots>
|
||||||
<updatePolicy>always</updatePolicy>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
<updatePolicy>always</updatePolicy>
|
||||||
</repository>
|
</snapshots>
|
||||||
</repositories>
|
</repository>
|
||||||
|
</repositories>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -119,15 +119,13 @@ public class LabyrinthServer {
|
||||||
}
|
}
|
||||||
responseHeaders.add("Content-type", output.getContentType());
|
responseHeaders.add("Content-type", output.getContentType());
|
||||||
if (output.isAttachment()) {
|
if (output.isAttachment()) {
|
||||||
responseHeaders.add(
|
responseHeaders.add("Content-disposition", String.format(
|
||||||
"Content-disposition",
|
"attachment; filename=\"labyrinth-%dx%d-%d.%s\"",
|
||||||
String.format("attachment; filename=\"labyrinth-%dx%d-%d.%s\"",
|
width,
|
||||||
width,
|
height,
|
||||||
height,
|
id,
|
||||||
id,
|
output.getFileExtension()
|
||||||
output.getFileExtension()
|
));
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
exchange.sendResponseHeaders(200, 0);
|
exchange.sendResponseHeaders(200, 0);
|
||||||
final OutputStream responseBody = exchange.getResponseBody();
|
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",
|
TEXT_PLAIN("text/plain; charset=UTF-8",
|
||||||
labyrinth -> TextRenderer.newInstance().render(labyrinth).getBytes(StandardCharsets.UTF_8),
|
labyrinth -> TextRenderer.newInstance().render(labyrinth).getBytes(StandardCharsets.UTF_8),
|
||||||
"txt",
|
"txt",
|
||||||
|
@ -303,7 +301,7 @@ public class LabyrinthServer {
|
||||||
this.names = List.of(names);
|
this.names = List.of(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Option<OutputType> ofString(@Nullable final String name) {
|
public static Option<OutputType> ofString(@Nullable final String name) {
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
return Option.none();
|
return Option.none();
|
||||||
}
|
}
|
||||||
|
@ -316,7 +314,7 @@ public class LabyrinthServer {
|
||||||
return this.names.last();
|
return this.names.last();
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] render(@NonNull final Labyrinth labyrinth) {
|
public byte[] render(@NonNull final Labyrinth labyrinth) {
|
||||||
return this.render.apply(labyrinth);
|
return this.render.apply(labyrinth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
|
||||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Value
|
@Value
|
||||||
|
@ -18,8 +17,7 @@ public class ServerConfig {
|
||||||
public static final String SYSPROP_HOST = "fritteli.labyrinth.server.host";
|
public static final String SYSPROP_HOST = "fritteli.labyrinth.server.host";
|
||||||
public static final String SYSPROP_PORT = "fritteli.labyrinth.server.port";
|
public static final String SYSPROP_PORT = "fritteli.labyrinth.server.port";
|
||||||
|
|
||||||
@NonNull
|
@NonNull InetAddress address;
|
||||||
InetAddress address;
|
|
||||||
int port;
|
int port;
|
||||||
|
|
||||||
public ServerConfig(@Nullable final String address, final int port) throws ConfigurationException {
|
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);
|
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
|
@NonNull
|
||||||
public static ServerConfig init() throws ConfigurationException {
|
public static ServerConfig init() throws ConfigurationException {
|
||||||
final String host = System.getProperty(SYSPROP_HOST);
|
final String host = System.getProperty(SYSPROP_HOST);
|
||||||
|
@ -36,26 +47,16 @@ public class ServerConfig {
|
||||||
return new ServerConfig(host, port);
|
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) {
|
private static int validatePort(@Nullable final String portString) {
|
||||||
if (portString == null) {
|
if (portString == null) {
|
||||||
log.info("No port configured; using default.");
|
log.info("No port configured; using default.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return Try.of(() -> Integer.valueOf(portString))
|
return Try.of(() -> Integer.valueOf(portString))
|
||||||
.map(ServerConfig::validatePort)
|
.map(ServerConfig::validatePort)
|
||||||
.getOrElseThrow(cause -> new ConfigurationException("Failed to parse port specified in system property '" + SYSPROP_PORT + "': " + portString, cause));
|
.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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,43 +24,6 @@ public class StaticResourcesFileHandler implements HttpHandler {
|
||||||
log.debug("Created {}", this.getClass().getSimpleName());
|
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
|
@Override
|
||||||
public void handle(@NonNull final HttpExchange exchange) throws IOException {
|
public void handle(@NonNull final HttpExchange exchange) throws IOException {
|
||||||
this.executorService.submit(() -> {
|
this.executorService.submit(() -> {
|
||||||
|
@ -86,7 +49,13 @@ public class StaticResourcesFileHandler implements HttpHandler {
|
||||||
notFound(exchange, path);
|
notFound(exchange, path);
|
||||||
return;
|
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.getResponseHeaders().add("Content-type", mimeType);
|
||||||
exchange.sendResponseHeaders(200, 0);
|
exchange.sendResponseHeaders(200, 0);
|
||||||
exchange.getResponseBody().write(responseBytes);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package ch.fritteli.labyrinth.server.undertow_playground;
|
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.HttpHandler;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.server.RoutingHandler;
|
import io.undertow.server.RoutingHandler;
|
||||||
|
@ -10,69 +12,69 @@ import io.vavr.control.Option;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class UndertowPlayground {
|
public class UndertowPlayground {
|
||||||
public static final RoutingHandler r = new RoutingHandler()
|
public static final RoutingHandler r = new RoutingHandler().get("/create/{output}", new HttpHandler() {
|
||||||
.get("/create/{output}", new HttpHandler() {
|
@Override
|
||||||
@Override
|
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
if (exchange.isInIoThread()) {
|
||||||
if (exchange.isInIoThread()) {
|
exchange.dispatch(this);
|
||||||
exchange.dispatch(this);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
final Map<String, Deque<String>> queryParameters = exchange.getQueryParameters();
|
||||||
final Map<String, Deque<String>> queryParameters = exchange.getQueryParameters();
|
final Option<String> output = getFirstOption(queryParameters, "output");
|
||||||
final Option<String> output = getFirstOption(queryParameters, "output");
|
final Option<Integer> width = getIntOption(queryParameters, "width");
|
||||||
final Option<Integer> width = getIntOption(queryParameters, "width");
|
final Option<Integer> height = getIntOption(queryParameters, "height");
|
||||||
final Option<Integer> height = getIntOption(queryParameters, "height");
|
final Option<Integer> id = getIntOption(queryParameters, "id");
|
||||||
final Option<Integer> id = getIntOption(queryParameters, "id");
|
|
||||||
|
|
||||||
log.info("Output: {}", output);
|
log.info("Output: {}", output);
|
||||||
log.info("Width: {}", width);
|
log.info("Width: {}", width);
|
||||||
log.info("Height: {}", height);
|
log.info("Height: {}", height);
|
||||||
log.info("Id: {}", id);
|
log.info("Id: {}", id);
|
||||||
exchange.getResponseSender().send(
|
final Integer theId = id.getOrElse(() -> new Random().nextInt());
|
||||||
"Output: " + output +
|
final Labyrinth labyrinth = new Labyrinth(width.get(), height.get(), theId);
|
||||||
", Width: " + width +
|
final LabyrinthServer.OutputType outputType = output.flatMap(LabyrinthServer.OutputType::ofString).get();
|
||||||
", Height: " + height +
|
final byte[] result = outputType.render(labyrinth);
|
||||||
", Id: " + id
|
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()));
|
||||||
.post("/render", new HttpHandler() {
|
exchange.getResponseHeaders()
|
||||||
@Override
|
.put(HttpString.tryFromString("X-Labyrinth-Height"), String.valueOf(height.get()));
|
||||||
public void handleRequest(final HttpServerExchange exchange) {
|
exchange.getResponseSender().send(ByteBuffer.wrap(result));
|
||||||
if (exchange.isInIoThread()) {
|
}
|
||||||
exchange.dispatch(this);
|
}).post("/render", new HttpHandler() {
|
||||||
return;
|
@Override
|
||||||
}
|
public void handleRequest(final HttpServerExchange exchange) {
|
||||||
exchange.getResponseSender().send("TODO: read body, render stuff");
|
if (exchange.isInIoThread()) {
|
||||||
}
|
exchange.dispatch(this);
|
||||||
})
|
return;
|
||||||
.setFallbackHandler(new HttpHandler() {
|
}
|
||||||
@Override
|
exchange.getResponseSender().send("TODO: read body, render stuff");
|
||||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
}
|
||||||
exchange.getResponseSender().send("Request: " + exchange.getRequestURI());
|
}).setFallbackHandler(new HttpHandler() {
|
||||||
final HeaderValues strings = exchange.getRequestHeaders().get(Headers.ACCEPT);
|
@Override
|
||||||
strings.peekFirst();
|
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||||
}
|
exchange.getResponseSender().send("Request: " + exchange.getRequestURI());
|
||||||
}
|
final HeaderValues strings = exchange.getRequestHeaders().get(Headers.ACCEPT);
|
||||||
);
|
strings.peekFirst();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static Option<String> getFirstOption(@NonNull final Map<String, Deque<String>> queryParams, @NonNull final String paramName) {
|
private static Option<Integer> getIntOption(@NonNull final Map<String, Deque<String>> queryParams,
|
||||||
return Option.of(queryParams.get(paramName))
|
@NonNull final String paramName) {
|
||||||
.map(Deque::peek)
|
return getFirstOption(queryParams, paramName).toTry().map(Integer::parseInt).toOption();
|
||||||
.flatMap(Option::of);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private static Option<Integer> getIntOption(@NonNull final Map<String, Deque<String>> queryParams, @NonNull final String paramName) {
|
private static Option<String> getFirstOption(@NonNull final Map<String, Deque<String>> queryParams,
|
||||||
return getFirstOption(queryParams, paramName)
|
@NonNull final String paramName) {
|
||||||
.toTry()
|
return Option.of(queryParams.get(paramName)).map(Deque::peek).flatMap(Option::of);
|
||||||
.map(Integer::parseInt)
|
|
||||||
.toOption();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<shutdownHook class="ch.qos.logback.core.hook.DefaultShutdownHook"/>
|
<shutdownHook class="ch.qos.logback.core.hook.DefaultShutdownHook"/>
|
||||||
|
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<!-- encoders are by default assigned the type
|
<!-- encoders are by default assigned the type
|
||||||
ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
|
ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<root level="info">
|
<root level="info">
|
||||||
<appender-ref ref="STDOUT"/>
|
<appender-ref ref="STDOUT"/>
|
||||||
</root>
|
</root>
|
||||||
<logger name="ch.fritteli.labyrinth.server.StaticResourcesFileHandler" level="debug"/>
|
<logger name="ch.fritteli.labyrinth.server.StaticResourcesFileHandler" level="debug"/>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Labyrinth Generator</title>
|
<title>Labyrinth Generator</title>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link href="style.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
@ -11,8 +11,8 @@
|
||||||
<p>Enter some values, click the "Create!" button and see what happens!</p>
|
<p>Enter some values, click the "Create!" button and see what happens!</p>
|
||||||
<form action="/create" method="get">
|
<form action="/create" method="get">
|
||||||
<div class="inputs-wrapper">
|
<div class="inputs-wrapper">
|
||||||
<label for="width">Width:</label><input id="width" name="width" type="number" min="1" required>
|
<label for="width">Width:</label><input id="width" min="1" name="width" required type="number">
|
||||||
<label for="height">Height:</label><input id="height" name="height" type="number" min="1" required>
|
<label for="height">Height:</label><input id="height" min="1" name="height" required type="number">
|
||||||
<label for="output">Output format:</label>
|
<label for="output">Output format:</label>
|
||||||
<select id="output" name="output" required>
|
<select id="output" name="output" required>
|
||||||
<option label="HTML Document" value="html"></option>
|
<option label="HTML Document" value="html"></option>
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<label for="id">Seed (optional):</label><input id="id" name="id" type="number">
|
<label for="id">Seed (optional):</label><input id="id" name="id" type="number">
|
||||||
</div>
|
</div>
|
||||||
<div class="controls-wrapper">
|
<div class="controls-wrapper">
|
||||||
<button type="submit" class="primary">Create!</button>
|
<button class="primary" type="submit">Create!</button>
|
||||||
<button type="reset">Reset form</button>
|
<button type="reset">Reset form</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
Loading…
Reference in a new issue