%s
- %d %s + %d %s-
+
-
%s
diff --git a/src/main/java/dev/plex/HTTPDModule.java b/src/main/java/dev/plex/HTTPDModule.java index 215bb57..70cd19a 100644 --- a/src/main/java/dev/plex/HTTPDModule.java +++ b/src/main/java/dev/plex/HTTPDModule.java @@ -19,6 +19,7 @@ import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletHandler; import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.server.*; +import org.eclipse.jetty.util.thread.QueuedThreadPool; import java.io.File; import java.util.EnumSet; @@ -66,7 +67,14 @@ public class HTTPDModule extends PlexModule serverThread = new Thread(() -> { - Server server = new Server(); + int maxThreads = moduleConfig.getInt("server.threads.max", 16); + int minThreads = Math.min(moduleConfig.getInt("server.threads.min", 2), maxThreads); + int idleTimeout = moduleConfig.getInt("server.threads.idle-timeout-ms", 30_000); + QueuedThreadPool pool = new QueuedThreadPool(maxThreads, minThreads, idleTimeout); + pool.setName("Plex-HTTPD"); + pool.setDaemon(true); + + Server server = new Server(pool); ServletHandler servletHandler = new ServletHandler(); context = new ServletContextHandler(ServletContextHandler.SESSIONS); @@ -74,10 +82,14 @@ public class HTTPDModule extends PlexModule context.setContextPath("/"); HttpConfiguration configuration = new HttpConfiguration(); configuration.addCustomizer(new ForwardedRequestCustomizer()); + configuration.setRequestHeaderSize(moduleConfig.getInt("server.limits.request-header-bytes", 8 * 1024)); + configuration.setSendServerVersion(false); HttpConnectionFactory factory = new HttpConnectionFactory(configuration); ServerConnector connector = new ServerConnector(server, factory); connector.setHost(moduleConfig.getString("server.bind-address")); connector.setPort(moduleConfig.getInt("server.port")); + connector.setIdleTimeout(moduleConfig.getLong("server.limits.idle-timeout-ms", 15_000L)); + connector.setAcceptQueueSize(moduleConfig.getInt("server.limits.accept-queue", 32)); context.addFilter(new FilterHolder(new RateLimitFilter()), "/*", EnumSet.of(DispatcherType.REQUEST)); diff --git a/src/main/java/dev/plex/request/AbstractServlet.java b/src/main/java/dev/plex/request/AbstractServlet.java index 508446c..4eb8643 100644 --- a/src/main/java/dev/plex/request/AbstractServlet.java +++ b/src/main/java/dev/plex/request/AbstractServlet.java @@ -16,6 +16,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.CharacterIterator; import java.text.StringCharacterIterator; @@ -153,7 +154,20 @@ public class AbstractServlet extends HttpServlet public static String signInPrompt(String action) { - return "You must sign in as staff " + action + "."; + return signInPrompt(null, action); + } + + public static String signInPrompt(HttpServletRequest request, String action) + { + String href = "/oauth2/login"; + if (request != null) + { + String path = getRequestPath(request); + String query = request.getQueryString(); + String returnTo = query == null || query.isEmpty() ? path : path + "?" + query; + href = href + "?return_to=" + URLEncoder.encode(returnTo, StandardCharsets.UTF_8); + } + return "You must sign in as staff " + action + "."; } public static String readFile(InputStream filename) diff --git a/src/main/java/dev/plex/request/SchematicUploadServlet.java b/src/main/java/dev/plex/request/SchematicUploadServlet.java index 34a1366..71ab021 100644 --- a/src/main/java/dev/plex/request/SchematicUploadServlet.java +++ b/src/main/java/dev/plex/request/SchematicUploadServlet.java @@ -30,7 +30,7 @@ public class SchematicUploadServlet extends HttpServlet AuthenticatedUser user = AbstractServlet.currentStaff(request); if (user == null) { - response.getWriter().println(schematicUploadBadHTML(AbstractServlet.signInPrompt("to upload schematics"))); + response.getWriter().println(schematicUploadBadHTML(AbstractServlet.signInPrompt(request, "to upload schematics"))); return; } File worldeditFolder = HTTPDModule.getWorldeditFolder(); diff --git a/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java b/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java index 11ff236..495efd8 100644 --- a/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java +++ b/src/main/java/dev/plex/request/impl/AuthenticationEndpoint.java @@ -14,9 +14,14 @@ import jakarta.servlet.http.HttpServletResponse; import org.json.JSONObject; import java.io.IOException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; public class AuthenticationEndpoint extends AbstractServlet { + private static final String RETURN_TO_COOKIE = "plex_return_to"; + @GetMapping(endpoint = "/oauth2/login") public String login(HttpServletRequest request, HttpServletResponse response) throws IOException { @@ -26,6 +31,20 @@ public class AuthenticationEndpoint extends AbstractServlet response.sendError(HttpServletResponse.SC_NOT_FOUND, "Authentication is not enabled."); return null; } + + String returnTo = sanitizeReturnTo(request.getParameter("return_to")); + String cookieValue = returnTo == null ? "" : URLEncoder.encode(returnTo, StandardCharsets.UTF_8); + Cookie returnCookie = new Cookie(RETURN_TO_COOKIE, cookieValue); + returnCookie.setHttpOnly(true); + returnCookie.setPath("/"); + returnCookie.setMaxAge(returnTo == null ? 0 : 600); + returnCookie.setAttribute("SameSite", "Lax"); + if (request.isSecure() || "https".equalsIgnoreCase(request.getHeader("X-Forwarded-Proto"))) + { + returnCookie.setSecure(true); + } + response.addCookie(returnCookie); + response.sendRedirect(provider.buildAuthorizeUrl(request)); return null; } @@ -54,7 +73,12 @@ public class AuthenticationEndpoint extends AbstractServlet + "
" + escape(e.getMessage()) + "
" + ""; } - response.sendRedirect("/"); + + String raw = readCookie(request, RETURN_TO_COOKIE); + String decoded = raw == null || raw.isEmpty() ? null : URLDecoder.decode(raw, StandardCharsets.UTF_8); + String target = sanitizeReturnTo(decoded); + clearReturnToCookie(request, response); + response.sendRedirect(target == null ? "/" : target); return null; } @@ -103,12 +127,17 @@ public class AuthenticationEndpoint extends AbstractServlet } private static String readSessionCookie(HttpServletRequest request) + { + return readCookie(request, OAuth2Provider.SESSION_COOKIE); + } + + private static String readCookie(HttpServletRequest request, String name) { Cookie[] cookies = request.getCookies(); if (cookies == null) return null; for (Cookie cookie : cookies) { - if (OAuth2Provider.SESSION_COOKIE.equals(cookie.getName())) + if (name.equals(cookie.getName())) { return cookie.getValue(); } @@ -116,6 +145,33 @@ public class AuthenticationEndpoint extends AbstractServlet return null; } + private void clearReturnToCookie(HttpServletRequest request, HttpServletResponse response) + { + Cookie clear = new Cookie(RETURN_TO_COOKIE, ""); + clear.setHttpOnly(true); + clear.setPath("/"); + clear.setMaxAge(0); + clear.setAttribute("SameSite", "Lax"); + if (request.isSecure() || "https".equalsIgnoreCase(request.getHeader("X-Forwarded-Proto"))) + { + clear.setSecure(true); + } + response.addCookie(clear); + } + + private static String sanitizeReturnTo(String value) + { + if (value == null || value.isEmpty()) return null; + if (!value.startsWith("/")) return null; + if (value.startsWith("//") || value.startsWith("/\\")) return null; + for (int i = 0; i < value.length(); i++) + { + char c = value.charAt(i); + if (c == '\n' || c == '\r' || c == '\\') return null; + } + return value; + } + private static String escape(String s) { if (s == null) return ""; diff --git a/src/main/java/dev/plex/request/impl/CommandsEndpoint.java b/src/main/java/dev/plex/request/impl/CommandsEndpoint.java index e9490ec..a229f08 100644 --- a/src/main/java/dev/plex/request/impl/CommandsEndpoint.java +++ b/src/main/java/dev/plex/request/impl/CommandsEndpoint.java @@ -77,7 +77,7 @@ public class CommandsEndpoint extends AbstractServlet %s - + %d %s @@ -113,11 +113,11 @@ public class CommandsEndpoint extends AbstractServlet %s %s -%s
- %d %s + %d %s%s
-