diff --git a/pom.xml b/pom.xml
index 668a742..c47be7e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
17
24.0.1
5.9.2
- 0.0.3
+ 0.0.4
1.4.6
1.18.26
2.0.7
diff --git a/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java b/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java
index f0f0321..16e0aae 100644
--- a/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java
+++ b/src/main/java/ch/fritteli/labyrinth/server/LabyrinthServer.java
@@ -5,24 +5,23 @@ import ch.fritteli.labyrinth.server.handler.RenderHandler;
import io.undertow.Undertow;
import io.undertow.server.RoutingHandler;
import io.vavr.control.Try;
+import java.net.InetSocketAddress;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LabyrinthServer {
- @NonNull
- private final ServerConfig config;
+
@NonNull
private final Undertow undertow;
private LabyrinthServer(@NonNull final ServerConfig config) {
- this.config = config;
final String hostAddress = config.getAddress().getHostAddress();
final int port = config.getPort();
log.info("Starting Server at http://{}:{}/", hostAddress, port);
final RoutingHandler routingHandler = new RoutingHandler()
.get(CreateHandler.PATH_TEMPLATE, new CreateHandler())
- .post("/render", new RenderHandler());
+ .post(RenderHandler.PATH_TEMPLATE, new RenderHandler());
this.undertow = Undertow.builder()
.addHttpListener(port, hostAddress)
@@ -45,7 +44,11 @@ public class LabyrinthServer {
private void start() {
Runtime.getRuntime().addShutdownHook(new Thread(this::stop, "listener-stopper"));
this.undertow.start();
- log.info("Listening on http://{}:{}", this.config.getAddress().getHostAddress(), this.config.getPort());
+ final InetSocketAddress address = (InetSocketAddress) this.undertow.getListenerInfo().get(0).getAddress();
+ final String hostAddress = address.getAddress().getHostAddress();
+ final int port = address.getPort();
+
+ log.info("Listening on http://{}:{}", hostAddress, port);
}
private void stop() {
diff --git a/src/main/java/ch/fritteli/labyrinth/server/OutputType.java b/src/main/java/ch/fritteli/labyrinth/server/OutputType.java
index 4668bdd..c1d08d9 100644
--- a/src/main/java/ch/fritteli/labyrinth/server/OutputType.java
+++ b/src/main/java/ch/fritteli/labyrinth/server/OutputType.java
@@ -30,13 +30,13 @@ public enum OutputType {
"html"),
PDF("application/pdf",
"pdf",
- labyrinth -> PDFRenderer.newInstance().render(labyrinth),
+ labyrinth -> PDFRenderer.newInstance().render(labyrinth).toByteArray(),
false,
"p",
"pdf"),
PDFFILE("application/pdf",
"pdf",
- labyrinth -> PDFRenderer.newInstance().render(labyrinth),
+ labyrinth -> PDFRenderer.newInstance().render(labyrinth).toByteArray(),
true,
"f",
"pdffile"),
diff --git a/src/main/java/ch/fritteli/labyrinth/server/handler/CreateHandler.java b/src/main/java/ch/fritteli/labyrinth/server/handler/CreateHandler.java
index c1578fd..344a907 100644
--- a/src/main/java/ch/fritteli/labyrinth/server/handler/CreateHandler.java
+++ b/src/main/java/ch/fritteli/labyrinth/server/handler/CreateHandler.java
@@ -55,6 +55,15 @@ public class CreateHandler extends AbstractHttpHandler {
.put(HttpString.tryFromString("X-Labyrinth-Width"), String.valueOf(labyrinth.getWidth()))
.put(HttpString.tryFromString("X-Labyrinth-Height"), String.valueOf(labyrinth.getHeight()))
.put(HttpString.tryFromString("X-Labyrinth-Generation-Duration-millis"), String.valueOf(durationMillis));
+ if (outputType.isAttachment()) {
+ exchange.getResponseHeaders()
+ .put(Headers.CONTENT_DISPOSITION, "attachment; filename=\"labyrinth-%dx%d-%d.%s\"".formatted(
+ labyrinth.getWidth(),
+ labyrinth.getHeight(),
+ labyrinth.getRandomSeed(),
+ outputType.getFileExtension()
+ ));
+ }
exchange.getResponseSender().send(ByteBuffer.wrap(bytes));
log.debug("Create request handled in {}ms.", durationMillis);
});
diff --git a/src/main/java/ch/fritteli/labyrinth/server/handler/ParametersToLabyrinthExtractor.java b/src/main/java/ch/fritteli/labyrinth/server/handler/ParametersToLabyrinthExtractor.java
index b766b77..a1cf854 100644
--- a/src/main/java/ch/fritteli/labyrinth/server/handler/ParametersToLabyrinthExtractor.java
+++ b/src/main/java/ch/fritteli/labyrinth/server/handler/ParametersToLabyrinthExtractor.java
@@ -4,39 +4,20 @@ import ch.fritteli.labyrinth.generator.model.Labyrinth;
import ch.fritteli.labyrinth.server.OutputType;
import io.vavr.Tuple;
import io.vavr.Tuple2;
-import io.vavr.collection.HashMap;
-import io.vavr.collection.HashMultimap;
-import io.vavr.collection.HashSet;
-import io.vavr.collection.List;
-import io.vavr.collection.Multimap;
-import io.vavr.collection.Set;
import io.vavr.collection.Stream;
import io.vavr.control.Option;
import io.vavr.control.Try;
import java.util.Deque;
import java.util.Map;
import java.util.Random;
-import java.util.function.Function;
-import lombok.Getter;
import lombok.NonNull;
-import lombok.extern.slf4j.Slf4j;
-import org.jetbrains.annotations.Nullable;
+import lombok.RequiredArgsConstructor;
+@RequiredArgsConstructor
class ParametersToLabyrinthExtractor {
@NonNull
- private final Multimap queryParameters;
-
- ParametersToLabyrinthExtractor(@NonNull final Map> queryParameters) {
- this.queryParameters = HashMap.ofAll(queryParameters)
- .foldLeft(
- HashMultimap.withSet().empty(),
- (map, tuple) -> RequestParameter.parseName(tuple._1()).map(parameter -> Stream.ofAll(tuple._2())
- .flatMap(parameter::extractParameterValue)
- .foldLeft(map, (m, value) -> m.put(parameter, value)))
- .getOrElse(map)
- );
- }
+ private final Map> queryParameters;
@NonNull
Try> createLabyrinth() {
@@ -69,43 +50,6 @@ class ParametersToLabyrinthExtractor {
@NonNull
private Option getParameterValue(@NonNull final RequestParameter parameter) {
- return (Option) this.queryParameters.getOrElse(parameter, List.empty())
- .headOption();
- }
-
- @Slf4j
- private enum RequestParameter {
- WIDTH(p -> Try.of(() -> Integer.parseInt(p))
- .toOption()
- .onEmpty(() -> log.debug("Unparseable value for parameter 'width': '{}'", p)), "w", "width"),
- HEIGHT(p -> Try.of(() -> Integer.parseInt(p))
- .toOption()
- .onEmpty(() -> log.debug("Unparseable value for parameter 'height': '{}'", p)), "h", "height"),
- ID(p -> Try.of(() -> Long.parseLong(p))
- .toOption()
- .onEmpty(() -> log.debug("Unparseable value for parameter 'id': '{}'", p)), "i", "id"),
- OUTPUT(p -> OutputType.ofString(p)
- .onEmpty(() -> log.debug("Unparseable value for parameter 'output': '{}'", p)), "o", "output");
- @NonNull
- private final Function> extractor;
- @Getter
- @NonNull
- private final Set names;
-
- RequestParameter(@NonNull final Function> extractor, @NonNull final String... names) {
- this.extractor = extractor;
- this.names = HashSet.of(names);
- }
-
- static Option parseName(@Nullable final String name) {
- if (name == null) {
- return Option.none();
- }
- return Stream.of(values()).find(param -> param.names.exists(name::equalsIgnoreCase));
- }
-
- @NonNull Option> extractParameterValue(@NonNull final String parameter) {
- return this.extractor.apply(parameter);
- }
+ return parameter.getParameterValue(this.queryParameters);
}
}
diff --git a/src/main/java/ch/fritteli/labyrinth/server/handler/RenderHandler.java b/src/main/java/ch/fritteli/labyrinth/server/handler/RenderHandler.java
index 5079a09..52a61d5 100644
--- a/src/main/java/ch/fritteli/labyrinth/server/handler/RenderHandler.java
+++ b/src/main/java/ch/fritteli/labyrinth/server/handler/RenderHandler.java
@@ -3,17 +3,19 @@ package ch.fritteli.labyrinth.server.handler;
import ch.fritteli.labyrinth.generator.model.Labyrinth;
import ch.fritteli.labyrinth.generator.serialization.SerializerDeserializer;
import ch.fritteli.labyrinth.server.OutputType;
-import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
+import io.undertow.util.HeaderValues;
import io.undertow.util.Headers;
import io.undertow.util.StatusCodes;
-import io.vavr.control.Option;
-import lombok.extern.slf4j.Slf4j;
-
import java.nio.ByteBuffer;
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
@Slf4j
public class RenderHandler extends AbstractHttpHandler {
+
+ public static final String PATH_TEMPLATE = "/render/{output}";
+
@Override
public void handle(final HttpServerExchange exchange) {
log.debug("Handling render request");
@@ -23,13 +25,10 @@ public class RenderHandler extends AbstractHttpHandler {
return;
}
exchange.getRequestReceiver().receiveFullBytes((httpServerExchange, bytes) -> {
- final Labyrinth labyrinth = SerializerDeserializer.deserialize(bytes);
- final OutputType output = Option.of(httpServerExchange.getRequestHeaders().get(Headers.ACCEPT))
- .exists(values -> values.contains(OutputType.HTML.getContentType())) ?
- OutputType.HTML :
- OutputType.TEXT_PLAIN;
+ final OutputType output = this.getOutputType(httpServerExchange);
final byte[] render;
try {
+ final Labyrinth labyrinth = SerializerDeserializer.deserialize(bytes);
render = output.render(labyrinth);
} catch (final Exception e) {
log.error("Error rendering binary labyrinth data", e);
@@ -46,4 +45,16 @@ public class RenderHandler extends AbstractHttpHandler {
.send(ByteBuffer.wrap(render));
});
}
+
+ @NonNull
+ private OutputType getOutputType(@NonNull final HttpServerExchange httpServerExchange) {
+ return RequestParameter.OUTPUT.getParameterValue(httpServerExchange.getQueryParameters())
+ .getOrElse(() -> {
+ final HeaderValues accept = httpServerExchange.getRequestHeaders().get(Headers.ACCEPT);
+ if (accept.contains(OutputType.HTML.getContentType())) {
+ return OutputType.HTML;
+ }
+ return OutputType.TEXT_PLAIN;
+ });
+ }
}
diff --git a/src/main/java/ch/fritteli/labyrinth/server/handler/RequestParameter.java b/src/main/java/ch/fritteli/labyrinth/server/handler/RequestParameter.java
new file mode 100644
index 0000000..314ee22
--- /dev/null
+++ b/src/main/java/ch/fritteli/labyrinth/server/handler/RequestParameter.java
@@ -0,0 +1,54 @@
+package ch.fritteli.labyrinth.server.handler;
+
+import ch.fritteli.labyrinth.server.OutputType;
+import io.vavr.Tuple2;
+import io.vavr.collection.HashMap;
+import io.vavr.collection.HashSet;
+import io.vavr.collection.Set;
+import io.vavr.control.Option;
+import io.vavr.control.Try;
+import java.util.Deque;
+import java.util.Map;
+import java.util.function.Function;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+enum RequestParameter {
+ WIDTH(p -> Try.of(() -> Integer.parseInt(p))
+ .toOption()
+ .onEmpty(() -> log.debug("Unparseable value for parameter 'width': '{}'", p)), "w", "width"),
+ HEIGHT(p -> Try.of(() -> Integer.parseInt(p))
+ .toOption()
+ .onEmpty(() -> log.debug("Unparseable value for parameter 'height': '{}'", p)), "h", "height"),
+ ID(p -> Try.of(() -> Long.parseLong(p))
+ .toOption()
+ .onEmpty(() -> log.debug("Unparseable value for parameter 'id': '{}'", p)), "i", "id"),
+ OUTPUT(p -> OutputType.ofString(p)
+ .onEmpty(() -> log.debug("Unparseable value for parameter 'output': '{}'", p)), "o", "output");
+ @NonNull
+ private final Function> extractor;
+ @Getter
+ @NonNull
+ private final Set names;
+
+ RequestParameter(@NonNull final Function> extractor, @NonNull final String... names) {
+ this.extractor = extractor;
+ this.names = HashSet.of(names);
+ }
+
+ @NonNull
+ Option> extractParameterValue(@NonNull final String parameter) {
+ return this.extractor.apply(parameter);
+ }
+
+ @NonNull
+ public Option getParameterValue(@NonNull final Map> queryParameters) {
+ return (Option) HashMap.ofAll(queryParameters)
+ .filterKeys(this.names::contains)
+ .flatMap(Tuple2::_2)
+ .flatMap(this::extractParameterValue)
+ .headOption();
+ }
+}