REST resolve

~4.5h work
This commit is contained in:
Benedikt Galbavy 2023-12-24 11:23:49 +01:00
parent 765af93728
commit 2444a071ac
9 changed files with 208 additions and 6 deletions

View File

@ -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.Router;
import at.nanopenguin.mtcg.http.Server; import at.nanopenguin.mtcg.http.Server;
@ -5,8 +7,10 @@ import java.io.IOException;
public class Main { public class Main {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
System.out.println("Hello world!"); Router router = new Router();
Server server = new Server(10001, 10, new Router()); router.addRoute(HttpMethod.GET, "/test/{var}/service", new TestService(), 1);
Server server = new Server(10001, 10, router);
server.start(); server.start();
} }
} }

View File

@ -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);
}

View File

@ -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 + "\"]");
}
}

View File

@ -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;
};
}
}

View File

@ -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<String, String> httpHeaders = new HashMap<String, String>();
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;
}
}

View File

@ -1,15 +1,16 @@
package at.nanopenguin.mtcg.http; package at.nanopenguin.mtcg.http;
import at.nanopenguin.mtcg.application.Service;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
public class RequestHandler implements Runnable { public class RequestHandler implements Runnable {
private final Socket serviceSocket; private final Socket serviceSocket;
private final Router router; private final Router router;
private BufferedReader br;
private OutputStream out;
public RequestHandler(Socket serviceSocket, Router router) { public RequestHandler(Socket serviceSocket, Router router) {
this.serviceSocket = serviceSocket; this.serviceSocket = serviceSocket;
@ -19,11 +20,36 @@ public class RequestHandler implements Runnable {
@Override @Override
public void run() { public void run() {
try { 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.write(response.get().getBytes());
out.flush(); out.flush();
System.out.println("request done");
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -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;
}
}

View File

@ -1,4 +1,56 @@
package at.nanopenguin.mtcg.http; package at.nanopenguin.mtcg.http;
import at.nanopenguin.mtcg.application.Service;
import java.util.*;
import java.util.stream.IntStream;
public class Router { public class Router {
private Map<HttpMethod, Map<String, Route>> routeMap = new HashMap<>();
public void addRoute(final HttpMethod method, final String route, final Service service, final int pathVarPos) {
Map<String, Route> map = this.routeMap.get(method);
if (method != null && map == null) {
this.routeMap.put(method, (map = new HashMap<>()));
}
List<String> routeComponents = new ArrayList<String>(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;
}
} }

View File

@ -28,6 +28,7 @@ public class Server {
final Socket serviceSocket = listener.accept(); final Socket serviceSocket = listener.accept();
final RequestHandler requestHandler = new RequestHandler(serviceSocket, this.router); final RequestHandler requestHandler = new RequestHandler(serviceSocket, this.router);
System.out.println("received request");
executorService.submit(requestHandler); executorService.submit(requestHandler);
} }