From 2444a071ac9335239811b84830784586598f6c2d Mon Sep 17 00:00:00 2001 From: Benedikt Galbavy Date: Sun, 24 Dec 2023 11:23:49 +0100 Subject: [PATCH] REST resolve ~4.5h work --- src/Main.java | 8 ++- .../nanopenguin/mtcg/application/Service.java | 11 ++++ .../mtcg/application/TestService.java | 18 ++++++ src/at/nanopenguin/mtcg/http/HttpMethod.java | 18 ++++++ src/at/nanopenguin/mtcg/http/HttpRequest.java | 59 +++++++++++++++++++ .../nanopenguin/mtcg/http/RequestHandler.java | 34 +++++++++-- src/at/nanopenguin/mtcg/http/Route.java | 13 ++++ src/at/nanopenguin/mtcg/http/Router.java | 52 ++++++++++++++++ src/at/nanopenguin/mtcg/http/Server.java | 1 + 9 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 src/at/nanopenguin/mtcg/application/Service.java create mode 100644 src/at/nanopenguin/mtcg/application/TestService.java create mode 100644 src/at/nanopenguin/mtcg/http/HttpMethod.java create mode 100644 src/at/nanopenguin/mtcg/http/HttpRequest.java create mode 100644 src/at/nanopenguin/mtcg/http/Route.java diff --git a/src/Main.java b/src/Main.java index 65710dc..9e22eaa 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,3 +1,5 @@ +import at.nanopenguin.mtcg.application.TestService; +import at.nanopenguin.mtcg.http.HttpMethod; import at.nanopenguin.mtcg.http.Router; import at.nanopenguin.mtcg.http.Server; @@ -5,8 +7,10 @@ import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { - System.out.println("Hello world!"); - Server server = new Server(10001, 10, new Router()); + Router router = new Router(); + router.addRoute(HttpMethod.GET, "/test/{var}/service", new TestService(), 1); + + Server server = new Server(10001, 10, router); server.start(); } } \ No newline at end of file diff --git a/src/at/nanopenguin/mtcg/application/Service.java b/src/at/nanopenguin/mtcg/application/Service.java new file mode 100644 index 0000000..f2c9d96 --- /dev/null +++ b/src/at/nanopenguin/mtcg/application/Service.java @@ -0,0 +1,11 @@ +package at.nanopenguin.mtcg.application; + +import at.nanopenguin.mtcg.http.Response; + +public interface Service { + String pathVariable = null; + + void setPathVariable(String var); + + Response handleRequest(String request); +} diff --git a/src/at/nanopenguin/mtcg/application/TestService.java b/src/at/nanopenguin/mtcg/application/TestService.java new file mode 100644 index 0000000..f8b38a5 --- /dev/null +++ b/src/at/nanopenguin/mtcg/application/TestService.java @@ -0,0 +1,18 @@ +package at.nanopenguin.mtcg.application; + +import at.nanopenguin.mtcg.http.HttpStatus; +import at.nanopenguin.mtcg.http.Response; + +public class TestService implements Service { + private String pathVariable = null; + + @Override + public void setPathVariable(String var) { + this.pathVariable = var; + } + + @Override + public Response handleRequest(String request) { + return new Response(HttpStatus.OK, "application/json", "{\"var\":\"" + this.pathVariable + "\"]"); + } +} diff --git a/src/at/nanopenguin/mtcg/http/HttpMethod.java b/src/at/nanopenguin/mtcg/http/HttpMethod.java new file mode 100644 index 0000000..67ed9f3 --- /dev/null +++ b/src/at/nanopenguin/mtcg/http/HttpMethod.java @@ -0,0 +1,18 @@ +package at.nanopenguin.mtcg.http; + +public enum HttpMethod { + GET, + POST, + DELETE, + PUT; + + public static HttpMethod strToMethod(String methodStr) { + return switch (methodStr) { + case "GET" -> HttpMethod.GET; + case "POST" -> HttpMethod.POST; + case "DELETE" -> HttpMethod.DELETE; + case "PUT" -> HttpMethod.PUT; + default -> null; + }; + } +} diff --git a/src/at/nanopenguin/mtcg/http/HttpRequest.java b/src/at/nanopenguin/mtcg/http/HttpRequest.java new file mode 100644 index 0000000..6353b22 --- /dev/null +++ b/src/at/nanopenguin/mtcg/http/HttpRequest.java @@ -0,0 +1,59 @@ +package at.nanopenguin.mtcg.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequest { + private final HttpMethod method; + private final String path; + private final String version; + private Map httpHeaders = new HashMap(); + private final String body; + + public HttpRequest(BufferedReader br) throws IOException { + String line = br.readLine(); + + if (line != null) { + String[] requestLine = line.split(" "); + this.method = HttpMethod.strToMethod(requestLine[0]); + this.path = requestLine[1]; + this.version = requestLine[2]; + + for (line = br.readLine(); !line.isEmpty(); line = br.readLine()) { + String[] headerEntry = line.split(": ", 2); + this.httpHeaders.put(headerEntry[0], headerEntry[1]); + } + + this.body = this.httpHeaders.containsKey("Content-Length") ? new String(new char[Integer.parseInt(this.httpHeaders.get("Content-Length"))]) : null; + + return; + } + + this.method = null; + this.path = null; + this.version = null; + this.body = null; + } + + public HttpMethod getMethod() { + return method; + } + + public String getPath() { + return path; + } + + public String getVersion() { + return version; + } + + public String getHttpHeader(String header) { + return httpHeaders.get(header); + } + + public String getBody() { + return body; + } +} diff --git a/src/at/nanopenguin/mtcg/http/RequestHandler.java b/src/at/nanopenguin/mtcg/http/RequestHandler.java index 05bea37..a54f323 100644 --- a/src/at/nanopenguin/mtcg/http/RequestHandler.java +++ b/src/at/nanopenguin/mtcg/http/RequestHandler.java @@ -1,15 +1,16 @@ package at.nanopenguin.mtcg.http; +import at.nanopenguin.mtcg.application.Service; + import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; public class RequestHandler implements Runnable { private final Socket serviceSocket; private final Router router; - private BufferedReader br; - private OutputStream out; public RequestHandler(Socket serviceSocket, Router router) { this.serviceSocket = serviceSocket; @@ -19,11 +20,36 @@ public class RequestHandler implements Runnable { @Override public void run() { try { - Response response = new Response(HttpStatus.OK, "application/json", "[]"); + BufferedReader br = new BufferedReader(new InputStreamReader(serviceSocket.getInputStream())); - this.out = this.serviceSocket.getOutputStream(); + System.out.println("creating httpRequest"); + HttpRequest httpRequest = new HttpRequest(br); + + Response response; + responseBuilder: { + if (httpRequest.getMethod() == null) { + System.out.println("generic error"); + response = new Response(HttpStatus.INTERNAL, "text/plain", ""); + break responseBuilder; + } + + System.out.println("getting service"); + Service service = router.resolveRoute(httpRequest.getMethod(), httpRequest.getPath()); + + if (service == null) { + System.out.println("service does not exist"); + response = new Response(HttpStatus.NOT_IMPLEMENTED, "text/plain", ""); + break responseBuilder; + } + System.out.println("creating response"); + response = service.handleRequest(httpRequest.getBody()); + } + + OutputStream out = this.serviceSocket.getOutputStream(); out.write(response.get().getBytes()); out.flush(); + + System.out.println("request done"); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/at/nanopenguin/mtcg/http/Route.java b/src/at/nanopenguin/mtcg/http/Route.java new file mode 100644 index 0000000..d396e50 --- /dev/null +++ b/src/at/nanopenguin/mtcg/http/Route.java @@ -0,0 +1,13 @@ +package at.nanopenguin.mtcg.http; + +import at.nanopenguin.mtcg.application.Service; + +public class Route { + public final Service service; + public final boolean hasPathVariable; + + public Route(Service service, boolean hasPathVariable) { + this.service = service; + this.hasPathVariable = hasPathVariable; + } +} diff --git a/src/at/nanopenguin/mtcg/http/Router.java b/src/at/nanopenguin/mtcg/http/Router.java index b76b936..38e6d99 100644 --- a/src/at/nanopenguin/mtcg/http/Router.java +++ b/src/at/nanopenguin/mtcg/http/Router.java @@ -1,4 +1,56 @@ package at.nanopenguin.mtcg.http; +import at.nanopenguin.mtcg.application.Service; + +import java.util.*; +import java.util.stream.IntStream; + public class Router { + private Map> routeMap = new HashMap<>(); + + public void addRoute(final HttpMethod method, final String route, final Service service, final int pathVarPos) { + Map map = this.routeMap.get(method); + if (method != null && map == null) { + this.routeMap.put(method, (map = new HashMap<>())); + } + + List routeComponents = new ArrayList(Arrays.asList(route.split("/"))); + routeComponents.remove(0); + routeComponents.set(pathVarPos, "{var}"); + + for (int i = 0; i < routeComponents.size(); i++) { + Route routeComponent = new Route(i == routeComponents.size() - 1 ? service : null, i == pathVarPos - 1); + String path = String.join("/", routeComponents.subList(0, i+1)); + map.put(path, routeComponent); + } + + } + + public Service resolveRoute(final HttpMethod method, final String route) { + System.out.println("resolving route " + route); + String[] routeComponents = route.split("/"); + Route component = this.routeMap.get(method).get(routeComponents[1]); + + if (component == null) { + return null; + } + + String pathVariable = null; + + int i = 1; + System.out.println(routeComponents[i]); + for (String search = routeComponents[i]; component.service == null; component = this.routeMap.get(method).get(search = String.join("/", search, routeComponents[++i]))) { + System.out.println(String.join("/", search, routeComponents[i+1])); + if (component.hasPathVariable) { + pathVariable = routeComponents[++i]; + search = String.join("/", search, "{var}"); + System.out.println(search + " - " + routeComponents[i+1]); + System.out.println(this.routeMap.get(HttpMethod.GET).containsKey("test/{var}")); + } + } + + component.service.setPathVariable(pathVariable); + return component.service; + + } } diff --git a/src/at/nanopenguin/mtcg/http/Server.java b/src/at/nanopenguin/mtcg/http/Server.java index 861dc2a..38ecb62 100644 --- a/src/at/nanopenguin/mtcg/http/Server.java +++ b/src/at/nanopenguin/mtcg/http/Server.java @@ -28,6 +28,7 @@ public class Server { final Socket serviceSocket = listener.accept(); final RequestHandler requestHandler = new RequestHandler(serviceSocket, this.router); + System.out.println("received request"); executorService.submit(requestHandler); }