From 2997e6ac94783f33fa1d9ad32c39953ddb416a20 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Sat, 6 Nov 2021 03:05:55 +0100 Subject: [PATCH 01/31] Incremented version number --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 65c9f91..7a07227 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 8 - 1.0.1-SNAPSHOT + 1.0.2-SNAPSHOT org.javawebstack From f289092bf737d7b858c4abf0a60585dd42fd53f8 Mon Sep 17 00:00:00 2001 From: Julian Gojani Date: Mon, 8 Nov 2021 17:58:53 +0100 Subject: [PATCH 02/31] Update pom.xml --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 7a07227..b54f0a3 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,12 @@ JavaWebStack https://javawebstack.org + + Julian Gojani + julian@gojani.xyz + JavaWebStack + https://javawebstack.org + From 1e8d867f855df819c30f455598c0a24b4e8f88ad Mon Sep 17 00:00:00 2001 From: x7airworker Date: Sun, 28 Nov 2021 14:06:20 +0100 Subject: [PATCH 03/31] Added controllerInitiator --- .../javawebstack/httpserver/HTTPServer.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/javawebstack/httpserver/HTTPServer.java b/src/main/java/org/javawebstack/httpserver/HTTPServer.java index a31fc67..bd7f498 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPServer.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPServer.java @@ -27,9 +27,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; -import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; @@ -52,6 +52,7 @@ public class HTTPServer implements RouteParamTransformerProvider { private List routeAutoInjectors = new ArrayList<>(); private final Map beforeMiddleware = new HashMap<>(); private final Map afterMiddleware = new HashMap<>(); + private Function, Object> controllerInitiator = this::defaultControllerInitiator; public HTTPServer() { routeParamTransformers.add(DefaultRouteParamTransformer.INSTANCE); @@ -233,19 +234,31 @@ public HTTPServer exceptionHandler(ExceptionHandler handler) { return this; } + private Object defaultControllerInitiator (Class clazz) { + try { + return clazz.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + + return null; + } + + public HTTPServer controllerInitiator (Function, Object> initiator) { + controllerInitiator = initiator; + return this; + } + public HTTPServer controller(Class parentClass, Package p) { return controller("", parentClass, p); } public HTTPServer controller(String globalPrefix, Class parentClass, Package p) { Reflections reflections = new Reflections(p.getName()); - reflections.getSubTypesOf(parentClass).forEach(c -> { - try { - Object controller = c.newInstance(); - controller(globalPrefix, controller); - } catch (InstantiationException | IllegalAccessException e) { - } - }); + reflections.getSubTypesOf(parentClass) + .stream() + .map(controllerInitiator) + .forEach(c -> controller(globalPrefix, c)); return this; } From d2fc01792a07ca0eec6b9c1cd00be37b7420f83d Mon Sep 17 00:00:00 2001 From: x7airworker Date: Mon, 29 Nov 2021 13:10:55 +0100 Subject: [PATCH 04/31] Bumped version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 65c9f91..7a07227 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ 8 8 - 1.0.1-SNAPSHOT + 1.0.2-SNAPSHOT org.javawebstack From 69eda02a243adf919210e6e6f2fe1c459030bdbd Mon Sep 17 00:00:00 2001 From: JanHolger Date: Mon, 29 Nov 2021 13:39:33 +0100 Subject: [PATCH 05/31] Fixed body returning null when parsing fails --- .../org/javawebstack/httpserver/Exchange.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/javawebstack/httpserver/Exchange.java b/src/main/java/org/javawebstack/httpserver/Exchange.java index 158861c..67dc768 100644 --- a/src/main/java/org/javawebstack/httpserver/Exchange.java +++ b/src/main/java/org/javawebstack/httpserver/Exchange.java @@ -1,9 +1,6 @@ package org.javawebstack.httpserver; -import org.javawebstack.abstractdata.AbstractElement; -import org.javawebstack.abstractdata.AbstractMapper; -import org.javawebstack.abstractdata.AbstractNull; -import org.javawebstack.abstractdata.AbstractObject; +import org.javawebstack.abstractdata.*; import org.javawebstack.httpserver.helper.HttpMethod; import org.javawebstack.httpserver.helper.MimeType; import org.javawebstack.validator.ValidationContext; @@ -69,21 +66,21 @@ public T body(Class clazz) { MimeType type = MimeType.byMimeType(contentType); if (type == null) type = MimeType.JSON; - AbstractElement request = AbstractNull.INSTANCE; + AbstractElement request = null; + boolean arrayLike = clazz.isArray() || Collection.class.isAssignableFrom(clazz); switch (type) { case JSON: request = AbstractElement.fromJson(body); break; case YAML: - request = AbstractElement.fromYaml(body, !(clazz.isArray() || Collection.class.isAssignableFrom(clazz))); + request = AbstractElement.fromYaml(body, !arrayLike); break; case X_WWW_FORM_URLENCODED: request = AbstractElement.fromFormData(body); break; - default: - request = new AbstractObject(); - break; } + if(request == null || request.isNull()) + request = arrayLike ? new AbstractArray() : new AbstractObject(); ValidationResult result = Validator.getValidator(clazz).validate(new ValidationContext().attrib("exchange", this), request); if (!result.isValid()) throw new ValidationException(result); From 9cd7a0c7dd6f38fb1ad5de412e9588d9bebac4c9 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Fri, 3 Dec 2021 21:27:33 +0100 Subject: [PATCH 06/31] Started to implement a custom http server as an alternative to jetty --- .../javawebstack/httpserver/HTTPMethod.java | 15 ++ .../javawebstack/httpserver/HTTPStatus.java | 90 +++++++++ .../httpserver/socket/HTTPServerSocket.java | 29 +++ .../httpserver/socket/HTTPSocket.java | 183 ++++++++++++++++++ .../httpserver/socket/HTTPSocketWorker.java | 51 +++++ 5 files changed, 368 insertions(+) create mode 100644 src/main/java/org/javawebstack/httpserver/HTTPMethod.java create mode 100644 src/main/java/org/javawebstack/httpserver/HTTPStatus.java create mode 100644 src/main/java/org/javawebstack/httpserver/socket/HTTPServerSocket.java create mode 100644 src/main/java/org/javawebstack/httpserver/socket/HTTPSocket.java create mode 100644 src/main/java/org/javawebstack/httpserver/socket/HTTPSocketWorker.java diff --git a/src/main/java/org/javawebstack/httpserver/HTTPMethod.java b/src/main/java/org/javawebstack/httpserver/HTTPMethod.java new file mode 100644 index 0000000..e5fdad6 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/HTTPMethod.java @@ -0,0 +1,15 @@ +package org.javawebstack.httpserver; + +public enum HTTPMethod { + + GET, + POST, + PUT, + PATCH, + DELETE, + HEAD, + OPTIONS, + TRACE, + CONNECT + +} diff --git a/src/main/java/org/javawebstack/httpserver/HTTPStatus.java b/src/main/java/org/javawebstack/httpserver/HTTPStatus.java new file mode 100644 index 0000000..e42c5c3 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/HTTPStatus.java @@ -0,0 +1,90 @@ +package org.javawebstack.httpserver; + +public enum HTTPStatus { + + OK(200, "OK"), + CREATED(201, "Created"), + ACCEPTED(202, "Accepted"), + NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"), + NO_CONTENT(204, "No Content"), + RESET_CONTENT(205, "Reset Content"), + PARTIAL_CONTENT(206, "Partial Content"), + MULTI_STATUS(207, "Multi-Status"), + ALREADY_REPORTED(208, "Already Reported"), + IM_USED(226, "IM Used"), + + MULTIPLE_CHOICES(300, "Multiple Choices"), + MOVED_PERMANENTLY(301, "Moved Permanently"), + FOUND(302, "Found"), + SEE_OTHER(303, "See Other"), + NOT_MODIFIED(304, "Not Modified"), + USE_PROXY(305, "Use Proxy"), + TEMPORARY_REDIRECT(307, "Temporary Redirect"), + PERMANENT_REDIRECT(308, "Permanent Redirect"), + + BAD_REQUEST(400, "Bad Request"), + UNAUTHORIZED(401, "Unauthorized"), + PAYMENT_REQUIRED(402, "Payment Required"), + FORBIDDEN(403, "Forbidden"), + NOT_FOUND(404, "Not Found"), + METHOD_NOT_ALLOWED(405, "Method Not Allowed"), + NOT_ACCEPTABLE(406, "Not Acceptable"), + PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"), + REQUEST_TIMEOUT(408, "Request Timeout"), + CONFLICT(409, "Conflict"), + GONE(410, "Gone"), + LENGTH_REQUIRED(411, "Length Required"), + PRECONDITION_FAILED(412, "Precondition Failed"), + REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"), + REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"), + UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"), + REQUEST_RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"), + EXPECTATION_FAILED(417, "Expectation Failed"), + IM_A_TEAPOT(418, "I'm a teapot"), + UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"), + LOCKED(423, "Locked"), + FAILED_DEPENDENCY(424, "Failed Dependency"), + UPGRADE_REQUIRED(426, "Upgrade Required"), + PRECONDITION_REQUIRED(428, "Precondition Required"), + TOO_MANY_REQUESTS(429, "Too Many Requests"), + REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"), + UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons"), + + INTERNAL_SERVER_ERROR(500, "Internal Server Error"), + NOT_IMPLEMENTED(501, "Not Implemented"), + BAD_GATEWAY(502, "Bad Gateway"), + SERVICE_UNAVAILABLE(503, "Service Unavailable"), + GATEWAY_TIMEOUT(504, "Gateway Timeout"), + HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version Not Supported"), + VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"), + INSUFFICIENT_STORAGE(507, "Insufficient Storage"), + LOOP_DETECTED(508, "Loop Detected"), + BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"), + NOT_EXTENDED(510, "Not Extended"), + NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); + + private final int status; + private final String message; + + HTTPStatus(int status, String message) { + this.status = status; + this.message = message; + } + + public int getStatus() { + return status; + } + + public String getMessage() { + return message; + } + + public static HTTPStatus byStatus(int status) { + for(HTTPStatus s : values()) { + if(s.status == status) + return s; + } + return null; + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/socket/HTTPServerSocket.java b/src/main/java/org/javawebstack/httpserver/socket/HTTPServerSocket.java new file mode 100644 index 0000000..1d4f766 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/socket/HTTPServerSocket.java @@ -0,0 +1,29 @@ +package org.javawebstack.httpserver.socket; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +public class HTTPServerSocket { + + private final ServerSocket serverSocket; + + public HTTPServerSocket(int port) throws IOException { + serverSocket = new ServerSocket(port); + } + + public void close() throws IOException { + serverSocket.close(); + } + + public boolean isClosed() { + return serverSocket.isClosed(); + } + + public HTTPSocket accept() throws IOException { + Socket socket = serverSocket.accept(); + return new HTTPSocket(socket); + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/socket/HTTPSocket.java b/src/main/java/org/javawebstack/httpserver/socket/HTTPSocket.java new file mode 100644 index 0000000..52ceb51 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/socket/HTTPSocket.java @@ -0,0 +1,183 @@ +package org.javawebstack.httpserver.socket; + +import org.javawebstack.httpserver.HTTPMethod; +import org.javawebstack.httpserver.HTTPStatus; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.*; + +public class HTTPSocket { + + private final Socket socket; + private final InputStream inputStream; + private final OutputStream outputStream; + private final HTTPMethod requestMethod; + private final String requestPath; + private String requestQuery; + private final String requestVersion; + private final Map> requestHeaders = new HashMap<>(); + private final Map> responseHeaders = new LinkedHashMap<>(); + private int responseStatus = 200; + private String responseStatusMessage = "OK"; + private boolean headersSent; + + public HTTPSocket(Socket socket) throws IOException { + this.socket = socket; + this.inputStream = socket.getInputStream(); + this.outputStream = socket.getOutputStream(); + socket.getOutputStream().flush(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int lb = -1; + while (true) { + int b = inputStream.read(); + if(b == -1) { + socket.close(); + throw new IOException("Unexpected end of stream"); + } + if(b == '\r' && lb == '\n') { + b = inputStream.read(); + break; + } + baos.write(b); + lb = b; + } + String[] lines = new String(baos.toByteArray(), StandardCharsets.UTF_8).split("\\r?\\n"); + if(lines.length < 2) { + socket.close(); + throw new IOException("Invalid http request"); + } + String[] first = lines[0].split(" "); + if(first.length != 3 || !first[1].startsWith("/")) { + socket.close(); + throw new IOException("Invalid http request"); + } + requestMethod = HTTPMethod.valueOf(first[0]); + String[] pathSplit = first[1].split("\\?", 2); + requestPath = pathSplit[0]; + if(pathSplit.length == 2) + requestQuery = pathSplit[1]; + requestVersion = first[2]; + if(!requestVersion.equals("HTTP/1.1") && !requestVersion.equals("HTTP/1.0")) { + setResponseStatus(HTTPStatus.HTTP_VERSION_NOT_SUPPORTED); + writeHeaders(); + close(); + throw new IOException("Unsupported http version"); + } + for(int i=1; i values = requestHeaders.computeIfAbsent(hspl[0].toLowerCase(Locale.ROOT), h -> new ArrayList<>()); + values.add(hspl[1]); + } + } + + public HTTPSocket setResponseStatus(HTTPStatus status) { + return setResponseStatus(status.getStatus(), status.getMessage()); + } + + public HTTPSocket setResponseStatus(int status) { + HTTPStatus s = HTTPStatus.byStatus(status); + return setResponseStatus(status, s != null ? s.getMessage() : "Unknown"); + } + + public HTTPSocket setResponseStatus(int status, String message) { + this.responseStatus = status; + this.responseStatusMessage = message; + return this; + } + + public HTTPSocket setResponseHeader(String name, String value) { + responseHeaders.put(name.toLowerCase(Locale.ROOT), Arrays.asList(value)); + return this; + } + + public HTTPSocket addResponseHeader(String name, String value) { + responseHeaders.computeIfAbsent(name.toLowerCase(Locale.ROOT), h -> new ArrayList<>()).add(value); + return this; + } + + public void close() throws IOException { + socket.close(); + } + + public void writeHeaders() throws IOException { + headersSent = true; + StringBuilder sb = new StringBuilder(requestVersion) + .append(' ') + .append(responseStatus) + .append(' ') + .append(responseStatusMessage) + .append("\r\n"); + responseHeaders.forEach((k, l) -> l.forEach(v -> sb.append(k.toLowerCase(Locale.ROOT)).append(": ").append(v).append("\r\n"))); + sb.append("\r\n"); + outputStream.write(sb.toString().getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + } + + public InputStream getInputStream() { + return inputStream; + } + + public OutputStream getOutputStream() { + return new HTTPOutputStream(); + } + + public HTTPMethod getRequestMethod() { + return requestMethod; + } + + public String getRequestPath() { + return requestPath; + } + + public String getRequestQuery() { + return requestQuery; + } + + public String getRequestVersion() { + return requestVersion; + } + + public Map> getRequestHeaders() { + return requestHeaders; + } + + public Map> getResponseHeaders() { + return responseHeaders; + } + + public int getResponseStatus() { + return responseStatus; + } + + public String getResponseStatusMessage() { + return responseStatusMessage; + } + + public boolean isClosed() { + return socket.isClosed(); + } + + private class HTTPOutputStream extends OutputStream { + public void write(int i) throws IOException { + if(!headersSent) + writeHeaders(); + outputStream.write(i); + } + public void close() throws IOException { + outputStream.close(); + } + public void flush() throws IOException { + outputStream.flush(); + } + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/socket/HTTPSocketWorker.java b/src/main/java/org/javawebstack/httpserver/socket/HTTPSocketWorker.java new file mode 100644 index 0000000..11ded81 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/socket/HTTPSocketWorker.java @@ -0,0 +1,51 @@ +package org.javawebstack.httpserver.socket; + +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +public class HTTPSocketWorker { + + private final Thread schedulerThread; + private ExecutorService executorService; + private final HTTPServerSocket serverSocket; + + public HTTPSocketWorker(HTTPServerSocket serverSocket, Consumer handler) { + this.serverSocket = serverSocket; + this.schedulerThread = new Thread(() -> { + while (!serverSocket.isClosed()) { + try { + HTTPSocket socket = serverSocket.accept(); + executorService.execute(() -> { + try { + handler.accept(socket); + if(!socket.isClosed()) + socket.close(); + } catch (IOException ex) {} + }); + } catch (IOException exception) {} + } + }); + } + + public HTTPSocketWorker start() { + this.executorService = Executors.newCachedThreadPool(); + this.schedulerThread.start(); + return this; + } + + public void join() { + try { + schedulerThread.join(); + } catch (InterruptedException e) {} + } + + public void stop() { + this.executorService.shutdown(); + try { + this.serverSocket.close(); + } catch (IOException e) {} + } + +} From fb70dd79a2c7f182d4843b7f6d22c9908353ce65 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Sun, 5 Dec 2021 09:35:18 +0100 Subject: [PATCH 07/31] Abstracted the http server logic to allow for using a custom http server implementation and finished the custom http implementation. Added a JettyHTTPSocketServer as a backup option, but it doesn't support websockets. --- .../org/javawebstack/httpserver/Exchange.java | 105 +++--- .../javawebstack/httpserver/HTTPMethod.java | 3 +- .../javawebstack/httpserver/HTTPServer.java | 109 +++--- .../javawebstack/httpserver/HTTPStatus.java | 4 + .../httpserver/adapter/IHTTPSocket.java | 58 ++++ .../adapter/IHTTPSocketHandler.java | 7 + .../httpserver/adapter/IHTTPSocketServer.java | 14 + .../adapter/jetty/JettyHTTPSocket.java | 113 ++++++ .../adapter/jetty/JettyHTTPSocketServer.java | 80 +++++ .../simple/SimpleHTTPSocket.java} | 36 +- .../simple/SimpleHTTPSocketServer.java | 63 ++++ .../httpserver/handler/StaticFileHandler.java | 2 +- .../httpserver/handler/WebSocketHandler.java | 4 +- .../httpserver/helper/HttpMethod.java | 15 - .../httpserver/helper/JettyNoLog.java | 64 ---- .../router/DefaultRouteAutoInjector.java | 4 +- .../javawebstack/httpserver/router/Route.java | 10 +- .../httpserver/router/RouteBinder.java | 56 +-- .../httpserver/socket/HTTPServerSocket.java | 29 -- .../httpserver/socket/HTTPSocketWorker.java | 51 --- .../httpserver/test/HTTPTest.java | 31 +- .../test/MockHttpServletRequest.java | 325 ------------------ .../test/MockHttpServletResponse.java | 178 ---------- .../test/MockServletInputStream.java | 46 --- .../test/MockServletOutputStream.java | 32 -- .../httpserver/test/TestExchange.java | 107 +++--- .../httpserver/test/TestHTTPSocket.java | 121 +++++++ .../httpserver/{helper => util}/MimeType.java | 2 +- .../util/websocket/WebSocketFrame.java | 140 ++++++++ .../util/websocket/WebSocketUtil.java | 104 ++++++ .../websocket/InternalWebSocketAdapter.java | 56 --- .../InternalWebSocketRequestHandler.java | 49 ++- .../httpserver/websocket/WebSocket.java | 32 +- 33 files changed, 973 insertions(+), 1077 deletions(-) create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocket.java create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketHandler.java create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java rename src/main/java/org/javawebstack/httpserver/{socket/HTTPSocket.java => adapter/simple/SimpleHTTPSocket.java} (81%) create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java delete mode 100644 src/main/java/org/javawebstack/httpserver/helper/HttpMethod.java delete mode 100644 src/main/java/org/javawebstack/httpserver/helper/JettyNoLog.java delete mode 100644 src/main/java/org/javawebstack/httpserver/socket/HTTPServerSocket.java delete mode 100644 src/main/java/org/javawebstack/httpserver/socket/HTTPSocketWorker.java delete mode 100644 src/main/java/org/javawebstack/httpserver/test/MockHttpServletRequest.java delete mode 100644 src/main/java/org/javawebstack/httpserver/test/MockHttpServletResponse.java delete mode 100644 src/main/java/org/javawebstack/httpserver/test/MockServletInputStream.java delete mode 100644 src/main/java/org/javawebstack/httpserver/test/MockServletOutputStream.java create mode 100644 src/main/java/org/javawebstack/httpserver/test/TestHTTPSocket.java rename src/main/java/org/javawebstack/httpserver/{helper => util}/MimeType.java (98%) create mode 100644 src/main/java/org/javawebstack/httpserver/util/websocket/WebSocketFrame.java create mode 100644 src/main/java/org/javawebstack/httpserver/util/websocket/WebSocketUtil.java delete mode 100644 src/main/java/org/javawebstack/httpserver/websocket/InternalWebSocketAdapter.java diff --git a/src/main/java/org/javawebstack/httpserver/Exchange.java b/src/main/java/org/javawebstack/httpserver/Exchange.java index 67dc768..0fd04ef 100644 --- a/src/main/java/org/javawebstack/httpserver/Exchange.java +++ b/src/main/java/org/javawebstack/httpserver/Exchange.java @@ -1,21 +1,20 @@ package org.javawebstack.httpserver; import org.javawebstack.abstractdata.*; -import org.javawebstack.httpserver.helper.HttpMethod; -import org.javawebstack.httpserver.helper.MimeType; +import org.javawebstack.httpserver.adapter.IHTTPSocket; +import org.javawebstack.httpserver.util.MimeType; import org.javawebstack.validator.ValidationContext; import org.javawebstack.validator.ValidationException; import org.javawebstack.validator.ValidationResult; import org.javawebstack.validator.Validator; -import javax.servlet.MultipartConfigElement; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class Exchange { @@ -26,22 +25,18 @@ public static Exchange current() { } private final HTTPServer server; - private final HttpMethod method; - private final String path; + private final HTTPMethod method; private byte[] body = null; private final Map pathVariables = new HashMap<>(); private final AbstractObject queryParameters; - private final HttpServletRequest request; - private final HttpServletResponse response; + private final IHTTPSocket socket; private final Map attributes = new HashMap<>(); - public Exchange(HTTPServer server, HttpServletRequest request, HttpServletResponse response) { + public Exchange(HTTPServer server, IHTTPSocket socket) { this.server = server; - this.request = request; - this.response = response; - this.path = request.getPathInfo(); - this.method = "websocket".equalsIgnoreCase(request.getHeader("Upgrade")) ? HttpMethod.WEBSOCKET : HttpMethod.valueOf(request.getMethod()); - this.queryParameters = AbstractElement.fromFormData(request.getQueryString()).object(); + this.socket = socket; + this.method = "websocket".equalsIgnoreCase(socket.getRequestHeader("upgrade")) ? HTTPMethod.WEBSOCKET : socket.getRequestMethod(); + this.queryParameters = AbstractElement.fromFormData(socket.getRequestQuery()).object(); } public T body(Class clazz) { @@ -91,22 +86,23 @@ public HTTPServer getServer() { return server; } - public HttpMethod getMethod() { + public HTTPMethod getMethod() { return method; } public String getPath() { - return path; + return socket.getRequestPath(); } public String getContentType() { - return request.getContentType() != null ? request.getContentType() : ""; + String contentType = socket.getRequestHeader("content-type"); + return contentType != null ? contentType : ""; } public byte[] read() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { - InputStream is = request.getInputStream(); + InputStream is = socket.getInputStream(); byte[] data = new byte[1024]; int r; while (is.available() > 0) { @@ -126,8 +122,8 @@ public Exchange write(String data) { public Exchange write(byte[] bytes) { try { - response.getOutputStream().write(bytes); - response.getOutputStream().flush(); + socket.getOutputStream().write(bytes); + socket.getOutputStream().flush(); } catch (IOException ignored) { } return this; @@ -135,8 +131,8 @@ public Exchange write(byte[] bytes) { public Exchange write(byte[] bytes, int offset, int length) { try { - response.getOutputStream().write(bytes, offset, length); - response.getOutputStream().flush(); + socket.getOutputStream().write(bytes, offset, length); + socket.getOutputStream().flush(); } catch (IOException ignored) { } return this; @@ -153,49 +149,50 @@ public Exchange write(InputStream stream) throws IOException { public Exchange close() { try { - response.getOutputStream().close(); + socket.close(); } catch (IOException ignored) { } return this; } public Exchange header(String header, String value) { - if (header.equalsIgnoreCase("content-type")) { - response.setContentType(value); - return this; - } - response.setHeader(header, value); + socket.setResponseHeader(header, value); return this; } public Exchange status(int code) { - response.setStatus(code); + socket.setResponseStatus(code); return this; } public String header(String header) { - return request.getHeader(header); + return socket.getRequestHeader(header); } public Exchange redirect(String url) { - response.setStatus(302); + socket.setResponseStatus(302); + socket.setResponseHeader("location", url); try { - response.sendRedirect(url); - } catch (IOException ex) { - throw new RuntimeException(ex); + socket.writeHeaders(); + } catch (IOException e) { + e.printStackTrace(); } return this; } public List locales() { - return Collections.list(request.getLocales()); + String locale = socket.getRequestHeader("locale"); + if(locale == null) + return new ArrayList<>(); + return Stream.of(locale.split(" ?,")).map(s -> s.split(";")[0]).map(Locale::forLanguageTag).collect(Collectors.toList()); } public Locale locale(Locale... possible) { + List requested = locales(); if (possible.length == 0) - return request.getLocale(); + return requested.size() > 0 ? requested.get(0) : null; List possibleList = Arrays.asList(possible); - for (Locale l : locales()) { + for (Locale l : requested) { if (possibleList.contains(l)) return l; } @@ -209,15 +206,11 @@ public Exchange contentType(MimeType type) { public Exchange contentType(String contentType) { if (contentType == null || contentType.equals("")) return contentType("text/plain"); - return header("Content-Type", contentType); - } - - public HttpServletRequest rawRequest() { - return request; + return header("content-type", contentType); } - public HttpServletResponse rawResponse() { - return response; + public IHTTPSocket socket() { + return socket; } public T attrib(String key) { @@ -255,7 +248,7 @@ public T query(String name, Class type, T defaultValue) { } public String remoteAddr() { - return request.getRemoteAddr(); + return socket.getRemoteAddress(); } public Map getPathVariables() { @@ -299,24 +292,4 @@ protected static AbstractElement getPathElement(AbstractElement source, String p return getPathElement(getPathElement(source, spl[0]), path.substring(spl[0].length() + 1)); } - public Exchange enableMultipart() { - enableMultipart(System.getProperty("java.io.tmpdir")); - return this; - } - - public Exchange enableMultipart(String location) { - enableMultipart(location, -1L); - return this; - } - - public Exchange enableMultipart(String location, long maxFileSize) { - enableMultipart(location, maxFileSize, 1_048_576); - return this; - } - - - public Exchange enableMultipart(String location, long maxFileSize, int fileSizeThreshold) { - request.setAttribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement(location, maxFileSize, -1L, fileSizeThreshold)); - return this; - } } diff --git a/src/main/java/org/javawebstack/httpserver/HTTPMethod.java b/src/main/java/org/javawebstack/httpserver/HTTPMethod.java index e5fdad6..6bc93f4 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPMethod.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPMethod.java @@ -10,6 +10,7 @@ public enum HTTPMethod { HEAD, OPTIONS, TRACE, - CONNECT + CONNECT, + WEBSOCKET } diff --git a/src/main/java/org/javawebstack/httpserver/HTTPServer.java b/src/main/java/org/javawebstack/httpserver/HTTPServer.java index bd7f498..04cefb7 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPServer.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPServer.java @@ -1,15 +1,10 @@ package org.javawebstack.httpserver; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; import org.javawebstack.abstractdata.AbstractMapper; import org.javawebstack.abstractdata.NamingPolicy; +import org.javawebstack.httpserver.adapter.IHTTPSocketServer; +import org.javawebstack.httpserver.adapter.simple.SimpleHTTPSocketServer; import org.javawebstack.httpserver.handler.*; -import org.javawebstack.httpserver.helper.HttpMethod; -import org.javawebstack.httpserver.helper.JettyNoLog; import org.javawebstack.httpserver.router.DefaultRouteAutoInjector; import org.javawebstack.httpserver.router.Route; import org.javawebstack.httpserver.router.RouteAutoInjector; @@ -20,12 +15,9 @@ import org.javawebstack.httpserver.transformer.route.RouteParamTransformerProvider; import org.javawebstack.httpserver.util.DirectoryFileProvider; import org.javawebstack.httpserver.util.ResourceFileProvider; -import org.javawebstack.httpserver.websocket.InternalWebSocketAdapter; import org.javawebstack.httpserver.websocket.InternalWebSocketRequestHandler; import org.reflections.Reflections; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.File; import java.nio.charset.StandardCharsets; import java.util.*; @@ -44,17 +36,20 @@ public class HTTPServer implements RouteParamTransformerProvider { private ExceptionHandler exceptionHandler = new ExceptionHandler.DefaultExceptionHandler(); private final List beforeRoutes = new ArrayList<>(); private final List afterRoutes = new ArrayList<>(); - private Server server; - private int port = 80; + private final IHTTPSocketServer server; private final List beforeInterceptors = new ArrayList<>(); private AbstractMapper abstractMapper = new AbstractMapper().setNamingPolicy(NamingPolicy.SNAKE_CASE); - private org.eclipse.jetty.websocket.server.WebSocketHandler webSocketHandler; - private List routeAutoInjectors = new ArrayList<>(); + private final List routeAutoInjectors = new ArrayList<>(); private final Map beforeMiddleware = new HashMap<>(); private final Map afterMiddleware = new HashMap<>(); private Function, Object> controllerInitiator = this::defaultControllerInitiator; public HTTPServer() { + this(new SimpleHTTPSocketServer()); + } + + public HTTPServer(IHTTPSocketServer server) { + this.server = server; routeParamTransformers.add(DefaultRouteParamTransformer.INSTANCE); routeAutoInjectors.add(DefaultRouteAutoInjector.INSTANCE); } @@ -88,43 +83,43 @@ public HTTPServer routeAutoInjector(RouteAutoInjector injector) { } public HTTPServer get(String pattern, RequestHandler... handlers) { - return route(HttpMethod.GET, pattern, handlers); + return route(HTTPMethod.GET, pattern, handlers); } public HTTPServer beforeGet(String pattern, RequestHandler... handlers) { - return beforeRoute(HttpMethod.GET, pattern, handlers); + return beforeRoute(HTTPMethod.GET, pattern, handlers); } public HTTPServer afterGet(String pattern, AfterRequestHandler... handlers) { - return afterRoute(HttpMethod.GET, pattern, handlers); + return afterRoute(HTTPMethod.GET, pattern, handlers); } public HTTPServer post(String pattern, RequestHandler... handlers) { - return route(HttpMethod.POST, pattern, handlers); + return route(HTTPMethod.POST, pattern, handlers); } public HTTPServer beforePost(String pattern, RequestHandler... handlers) { - return beforeRoute(HttpMethod.POST, pattern, handlers); + return beforeRoute(HTTPMethod.POST, pattern, handlers); } public HTTPServer afterPost(String pattern, AfterRequestHandler... handlers) { - return afterRoute(HttpMethod.POST, pattern, handlers); + return afterRoute(HTTPMethod.POST, pattern, handlers); } public HTTPServer put(String pattern, RequestHandler... handlers) { - return route(HttpMethod.PUT, pattern, handlers); + return route(HTTPMethod.PUT, pattern, handlers); } public HTTPServer beforePut(String pattern, RequestHandler... handlers) { - return beforeRoute(HttpMethod.PUT, pattern, handlers); + return beforeRoute(HTTPMethod.PUT, pattern, handlers); } public HTTPServer afterPut(String pattern, AfterRequestHandler... handlers) { - return afterRoute(HttpMethod.PUT, pattern, handlers); + return afterRoute(HTTPMethod.PUT, pattern, handlers); } public HTTPServer delete(String pattern, RequestHandler... handlers) { - return route(HttpMethod.DELETE, pattern, handlers); + return route(HTTPMethod.DELETE, pattern, handlers); } public HTTPServer staticDirectory(String pathPrefix, File directory) { @@ -148,60 +143,60 @@ public HTTPServer staticHandler(String pathPrefix, StaticFileHandler handler) { } public HTTPServer beforeDelete(String pattern, RequestHandler... handlers) { - return beforeRoute(HttpMethod.DELETE, pattern, handlers); + return beforeRoute(HTTPMethod.DELETE, pattern, handlers); } public HTTPServer afterDelete(String pattern, AfterRequestHandler... handlers) { - return afterRoute(HttpMethod.DELETE, pattern, handlers); + return afterRoute(HTTPMethod.DELETE, pattern, handlers); } - public HTTPServer route(HttpMethod method, String pattern, RequestHandler... handlers) { + public HTTPServer route(HTTPMethod method, String pattern, RequestHandler... handlers) { routes.add(new Route(this, method, pattern, Arrays.asList(handlers))); return this; } - public HTTPServer beforeRoute(HttpMethod method, String pattern, RequestHandler... handlers) { + public HTTPServer beforeRoute(HTTPMethod method, String pattern, RequestHandler... handlers) { beforeRoutes.add(new Route(this, method, pattern, Arrays.asList(handlers))); return this; } - public HTTPServer afterRoute(HttpMethod method, String pattern, AfterRequestHandler... handlers) { + public HTTPServer afterRoute(HTTPMethod method, String pattern, AfterRequestHandler... handlers) { afterRoutes.add(new Route(this, method, pattern, null).setAfterHandlers(Arrays.asList(handlers))); return this; } - public HTTPServer route(HttpMethod[] methods, String pattern, RequestHandler... handlers) { - for (HttpMethod method : methods) + public HTTPServer route(HTTPMethod[] methods, String pattern, RequestHandler... handlers) { + for (HTTPMethod method : methods) route(method, pattern, handlers); return this; } - public HTTPServer beforeRoute(HttpMethod[] methods, String pattern, RequestHandler... handlers) { - for (HttpMethod method : methods) + public HTTPServer beforeRoute(HTTPMethod[] methods, String pattern, RequestHandler... handlers) { + for (HTTPMethod method : methods) beforeRoute(method, pattern, handlers); return this; } - public HTTPServer afterRoute(HttpMethod[] methods, String pattern, AfterRequestHandler... handlers) { - for (HttpMethod method : methods) + public HTTPServer afterRoute(HTTPMethod[] methods, String pattern, AfterRequestHandler... handlers) { + for (HTTPMethod method : methods) afterRoute(method, pattern, handlers); return this; } public HTTPServer any(String pattern, RequestHandler... handlers) { - return route(HttpMethod.values(), pattern, handlers); + return route(HTTPMethod.values(), pattern, handlers); } public HTTPServer beforeAny(String pattern, RequestHandler... handlers) { - return beforeRoute(HttpMethod.values(), pattern, handlers); + return beforeRoute(HTTPMethod.values(), pattern, handlers); } public HTTPServer afterAny(String pattern, AfterRequestHandler... handlers) { - return afterRoute(HttpMethod.values(), pattern, handlers); + return afterRoute(HTTPMethod.values(), pattern, handlers); } public HTTPServer webSocket(String pattern, WebSocketHandler handler) { - return route(HttpMethod.WEBSOCKET, pattern, new InternalWebSocketRequestHandler(handler)); + return route(HTTPMethod.WEBSOCKET, pattern, new InternalWebSocketRequestHandler(handler)); } public HTTPServer middleware(String name, RequestHandler handler) { @@ -272,24 +267,15 @@ public HTTPServer controller(String globalPrefix, Object controller) { } public HTTPServer port(int port) { - this.port = port; + server.setPort(port); return this; } public HTTPServer start() { - Log.setLog(new JettyNoLog()); - server = new Server(port); - server.setHandler(new HttpHandler()); - webSocketHandler = new org.eclipse.jetty.websocket.server.WebSocketHandler() { - public void configure(WebSocketServletFactory webSocketServletFactory) { - webSocketServletFactory.register(InternalWebSocketAdapter.class); - } - }; - webSocketHandler.setServer(server); + server.setHandler(socket -> execute(new Exchange(this, socket))); try { server.start(); - webSocketHandler.start(); - logger.info("HTTP-Server started on port " + port); + logger.info("HTTP-Server started on port " + server.getPort()); } catch (Exception ex) { throw new RuntimeException(ex); } @@ -297,11 +283,7 @@ public void configure(WebSocketServletFactory webSocketServletFactory) { } public void join() { - try { - server.join(); - } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } + server.join(); } public void stop() { @@ -350,7 +332,7 @@ public void execute(Exchange exchange) { exchange.getPathVariables().putAll(pathVariables); for (RequestHandler handler : route.getHandlers()) { response = handler.handle(exchange); - if (exchange.getMethod() == HttpMethod.WEBSOCKET) { + if (exchange.getMethod() == HTTPMethod.WEBSOCKET) { Exchange.exchanges.remove(); return; } @@ -377,7 +359,7 @@ public void execute(Exchange exchange) { } if (response != null) exchange.write(transformResponse(exchange, response)); - if (exchange.getMethod() != HttpMethod.WEBSOCKET) + if (exchange.getMethod() != HTTPMethod.WEBSOCKET) exchange.close(); Exchange.exchanges.remove(); return; @@ -399,7 +381,6 @@ public List getRouteParamTransformer() { public RouteParamTransformer getRouteParamTransformer(String type) { return routeParamTransformers.stream().filter(t -> t.canTransform(type)).findFirst().orElse(null); } - public RequestHandler getBeforeMiddleware(String name) { return beforeMiddleware.get(name); } @@ -423,16 +404,6 @@ public byte[] transformResponse(Exchange exchange, Object object) { return object.toString().getBytes(StandardCharsets.UTF_8); } - private class HttpHandler extends AbstractHandler { - public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { - execute(new Exchange(HTTPServer.this, httpServletRequest, httpServletResponse)); - } - } - - public org.eclipse.jetty.websocket.server.WebSocketHandler getInternalWebSocketHandler() { - return webSocketHandler; - } - public ExceptionHandler getExceptionHandler() { return exceptionHandler; } diff --git a/src/main/java/org/javawebstack/httpserver/HTTPStatus.java b/src/main/java/org/javawebstack/httpserver/HTTPStatus.java index e42c5c3..1b03a38 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPStatus.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPStatus.java @@ -2,6 +2,10 @@ public enum HTTPStatus { + CONTINUE(100, "Continue"), + SWITCHING_PROTOCOLS(101, "Switching Protocols"), + PROCESSING(102, "Processing"), + OK(200, "OK"), CREATED(201, "Created"), ACCEPTED(202, "Accepted"), diff --git a/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocket.java new file mode 100644 index 0000000..bf4bc33 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocket.java @@ -0,0 +1,58 @@ +package org.javawebstack.httpserver.adapter; + +import org.javawebstack.httpserver.HTTPMethod; +import org.javawebstack.httpserver.HTTPStatus; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.*; + +public interface IHTTPSocket { + + InputStream getInputStream() throws IOException; + + OutputStream getOutputStream() throws IOException; + + void close() throws IOException; + + boolean isClosed(); + + default IHTTPSocket setResponseStatus(HTTPStatus status) { + return setResponseStatus(status.getStatus(), status.getMessage()); + } + + default IHTTPSocket setResponseStatus(int status) { + HTTPStatus s = HTTPStatus.byStatus(status); + return setResponseStatus(status, s != null ? s.getMessage() : "Unknown"); + } + + IHTTPSocket setResponseStatus(int status, String message); + + IHTTPSocket setResponseHeader(String name, String value); + + IHTTPSocket addResponseHeader(String name, String value); + + HTTPMethod getRequestMethod(); + + String getRequestPath(); + + String getRequestQuery(); + + String getRequestVersion(); + + Set getRequestHeaderNames(); + + String getRequestHeader(String name); + + List getRequestHeaders(String name); + + int getResponseStatus(); + + String getResponseStatusMessage(); + + void writeHeaders() throws IOException; + + String getRemoteAddress(); + +} diff --git a/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketHandler.java b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketHandler.java new file mode 100644 index 0000000..d21ba29 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketHandler.java @@ -0,0 +1,7 @@ +package org.javawebstack.httpserver.adapter; + +public interface IHTTPSocketHandler { + + void handle(IHTTPSocket socket); + +} diff --git a/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java new file mode 100644 index 0000000..1679146 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java @@ -0,0 +1,14 @@ +package org.javawebstack.httpserver.adapter; + +import java.io.IOException; + +public interface IHTTPSocketServer { + + void setPort(int port); + int getPort(); + void start() throws IOException; + void stop(); + void join(); + void setHandler(IHTTPSocketHandler handler); + +} diff --git a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java new file mode 100644 index 0000000..9c959ce --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java @@ -0,0 +1,113 @@ +package org.javawebstack.httpserver.adapter.jetty; + +import org.javawebstack.httpserver.HTTPMethod; +import org.javawebstack.httpserver.HTTPStatus; +import org.javawebstack.httpserver.adapter.IHTTPSocket; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class JettyHTTPSocket implements IHTTPSocket { + + private final HttpServletRequest request; + private final HttpServletResponse response; + private boolean closed; + + public JettyHTTPSocket(HttpServletRequest request, HttpServletResponse response) { + this.request = request; + this.response = response; + } + + public HttpServletRequest rawRequest() { + return request; + } + + public HttpServletResponse rawResponse() { + return response; + } + + public InputStream getInputStream() throws IOException { + return request.getInputStream(); + } + + public OutputStream getOutputStream() throws IOException { + return response.getOutputStream(); + } + + public void close() throws IOException { + closed = true; + response.getOutputStream().close(); + } + + public boolean isClosed() { + return closed; + } + + public IHTTPSocket setResponseStatus(int status, String message) { + response.setStatus(status, message); + return this; + } + + public IHTTPSocket setResponseHeader(String name, String value) { + response.setHeader(name, value); + return this; + } + + public IHTTPSocket addResponseHeader(String name, String value) { + response.addHeader(name, value); + return this; + } + + public HTTPMethod getRequestMethod() { + return HTTPMethod.valueOf(request.getMethod()); + } + + public String getRequestPath() { + return request.getPathInfo(); + } + + public String getRequestQuery() { + return request.getQueryString(); + } + + public String getRequestVersion() { + return "HTTP/1.1"; + } + + public Set getRequestHeaderNames() { + return new HashSet<>(Collections.list(request.getHeaderNames())); + } + + public String getRequestHeader(String name) { + return request.getHeader(name); + } + + public List getRequestHeaders(String name) { + return Collections.list(request.getHeaders(name)); + } + + public int getResponseStatus() { + return response.getStatus(); + } + + public String getResponseStatusMessage() { + HTTPStatus status = HTTPStatus.byStatus(response.getStatus()); + return status == null ? null : status.getMessage(); + } + + public void writeHeaders() throws IOException { + response.flushBuffer(); + } + + public String getRemoteAddress() { + return request.getRemoteAddr(); + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java new file mode 100644 index 0000000..c0f1def --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java @@ -0,0 +1,80 @@ +package org.javawebstack.httpserver.adapter.jetty; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.handler.ErrorHandler; +import org.javawebstack.httpserver.adapter.IHTTPSocketHandler; +import org.javawebstack.httpserver.adapter.IHTTPSocketServer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpUpgradeHandler; +import javax.servlet.http.WebConnection; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class JettyHTTPSocketServer implements IHTTPSocketServer { + + private Server server; + private IHTTPSocketHandler handler; + private int port; + + public void setPort(int port) { + this.port = port; + } + + public int getPort() { + return port; + } + + public void start() throws IOException { + server = new Server(port); + server.setHandler(new AbstractHandler() { + public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException { + if("websocket".equals(httpServletRequest.getHeader("Upgrade"))) { + httpServletResponse.setStatus(400); + httpServletResponse.getOutputStream().write("Websockets are not supported by this server!".getBytes(StandardCharsets.UTF_8)); + httpServletResponse.getOutputStream().close(); + return; + } + handler.handle(new JettyHTTPSocket(httpServletRequest, httpServletResponse)); + } + }); + server.setErrorHandler(new ErrorHandler()); + try { + server.start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void stop() { + try { + server.stop(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void join() { + try { + server.join(); + } catch (InterruptedException e) {} + } + + public void setHandler(IHTTPSocketHandler handler) { + this.handler = handler; + } + + private static class DummyUpgradeHandler implements HttpUpgradeHandler { + public void init(WebConnection webConnection) { + + } + public void destroy() { + + } + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/socket/HTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocket.java similarity index 81% rename from src/main/java/org/javawebstack/httpserver/socket/HTTPSocket.java rename to src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocket.java index 52ceb51..6d9ad27 100644 --- a/src/main/java/org/javawebstack/httpserver/socket/HTTPSocket.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocket.java @@ -1,7 +1,8 @@ -package org.javawebstack.httpserver.socket; +package org.javawebstack.httpserver.adapter.simple; import org.javawebstack.httpserver.HTTPMethod; import org.javawebstack.httpserver.HTTPStatus; +import org.javawebstack.httpserver.adapter.IHTTPSocket; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -11,7 +12,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; -public class HTTPSocket { +public class SimpleHTTPSocket implements IHTTPSocket { private final Socket socket; private final InputStream inputStream; @@ -26,7 +27,7 @@ public class HTTPSocket { private String responseStatusMessage = "OK"; private boolean headersSent; - public HTTPSocket(Socket socket) throws IOException { + public SimpleHTTPSocket(Socket socket) throws IOException { this.socket = socket; this.inputStream = socket.getInputStream(); this.outputStream = socket.getOutputStream(); @@ -79,27 +80,31 @@ public HTTPSocket(Socket socket) throws IOException { } } - public HTTPSocket setResponseStatus(HTTPStatus status) { + public String getRemoteAddress() { + return socket.getInetAddress().getHostAddress(); + } + + public SimpleHTTPSocket setResponseStatus(HTTPStatus status) { return setResponseStatus(status.getStatus(), status.getMessage()); } - public HTTPSocket setResponseStatus(int status) { + public SimpleHTTPSocket setResponseStatus(int status) { HTTPStatus s = HTTPStatus.byStatus(status); return setResponseStatus(status, s != null ? s.getMessage() : "Unknown"); } - public HTTPSocket setResponseStatus(int status, String message) { + public SimpleHTTPSocket setResponseStatus(int status, String message) { this.responseStatus = status; this.responseStatusMessage = message; return this; } - public HTTPSocket setResponseHeader(String name, String value) { + public SimpleHTTPSocket setResponseHeader(String name, String value) { responseHeaders.put(name.toLowerCase(Locale.ROOT), Arrays.asList(value)); return this; } - public HTTPSocket addResponseHeader(String name, String value) { + public SimpleHTTPSocket addResponseHeader(String name, String value) { responseHeaders.computeIfAbsent(name.toLowerCase(Locale.ROOT), h -> new ArrayList<>()).add(value); return this; } @@ -109,6 +114,8 @@ public void close() throws IOException { } public void writeHeaders() throws IOException { + if(headersSent) + return; headersSent = true; StringBuilder sb = new StringBuilder(requestVersion) .append(' ') @@ -146,12 +153,17 @@ public String getRequestVersion() { return requestVersion; } - public Map> getRequestHeaders() { - return requestHeaders; + public Set getRequestHeaderNames() { + return requestHeaders.keySet(); + } + + public String getRequestHeader(String name) { + List values = requestHeaders.get(name.toLowerCase(Locale.ROOT)); + return values == null || values.size() == 0 ? null : values.get(0); } - public Map> getResponseHeaders() { - return responseHeaders; + public List getRequestHeaders(String name) { + return requestHeaders.getOrDefault(name.toLowerCase(Locale.ROOT), Collections.emptyList()); } public int getResponseStatus() { diff --git a/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java new file mode 100644 index 0000000..91aaeef --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java @@ -0,0 +1,63 @@ +package org.javawebstack.httpserver.adapter.simple; + +import org.javawebstack.httpserver.adapter.IHTTPSocketHandler; +import org.javawebstack.httpserver.adapter.IHTTPSocketServer; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class SimpleHTTPSocketServer implements IHTTPSocketServer { + + private final Thread schedulerThread; + private ExecutorService executorService; + private ServerSocket serverSocket; + private int port = 80; + private IHTTPSocketHandler handler; + + public SimpleHTTPSocketServer() { + this.schedulerThread = new Thread(() -> { + while (!serverSocket.isClosed()) { + try { + Socket socket = serverSocket.accept(); + SimpleHTTPSocket httpSocket = new SimpleHTTPSocket(socket); + executorService.execute(() -> handler.handle(httpSocket)); + } catch (IOException exception) {} + } + }); + } + + public void setPort(int port) { + this.port = port; + } + + public int getPort() { + return port; + } + + public void setHandler(IHTTPSocketHandler handler) { + this.handler = handler; + } + + public void start() throws IOException { + this.serverSocket = new ServerSocket(port); + this.executorService = Executors.newCachedThreadPool(); + this.schedulerThread.start(); + } + + public void join() { + try { + schedulerThread.join(); + } catch (InterruptedException e) {} + } + + public void stop() { + this.executorService.shutdown(); + try { + this.serverSocket.close(); + } catch (IOException e) {} + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/handler/StaticFileHandler.java b/src/main/java/org/javawebstack/httpserver/handler/StaticFileHandler.java index 013f8d7..f6fb6a9 100644 --- a/src/main/java/org/javawebstack/httpserver/handler/StaticFileHandler.java +++ b/src/main/java/org/javawebstack/httpserver/handler/StaticFileHandler.java @@ -1,7 +1,7 @@ package org.javawebstack.httpserver.handler; import org.javawebstack.httpserver.Exchange; -import org.javawebstack.httpserver.helper.MimeType; +import org.javawebstack.httpserver.util.MimeType; import org.javawebstack.httpserver.util.FileProvider; import java.io.IOException; diff --git a/src/main/java/org/javawebstack/httpserver/handler/WebSocketHandler.java b/src/main/java/org/javawebstack/httpserver/handler/WebSocketHandler.java index de6dacf..a194a40 100644 --- a/src/main/java/org/javawebstack/httpserver/handler/WebSocketHandler.java +++ b/src/main/java/org/javawebstack/httpserver/handler/WebSocketHandler.java @@ -7,5 +7,7 @@ public interface WebSocketHandler { void onMessage(WebSocket socket, String message); - void onClose(WebSocket socket, int code, String reason); + void onMessage(WebSocket socket, byte[] message); + + void onClose(WebSocket socket, Integer code, String reason); } diff --git a/src/main/java/org/javawebstack/httpserver/helper/HttpMethod.java b/src/main/java/org/javawebstack/httpserver/helper/HttpMethod.java deleted file mode 100644 index cb60e36..0000000 --- a/src/main/java/org/javawebstack/httpserver/helper/HttpMethod.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.javawebstack.httpserver.helper; - -public enum HttpMethod { - GET, - POST, - PUT, - PATCH, - DELETE, - HEAD, - OPTIONS, - CONNECT, - MOVE, - TRACE, - WEBSOCKET -} diff --git a/src/main/java/org/javawebstack/httpserver/helper/JettyNoLog.java b/src/main/java/org/javawebstack/httpserver/helper/JettyNoLog.java deleted file mode 100644 index 25dc314..0000000 --- a/src/main/java/org/javawebstack/httpserver/helper/JettyNoLog.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.javawebstack.httpserver.helper; - -import org.eclipse.jetty.util.log.AbstractLogger; -import org.eclipse.jetty.util.log.Logger; - -public class JettyNoLog extends AbstractLogger implements Logger { - - protected Logger newLogger(String s) { - return this; - } - - public String getName() { - return "NoLog"; - } - - public void warn(String s, Object... objects) { - - } - - public void warn(Throwable throwable) { - - } - - public void warn(String s, Throwable throwable) { - - } - - public void info(String s, Object... objects) { - - } - - public void info(Throwable throwable) { - - } - - public void info(String s, Throwable throwable) { - - } - - public boolean isDebugEnabled() { - return false; - } - - public void setDebugEnabled(boolean b) { - - } - - public void debug(String s, Object... objects) { - - } - - public void debug(Throwable throwable) { - - } - - public void debug(String s, Throwable throwable) { - - } - - public void ignore(Throwable throwable) { - - } - -} diff --git a/src/main/java/org/javawebstack/httpserver/router/DefaultRouteAutoInjector.java b/src/main/java/org/javawebstack/httpserver/router/DefaultRouteAutoInjector.java index 09dc6c3..54a2afe 100644 --- a/src/main/java/org/javawebstack/httpserver/router/DefaultRouteAutoInjector.java +++ b/src/main/java/org/javawebstack/httpserver/router/DefaultRouteAutoInjector.java @@ -1,7 +1,7 @@ package org.javawebstack.httpserver.router; import org.javawebstack.httpserver.Exchange; -import org.javawebstack.httpserver.helper.HttpMethod; +import org.javawebstack.httpserver.HTTPMethod; import org.javawebstack.httpserver.websocket.WebSocket; import java.util.Map; @@ -14,7 +14,7 @@ public Object getValue(Exchange exchange, Map extraArgs, Class variables = new HashMap<>(); private List handlers; private List afterHandlers; - public Route(RouteParamTransformerProvider routeParamTransformerProvider, HttpMethod method, String pattern, List handlers) { + public Route(RouteParamTransformerProvider routeParamTransformerProvider, HTTPMethod method, String pattern, List handlers) { this(routeParamTransformerProvider, method, pattern, ":", handlers); } - public Route(RouteParamTransformerProvider routeParamTransformerProvider, HttpMethod method, String pattern, String variableDelimiter, List handlers) { + public Route(RouteParamTransformerProvider routeParamTransformerProvider, HTTPMethod method, String pattern, String variableDelimiter, List handlers) { this.handlers = handlers; this.method = method; this.routeParamTransformerProvider = routeParamTransformerProvider; @@ -95,7 +95,7 @@ public Map match(Exchange exchange) { return match(exchange, exchange.getMethod(), exchange.getPath()); } - public Map match(Exchange exchange, HttpMethod method, String path) { + public Map match(Exchange exchange, HTTPMethod method, String path) { if (this.method != method) return null; Matcher matcher = pattern.matcher(path); diff --git a/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java b/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java index d1fcf33..efe7f9d 100644 --- a/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java +++ b/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java @@ -1,11 +1,11 @@ package org.javawebstack.httpserver.router; import org.javawebstack.httpserver.Exchange; +import org.javawebstack.httpserver.HTTPMethod; import org.javawebstack.httpserver.HTTPServer; import org.javawebstack.httpserver.handler.AfterRequestHandler; import org.javawebstack.httpserver.handler.RequestHandler; import org.javawebstack.httpserver.handler.WebSocketHandler; -import org.javawebstack.httpserver.helper.HttpMethod; import org.javawebstack.httpserver.router.annotation.PathPrefix; import org.javawebstack.httpserver.router.annotation.With; import org.javawebstack.httpserver.router.annotation.params.*; @@ -33,10 +33,10 @@ public void bind(String globalPrefix, Object controller) { prefixes.add(""); With with = Arrays.stream(controller.getClass().getDeclaredAnnotationsByType(With.class)).findFirst().orElse(null); class Bind { - final HttpMethod method; + final HTTPMethod method; final String path; - public Bind(HttpMethod method, String path) { + public Bind(HTTPMethod method, String path) { this.method = method; this.path = path; } @@ -54,41 +54,41 @@ public Bind(HttpMethod method, String path) { // Registering HTTP-Method annotations. //region Registering HTTP-Method Annotations for (Get a : getAnnotations(Get.class, method)) { - bindMiddlewares(HttpMethod.GET, globalPrefix, prefixes, a.value(), middlewares); - binds.add(new Bind(HttpMethod.GET, a.value())); + bindMiddlewares(HTTPMethod.GET, globalPrefix, prefixes, a.value(), middlewares); + binds.add(new Bind(HTTPMethod.GET, a.value())); } for (Post a : getAnnotations(Post.class, method)) { - bindMiddlewares(HttpMethod.POST, globalPrefix, prefixes, a.value(), middlewares); - binds.add(new Bind(HttpMethod.POST, a.value())); + bindMiddlewares(HTTPMethod.POST, globalPrefix, prefixes, a.value(), middlewares); + binds.add(new Bind(HTTPMethod.POST, a.value())); } for (Put a : getAnnotations(Put.class, method)) { - bindMiddlewares(HttpMethod.PUT, globalPrefix, prefixes, a.value(), middlewares); - binds.add(new Bind(HttpMethod.PUT, a.value())); + bindMiddlewares(HTTPMethod.PUT, globalPrefix, prefixes, a.value(), middlewares); + binds.add(new Bind(HTTPMethod.PUT, a.value())); } for (Delete a : getAnnotations(Delete.class, method)) { - bindMiddlewares(HttpMethod.DELETE, globalPrefix, prefixes, a.value(), middlewares); - binds.add(new Bind(HttpMethod.DELETE, a.value())); + bindMiddlewares(HTTPMethod.DELETE, globalPrefix, prefixes, a.value(), middlewares); + binds.add(new Bind(HTTPMethod.DELETE, a.value())); } for (Patch a : getAnnotations(Patch.class, method)) { - bindMiddlewares(HttpMethod.PATCH, globalPrefix, prefixes, a.value(), middlewares); - binds.add(new Bind(HttpMethod.PATCH, a.value())); + bindMiddlewares(HTTPMethod.PATCH, globalPrefix, prefixes, a.value(), middlewares); + binds.add(new Bind(HTTPMethod.PATCH, a.value())); } for (Trace a : getAnnotations(Trace.class, method)) { - bindMiddlewares(HttpMethod.TRACE, globalPrefix, prefixes, a.value(), middlewares); - binds.add(new Bind(HttpMethod.TRACE, a.value())); + bindMiddlewares(HTTPMethod.TRACE, globalPrefix, prefixes, a.value(), middlewares); + binds.add(new Bind(HTTPMethod.TRACE, a.value())); } for (Options a : getAnnotations(Options.class, method)) { - bindMiddlewares(HttpMethod.OPTIONS, globalPrefix, prefixes, a.value(), middlewares); - binds.add(new Bind(HttpMethod.OPTIONS, a.value())); + bindMiddlewares(HTTPMethod.OPTIONS, globalPrefix, prefixes, a.value(), middlewares); + binds.add(new Bind(HTTPMethod.OPTIONS, a.value())); } for (Head a : getAnnotations(Head.class, method)) { - bindMiddlewares(HttpMethod.HEAD, globalPrefix, prefixes, a.value(), middlewares); - binds.add(new Bind(HttpMethod.HEAD, a.value())); + bindMiddlewares(HTTPMethod.HEAD, globalPrefix, prefixes, a.value(), middlewares); + binds.add(new Bind(HTTPMethod.HEAD, a.value())); } for (WebSocketMessage a : getAnnotations(WebSocketMessage.class, method)) { WebSocketBindHandler handler = websocketHandlers.get(a.name()); if (handler == null) { - bindMiddlewares(HttpMethod.GET, globalPrefix, prefixes, a.value(), middlewares); + bindMiddlewares(HTTPMethod.GET, globalPrefix, prefixes, a.value(), middlewares); handler = new WebSocketBindHandler(); for (String prefix : prefixes) server.webSocket(buildPattern(globalPrefix, prefix, a.value()), handler); @@ -99,7 +99,7 @@ public Bind(HttpMethod method, String path) { for (WebSocketConnect a : getAnnotations(WebSocketConnect.class, method)) { WebSocketBindHandler handler = websocketHandlers.get(a.name()); if (handler == null) { - bindMiddlewares(HttpMethod.GET, globalPrefix, prefixes, a.value(), middlewares); + bindMiddlewares(HTTPMethod.GET, globalPrefix, prefixes, a.value(), middlewares); handler = new WebSocketBindHandler(); for (String prefix : prefixes) server.webSocket(buildPattern(globalPrefix, prefix, a.value()), handler); @@ -110,7 +110,7 @@ public Bind(HttpMethod method, String path) { for (WebSocketClose a : getAnnotations(WebSocketClose.class, method)) { WebSocketBindHandler handler = websocketHandlers.get(a.name()); if (handler == null) { - bindMiddlewares(HttpMethod.GET, globalPrefix, prefixes, a.value(), middlewares); + bindMiddlewares(HTTPMethod.GET, globalPrefix, prefixes, a.value(), middlewares); handler = new WebSocketBindHandler(); for (String prefix : prefixes) server.webSocket(buildPattern(globalPrefix, prefix, a.value()), handler); @@ -131,7 +131,7 @@ public Bind(HttpMethod method, String path) { } } - private void bindMiddlewares(HttpMethod method, String globalPrefix, List prefixes, String path, List middlewares) { + private void bindMiddlewares(HTTPMethod method, String globalPrefix, List prefixes, String path, List middlewares) { for (String name : middlewares) { RequestHandler before = server.getBeforeMiddleware(name); AfterRequestHandler after = server.getAfterMiddleware(name); @@ -279,7 +279,15 @@ public void onMessage(WebSocket socket, String message) { }}); } - public void onClose(WebSocket socket, int code, String reason) { + public void onMessage(WebSocket socket, byte[] message) { + if (messageHandler != null) + messageHandler.invoke(socket.getExchange(), new HashMap() {{ + put("websocket", socket); + put("websocketMessage", message); + }}); + } + + public void onClose(WebSocket socket, Integer code, String reason) { if (closeHandler != null) closeHandler.invoke(socket.getExchange(), new HashMap() {{ put("websocket", socket); diff --git a/src/main/java/org/javawebstack/httpserver/socket/HTTPServerSocket.java b/src/main/java/org/javawebstack/httpserver/socket/HTTPServerSocket.java deleted file mode 100644 index 1d4f766..0000000 --- a/src/main/java/org/javawebstack/httpserver/socket/HTTPServerSocket.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.javawebstack.httpserver.socket; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.nio.charset.StandardCharsets; - -public class HTTPServerSocket { - - private final ServerSocket serverSocket; - - public HTTPServerSocket(int port) throws IOException { - serverSocket = new ServerSocket(port); - } - - public void close() throws IOException { - serverSocket.close(); - } - - public boolean isClosed() { - return serverSocket.isClosed(); - } - - public HTTPSocket accept() throws IOException { - Socket socket = serverSocket.accept(); - return new HTTPSocket(socket); - } - -} diff --git a/src/main/java/org/javawebstack/httpserver/socket/HTTPSocketWorker.java b/src/main/java/org/javawebstack/httpserver/socket/HTTPSocketWorker.java deleted file mode 100644 index 11ded81..0000000 --- a/src/main/java/org/javawebstack/httpserver/socket/HTTPSocketWorker.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.javawebstack.httpserver.socket; - -import java.io.IOException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Consumer; - -public class HTTPSocketWorker { - - private final Thread schedulerThread; - private ExecutorService executorService; - private final HTTPServerSocket serverSocket; - - public HTTPSocketWorker(HTTPServerSocket serverSocket, Consumer handler) { - this.serverSocket = serverSocket; - this.schedulerThread = new Thread(() -> { - while (!serverSocket.isClosed()) { - try { - HTTPSocket socket = serverSocket.accept(); - executorService.execute(() -> { - try { - handler.accept(socket); - if(!socket.isClosed()) - socket.close(); - } catch (IOException ex) {} - }); - } catch (IOException exception) {} - } - }); - } - - public HTTPSocketWorker start() { - this.executorService = Executors.newCachedThreadPool(); - this.schedulerThread.start(); - return this; - } - - public void join() { - try { - schedulerThread.join(); - } catch (InterruptedException e) {} - } - - public void stop() { - this.executorService.shutdown(); - try { - this.serverSocket.close(); - } catch (IOException e) {} - } - -} diff --git a/src/main/java/org/javawebstack/httpserver/test/HTTPTest.java b/src/main/java/org/javawebstack/httpserver/test/HTTPTest.java index 3b9b3ec..375b94d 100644 --- a/src/main/java/org/javawebstack/httpserver/test/HTTPTest.java +++ b/src/main/java/org/javawebstack/httpserver/test/HTTPTest.java @@ -1,9 +1,13 @@ package org.javawebstack.httpserver.test; +import org.javawebstack.httpserver.HTTPMethod; import org.javawebstack.httpserver.HTTPServer; -import org.javawebstack.httpserver.helper.HttpMethod; +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; public abstract class HTTPTest { @@ -28,7 +32,7 @@ public void setBearerToken(String token) { } public TestExchange httpGet(String url) { - return httpRequest(HttpMethod.GET, url, null); + return httpRequest(HTTPMethod.GET, url, null); } public TestExchange httpPost(String url) { @@ -36,7 +40,7 @@ public TestExchange httpPost(String url) { } public TestExchange httpPost(String url, Object content) { - return httpRequest(HttpMethod.POST, url, content); + return httpRequest(HTTPMethod.POST, url, content); } public TestExchange httpPut(String url) { @@ -44,7 +48,7 @@ public TestExchange httpPut(String url) { } public TestExchange httpPut(String url, Object content) { - return httpRequest(HttpMethod.PUT, url, content); + return httpRequest(HTTPMethod.PUT, url, content); } public TestExchange httpDelete(String url) { @@ -52,25 +56,22 @@ public TestExchange httpDelete(String url) { } public TestExchange httpDelete(String url, Object content) { - return httpRequest(HttpMethod.DELETE, url, content); + return httpRequest(HTTPMethod.DELETE, url, content); } - public TestExchange httpRequest(HttpMethod method, String url, Object content) { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setMethod(method); - request.setPath(url); - defaultHeaders.forEach(request::addHeader); + public TestExchange httpRequest(HTTPMethod method, String url, Object content) { + TestHTTPSocket socket = new TestHTTPSocket(method, url); + defaultHeaders.forEach((k, v) -> socket.getRequestHeaders().put(k.toLowerCase(Locale.ROOT), Collections.singletonList(v))); if (content != null) { if (content instanceof String) { - request.setContent((String) content); + socket.setInputStream(new ByteArrayInputStream(((String) content).getBytes(StandardCharsets.UTF_8))); } else if (content instanceof byte[]) { - request.setContent((byte[]) content); + socket.setInputStream(new ByteArrayInputStream((byte[]) content)); } else { - request.setContent(server.getAbstractMapper().toAbstract(content).toJsonString()); + socket.setInputStream(new ByteArrayInputStream(server.getAbstractMapper().toAbstract(content).toJsonString().getBytes(StandardCharsets.UTF_8))); } } - MockHttpServletResponse response = new MockHttpServletResponse(); - TestExchange exchange = new TestExchange(server, request, response); + TestExchange exchange = new TestExchange(server, socket); server.execute(exchange); return exchange; } diff --git a/src/main/java/org/javawebstack/httpserver/test/MockHttpServletRequest.java b/src/main/java/org/javawebstack/httpserver/test/MockHttpServletRequest.java deleted file mode 100644 index 969b19f..0000000 --- a/src/main/java/org/javawebstack/httpserver/test/MockHttpServletRequest.java +++ /dev/null @@ -1,325 +0,0 @@ -package org.javawebstack.httpserver.test; - -import org.javawebstack.httpserver.helper.HttpMethod; - -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.nio.charset.StandardCharsets; -import java.security.Principal; -import java.util.*; - -public class MockHttpServletRequest implements HttpServletRequest { - - private MockServletInputStream inputStream = new MockServletInputStream(); - private final Map headers = new HashMap<>(); - private String method = "GET"; - private String pathInfo = "/"; - private String queryString = ""; - - public void setContent(byte[] content) { - inputStream = new MockServletInputStream(content); - } - - public void setContent(String content) { - setContent(content.getBytes(StandardCharsets.UTF_8)); - } - - public void addHeader(String key, String value) { - headers.put(key, value); - } - - public void setMethod(HttpMethod method) { - this.method = method.name(); - } - - public void setPath(String path) { - String[] spl = path.split("\\?"); - this.pathInfo = spl[0]; - queryString = spl.length > 1 ? spl[1] : ""; - } - - public String getAuthType() { - return null; - } - - public Cookie[] getCookies() { - return new Cookie[0]; - } - - public long getDateHeader(String s) { - return 0; - } - - public String getHeader(String s) { - return headers.get(s); - } - - public Enumeration getHeaders(String s) { - String header = getHeader(s); - Set set = new HashSet<>(); - if (header != null) - set.add(header); - return Collections.enumeration(set); - } - - public Enumeration getHeaderNames() { - return Collections.enumeration(headers.keySet()); - } - - public int getIntHeader(String s) { - return Integer.parseInt(getHeader(s)); - } - - public String getMethod() { - return method; - } - - public String getPathInfo() { - return pathInfo; - } - - public String getPathTranslated() { - return null; - } - - public String getContextPath() { - return null; - } - - public String getQueryString() { - return queryString; - } - - public String getRemoteUser() { - return null; - } - - public boolean isUserInRole(String s) { - return false; - } - - public Principal getUserPrincipal() { - return null; - } - - public String getRequestedSessionId() { - return null; - } - - public String getRequestURI() { - return null; - } - - public StringBuffer getRequestURL() { - return null; - } - - public String getServletPath() { - return null; - } - - public HttpSession getSession(boolean b) { - return null; - } - - public HttpSession getSession() { - return null; - } - - public String changeSessionId() { - return null; - } - - public boolean isRequestedSessionIdValid() { - return false; - } - - public boolean isRequestedSessionIdFromCookie() { - return false; - } - - public boolean isRequestedSessionIdFromURL() { - return false; - } - - public boolean isRequestedSessionIdFromUrl() { - return false; - } - - public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException { - return false; - } - - public void login(String s, String s1) throws ServletException { - - } - - public void logout() throws ServletException { - - } - - public Collection getParts() throws IOException, ServletException { - return null; - } - - public Part getPart(String s) throws IOException, ServletException { - return null; - } - - public T upgrade(Class aClass) throws IOException, ServletException { - return null; - } - - public Object getAttribute(String s) { - return null; - } - - public Enumeration getAttributeNames() { - return null; - } - - public String getCharacterEncoding() { - return null; - } - - public void setCharacterEncoding(String s) throws UnsupportedEncodingException { - - } - - public int getContentLength() { - return inputStream.size(); - } - - public long getContentLengthLong() { - return getContentLength(); - } - - public String getContentType() { - return getHeader("Content-Type"); - } - - public ServletInputStream getInputStream() throws IOException { - return inputStream; - } - - public String getParameter(String s) { - return null; - } - - public Enumeration getParameterNames() { - return null; - } - - public String[] getParameterValues(String s) { - return new String[0]; - } - - public Map getParameterMap() { - return null; - } - - public String getProtocol() { - return null; - } - - public String getScheme() { - return null; - } - - public String getServerName() { - return null; - } - - public int getServerPort() { - return 80; - } - - public BufferedReader getReader() throws IOException { - return new BufferedReader(new InputStreamReader(inputStream)); - } - - public String getRemoteAddr() { - return "127.0.0.1"; - } - - public String getRemoteHost() { - return "localhost"; - } - - public void setAttribute(String s, Object o) { - - } - - public void removeAttribute(String s) { - - } - - public Locale getLocale() { - return Locale.ENGLISH; - } - - public Enumeration getLocales() { - return Collections.enumeration(Collections.singletonList(getLocale())); - } - - public boolean isSecure() { - return false; - } - - public RequestDispatcher getRequestDispatcher(String s) { - return null; - } - - public String getRealPath(String s) { - return null; - } - - public int getRemotePort() { - return 0; - } - - public String getLocalName() { - return "localhost"; - } - - public String getLocalAddr() { - return "127.0.0.1"; - } - - public int getLocalPort() { - return 80; - } - - public ServletContext getServletContext() { - return null; - } - - public AsyncContext startAsync() throws IllegalStateException { - return null; - } - - public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { - return null; - } - - public boolean isAsyncStarted() { - return false; - } - - public boolean isAsyncSupported() { - return false; - } - - public AsyncContext getAsyncContext() { - return null; - } - - public DispatcherType getDispatcherType() { - return null; - } - -} diff --git a/src/main/java/org/javawebstack/httpserver/test/MockHttpServletResponse.java b/src/main/java/org/javawebstack/httpserver/test/MockHttpServletResponse.java deleted file mode 100644 index 5b33e84..0000000 --- a/src/main/java/org/javawebstack/httpserver/test/MockHttpServletResponse.java +++ /dev/null @@ -1,178 +0,0 @@ -package org.javawebstack.httpserver.test; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.*; - -public class MockHttpServletResponse implements HttpServletResponse { - - private int status = 200; - private final MockServletOutputStream outputStream = new MockServletOutputStream(); - private final Map headers = new HashMap<>(); - - public void addCookie(Cookie cookie) { - - } - - public boolean containsHeader(String s) { - return headers.containsKey(s); - } - - public String encodeURL(String s) { - return null; - } - - public String encodeRedirectURL(String s) { - return null; - } - - public String encodeUrl(String s) { - return null; - } - - public String encodeRedirectUrl(String s) { - return null; - } - - public void sendError(int i, String s) throws IOException { - sendError(i); - } - - public void sendError(int i) throws IOException { - setStatus(i); - outputStream.close(); - } - - public void sendRedirect(String s) throws IOException { - setStatus(302); - setHeader("Location", s); - } - - public void setDateHeader(String s, long l) { - - } - - public void addDateHeader(String s, long l) { - - } - - public void setHeader(String s, String s1) { - headers.put(s, s1); - } - - public void addHeader(String s, String s1) { - headers.put(s, s1); - } - - public void setIntHeader(String s, int i) { - setHeader(s, String.valueOf(i)); - } - - public void addIntHeader(String s, int i) { - addHeader(s, String.valueOf(i)); - } - - public void setStatus(int i) { - this.status = i; - } - - public void setStatus(int i, String s) { - setStatus(i); - } - - public int getStatus() { - return status; - } - - public String getHeader(String s) { - return headers.get(s); - } - - public Collection getHeaders(String s) { - String header = getHeader(s); - Set set = new HashSet<>(); - if (header != null) - set.add(header); - return set; - } - - public Collection getHeaderNames() { - return headers.keySet(); - } - - public String getCharacterEncoding() { - return null; - } - - public String getContentType() { - return getHeader("Content-Type"); - } - - public ServletOutputStream getOutputStream() throws IOException { - return outputStream; - } - - public PrintWriter getWriter() throws IOException { - return new PrintWriter(outputStream); - } - - public void setCharacterEncoding(String s) { - - } - - public void setContentLength(int i) { - setIntHeader("Content-Length", i); - } - - public void setContentLengthLong(long l) { - setHeader("Content-Length", String.valueOf(l)); - } - - public void setContentType(String s) { - setHeader("Content-Type", s); - } - - public void setBufferSize(int i) { - - } - - public int getBufferSize() { - return 0; - } - - public void flushBuffer() throws IOException { - - } - - public void resetBuffer() { - - } - - public boolean isCommitted() { - return false; - } - - public void reset() { - - } - - public void setLocale(Locale locale) { - - } - - public Locale getLocale() { - return null; - } - - public byte[] getContent() { - return outputStream.getBytes(); - } - - public String getContentString() { - return outputStream.getString(); - } - -} diff --git a/src/main/java/org/javawebstack/httpserver/test/MockServletInputStream.java b/src/main/java/org/javawebstack/httpserver/test/MockServletInputStream.java deleted file mode 100644 index e6dd2e4..0000000 --- a/src/main/java/org/javawebstack/httpserver/test/MockServletInputStream.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.javawebstack.httpserver.test; - -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import java.io.ByteArrayInputStream; -import java.io.IOException; - -public class MockServletInputStream extends ServletInputStream { - - private final int size; - private final ByteArrayInputStream inputStream; - - public MockServletInputStream(byte[] bytes) { - inputStream = new ByteArrayInputStream(bytes); - size = bytes.length; - } - - public MockServletInputStream() { - this(new byte[0]); - } - - public boolean isFinished() { - return inputStream.available() == 0; - } - - public boolean isReady() { - return true; - } - - public int available() { - return inputStream.available(); - } - - public void setReadListener(ReadListener readListener) { - - } - - public int read() throws IOException { - return inputStream.read(); - } - - public int size() { - return size; - } - -} diff --git a/src/main/java/org/javawebstack/httpserver/test/MockServletOutputStream.java b/src/main/java/org/javawebstack/httpserver/test/MockServletOutputStream.java deleted file mode 100644 index 49e341f..0000000 --- a/src/main/java/org/javawebstack/httpserver/test/MockServletOutputStream.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.javawebstack.httpserver.test; - -import javax.servlet.ServletOutputStream; -import javax.servlet.WriteListener; -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; - -public class MockServletOutputStream extends ServletOutputStream { - - private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - - public boolean isReady() { - return true; - } - - public void setWriteListener(WriteListener writeListener) { - - } - - public void write(int b) { - buffer.write(b); - } - - public byte[] getBytes() { - return buffer.toByteArray(); - } - - public String getString() { - return new String(getBytes(), StandardCharsets.UTF_8); - } - -} diff --git a/src/main/java/org/javawebstack/httpserver/test/TestExchange.java b/src/main/java/org/javawebstack/httpserver/test/TestExchange.java index 8c237b2..a7de3ba 100644 --- a/src/main/java/org/javawebstack/httpserver/test/TestExchange.java +++ b/src/main/java/org/javawebstack/httpserver/test/TestExchange.java @@ -3,20 +3,24 @@ import org.javawebstack.abstractdata.AbstractElement; import org.javawebstack.httpserver.Exchange; import org.javawebstack.httpserver.HTTPServer; -import org.javawebstack.httpserver.helper.MimeType; +import org.javawebstack.httpserver.adapter.IHTTPSocket; +import org.javawebstack.httpserver.util.MimeType; import org.junit.jupiter.api.Assertions; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Locale; public class TestExchange extends Exchange { - private MockHttpServletRequest mockReq; - private MockHttpServletResponse mockRes; + + private TestHTTPSocket testSocket; - public TestExchange(HTTPServer service, MockHttpServletRequest request, MockHttpServletResponse response) { - super(service, request, response); - this.mockReq = request; - this.mockRes = response; + public TestExchange(HTTPServer service, TestHTTPSocket socket) { + super(service, socket); + this.testSocket = socket; } public TestExchange print() { @@ -24,67 +28,67 @@ public TestExchange print() { return this; } - public MockHttpServletRequest mockRequest() { - return mockReq; - } - - public MockHttpServletResponse mockResponse() { - return mockRes; + public String getOutputString() { + try { + return new String(((ByteArrayOutputStream) testSocket.getOutputStream()).toByteArray(), StandardCharsets.UTF_8); + } catch (IOException ignored) { + return null; + } } public TestExchange printResponse() { - System.out.println("HTTP Response " + mockRes.getStatus()); - System.out.println(mockRes.getContentString()); + System.out.println("HTTP Response " + testSocket.getResponseStatus()); + System.out.println(getOutputString()); return this; } public TestExchange assertStatus(int status) { - Assertions.assertEquals(status, mockRes.getStatus()); + Assertions.assertEquals(status, testSocket.getResponseStatus()); return this; } public TestExchange assertStatus(int status, String message) { - Assertions.assertEquals(status, mockRes.getStatus(), message); + Assertions.assertEquals(status, testSocket.getResponseStatus(), message); return this; } public TestExchange assertNotStatus(int status) { - Assertions.assertNotEquals(status, mockRes.getStatus()); + Assertions.assertNotEquals(status, testSocket.getResponseStatus()); return this; } public TestExchange assertNotStatus(int status, String message) { - Assertions.assertNotEquals(status, mockRes.getStatus(), message); + Assertions.assertNotEquals(status, testSocket.getResponseStatus(), message); return this; } public TestExchange assertOk() { - Assertions.assertEquals(200, mockRes.getStatus()); + Assertions.assertEquals(200, testSocket.getResponseStatus()); return this; } public TestExchange assertOk(String message) { - Assertions.assertEquals(200, mockRes.getStatus(), message); + Assertions.assertEquals(200, testSocket.getResponseStatus(), message); return this; } public TestExchange assertCreated() { - Assertions.assertEquals(201, mockRes.getStatus()); + Assertions.assertEquals(201, testSocket.getResponseStatus()); return this; } public TestExchange assertCreated(String message) { - Assertions.assertEquals(201, mockRes.getStatus(), message); + Assertions.assertEquals(201, testSocket.getResponseStatus(), message); return this; } public TestExchange assertNoContent() { - Assertions.assertEquals(204, mockRes.getStatus()); + Assertions.assertEquals(204, testSocket.getResponseStatus()); return this; } public TestExchange assertNoContent(String message) { - Assertions.assertEquals(204, mockRes.getStatus(), message); + Assertions.assertEquals(204, testSocket.getResponseStatus(), message); return this; } @@ -100,93 +104,93 @@ public TestExchange assertRedirect(String message) { public TestExchange assertRedirectTo(String url) { assertRedirect(); - Assertions.assertEquals(url, mockRes.getHeader("Location")); + Assertions.assertEquals(url, testSocket.getResponseHeaders().get("location")); return this; } public TestExchange assertRedirectTo(String url, String message) { assertRedirect(message); - Assertions.assertEquals(url, mockRes.getHeader("Location"), message); + Assertions.assertEquals(url, testSocket.getResponseHeaders().get("location"), message); return this; } public TestExchange assertNotFound() { - Assertions.assertEquals(404, mockRes.getStatus()); + Assertions.assertEquals(404, testSocket.getResponseStatus()); return this; } public TestExchange assertNotFound(String message) { - Assertions.assertEquals(404, mockRes.getStatus(), message); + Assertions.assertEquals(404, testSocket.getResponseStatus(), message); return this; } public TestExchange assertBadRequest() { - Assertions.assertEquals(400, mockRes.getStatus()); + Assertions.assertEquals(400, testSocket.getResponseStatus()); return this; } public TestExchange assertBadRequest(String message) { - Assertions.assertEquals(400, mockRes.getStatus(), message); + Assertions.assertEquals(400, testSocket.getResponseStatus(), message); return this; } public TestExchange assertForbidden() { - Assertions.assertEquals(403, mockRes.getStatus()); + Assertions.assertEquals(403, testSocket.getResponseStatus()); return this; } public TestExchange assertForbidden(String message) { - Assertions.assertEquals(403, mockRes.getStatus(), message); + Assertions.assertEquals(403, testSocket.getResponseStatus(), message); return this; } public TestExchange assertUnauthorized() { - Assertions.assertEquals(401, mockRes.getStatus()); + Assertions.assertEquals(401, testSocket.getResponseStatus()); return this; } public TestExchange assertUnauthorized(String message) { - Assertions.assertEquals(401, mockRes.getStatus(), message); + Assertions.assertEquals(401, testSocket.getResponseStatus(), message); return this; } public TestExchange assertSuccess() { - Assertions.assertEquals(200, (mockRes.getStatus() / 100) * 100); + Assertions.assertEquals(200, (testSocket.getResponseStatus() / 100) * 100); return this; } public TestExchange assertSuccess(String message) { - Assertions.assertEquals(200, (mockRes.getStatus() / 100) * 100, message); + Assertions.assertEquals(200, (testSocket.getResponseStatus() / 100) * 100, message); return this; } public TestExchange assertError() { - Assertions.assertTrue(mockRes.getStatus() >= 400); + Assertions.assertTrue(testSocket.getResponseStatus() >= 400); return this; } public TestExchange assertError(String message) { - Assertions.assertTrue(mockRes.getStatus() >= 400, message); + Assertions.assertTrue(testSocket.getResponseStatus() >= 400, message); return this; } public TestExchange assertHeader(String key, String value) { - Assertions.assertEquals(value, mockRes.getHeader(key)); + Assertions.assertEquals(value, testSocket.getResponseHeaders().get(key.toLowerCase(Locale.ROOT))); return this; } public TestExchange assertHeader(String key, String value, String message) { - Assertions.assertEquals(value, mockRes.getHeader(key), message); + Assertions.assertEquals(value, testSocket.getResponseHeaders().get(key.toLowerCase(Locale.ROOT)), message); return this; } public TestExchange assertJsonPath(String path, Object value) { - Assertions.assertTrue(checkGraph(getPathElement(mockResponseBody(mockRes), path), value)); + Assertions.assertTrue(checkGraph(getPathElement(mockResponseBody(), path), value)); return this; } public TestExchange assertJsonPath(String path, Object value, String message) { - Assertions.assertTrue(checkGraph(getPathElement(mockResponseBody(mockRes), path), value), message); + Assertions.assertTrue(checkGraph(getPathElement(mockResponseBody(), path), value), message); return this; } @@ -201,26 +205,27 @@ public TestExchange assertJson(Object value, String message) { } public TestExchange assertBody(String content) { - Assertions.assertEquals(content, mockRes.getContentString()); + Assertions.assertEquals(content, getOutputString()); return this; } public TestExchange assertBody(String content, String message) { - Assertions.assertEquals(content, mockRes.getContentString(), message); + Assertions.assertEquals(content, getOutputString(), message); return this; } - private AbstractElement mockResponseBody(MockHttpServletResponse response) { - MimeType type = MimeType.byMimeType(response.getContentType()); + private AbstractElement mockResponseBody() { + List contentTypes = testSocket.getResponseHeaders().get("content-type"); + MimeType type = MimeType.byMimeType(contentTypes.size() > 0 ? contentTypes.get(0) : null); if (type == null) type = MimeType.JSON; switch (type) { default: - return AbstractElement.fromJson(response.getContentString()); + return AbstractElement.fromJson(getOutputString()); case YAML: - return AbstractElement.fromYaml(response.getContentString(), true); + return AbstractElement.fromYaml(getOutputString(), true); case X_WWW_FORM_URLENCODED: - return AbstractElement.fromFormData(response.getContentString()); + return AbstractElement.fromFormData(getOutputString()); } } @@ -280,6 +285,6 @@ private boolean isRedirect() { redirectCodes.add(307); redirectCodes.add(308); - return redirectCodes.contains(mockRes.getStatus()); + return redirectCodes.contains(testSocket.getResponseStatus()); } } diff --git a/src/main/java/org/javawebstack/httpserver/test/TestHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/test/TestHTTPSocket.java new file mode 100644 index 0000000..5dc1a70 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/test/TestHTTPSocket.java @@ -0,0 +1,121 @@ +package org.javawebstack.httpserver.test; + +import org.javawebstack.httpserver.HTTPMethod; +import org.javawebstack.httpserver.adapter.IHTTPSocket; + +import java.io.*; +import java.util.*; + +public class TestHTTPSocket implements IHTTPSocket { + + private final HTTPMethod requestMethod; + private final String requestPath; + private final String requestQuery; + private InputStream inputStream = new ByteArrayInputStream(new byte[0]); + private final OutputStream outputStream = new ByteArrayOutputStream(); + private boolean closed; + private int responseStatus = 200; + private String responseStatusMessage = "OK"; + private final Map> requestHeaders = new HashMap<>(); + private final Map> responseHeaders = new HashMap<>(); + + public Map> getRequestHeaders() { + return requestHeaders; + } + + public Map> getResponseHeaders() { + return responseHeaders; + } + + public TestHTTPSocket(HTTPMethod method, String url) { + this.requestMethod = method; + String[] pathSplit = url.split("\\?", 2); + requestPath = pathSplit[0]; + if(pathSplit.length == 2) + requestQuery = pathSplit[1]; + else + requestQuery = null; + } + + public TestHTTPSocket setInputStream(InputStream inputStream) { + this.inputStream = inputStream; + return this; + } + + public InputStream getInputStream() throws IOException { + return inputStream; + } + + public OutputStream getOutputStream() throws IOException { + return outputStream; + } + + public void close() throws IOException { + closed = true; + } + + public boolean isClosed() { + return closed; + } + + public TestHTTPSocket setResponseStatus(int status, String message) { + this.responseStatus = status; + this.responseStatusMessage = message; + return this; + } + + public TestHTTPSocket setResponseHeader(String name, String value) { + responseHeaders.put(name.toLowerCase(Locale.ROOT), Arrays.asList(value)); + return this; + } + + public TestHTTPSocket addResponseHeader(String name, String value) { + responseHeaders.computeIfAbsent(name.toLowerCase(Locale.ROOT), h -> new ArrayList<>()).add(value); + return this; + } + + public HTTPMethod getRequestMethod() { + return requestMethod; + } + + public String getRequestPath() { + return requestPath; + } + + public String getRequestQuery() { + return requestQuery; + } + + public String getRequestVersion() { + return "HTTP/1.1"; + } + + public Set getRequestHeaderNames() { + return Collections.emptySet(); + } + + public String getRequestHeader(String name) { + return null; + } + + public List getRequestHeaders(String name) { + return null; + } + + public int getResponseStatus() { + return responseStatus; + } + + public String getResponseStatusMessage() { + return responseStatusMessage; + } + + public void writeHeaders() throws IOException { + + } + + public String getRemoteAddress() { + return null; + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/helper/MimeType.java b/src/main/java/org/javawebstack/httpserver/util/MimeType.java similarity index 98% rename from src/main/java/org/javawebstack/httpserver/helper/MimeType.java rename to src/main/java/org/javawebstack/httpserver/util/MimeType.java index 148853a..3703170 100644 --- a/src/main/java/org/javawebstack/httpserver/helper/MimeType.java +++ b/src/main/java/org/javawebstack/httpserver/util/MimeType.java @@ -1,4 +1,4 @@ -package org.javawebstack.httpserver.helper; +package org.javawebstack.httpserver.util; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/org/javawebstack/httpserver/util/websocket/WebSocketFrame.java b/src/main/java/org/javawebstack/httpserver/util/websocket/WebSocketFrame.java new file mode 100644 index 0000000..9c54c14 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/util/websocket/WebSocketFrame.java @@ -0,0 +1,140 @@ +package org.javawebstack.httpserver.util.websocket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class WebSocketFrame { + + private byte flags; + private byte opcode; + private byte[] maskKey; + private byte[] payload; + + public boolean isFin() { + return (flags & 0b1000_0000) > 0; + } + + public WebSocketFrame setFin(boolean fin) { + flags = (byte) ((flags & 0b0111_1111) | ((fin ? 1 : 0) << 7)); + return this; + } + + public boolean isRsv1() { + return (flags & 0b0100_0000) > 0; + } + + public WebSocketFrame setRsv1(boolean rsv1) { + flags = (byte) ((flags & 0b1011_1111) | ((rsv1 ? 1 : 0) << 6)); + return this; + } + + public boolean isRsv2() { + return (flags & 0b0010_0000) > 0; + } + + public WebSocketFrame setRsv2(boolean rsv2) { + flags = (byte) ((flags & 0b1101_1111) | ((rsv2 ? 1 : 0) << 5)); + return this; + } + + public boolean isRsv3() { + return (flags & 0b0001_0000) > 0; + } + + public WebSocketFrame setRsv3(boolean rsv3) { + flags = (byte) ((flags & 0b1110_1111) | ((rsv3 ? 1 : 0) << 4)); + return this; + } + + public byte getOpcode() { + return opcode; + } + + public WebSocketFrame setOpcode(byte opcode) { + this.opcode = opcode; + return this; + } + + public byte[] getMaskKey() { + return maskKey; + } + + public WebSocketFrame setMaskKey(byte[] maskKey) { + this.maskKey = maskKey; + return this; + } + + public byte[] getPayload() { + return payload; + } + + public WebSocketFrame setPayload(byte[] payload) { + this.payload = payload; + return this; + } + + public void write(OutputStream stream) throws IOException { + stream.write(flags | opcode); + int lengthByte = payload.length > 125 ? (payload.length > 0xFFFF ? 127 : 126) : payload.length; + stream.write((maskKey != null ? 0b1000_0000 : 0) | lengthByte); + if(lengthByte == 127) { + stream.write(payload.length >> 24); + stream.write((payload.length & 0xFF0000) >> 16); + } + if(lengthByte > 125) { + stream.write((payload.length & 0xFF00) >> 8); + stream.write(payload.length & 0xFF); + } + if(maskKey != null) + stream.write(maskKey); + if(maskKey != null) { + for(int i=0; i> 7) == 1 ? new byte[4] : null; + int len = b & 0b0111_1111; + if(len == 126) { + len = safeRead(stream) << 8; + len |= safeRead(stream); + } else if(len == 127) { + len = safeRead(stream) << 24; + len |= safeRead(stream) << 16; + len |= safeRead(stream) << 8; + len |= safeRead(stream); + } + if(frame.maskKey != null) { + frame.maskKey[0] = (byte) safeRead(stream); + frame.maskKey[1] = (byte) safeRead(stream); + frame.maskKey[2] = (byte) safeRead(stream); + frame.maskKey[3] = (byte) safeRead(stream); + } + frame.payload = new byte[len]; + if(frame.maskKey != null) { + for(int i=0; i> 8); + payload[1] = (byte) (code & 0xF); + if(reasonBytes != null) + System.arraycopy(reasonBytes, 0, payload, 2, reasonBytes.length); + } + new WebSocketFrame().setFin(true).setOpcode(OP_CLOSE).setPayload(payload).write(socket.getOutputStream()); + socket.close(); + } + + public static ClosePayload parseClose(byte[] payload) { + ClosePayload close = new ClosePayload(); + if(payload.length >= 2) { + close.code = (payload[0] << 8) | payload[1]; + if(payload.length > 2) { + byte[] reasonBytes = new byte[payload.length - 2]; + System.arraycopy(payload, 2, reasonBytes, 0, reasonBytes.length); + close.reason = new String(reasonBytes, StandardCharsets.UTF_8); + } + } + return close; + } + + public static void send(IHTTPSocket socket, String message) throws IOException { + new WebSocketFrame().setFin(true).setOpcode(OP_TEXT).setPayload(message.getBytes(StandardCharsets.UTF_8)).write(socket.getOutputStream()); + } + + public static void send(IHTTPSocket socket, byte[] message) throws IOException { + new WebSocketFrame().setFin(true).setOpcode(OP_BINARY).setPayload(message).write(socket.getOutputStream()); + } + + public static class ClosePayload { + + private Integer code; + private String reason; + + public Integer getCode() { + return code; + } + + public String getReason() { + return reason; + } + } + + private static String calcKey(String key) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + digest.update((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes(StandardCharsets.US_ASCII)); + return new String(Base64.getEncoder().encode(digest.digest()), StandardCharsets.US_ASCII); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/websocket/InternalWebSocketAdapter.java b/src/main/java/org/javawebstack/httpserver/websocket/InternalWebSocketAdapter.java deleted file mode 100644 index 87436b6..0000000 --- a/src/main/java/org/javawebstack/httpserver/websocket/InternalWebSocketAdapter.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.javawebstack.httpserver.websocket; - -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; - -import java.util.HashMap; -import java.util.Map; - -@org.eclipse.jetty.websocket.api.annotations.WebSocket -public class InternalWebSocketAdapter { - public static Map webSockets = new HashMap<>(); - - @OnWebSocketConnect - public void onConnect(Session session) { - WebSocket socket = webSockets.get(session.getUpgradeResponse().getHeader("X-Server-WSID")); - if (socket == null) { - session.close(500, "Server Error"); - return; - } - socket.setSession(session); - try { - socket.getHandler().onConnect(socket); - } catch (Throwable t) { - socket.getExchange().getServer().getExceptionHandler().handle(socket.getExchange(), t); - } - } - - @OnWebSocketMessage - public void onMessage(Session session, String message) { - WebSocket socket = webSockets.get(session.getUpgradeResponse().getHeader("X-Server-WSID")); - if (socket == null) { - session.close(500, "Server Error"); - return; - } - try { - socket.getHandler().onMessage(socket, message); - } catch (Throwable t) { - socket.getExchange().getServer().getExceptionHandler().handle(socket.getExchange(), t); - } - } - - @OnWebSocketClose - public void onClose(Session session, int code, String reason) { - WebSocket socket = webSockets.get(session.getUpgradeResponse().getHeader("X-Server-WSID")); - if (socket != null) { - try { - socket.getHandler().onClose(socket, code, reason); - } catch (Throwable t) { - socket.getExchange().getServer().getExceptionHandler().handle(socket.getExchange(), t); - } - webSockets.remove(session.getUpgradeResponse().getHeader("X-Server-WSID")); - } - } -} diff --git a/src/main/java/org/javawebstack/httpserver/websocket/InternalWebSocketRequestHandler.java b/src/main/java/org/javawebstack/httpserver/websocket/InternalWebSocketRequestHandler.java index 31a805e..f781f90 100644 --- a/src/main/java/org/javawebstack/httpserver/websocket/InternalWebSocketRequestHandler.java +++ b/src/main/java/org/javawebstack/httpserver/websocket/InternalWebSocketRequestHandler.java @@ -1,15 +1,17 @@ package org.javawebstack.httpserver.websocket; -import org.eclipse.jetty.server.Request; import org.javawebstack.httpserver.Exchange; +import org.javawebstack.httpserver.adapter.IHTTPSocket; import org.javawebstack.httpserver.handler.RequestHandler; import org.javawebstack.httpserver.handler.WebSocketHandler; +import org.javawebstack.httpserver.util.websocket.WebSocketFrame; +import org.javawebstack.httpserver.util.websocket.WebSocketUtil; -import javax.servlet.ServletException; import java.io.IOException; -import java.util.UUID; +import java.nio.charset.StandardCharsets; public class InternalWebSocketRequestHandler implements RequestHandler { + private final WebSocketHandler handler; public InternalWebSocketRequestHandler(WebSocketHandler handler) { @@ -17,14 +19,41 @@ public InternalWebSocketRequestHandler(WebSocketHandler handler) { } public Object handle(Exchange exchange) { - String id = UUID.randomUUID().toString(); - exchange.rawResponse().setHeader("X-Server-WSID", id); - InternalWebSocketAdapter.webSockets.put(id, new WebSocket(exchange, handler)); + IHTTPSocket socket = exchange.socket(); try { - exchange.getServer().getInternalWebSocketHandler().handle(exchange.getPath(), (Request) exchange.rawRequest(), exchange.rawRequest(), exchange.rawResponse()); - } catch (IOException | ServletException ignored) { - ignored.printStackTrace(); - } + if(!WebSocketUtil.accept(socket, null)) + return null; + WebSocket webSocket = new WebSocket(exchange); + handler.onConnect(webSocket); + WebSocketFrame frame; + while (true) { + try { + frame = WebSocketFrame.read(socket.getInputStream()); + } catch (IOException ex) { + handler.onClose(webSocket, null, null); + socket.close(); + break; + } + if(frame.getOpcode() == WebSocketUtil.OP_CLOSE) { + WebSocketUtil.ClosePayload close = WebSocketUtil.parseClose(frame.getPayload()); + handler.onClose(webSocket, close.getCode(), close.getReason()); + socket.close(); + break; + } + if(frame.getOpcode() == WebSocketUtil.OP_PING) { + frame.setOpcode(WebSocketUtil.OP_PONG).setMaskKey(null).write(socket.getOutputStream()); + continue; + } + if(frame.getOpcode() == WebSocketUtil.OP_BINARY) { + handler.onMessage(webSocket, frame.getPayload()); + continue; + } + if(frame.getOpcode() == WebSocketUtil.OP_TEXT) { + handler.onMessage(webSocket, new String(frame.getPayload(), StandardCharsets.UTF_8)); + continue; + } + } + } catch (IOException ignored) {} return null; } } diff --git a/src/main/java/org/javawebstack/httpserver/websocket/WebSocket.java b/src/main/java/org/javawebstack/httpserver/websocket/WebSocket.java index 0fc7d6e..014b0f2 100644 --- a/src/main/java/org/javawebstack/httpserver/websocket/WebSocket.java +++ b/src/main/java/org/javawebstack/httpserver/websocket/WebSocket.java @@ -1,58 +1,44 @@ package org.javawebstack.httpserver.websocket; -import org.eclipse.jetty.websocket.api.Session; import org.javawebstack.httpserver.Exchange; -import org.javawebstack.httpserver.handler.WebSocketHandler; +import org.javawebstack.httpserver.util.websocket.WebSocketUtil; import java.io.IOException; -import java.nio.ByteBuffer; public class WebSocket { private final Exchange exchange; - private final WebSocketHandler handler; - private Session session; - public WebSocket(Exchange exchange, WebSocketHandler handler) { + public WebSocket(Exchange exchange) { this.exchange = exchange; - this.handler = handler; } public Exchange getExchange() { return exchange; } - public WebSocketHandler getHandler() { - return handler; - } - - void setSession(Session session) { - this.session = session; - } - public void close() { - session.close(); + close(null, null); } - public void close(int code, String reason) { - session.close(code, reason); + public void close(Integer code, String reason) { + try { + WebSocketUtil.close(exchange.socket(), code, reason); + } catch (IOException ignored) {} } public void send(String message) { try { - session.getRemote().sendString(message); + WebSocketUtil.send(exchange.socket(), message); } catch (IOException ignored) { } } public void send(byte[] message) { try { - session.getRemote().sendBytes(ByteBuffer.wrap(message)); + WebSocketUtil.send(exchange.socket(), message); } catch (IOException ignored) { } } - public Session getSession() { - return session; - } } From 313ac6705bd80648cdaa1dcecd4f780aa256cb91 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Sun, 5 Dec 2021 09:36:33 +0100 Subject: [PATCH 08/31] Fixed locale header --- src/main/java/org/javawebstack/httpserver/Exchange.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/javawebstack/httpserver/Exchange.java b/src/main/java/org/javawebstack/httpserver/Exchange.java index 0fd04ef..2b717e3 100644 --- a/src/main/java/org/javawebstack/httpserver/Exchange.java +++ b/src/main/java/org/javawebstack/httpserver/Exchange.java @@ -181,7 +181,7 @@ public Exchange redirect(String url) { } public List locales() { - String locale = socket.getRequestHeader("locale"); + String locale = socket.getRequestHeader("accept-language"); if(locale == null) return new ArrayList<>(); return Stream.of(locale.split(" ?,")).map(s -> s.split(";")[0]).map(Locale::forLanguageTag).collect(Collectors.toList()); From bbe6c85fb8d874ec42b6c6062e9727b2d60b13f3 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Sun, 5 Dec 2021 10:03:10 +0100 Subject: [PATCH 09/31] Made jetty optional and upgraded junit --- pom.xml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index b54f0a3..8de625a 100644 --- a/pom.xml +++ b/pom.xml @@ -56,11 +56,7 @@ org.eclipse.jetty jetty-server 9.4.44.v20210927 - - - org.eclipse.jetty.websocket - websocket-server - 9.4.43.v20210629 + true org.reflections @@ -70,12 +66,12 @@ org.junit.jupiter junit-jupiter-api - 5.8.1 + 5.8.2 org.junit.jupiter junit-jupiter-engine - 5.8.1 + 5.8.2 test From 620c5da0882a854a94d047d4bfe457fa5c4d1ada Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 7 Dec 2021 16:29:05 +0100 Subject: [PATCH 10/31] Upgraded jetty from 9.44.x to 11.0.7 because the abstraction allowed it and there are reported issues with older versions --- pom.xml | 2 +- .../org/javawebstack/httpserver/HTTPServer.java | 1 + .../adapter/jetty/JettyHTTPSocket.java | 4 ++-- .../adapter/jetty/JettyHTTPSocketServer.java | 17 +++-------------- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 8de625a..1c47f17 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ org.eclipse.jetty jetty-server - 9.4.44.v20210927 + 11.0.7 true diff --git a/src/main/java/org/javawebstack/httpserver/HTTPServer.java b/src/main/java/org/javawebstack/httpserver/HTTPServer.java index 04cefb7..cc4931e 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPServer.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPServer.java @@ -407,4 +407,5 @@ public byte[] transformResponse(Exchange exchange, Object object) { public ExceptionHandler getExceptionHandler() { return exceptionHandler; } + } diff --git a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java index 9c959ce..7cc94dd 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java @@ -1,11 +1,11 @@ package org.javawebstack.httpserver.adapter.jetty; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.javawebstack.httpserver.HTTPMethod; import org.javawebstack.httpserver.HTTPStatus; import org.javawebstack.httpserver.adapter.IHTTPSocket; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; diff --git a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java index c0f1def..3a4f0b1 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java @@ -1,5 +1,8 @@ package org.javawebstack.httpserver.adapter.jetty; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; @@ -7,11 +10,6 @@ import org.javawebstack.httpserver.adapter.IHTTPSocketHandler; import org.javawebstack.httpserver.adapter.IHTTPSocketServer; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpUpgradeHandler; -import javax.servlet.http.WebConnection; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -68,13 +66,4 @@ public void setHandler(IHTTPSocketHandler handler) { this.handler = handler; } - private static class DummyUpgradeHandler implements HttpUpgradeHandler { - public void init(WebConnection webConnection) { - - } - public void destroy() { - - } - } - } From 11d4a67477062b71ec0c0eb05755bc12d3bfcf26 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 7 Dec 2021 16:37:39 +0100 Subject: [PATCH 11/31] Added a check for websockets and switched back to jetty as the default implementation for until the own implementation is fully stable. --- pom.xml | 2 +- src/main/java/org/javawebstack/httpserver/HTTPServer.java | 5 ++++- .../javawebstack/httpserver/adapter/IHTTPSocketServer.java | 1 + .../httpserver/adapter/jetty/JettyHTTPSocketServer.java | 4 ++++ .../httpserver/adapter/simple/SimpleHTTPSocketServer.java | 4 ++++ 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1c47f17..c47b35c 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ org.eclipse.jetty jetty-server 11.0.7 - true + org.reflections diff --git a/src/main/java/org/javawebstack/httpserver/HTTPServer.java b/src/main/java/org/javawebstack/httpserver/HTTPServer.java index cc4931e..4868de8 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPServer.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPServer.java @@ -3,6 +3,7 @@ import org.javawebstack.abstractdata.AbstractMapper; import org.javawebstack.abstractdata.NamingPolicy; import org.javawebstack.httpserver.adapter.IHTTPSocketServer; +import org.javawebstack.httpserver.adapter.jetty.JettyHTTPSocketServer; import org.javawebstack.httpserver.adapter.simple.SimpleHTTPSocketServer; import org.javawebstack.httpserver.handler.*; import org.javawebstack.httpserver.router.DefaultRouteAutoInjector; @@ -45,7 +46,7 @@ public class HTTPServer implements RouteParamTransformerProvider { private Function, Object> controllerInitiator = this::defaultControllerInitiator; public HTTPServer() { - this(new SimpleHTTPSocketServer()); + this(new JettyHTTPSocketServer()); } public HTTPServer(IHTTPSocketServer server) { @@ -196,6 +197,8 @@ public HTTPServer afterAny(String pattern, AfterRequestHandler... handlers) { } public HTTPServer webSocket(String pattern, WebSocketHandler handler) { + if(!server.isWebSocketSupported()) + throw new UnsupportedOperationException(server.getClass().getName() + " does not support websockets!"); return route(HTTPMethod.WEBSOCKET, pattern, new InternalWebSocketRequestHandler(handler)); } diff --git a/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java index 1679146..08b88e7 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java @@ -10,5 +10,6 @@ public interface IHTTPSocketServer { void stop(); void join(); void setHandler(IHTTPSocketHandler handler); + boolean isWebSocketSupported(); } diff --git a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java index 3a4f0b1..ed0ae8d 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java @@ -66,4 +66,8 @@ public void setHandler(IHTTPSocketHandler handler) { this.handler = handler; } + public boolean isWebSocketSupported() { + return false; + } + } diff --git a/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java index 91aaeef..7b64f6a 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java @@ -60,4 +60,8 @@ public void stop() { } catch (IOException e) {} } + public boolean isWebSocketSupported() { + return false; + } + } From fe7c3fb65393ab744eea97ca583ae4d2db418917 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 7 Dec 2021 17:45:12 +0100 Subject: [PATCH 12/31] Quick fix for ci --- .github/workflows/maven-deploy.yml | 4 ++-- pom.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/maven-deploy.yml b/.github/workflows/maven-deploy.yml index c6ac3fb..3bf132b 100644 --- a/.github/workflows/maven-deploy.yml +++ b/.github/workflows/maven-deploy.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 1.11 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 1.11 - name: Build run: mvn -B package --file pom.xml - name: Install GPG Key diff --git a/pom.xml b/pom.xml index c47b35c..e311e65 100644 --- a/pom.xml +++ b/pom.xml @@ -5,8 +5,8 @@ 4.0.0 - 8 - 8 + 11 + 11 1.0.2-SNAPSHOT From b65bfa3a8b4de8310cb3959dea9bd9af75e9adc8 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 7 Dec 2021 20:58:42 +0100 Subject: [PATCH 13/31] Quick fix for websocket support in SimpleHTTPSocketServer --- .../httpserver/adapter/simple/SimpleHTTPSocketServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java index 7b64f6a..833b951 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java @@ -61,7 +61,7 @@ public void stop() { } public boolean isWebSocketSupported() { - return false; + return true; } } From 0c5b8f45f6c013c95de6778b107df37dd1bcf7ca Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 14 Dec 2021 16:26:44 +0100 Subject: [PATCH 14/31] Implemented Undertow --- pom.xml | 5 + .../javawebstack/httpserver/HTTPServer.java | 1 - .../adapter/untertow/UndertowHTTPSocket.java | 112 ++++++++++++++++++ .../untertow/UndertowHTTPSocketServer.java | 49 ++++++++ 4 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocketServer.java diff --git a/pom.xml b/pom.xml index e311e65..90ee51f 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,11 @@ junit-jupiter-api 5.8.2 + + io.undertow + undertow-core + 2.2.13.Final + org.junit.jupiter junit-jupiter-engine diff --git a/src/main/java/org/javawebstack/httpserver/HTTPServer.java b/src/main/java/org/javawebstack/httpserver/HTTPServer.java index 4868de8..57125cc 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPServer.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPServer.java @@ -4,7 +4,6 @@ import org.javawebstack.abstractdata.NamingPolicy; import org.javawebstack.httpserver.adapter.IHTTPSocketServer; import org.javawebstack.httpserver.adapter.jetty.JettyHTTPSocketServer; -import org.javawebstack.httpserver.adapter.simple.SimpleHTTPSocketServer; import org.javawebstack.httpserver.handler.*; import org.javawebstack.httpserver.router.DefaultRouteAutoInjector; import org.javawebstack.httpserver.router.Route; diff --git a/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java new file mode 100644 index 0000000..8f5b0a5 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java @@ -0,0 +1,112 @@ +package org.javawebstack.httpserver.adapter.untertow; + +import io.undertow.server.HttpServerExchange; +import io.undertow.util.HeaderValues; +import io.undertow.util.HttpString; +import org.javawebstack.httpserver.HTTPMethod; +import org.javawebstack.httpserver.HTTPStatus; +import org.javawebstack.httpserver.adapter.IHTTPSocket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.*; +import java.util.stream.Collectors; + +public class UndertowHTTPSocket implements IHTTPSocket { + + private final HttpServerExchange exchange; + + public UndertowHTTPSocket(HttpServerExchange exchange) { + this.exchange = exchange; + } + + public InputStream getInputStream() throws IOException { + return exchange.getInputStream(); + } + + public OutputStream getOutputStream() throws IOException { + return exchange.getOutputStream(); + } + + public void close() throws IOException { + exchange.endExchange(); + } + + public boolean isClosed() { + return exchange.isComplete(); + } + + public IHTTPSocket setResponseStatus(int status, String message) { + exchange.setStatusCode(status); + return this; + } + + public IHTTPSocket setResponseHeader(String name, String value) { + exchange.getResponseHeaders().put(new HttpString(name), value); + return this; + } + + public IHTTPSocket addResponseHeader(String name, String value) { + exchange.getResponseHeaders().add(new HttpString(name), value); + return this; + } + + public HTTPMethod getRequestMethod() { + return HTTPMethod.valueOf(exchange.getRequestMethod().toString()); + } + + public String getRequestPath() { + return exchange.getRequestPath(); + } + + public String getRequestQuery() { + return exchange.getQueryString(); + } + + public String getRequestVersion() { + return exchange.getProtocol().toString(); + } + + public Set getRequestHeaderNames() { + return exchange.getRequestHeaders().getHeaderNames().stream().map(HttpString::toString).collect(Collectors.toSet()); + } + + public String getRequestHeader(String name) { + HeaderValues values = exchange.getRequestHeaders().get(name); + if(values == null) + return null; + return values.getFirst(); + } + + public List getRequestHeaders(String name) { + HeaderValues values = exchange.getRequestHeaders().get(name); + if(values == null) + return Collections.emptyList(); + return new ArrayList<>(values); + } + + public int getResponseStatus() { + return exchange.getStatusCode(); + } + + public String getResponseStatusMessage() { + HTTPStatus status = HTTPStatus.byStatus(getResponseStatus()); + if(status == null) + return null; + return status.getMessage(); + } + + public void writeHeaders() throws IOException { + exchange.getResponseSender().send(""); + } + + public String getRemoteAddress() { + return exchange.getSourceAddress().getAddress().getHostAddress(); + } + + public HttpServerExchange getExchange() { + return exchange; + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocketServer.java new file mode 100644 index 0000000..2ef474b --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocketServer.java @@ -0,0 +1,49 @@ +package org.javawebstack.httpserver.adapter.untertow; + +import io.undertow.Undertow; +import org.javawebstack.httpserver.adapter.IHTTPSocketHandler; +import org.javawebstack.httpserver.adapter.IHTTPSocketServer; + +import java.io.IOException; + +public class UndertowHTTPSocketServer implements IHTTPSocketServer { + + private int port = 80; + private Undertow server; + private IHTTPSocketHandler handler; + + public void setPort(int port) { + this.port = port; + } + + public int getPort() { + return port; + } + + public void start() throws IOException { + server = Undertow.builder() + .addHttpListener(port, "0.0.0.0") + .setHandler(httpServerExchange -> handler.handle(new UndertowHTTPSocket(httpServerExchange))) + .build(); + server.start(); + } + + public void stop() { + server.stop(); + } + + public void join() { + try { + server.getWorker().awaitTermination(); + } catch (InterruptedException e) {} + } + + public void setHandler(IHTTPSocketHandler handler) { + this.handler = handler; + } + + public boolean isWebSocketSupported() { + return false; + } + +} From 5fb52aca9e3dd6b7fd624ff5621d910c3febb4b6 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 14 Dec 2021 16:57:03 +0100 Subject: [PATCH 15/31] Implemented Undertow --- .../httpserver/adapter/untertow/UndertowHTTPSocket.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java index 8f5b0a5..2fdace6 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java @@ -1,5 +1,6 @@ package org.javawebstack.httpserver.adapter.untertow; +import io.undertow.server.BlockingHttpExchange; import io.undertow.server.HttpServerExchange; import io.undertow.util.HeaderValues; import io.undertow.util.HttpString; @@ -16,9 +17,11 @@ public class UndertowHTTPSocket implements IHTTPSocket { private final HttpServerExchange exchange; + private final BlockingHttpExchange blockingExchange; public UndertowHTTPSocket(HttpServerExchange exchange) { this.exchange = exchange; + this.blockingExchange = exchange.startBlocking(); } public InputStream getInputStream() throws IOException { @@ -30,6 +33,7 @@ public OutputStream getOutputStream() throws IOException { } public void close() throws IOException { + blockingExchange.close(); exchange.endExchange(); } From a576cd0fbc184b204c0d9a0ff66c23cc87388006 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Wed, 15 Dec 2021 00:35:34 +0100 Subject: [PATCH 16/31] Fixed Undertow and added websocket support for it --- .../simple/SimpleHTTPSocketServer.java | 13 +++- .../undertow/StreamSinkOutputStream.java | 29 +++++++ .../undertow/StreamSourceInputStream.java | 29 +++++++ .../UndertowHTTPSocket.java | 24 +++--- .../undertow/UndertowHTTPSocketServer.java | 75 +++++++++++++++++++ .../untertow/UndertowHTTPSocketServer.java | 49 ------------ .../util/websocket/WebSocketFrame.java | 18 ++--- .../InternalWebSocketRequestHandler.java | 2 - 8 files changed, 168 insertions(+), 71 deletions(-) create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSinkOutputStream.java create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSourceInputStream.java rename src/main/java/org/javawebstack/httpserver/adapter/{untertow => undertow}/UndertowHTTPSocket.java (80%) create mode 100644 src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocketServer.java delete mode 100644 src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocketServer.java diff --git a/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java index 833b951..af579a6 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java @@ -2,6 +2,7 @@ import org.javawebstack.httpserver.adapter.IHTTPSocketHandler; import org.javawebstack.httpserver.adapter.IHTTPSocketServer; +import org.javawebstack.httpserver.util.websocket.WebSocketUtil; import java.io.IOException; import java.net.ServerSocket; @@ -23,7 +24,17 @@ public SimpleHTTPSocketServer() { try { Socket socket = serverSocket.accept(); SimpleHTTPSocket httpSocket = new SimpleHTTPSocket(socket); - executorService.execute(() -> handler.handle(httpSocket)); + executorService.execute(() -> { + if(httpSocket.getRequestHeaderNames().contains("sec-websocket-key")) { + try { + if(!WebSocketUtil.accept(httpSocket, null)) + return; + } catch (IOException e) { + return; + } + } + handler.handle(httpSocket); + }); } catch (IOException exception) {} } }); diff --git a/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSinkOutputStream.java b/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSinkOutputStream.java new file mode 100644 index 0000000..56fcb2b --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSinkOutputStream.java @@ -0,0 +1,29 @@ +package org.javawebstack.httpserver.adapter.undertow; + +import org.xnio.channels.StreamSinkChannel; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +public class StreamSinkOutputStream extends OutputStream { + + private final StreamSinkChannel sink; + private final ByteBuffer byteBuffer = ByteBuffer.allocate(1); + + public StreamSinkOutputStream(StreamSinkChannel sink) { + this.sink = sink; + } + + public void write(int i) throws IOException { + byteBuffer.position(0); + byteBuffer.put((byte) i); + byteBuffer.position(0); + sink.write(byteBuffer); + } + + public void close() throws IOException { + sink.close(); + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSourceInputStream.java b/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSourceInputStream.java new file mode 100644 index 0000000..3ed1416 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSourceInputStream.java @@ -0,0 +1,29 @@ +package org.javawebstack.httpserver.adapter.undertow; + +import org.xnio.channels.StreamSourceChannel; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +public class StreamSourceInputStream extends InputStream { + + private final StreamSourceChannel source; + private final ByteBuffer byteBuffer = ByteBuffer.allocate(1); + + public StreamSourceInputStream(StreamSourceChannel source) { + this.source = source; + } + + public synchronized int read() throws IOException { + byteBuffer.position(0); + int r; + while ((r = source.read(byteBuffer)) == 0) + Thread.yield(); + if(r == -1) + return -1; + int b = byteBuffer.position(0).get(); + return b; + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java similarity index 80% rename from src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java rename to src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java index 2fdace6..017f41b 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocket.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java @@ -1,6 +1,5 @@ -package org.javawebstack.httpserver.adapter.untertow; +package org.javawebstack.httpserver.adapter.undertow; -import io.undertow.server.BlockingHttpExchange; import io.undertow.server.HttpServerExchange; import io.undertow.util.HeaderValues; import io.undertow.util.HttpString; @@ -17,24 +16,29 @@ public class UndertowHTTPSocket implements IHTTPSocket { private final HttpServerExchange exchange; - private final BlockingHttpExchange blockingExchange; + private final InputStream inputStream; + private final OutputStream outputStream; + private boolean closed; - public UndertowHTTPSocket(HttpServerExchange exchange) { + public UndertowHTTPSocket(HttpServerExchange exchange, InputStream inputStream, OutputStream outputStream) { this.exchange = exchange; - this.blockingExchange = exchange.startBlocking(); + this.inputStream = inputStream == null ? exchange.getInputStream() : inputStream; + this.outputStream = outputStream == null ? exchange.getOutputStream() : outputStream; } public InputStream getInputStream() throws IOException { - return exchange.getInputStream(); + return inputStream; } public OutputStream getOutputStream() throws IOException { - return exchange.getOutputStream(); + return outputStream; } public void close() throws IOException { - blockingExchange.close(); - exchange.endExchange(); + if(closed) + return; + closed = true; + exchange.getOutputStream().close(); } public boolean isClosed() { @@ -102,7 +106,7 @@ public String getResponseStatusMessage() { } public void writeHeaders() throws IOException { - exchange.getResponseSender().send(""); + exchange.getOutputStream().write(new byte[0]); } public String getRemoteAddress() { diff --git a/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocketServer.java new file mode 100644 index 0000000..5d26676 --- /dev/null +++ b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocketServer.java @@ -0,0 +1,75 @@ +package org.javawebstack.httpserver.adapter.undertow; + +import io.undertow.Undertow; +import io.undertow.server.handlers.BlockingHandler; +import io.undertow.websockets.core.WebSocketVersion; +import org.javawebstack.httpserver.adapter.IHTTPSocketHandler; +import org.javawebstack.httpserver.adapter.IHTTPSocketServer; +import org.javawebstack.httpserver.util.websocket.WebSocketUtil; +import org.xnio.Options; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class UndertowHTTPSocketServer implements IHTTPSocketServer { + + private int port = 80; + private Undertow server; + private IHTTPSocketHandler handler; + private ExecutorService executorService; + + public void setPort(int port) { + this.port = port; + } + + public int getPort() { + return port; + } + + public void start() throws IOException { + executorService = Executors.newCachedThreadPool(); + server = Undertow.builder() + .addHttpListener(port, "0.0.0.0") + .setServerOption(Options.KEEP_ALIVE, true) + .setHandler(new BlockingHandler(httpServerExchange -> { + if(httpServerExchange.getRequestHeaders().contains("sec-websocket-key")) { + httpServerExchange.upgradeChannel((streamConnection, httpServerExchange1) -> { + InputStream inputStream = new StreamSourceInputStream(streamConnection.getSourceChannel()); + OutputStream outputStream = new StreamSinkOutputStream(streamConnection.getSinkChannel()); + handler.handle(new UndertowHTTPSocket(httpServerExchange1, inputStream, outputStream)); + }); + httpServerExchange.putAttachment(WebSocketVersion.ATTACHMENT_KEY, WebSocketVersion.V13); + if(!WebSocketUtil.accept(new UndertowHTTPSocket(httpServerExchange, null, null), null)) + return; + httpServerExchange.endExchange(); + } else { + handler.handle(new UndertowHTTPSocket(httpServerExchange, null, null)); + } + })) + .build(); + server.start(); + } + + public void stop() { + executorService.shutdown(); + server.stop(); + } + + public void join() { + try { + server.getWorker().awaitTermination(); + } catch (InterruptedException e) {} + } + + public void setHandler(IHTTPSocketHandler handler) { + this.handler = handler; + } + + public boolean isWebSocketSupported() { + return true; + } + +} diff --git a/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocketServer.java deleted file mode 100644 index 2ef474b..0000000 --- a/src/main/java/org/javawebstack/httpserver/adapter/untertow/UndertowHTTPSocketServer.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.javawebstack.httpserver.adapter.untertow; - -import io.undertow.Undertow; -import org.javawebstack.httpserver.adapter.IHTTPSocketHandler; -import org.javawebstack.httpserver.adapter.IHTTPSocketServer; - -import java.io.IOException; - -public class UndertowHTTPSocketServer implements IHTTPSocketServer { - - private int port = 80; - private Undertow server; - private IHTTPSocketHandler handler; - - public void setPort(int port) { - this.port = port; - } - - public int getPort() { - return port; - } - - public void start() throws IOException { - server = Undertow.builder() - .addHttpListener(port, "0.0.0.0") - .setHandler(httpServerExchange -> handler.handle(new UndertowHTTPSocket(httpServerExchange))) - .build(); - server.start(); - } - - public void stop() { - server.stop(); - } - - public void join() { - try { - server.getWorker().awaitTermination(); - } catch (InterruptedException e) {} - } - - public void setHandler(IHTTPSocketHandler handler) { - this.handler = handler; - } - - public boolean isWebSocketSupported() { - return false; - } - -} diff --git a/src/main/java/org/javawebstack/httpserver/util/websocket/WebSocketFrame.java b/src/main/java/org/javawebstack/httpserver/util/websocket/WebSocketFrame.java index 9c54c14..c0948d4 100644 --- a/src/main/java/org/javawebstack/httpserver/util/websocket/WebSocketFrame.java +++ b/src/main/java/org/javawebstack/httpserver/util/websocket/WebSocketFrame.java @@ -98,11 +98,11 @@ public void write(OutputStream stream) throws IOException { public static WebSocketFrame read(InputStream stream) throws IOException { WebSocketFrame frame = new WebSocketFrame(); - int b = safeRead(stream); + byte b = safeRead(stream); frame.flags = (byte) (b & 0xF0); frame.opcode = (byte) (b & 0x0F); b = safeRead(stream); - frame.maskKey = (b >> 7) == 1 ? new byte[4] : null; + frame.maskKey = ((b & 0xFF) >> 7) == 1 ? new byte[4] : null; int len = b & 0b0111_1111; if(len == 126) { len = safeRead(stream) << 8; @@ -114,10 +114,10 @@ public static WebSocketFrame read(InputStream stream) throws IOException { len |= safeRead(stream); } if(frame.maskKey != null) { - frame.maskKey[0] = (byte) safeRead(stream); - frame.maskKey[1] = (byte) safeRead(stream); - frame.maskKey[2] = (byte) safeRead(stream); - frame.maskKey[3] = (byte) safeRead(stream); + frame.maskKey[0] = safeRead(stream); + frame.maskKey[1] = safeRead(stream); + frame.maskKey[2] = safeRead(stream); + frame.maskKey[3] = safeRead(stream); } frame.payload = new byte[len]; if(frame.maskKey != null) { @@ -125,16 +125,16 @@ public static WebSocketFrame read(InputStream stream) throws IOException { frame.payload[i] = (byte) (safeRead(stream) ^ frame.maskKey[i % 4]); } else { for(int i=0; i Date: Wed, 15 Dec 2021 09:01:38 +0100 Subject: [PATCH 17/31] Removed jetty and downgraded back to Java 8 --- pom.xml | 10 +- .../javawebstack/httpserver/HTTPServer.java | 4 +- .../adapter/jetty/JettyHTTPSocket.java | 113 ------------------ .../adapter/jetty/JettyHTTPSocketServer.java | 73 ----------- 4 files changed, 4 insertions(+), 196 deletions(-) delete mode 100644 src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java delete mode 100644 src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java diff --git a/pom.xml b/pom.xml index 90ee51f..648580c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,8 +5,8 @@ 4.0.0 - 11 - 11 + 8 + 8 1.0.2-SNAPSHOT @@ -52,12 +52,6 @@ validator 1.0.0 - - org.eclipse.jetty - jetty-server - 11.0.7 - - org.reflections reflections diff --git a/src/main/java/org/javawebstack/httpserver/HTTPServer.java b/src/main/java/org/javawebstack/httpserver/HTTPServer.java index 57125cc..ed8d96a 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPServer.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPServer.java @@ -3,7 +3,7 @@ import org.javawebstack.abstractdata.AbstractMapper; import org.javawebstack.abstractdata.NamingPolicy; import org.javawebstack.httpserver.adapter.IHTTPSocketServer; -import org.javawebstack.httpserver.adapter.jetty.JettyHTTPSocketServer; +import org.javawebstack.httpserver.adapter.undertow.UndertowHTTPSocketServer; import org.javawebstack.httpserver.handler.*; import org.javawebstack.httpserver.router.DefaultRouteAutoInjector; import org.javawebstack.httpserver.router.Route; @@ -45,7 +45,7 @@ public class HTTPServer implements RouteParamTransformerProvider { private Function, Object> controllerInitiator = this::defaultControllerInitiator; public HTTPServer() { - this(new JettyHTTPSocketServer()); + this(new UndertowHTTPSocketServer()); } public HTTPServer(IHTTPSocketServer server) { diff --git a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java deleted file mode 100644 index 7cc94dd..0000000 --- a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocket.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.javawebstack.httpserver.adapter.jetty; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.javawebstack.httpserver.HTTPMethod; -import org.javawebstack.httpserver.HTTPStatus; -import org.javawebstack.httpserver.adapter.IHTTPSocket; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class JettyHTTPSocket implements IHTTPSocket { - - private final HttpServletRequest request; - private final HttpServletResponse response; - private boolean closed; - - public JettyHTTPSocket(HttpServletRequest request, HttpServletResponse response) { - this.request = request; - this.response = response; - } - - public HttpServletRequest rawRequest() { - return request; - } - - public HttpServletResponse rawResponse() { - return response; - } - - public InputStream getInputStream() throws IOException { - return request.getInputStream(); - } - - public OutputStream getOutputStream() throws IOException { - return response.getOutputStream(); - } - - public void close() throws IOException { - closed = true; - response.getOutputStream().close(); - } - - public boolean isClosed() { - return closed; - } - - public IHTTPSocket setResponseStatus(int status, String message) { - response.setStatus(status, message); - return this; - } - - public IHTTPSocket setResponseHeader(String name, String value) { - response.setHeader(name, value); - return this; - } - - public IHTTPSocket addResponseHeader(String name, String value) { - response.addHeader(name, value); - return this; - } - - public HTTPMethod getRequestMethod() { - return HTTPMethod.valueOf(request.getMethod()); - } - - public String getRequestPath() { - return request.getPathInfo(); - } - - public String getRequestQuery() { - return request.getQueryString(); - } - - public String getRequestVersion() { - return "HTTP/1.1"; - } - - public Set getRequestHeaderNames() { - return new HashSet<>(Collections.list(request.getHeaderNames())); - } - - public String getRequestHeader(String name) { - return request.getHeader(name); - } - - public List getRequestHeaders(String name) { - return Collections.list(request.getHeaders(name)); - } - - public int getResponseStatus() { - return response.getStatus(); - } - - public String getResponseStatusMessage() { - HTTPStatus status = HTTPStatus.byStatus(response.getStatus()); - return status == null ? null : status.getMessage(); - } - - public void writeHeaders() throws IOException { - response.flushBuffer(); - } - - public String getRemoteAddress() { - return request.getRemoteAddr(); - } - -} diff --git a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java deleted file mode 100644 index ed0ae8d..0000000 --- a/src/main/java/org/javawebstack/httpserver/adapter/jetty/JettyHTTPSocketServer.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.javawebstack.httpserver.adapter.jetty; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.server.handler.ErrorHandler; -import org.javawebstack.httpserver.adapter.IHTTPSocketHandler; -import org.javawebstack.httpserver.adapter.IHTTPSocketServer; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -public class JettyHTTPSocketServer implements IHTTPSocketServer { - - private Server server; - private IHTTPSocketHandler handler; - private int port; - - public void setPort(int port) { - this.port = port; - } - - public int getPort() { - return port; - } - - public void start() throws IOException { - server = new Server(port); - server.setHandler(new AbstractHandler() { - public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException { - if("websocket".equals(httpServletRequest.getHeader("Upgrade"))) { - httpServletResponse.setStatus(400); - httpServletResponse.getOutputStream().write("Websockets are not supported by this server!".getBytes(StandardCharsets.UTF_8)); - httpServletResponse.getOutputStream().close(); - return; - } - handler.handle(new JettyHTTPSocket(httpServletRequest, httpServletResponse)); - } - }); - server.setErrorHandler(new ErrorHandler()); - try { - server.start(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void stop() { - try { - server.stop(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void join() { - try { - server.join(); - } catch (InterruptedException e) {} - } - - public void setHandler(IHTTPSocketHandler handler) { - this.handler = handler; - } - - public boolean isWebSocketSupported() { - return false; - } - -} From 19c936a9c06c1feb8e3c1e24bbcb8d91f1cebe83 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Wed, 15 Dec 2021 09:39:48 +0100 Subject: [PATCH 18/31] Upgraded to latest Undertow --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 648580c..b7e332a 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ io.undertow undertow-core - 2.2.13.Final + 2.2.14.Final org.junit.jupiter From 30409cb715e633873c88ab87970171cb266b423f Mon Sep 17 00:00:00 2001 From: JanHolger Date: Tue, 11 Jan 2022 18:12:22 +0100 Subject: [PATCH 19/31] Added an option to set the maximum worker threads --- .../java/org/javawebstack/httpserver/HTTPServer.java | 5 +++++ .../httpserver/adapter/IHTTPSocketServer.java | 1 + .../adapter/simple/SimpleHTTPSocketServer.java | 10 +++++++--- .../adapter/undertow/UndertowHTTPSocketServer.java | 11 ++++++++--- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/javawebstack/httpserver/HTTPServer.java b/src/main/java/org/javawebstack/httpserver/HTTPServer.java index ed8d96a..204b58d 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPServer.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPServer.java @@ -54,6 +54,11 @@ public HTTPServer(IHTTPSocketServer server) { routeAutoInjectors.add(DefaultRouteAutoInjector.INSTANCE); } + public HTTPServer maxThreads(int maxThreads) { + this.server.setMaxThreads(maxThreads); + return this; + } + public HTTPServer logger(Logger logger) { this.logger = logger; return this; diff --git a/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java index 08b88e7..28dd1dd 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/IHTTPSocketServer.java @@ -6,6 +6,7 @@ public interface IHTTPSocketServer { void setPort(int port); int getPort(); + void setMaxThreads(int maxThreads); void start() throws IOException; void stop(); void join(); diff --git a/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java index af579a6..1341629 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/simple/SimpleHTTPSocketServer.java @@ -7,8 +7,7 @@ import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.*; public class SimpleHTTPSocketServer implements IHTTPSocketServer { @@ -16,6 +15,7 @@ public class SimpleHTTPSocketServer implements IHTTPSocketServer { private ExecutorService executorService; private ServerSocket serverSocket; private int port = 80; + private int maxThreads = 64; private IHTTPSocketHandler handler; public SimpleHTTPSocketServer() { @@ -52,9 +52,13 @@ public void setHandler(IHTTPSocketHandler handler) { this.handler = handler; } + public void setMaxThreads(int maxThreads) { + this.maxThreads = maxThreads; + } + public void start() throws IOException { this.serverSocket = new ServerSocket(port); - this.executorService = Executors.newCachedThreadPool(); + this.executorService = new ThreadPoolExecutor(1, maxThreads, 60L, TimeUnit.SECONDS, new SynchronousQueue()); this.schedulerThread.start(); } diff --git a/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocketServer.java b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocketServer.java index 5d26676..178d0d2 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocketServer.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocketServer.java @@ -11,12 +11,12 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.*; public class UndertowHTTPSocketServer implements IHTTPSocketServer { private int port = 80; + private int maxThreads = 64; private Undertow server; private IHTTPSocketHandler handler; private ExecutorService executorService; @@ -30,11 +30,12 @@ public int getPort() { } public void start() throws IOException { - executorService = Executors.newCachedThreadPool(); + executorService = new ThreadPoolExecutor(1, maxThreads, 60L, TimeUnit.SECONDS, new SynchronousQueue()); server = Undertow.builder() .addHttpListener(port, "0.0.0.0") .setServerOption(Options.KEEP_ALIVE, true) .setHandler(new BlockingHandler(httpServerExchange -> { + httpServerExchange.setDispatchExecutor(executorService); if(httpServerExchange.getRequestHeaders().contains("sec-websocket-key")) { httpServerExchange.upgradeChannel((streamConnection, httpServerExchange1) -> { InputStream inputStream = new StreamSourceInputStream(streamConnection.getSourceChannel()); @@ -68,6 +69,10 @@ public void setHandler(IHTTPSocketHandler handler) { this.handler = handler; } + public void setMaxThreads(int maxThreads) { + this.maxThreads = maxThreads; + } + public boolean isWebSocketSupported() { return true; } From b0931593461884eb4e4180d4143da092c16a9d00 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Thu, 10 Feb 2022 19:13:44 +0100 Subject: [PATCH 20/31] Added a fix for setResponseStatus when the response is already started --- .../httpserver/adapter/undertow/UndertowHTTPSocket.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java index 017f41b..b6a110b 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java @@ -46,7 +46,8 @@ public boolean isClosed() { } public IHTTPSocket setResponseStatus(int status, String message) { - exchange.setStatusCode(status); + if(!exchange.isResponseStarted()) + exchange.setStatusCode(status); return this; } From e41b70035bc26512e7f424a31b93f34e5785524a Mon Sep 17 00:00:00 2001 From: JanHolger Date: Thu, 10 Feb 2022 19:35:45 +0100 Subject: [PATCH 21/31] Tried to fix empty request body in Undertow --- .../adapter/undertow/UndertowHTTPSocket.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java index b6a110b..5701542 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java @@ -22,7 +22,17 @@ public class UndertowHTTPSocket implements IHTTPSocket { public UndertowHTTPSocket(HttpServerExchange exchange, InputStream inputStream, OutputStream outputStream) { this.exchange = exchange; - this.inputStream = inputStream == null ? exchange.getInputStream() : inputStream; + InputStream is = inputStream == null ? exchange.getInputStream() : inputStream; + this.inputStream = new InputStream() { + public int read() throws IOException { + int b = is.read(); + while (b == -1 && !exchange.isRequestComplete()) { + Thread.yield(); + b = is.read(); + } + return b; + } + }; this.outputStream = outputStream == null ? exchange.getOutputStream() : outputStream; } From 4dbf69a551ae6b37ee07f4ba3558710b400904e1 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Thu, 10 Feb 2022 19:58:00 +0100 Subject: [PATCH 22/31] Tried to fix empty request body in Undertow --- .../adapter/undertow/UndertowHTTPSocket.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java index 5701542..356b513 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/undertow/UndertowHTTPSocket.java @@ -25,12 +25,13 @@ public UndertowHTTPSocket(HttpServerExchange exchange, InputStream inputStream, InputStream is = inputStream == null ? exchange.getInputStream() : inputStream; this.inputStream = new InputStream() { public int read() throws IOException { - int b = is.read(); - while (b == -1 && !exchange.isRequestComplete()) { - Thread.yield(); - b = is.read(); - } - return b; + return is.read(); + } + public int available() throws IOException { + int a = is.available(); + if(a == 0 && !exchange.isRequestComplete()) + a = 1; + return a; } }; this.outputStream = outputStream == null ? exchange.getOutputStream() : outputStream; From f2db13c410aae0b50d58b28d3ad1d8bab687fd03 Mon Sep 17 00:00:00 2001 From: Jake Esser Date: Sat, 16 Apr 2022 23:35:54 +0200 Subject: [PATCH 23/31] Added recursive method resolution of controllers --- .../org/javawebstack/httpserver/router/RouteBinder.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java b/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java index efe7f9d..226f384 100644 --- a/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java +++ b/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java @@ -42,7 +42,7 @@ public Bind(HTTPMethod method, String path) { } } Map websocketHandlers = new HashMap<>(); - for (Method method : controller.getClass().getDeclaredMethods()) { + for (Method method : getMethodsRecursive(controller.getClass())) { List binds = new ArrayList<>(); With methodWith = getAnnotations(With.class, method).stream().findFirst().orElse(null); List middlewares = new ArrayList<>(); @@ -183,6 +183,13 @@ private static T getAnnotation(Class type, Method meth return annotations.length == 0 ? null : annotations[0]; } + private static List getMethodsRecursive(Class type) { + List methods = new ArrayList<>(Arrays.asList(type.getDeclaredMethods())); + if (type.getSuperclass() != null) + methods.addAll(getMethodsRecursive(type.getSuperclass())); + return methods; + } + private static class BindMapper { private final HTTPServer server; From 1538312e09a94ea388d8d1422a5640950d96647f Mon Sep 17 00:00:00 2001 From: Jake Esser Date: Sat, 16 Apr 2022 23:40:34 +0200 Subject: [PATCH 24/31] Filter Object parentClass --- .../java/org/javawebstack/httpserver/router/RouteBinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java b/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java index 226f384..e8eb2eb 100644 --- a/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java +++ b/src/main/java/org/javawebstack/httpserver/router/RouteBinder.java @@ -185,7 +185,7 @@ private static T getAnnotation(Class type, Method meth private static List getMethodsRecursive(Class type) { List methods = new ArrayList<>(Arrays.asList(type.getDeclaredMethods())); - if (type.getSuperclass() != null) + if (type.getSuperclass() != null && type.getSuperclass() != Object.class) methods.addAll(getMethodsRecursive(type.getSuperclass())); return methods; } From b56577d81b8d180950365ef1ba15bb2b821cb450 Mon Sep 17 00:00:00 2001 From: Jan Bebendorf Date: Mon, 2 May 2022 11:47:46 +0200 Subject: [PATCH 25/31] Update maven-deploy.yml --- .github/workflows/maven-deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven-deploy.yml b/.github/workflows/maven-deploy.yml index 3bf132b..c6ac3fb 100644 --- a/.github/workflows/maven-deploy.yml +++ b/.github/workflows/maven-deploy.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.11 + - name: Set up JDK 1.8 uses: actions/setup-java@v1 with: - java-version: 1.11 + java-version: 1.8 - name: Build run: mvn -B package --file pom.xml - name: Install GPG Key From a67492a70498ade2cb95bc39dfa9ec3df016aea9 Mon Sep 17 00:00:00 2001 From: Jan Bebendorf Date: Mon, 2 May 2022 11:57:44 +0200 Subject: [PATCH 26/31] Update pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b7e332a..663932d 100644 --- a/pom.xml +++ b/pom.xml @@ -50,7 +50,7 @@ org.javawebstack validator - 1.0.0 + 1.0.1 org.reflections From 4b73981a19f6dea4ea7c2ff544b3e593f51e401b Mon Sep 17 00:00:00 2001 From: Jake Esser Date: Tue, 7 Jun 2022 20:29:10 +0200 Subject: [PATCH 27/31] Added _method support for forms --- pom.xml | 9 +++++- .../org/javawebstack/httpserver/Exchange.java | 30 ++++++++++++++----- .../javawebstack/httpserver/HTTPServer.java | 9 ++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 663932d..d1083ce 100644 --- a/pom.xml +++ b/pom.xml @@ -46,11 +46,18 @@ https://github.com/JavaWebStack/http-server/tree/master + + + ossrh + https://s01.oss.sonatype.org/content/repositories/snapshots + + + org.javawebstack validator - 1.0.1 + 1.0.1-SNAPSHOT org.reflections diff --git a/src/main/java/org/javawebstack/httpserver/Exchange.java b/src/main/java/org/javawebstack/httpserver/Exchange.java index 2b717e3..246cba7 100644 --- a/src/main/java/org/javawebstack/httpserver/Exchange.java +++ b/src/main/java/org/javawebstack/httpserver/Exchange.java @@ -11,6 +11,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.stream.Collectors; @@ -35,7 +36,7 @@ public static Exchange current() { public Exchange(HTTPServer server, IHTTPSocket socket) { this.server = server; this.socket = socket; - this.method = "websocket".equalsIgnoreCase(socket.getRequestHeader("upgrade")) ? HTTPMethod.WEBSOCKET : socket.getRequestMethod(); + this.method = getRequestMethodFromSocket(socket); this.queryParameters = AbstractElement.fromFormData(socket.getRequestQuery()).object(); } @@ -52,13 +53,7 @@ public T body(Class clazz) { if (body.length() == 0) body = "{}"; - String contentType = getContentType().toLowerCase(); - - if (contentType.contains(";")) { - contentType = contentType.split(";")[0].trim(); - } - - MimeType type = MimeType.byMimeType(contentType); + MimeType type = getMimeType(); if (type == null) type = MimeType.JSON; AbstractElement request = null; @@ -292,4 +287,23 @@ protected static AbstractElement getPathElement(AbstractElement source, String p return getPathElement(getPathElement(source, spl[0]), path.substring(spl[0].length() + 1)); } + private HTTPMethod getRequestMethodFromSocket(IHTTPSocket socket) { + if (socket.getRequestHeader("upgrade").equalsIgnoreCase("websocket")) + return HTTPMethod.WEBSOCKET; + if ((socket.getRequestMethod() == HTTPMethod.GET || socket.getRequestMethod() == HTTPMethod.POST) && getMimeType() == MimeType.X_WWW_FORM_URLENCODED) { + String rawMethodOverride = getBodyPathElement("_method").string(); + if (rawMethodOverride != null) + return HTTPMethod.valueOf(rawMethodOverride); + } + return socket.getRequestMethod(); + } + + public MimeType getMimeType() { + String contentType = getContentType().toLowerCase(); + if (contentType.contains(";")) { + contentType = contentType.split(";")[0].trim(); + } + + return MimeType.byMimeType(contentType); + } } diff --git a/src/main/java/org/javawebstack/httpserver/HTTPServer.java b/src/main/java/org/javawebstack/httpserver/HTTPServer.java index 204b58d..c3ee9b8 100644 --- a/src/main/java/org/javawebstack/httpserver/HTTPServer.java +++ b/src/main/java/org/javawebstack/httpserver/HTTPServer.java @@ -43,6 +43,7 @@ public class HTTPServer implements RouteParamTransformerProvider { private final Map beforeMiddleware = new HashMap<>(); private final Map afterMiddleware = new HashMap<>(); private Function, Object> controllerInitiator = this::defaultControllerInitiator; + private boolean formMethods = true; public HTTPServer() { this(new UndertowHTTPSocketServer()); @@ -415,4 +416,12 @@ public ExceptionHandler getExceptionHandler() { return exceptionHandler; } + public boolean isFormMethods() { + return formMethods; + } + + public HTTPServer disableFormMethods() { + formMethods = false; + return this; + } } From cae00df7e188788c0836ae5dbbccc2a6d05bef30 Mon Sep 17 00:00:00 2001 From: Jake Esser Date: Tue, 7 Jun 2022 20:41:41 +0200 Subject: [PATCH 28/31] Added _method override support --- src/main/java/org/javawebstack/httpserver/Exchange.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/javawebstack/httpserver/Exchange.java b/src/main/java/org/javawebstack/httpserver/Exchange.java index 246cba7..8b55dff 100644 --- a/src/main/java/org/javawebstack/httpserver/Exchange.java +++ b/src/main/java/org/javawebstack/httpserver/Exchange.java @@ -288,7 +288,7 @@ protected static AbstractElement getPathElement(AbstractElement source, String p } private HTTPMethod getRequestMethodFromSocket(IHTTPSocket socket) { - if (socket.getRequestHeader("upgrade").equalsIgnoreCase("websocket")) + if ("websocket".equalsIgnoreCase(socket.getRequestHeader("upgrade"))) return HTTPMethod.WEBSOCKET; if ((socket.getRequestMethod() == HTTPMethod.GET || socket.getRequestMethod() == HTTPMethod.POST) && getMimeType() == MimeType.X_WWW_FORM_URLENCODED) { String rawMethodOverride = getBodyPathElement("_method").string(); From 62f1e5676d28c8085ce9dda9ea4b407ca0a262d5 Mon Sep 17 00:00:00 2001 From: Jake Esser Date: Tue, 7 Jun 2022 20:42:58 +0200 Subject: [PATCH 29/31] Forgot isFormMethods check --- src/main/java/org/javawebstack/httpserver/Exchange.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/javawebstack/httpserver/Exchange.java b/src/main/java/org/javawebstack/httpserver/Exchange.java index 8b55dff..c29e568 100644 --- a/src/main/java/org/javawebstack/httpserver/Exchange.java +++ b/src/main/java/org/javawebstack/httpserver/Exchange.java @@ -290,7 +290,7 @@ protected static AbstractElement getPathElement(AbstractElement source, String p private HTTPMethod getRequestMethodFromSocket(IHTTPSocket socket) { if ("websocket".equalsIgnoreCase(socket.getRequestHeader("upgrade"))) return HTTPMethod.WEBSOCKET; - if ((socket.getRequestMethod() == HTTPMethod.GET || socket.getRequestMethod() == HTTPMethod.POST) && getMimeType() == MimeType.X_WWW_FORM_URLENCODED) { + if (server.isFormMethods() && (socket.getRequestMethod() == HTTPMethod.GET || socket.getRequestMethod() == HTTPMethod.POST) && getMimeType() == MimeType.X_WWW_FORM_URLENCODED) { String rawMethodOverride = getBodyPathElement("_method").string(); if (rawMethodOverride != null) return HTTPMethod.valueOf(rawMethodOverride); From 9efbf595a0e5b77d0eedf71561983fcfce7aea30 Mon Sep 17 00:00:00 2001 From: JanHolger Date: Fri, 7 Oct 2022 22:19:17 +0200 Subject: [PATCH 30/31] Upgraded validator to release version --- README.md | 2 +- pom.xml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index eee3858..b7d54a3 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,6 @@ work-in-progress project though so it's not yet complete. org.javawebstack http-server - 1.0.1 + 1.0.2 ``` \ No newline at end of file diff --git a/pom.xml b/pom.xml index d1083ce..ecab3e2 100644 --- a/pom.xml +++ b/pom.xml @@ -46,18 +46,11 @@ https://github.com/JavaWebStack/http-server/tree/master - - - ossrh - https://s01.oss.sonatype.org/content/repositories/snapshots - - - org.javawebstack validator - 1.0.1-SNAPSHOT + 1.0.1 org.reflections @@ -67,17 +60,24 @@ org.junit.jupiter junit-jupiter-api - 5.8.2 + 5.9.0 io.undertow undertow-core - 2.2.14.Final + 2.2.19.Final + + + + org.jboss.xnio + xnio-api + 3.8.8.Final + org.junit.jupiter junit-jupiter-engine - 5.8.2 + 5.9.0 test From 6d8e5b7012944775819e3da332bd2912c47791bb Mon Sep 17 00:00:00 2001 From: JanHolger Date: Fri, 7 Oct 2022 22:24:59 +0200 Subject: [PATCH 31/31] Fixed build --- .../httpserver/adapter/undertow/StreamSourceInputStream.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSourceInputStream.java b/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSourceInputStream.java index 3ed1416..79ffba5 100644 --- a/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSourceInputStream.java +++ b/src/main/java/org/javawebstack/httpserver/adapter/undertow/StreamSourceInputStream.java @@ -22,8 +22,8 @@ public synchronized int read() throws IOException { Thread.yield(); if(r == -1) return -1; - int b = byteBuffer.position(0).get(); - return b; + byteBuffer.position(0); + return byteBuffer.get(); } }