diff --git a/src/main/java/com/kttdevelopment/simplehttpserver/HttpSession.java b/src/main/java/com/kttdevelopment/simplehttpserver/HttpSession.java index c2b1e21..7d10c62 100644 --- a/src/main/java/com/kttdevelopment/simplehttpserver/HttpSession.java +++ b/src/main/java/com/kttdevelopment/simplehttpserver/HttpSession.java @@ -16,26 +16,11 @@ public abstract class HttpSession { /** * Creates an empty {@link HttpSession}. Applications don't use this method. * - * @see HttpSessionImpl#createHttpSession() * @since 02.00.00 * @author Ktt Development */ HttpSession(){ } -// - - /** - * Creates a {@link HttpSession}. - * - * @return a {@link HttpSession} - * - * @since 02.00.00 - * @author Ktt Development - */ - synchronized static HttpSession create(){ - return HttpSessionImpl.createHttpSession(); - } - // /** diff --git a/src/main/java/com/kttdevelopment/simplehttpserver/HttpSessionHandler.java b/src/main/java/com/kttdevelopment/simplehttpserver/HttpSessionHandler.java new file mode 100644 index 0000000..4524974 --- /dev/null +++ b/src/main/java/com/kttdevelopment/simplehttpserver/HttpSessionHandler.java @@ -0,0 +1,145 @@ +package com.kttdevelopment.simplehttpserver; + +import com.sun.net.httpserver.HttpExchange; + +import java.util.*; + +/** + * This class assigns {@link HttpSession} to every client. + * + * @since 03.03.00 + * @version 03.03.00 + * @author Ktt Development + */ +public class HttpSessionHandler { + + private final Map sessions = new HashMap<>(); + + private final String cookie; + + /** + * Creates a session handler using the cookie __session-id. + * + * @since 03.03.00 + * @author Ktt Development + */ + public HttpSessionHandler(){ + cookie = "__session-id"; + } + + /** + * Creates a session handler, storing the id at the specified cookie. + * + * @param cookie cookie to store session id + * + * @since 03.03.00 + * @author Ktt Development + */ + public HttpSessionHandler(final String cookie){ + this.cookie = cookie; + } + + /** + * Assigns a session id to a client. It is important to make sure duplicate ids do not occur. + * + * @param exchange http exchange + * @return session id + * + * @since 03.03.00 + * @author Ktt Development + */ + public synchronized String assignSessionID(final HttpExchange exchange){ + String id; + do id = UUID.randomUUID().toString(); + while(sessions.containsKey(id)); + return id; + } + + /** + * Returns the session of the client or assigns one if it does not yet have one Session will only be saved client side if the exchange headers are sent using {@link HttpExchange#sendResponseHeaders(int, long)}. + * + * @param exchange http exchange + * + * @since 03.03.00 + * @author Ktt Development + */ + public final HttpSession getSession(final HttpExchange exchange){ + final String sessionId; + final HttpSession session; + + final String rcookies = exchange.getRequestHeaders().getFirst("Cookie"); + final Map cookies = new HashMap<>(); + + if(rcookies != null && !rcookies.isEmpty()){ + final String[] pairs = rcookies.split("; "); + for(final String pair : pairs){ + final String[] value = pair.split("="); + cookies.put(value[0],value[1]); + } + } + + if((sessionId = cookies.get(cookie)) == null || !sessions.containsKey(sessionId)){ + session = new HttpSession() { + + private final String sessionID; + private final long creationTime; + private long lastAccessTime; + + { + sessionID = assignSessionID(exchange); + creationTime = System.currentTimeMillis(); + lastAccessTime = creationTime; + sessions.put(sessionID,this); + } + + @Override + public final String getSessionID(){ + return sessionID; + } + + // + + @Override + public final long getCreationTime(){ + return creationTime; + } + + @Override + public final long getLastAccessTime(){ + return lastAccessTime; + } + + @Override + public synchronized final void updateLastAccessTime(){ + lastAccessTime = System.currentTimeMillis(); + } + + // + + @SuppressWarnings("StringBufferReplaceableByString") + @Override + public final String toString(){ + final StringBuilder OUT = new StringBuilder(); + OUT.append("HttpSession") .append("{"); + OUT.append("sessionID") .append("=") .append(sessionID) .append(", "); + OUT.append("creationTime") .append("=") .append(creationTime) .append(", "); + OUT.append("lastAccessTime").append("=") .append(lastAccessTime); + OUT.append("}"); + return OUT.toString(); + } + + }; + + final SimpleHttpCookie out = + new SimpleHttpCookie.Builder(cookie,session.getSessionID()) + .setPath("/") + .setHttpOnly(true) + .build(); + exchange.getResponseHeaders().add("Set-Cookie",out.toCookieHeaderString()); + }else{ + session = sessions.get(sessionId); + } + return session; + } + +} diff --git a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchange.java b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchange.java index c34f3af..b5a35da 100644 --- a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchange.java +++ b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchange.java @@ -346,19 +346,6 @@ static SimpleHttpExchange create(final HttpExchange exchange){ */ public abstract void setCookie(final SimpleHttpCookie cookie); -// - - /** - * Returns the http session of the exchange if one exists; if one does not exist it will create one. A new session will only persist if the exchange is sent. - * - * @return http session - * - * @see HttpSession - * @since 02.00.00 - * @author Ktt Development - */ - public abstract HttpSession getHttpSession(); - // /** diff --git a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchangeImpl.java b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchangeImpl.java index bb87038..a3c5329 100644 --- a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchangeImpl.java +++ b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpExchangeImpl.java @@ -317,32 +317,11 @@ public synchronized final void setCookie(final String key, final String value){ @Override public synchronized final void setCookie(final SimpleHttpCookie cookie){ final String cstring = cookie.toCookieHeaderString(); - if(cstring.startsWith("__session-id=")) - throw new IllegalArgumentException("The cookie '__session-id' can not be set because it is reserved by the server"); getResponseHeaders().add("Set-Cookie",cstring); } // - - @Override - public synchronized final HttpSession getHttpSession(){ - final String sessionId; - final HttpSession session; - - if((sessionId = cookies.get("__session-id")) == null || !HttpSession.sessions.containsKey(sessionId)){ - session = HttpSession.create(); - final SimpleHttpCookie cookie = - new SimpleHttpCookie.Builder("__session-id",session.getSessionID()) - .setPath("/") - .setHttpOnly(true) - .build(); - getResponseHeaders().add("Set-Cookie",cookie.toCookieHeaderString()); // bypass implementation - }else{ - session = HttpSession.sessions.get(sessionId); - } - return session; - } - + @Override public final OutputStream getOutputStream(){ return outputStream; diff --git a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpHandler.java b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpHandler.java index c461f27..d564d4e 100644 --- a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpHandler.java +++ b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpHandler.java @@ -14,26 +14,23 @@ * @version 03.03.00 * @author Ktt Development */ -public interface SimpleHttpHandler { -/* + +public interface SimpleHttpHandler extends HttpHandler { + /** - * Encapsulates the {@link #handle(SimpleHttpExchange)} for the authenticator. Applications do not use this. + * Encapsulates the {@link #handle(SimpleHttpExchange)} for the authenticator. This method is reserved by the server; do not override this, it will break the {@link #handle(SimpleHttpExchange)} method. * * @param exchange client information * @throws IOException internal failure * * @since 02.00.00 * @author Ktt Development - / + */ @Override - default final void handle(final HttpExchange exchange) throws IOException{ - final SimpleHttpExchange sxe = SimpleHttpExchange.create(exchange); - if(authenticate(sxe)) - handle(sxe); - else - sxe.close(); + default void handle(final HttpExchange exchange) throws IOException{ + handle(SimpleHttpExchange.create(exchange)); } -*/ + /** * Handlers the given request and generates a response if no exceptions occur. * diff --git a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpServer.java b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpServer.java index b21eef1..7cdf7b8 100644 --- a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpServer.java +++ b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpServer.java @@ -209,6 +209,14 @@ public static SimpleHttpServer create(final int port, final int backlog) throws */ public abstract Executor getExecutor(); +// + + public abstract void setHttpSessionHandler(final HttpSessionHandler sessionHandler); + + public abstract HttpSessionHandler getHttpSessionHandler(); + + public abstract HttpSession getHttpSession(final HttpExchange exchange); + // /** @@ -221,7 +229,6 @@ public static SimpleHttpServer create(final int port, final int backlog) throws * * @see HttpContext * @see #createContext(String, HttpHandler) - * @see #createContext(String, SimpleHttpHandler) * @see #removeContext(String) * @see #removeContext(HttpContext) * @since 02.00.00 @@ -241,7 +248,6 @@ public static SimpleHttpServer create(final int port, final int backlog) throws * @see HttpContext * @see HttpHandler * @see #createContext(String) - * @see #createContext(String, SimpleHttpHandler) * @see #removeContext(String) * @see #removeContext(HttpContext) * @since 02.00.00 @@ -249,26 +255,6 @@ public static SimpleHttpServer create(final int port, final int backlog) throws */ public abstract HttpContext createContext(final String context, final HttpHandler handler); - /** - * Creates a context mapped to a specific {@link HttpHandler}. - * - * @param context the context - * @param handler the handler - * @return the http context associated with the context - * @throws IllegalArgumentException if the context is invalid or taken - * @throws NullPointerException if the context is null - * - * @see HttpContext - * @see SimpleHttpHandler - * @see #createContext(String) - * @see #createContext(String, HttpHandler) - * @see #removeContext(String) - * @see #removeContext(HttpContext) - * @since 03.03.00 - * @author Ktt Development - */ - public abstract HttpContext createContext(final String context, final SimpleHttpHandler handler); - // /** @@ -283,7 +269,6 @@ public static SimpleHttpServer create(final int port, final int backlog) throws * @see HttpContext * @see Authenticator * @see #createContext(String, HttpHandler, Authenticator) - * @see #createContext(String, SimpleHttpHandler, Authenticator) * @see #removeContext(String) * @see #removeContext(HttpContext) * @since 03.03.00 @@ -305,7 +290,6 @@ public static SimpleHttpServer create(final int port, final int backlog) throws * @see HttpHandler * @see Authenticator * @see #createContext(String, Authenticator) - * @see #createContext(String, SimpleHttpHandler, Authenticator) * @see #removeContext(String) * @see #removeContext(HttpContext) * @since 03.03.00 @@ -313,28 +297,6 @@ public static SimpleHttpServer create(final int port, final int backlog) throws */ public abstract HttpContext createContext(final String context, final HttpHandler handler, final Authenticator authenticator); - /** - * Creates a context mapped to a specific {@link HttpContext} with an {@link Authenticator}. - * - * @param context the context - * @param handler the handler - * @param authenticator authenticator - * @return the http context associated with the context - * @throws IllegalArgumentException if the context is invalid or taken - * @throws NullPointerException if the context is null - * - * @see HttpContext - * @see SimpleHttpHandler - * @see Authenticator - * @see #createContext(String, Authenticator) - * @see #createContext(String, HttpHandler, Authenticator) - * @see #removeContext(String) - * @see #removeContext(HttpContext) - * @since 03.03.00 - * @author Ktt Development - */ - public abstract HttpContext createContext(final String context, final SimpleHttpHandler handler, final Authenticator authenticator); - // /** diff --git a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpServerImpl.java b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpServerImpl.java index 79b88bd..83484ce 100644 --- a/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpServerImpl.java +++ b/src/main/java/com/kttdevelopment/simplehttpserver/SimpleHttpServerImpl.java @@ -37,7 +37,9 @@ static SimpleHttpServer createSimpleHttpServer(final Integer port, final Integer private final HttpServer server = HttpServer.create(); - private final HashMap contexts = new HashMap<>(); + private HttpSessionHandler sessionHandler; + + private final Map contexts = new HashMap<>(); private boolean running = false; @@ -46,6 +48,11 @@ static SimpleHttpServer createSimpleHttpServer(final Integer port, final Integer server.bind(new InetSocketAddress(port),backlog != null ? backlog : 0); } + private void handle(final HttpExchange exchange){ + if(sessionHandler != null) + sessionHandler.getSession(exchange).updateLastAccessTime(); + } + // @Override @@ -98,30 +105,41 @@ public final Executor getExecutor(){ return server.getExecutor(); } - // + @Override + public synchronized final void setHttpSessionHandler(final HttpSessionHandler sessionHandler){ + this.sessionHandler = sessionHandler; + } + + @Override + public final HttpSessionHandler getHttpSessionHandler(){ + return sessionHandler; + } + + @Override + public HttpSession getHttpSession(final HttpExchange exchange){ + return sessionHandler.getSession(exchange); + } + + // @Override public synchronized final HttpContext createContext(final String path){ - final HttpContext context = server.createContext(getContext(path)); - contexts.put(context,context.getHandler()); - return context; + return createContext(path,(HttpExchange exchange) -> {}); } @Override public synchronized final HttpContext createContext(final String path, final HttpHandler handler){ if(!getContext(path).equals("/") && handler instanceof RootHandler) throw new IllegalArgumentException("RootHandler can only be used at the root '/' context"); - final HttpContext context = server.createContext(getContext(path),handler); + + final HttpHandler wrapper = exchange -> { + handle(exchange); + handler.handle(exchange); + }; + final HttpContext context = server.createContext(getContext(path),wrapper); + contexts.put(context,handler); - return context; - } - @Override - public synchronized final HttpContext createContext(final String path, final SimpleHttpHandler handler){ - if(!getContext(path).equals("/") && handler instanceof RootHandler) - throw new IllegalArgumentException("RootHandler can only be used at the root '/' context"); - final HttpContext context = server.createContext(getContext(path),(exchange) -> handler.handle(SimpleHttpExchange.create(exchange))); - contexts.put(context,context.getHandler()); return context; } @@ -141,14 +159,6 @@ public synchronized final HttpContext createContext(final String path, final Htt return context; } - @Override - public synchronized final HttpContext createContext(final String path, final SimpleHttpHandler handler, final Authenticator authenticator){ - final HttpContext context = createContext(path,handler); - context.setAuthenticator(authenticator); - return context; - } - - // private String generateRandomContext(){ @@ -249,11 +259,9 @@ public synchronized final void removeContext(final HttpContext context){ @Override public final HttpHandler getContextHandler(final String path){ - for(final HttpContext context : contexts.keySet()){ - if(context.getPath().equals(getContext(path))){ + for(final HttpContext context : contexts.keySet()) + if(context.getPath().equals(getContext(path))) return context.getHandler(); - } - } return null; }