diff --git a/gradle.properties b/gradle.properties index 378d12e..d24d726 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib name = net-http -version = 3.4.1 +version = 3.4.2 org.gradle.warning.mode = ALL diff --git a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/netty/client/secure/Https1Test.java b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/netty/client/secure/Https1Test.java index 9ddfe9c..e38f9bc 100644 --- a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/netty/client/secure/Https1Test.java +++ b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/netty/client/secure/Https1Test.java @@ -117,29 +117,6 @@ class Https1Test { } } - @Test - void testHebisGetRequest() throws Exception { - // we test HEBIS here with strange certificate setup and TLS 1.2 only - NettyHttpClientConfig config = new NettyHttpsClientConfig() - .setDebug(true); - try (NettyHttpClient client = NettyHttpClient.builder() - .setConfig(config) - .build()){ - HttpRequest request = HttpRequest.post() - .setURL("https://hebis.rz.uni-frankfurt.de/HEBCGI/vuefl_recv_data.pl") - .setResponseListener(resp -> - logger.log(Level.INFO, - "got response: " + - " status = " + resp.getStatus() + - " headers = " + resp.getHeaders() + - " body = " + resp.getBodyAsChars(StandardCharsets.UTF_8) + - " ssl = " + dumpCertificates((HttpsResponse) resp)) - ) - .build(); - client.execute(request).get().close(); - } - } - @Test void testSequentialRequests() throws Exception { NettyHttpClientConfig config = new NettyHttpsClientConfig() diff --git a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/netty/client/secure/JdkClientTest.java b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/netty/client/secure/JdkClientTest.java index c916b62..d76bd04 100644 --- a/net-http-client-netty-secure/src/test/java/org/xbib/net/http/netty/client/secure/JdkClientTest.java +++ b/net-http-client-netty-secure/src/test/java/org/xbib/net/http/netty/client/secure/JdkClientTest.java @@ -43,19 +43,6 @@ public class JdkClientTest { logger.log(Level.INFO, response.body()); } - @Test - void testHebisGetRequest() throws Exception { - HttpClient httpClient = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) - .build(); - HttpRequest request = HttpRequest.newBuilder() - .uri(new URI("https://hebis.rz.uni-frankfurt.de/HEBCGI/vuefl_recv_data.pl")) - .build(); - HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - logger.log(Level.INFO, Integer.toString(response.statusCode())); - logger.log(Level.INFO, response.body()); - } - private static HttpRequest.BodyPublisher buildFormDataFromMap(Map data) { var builder = new StringBuilder(); for (Map.Entry entry : data.entrySet()) { diff --git a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/WebApplication.java b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/WebApplication.java index fbe9cbf..1711df0 100644 --- a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/WebApplication.java +++ b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/WebApplication.java @@ -3,16 +3,11 @@ package org.xbib.net.http.server.application.web; import java.nio.file.Paths; import java.time.Duration; -import org.xbib.net.http.cookie.SameSite; import org.xbib.net.http.server.application.BaseApplication; -import org.xbib.net.http.server.HttpHandler; import org.xbib.net.http.server.route.HttpRouterContext; import org.xbib.net.http.server.persist.Codec; -import org.xbib.net.http.server.session.IncomingSessionHandler; -import org.xbib.net.http.server.session.OutgoingSessionHandler; import org.xbib.net.http.server.session.Session; import org.xbib.net.http.server.session.file.FileJsonSessionCodec; -import org.xbib.net.util.RandomUtil; public class WebApplication extends BaseApplication { @@ -28,39 +23,9 @@ public class WebApplication extends BaseApplication { } } + @Override protected Codec newSessionCodec(HttpRouterContext httpRouterContext) { return new FileJsonSessionCodec(sessionName, this, 1024, Duration.ofDays(1), Paths.get("/var/tmp/session")); } - - protected HttpHandler newIncomingSessionHandler(HttpRouterContext httpRouterContext) { - @SuppressWarnings("unchecked") - Codec sessionCodec = httpRouterContext.getAttributes().get(Codec.class, "sessioncodec"); - return new IncomingSessionHandler( - getSecret(), - "HmacSHA1", - sessionName, - sessionCodec, - getStaticFileSuffixes(), - "user_id", - "e_user_id", - () -> RandomUtil.randomString(16)); - } - - protected OutgoingSessionHandler newOutgoingSessionHandler(HttpRouterContext httpRouterContext) { - @SuppressWarnings("unchecked") - Codec sessionCodec = httpRouterContext.getAttributes().get(Codec.class, "sessioncodec"); - return new OutgoingSessionHandler( - getSecret(), - "HmacSHA1", - sessionName, - sessionCodec, - getStaticFileSuffixes(), - "user_id", - "e_user_id", - Duration.ofDays(1), - true, - false, - SameSite.LAX); - } } diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplication.java b/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplication.java index 6f24e09..a17e366 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplication.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplication.java @@ -1,6 +1,5 @@ package org.xbib.net.http.server.application; -import java.io.Closeable; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -30,6 +29,7 @@ import org.xbib.net.http.server.render.HttpResponseRenderer; import org.xbib.net.http.server.route.HttpRouter; import org.xbib.net.http.server.session.IncomingSessionHandler; import org.xbib.net.http.server.session.OutgoingSessionHandler; +import org.xbib.net.http.server.session.PersistSessionHandler; import org.xbib.net.http.server.session.Session; import org.xbib.net.http.server.session.memory.MemoryPropertiesSessionCodec; import org.xbib.net.http.server.validate.HttpRequestValidator; @@ -149,7 +149,8 @@ public class BaseApplication implements Application { Codec sessionCodec = newSessionCodec(httpRouterContext); httpRouterContext.getAttributes().put("sessioncodec", sessionCodec); httpRouterContext.addOpenHandler(newIncomingSessionHandler(sessionCodec)); - httpRouterContext.addCloseHandler(newOutgoingSessionHandler(sessionCodec)); + httpRouterContext.addCloseHandler(newOutgoingSessionHandler()); + httpRouterContext.addReleaseHandler(newPersistSessionHandler(sessionCodec)); } httpRouterContext.addCloseHandler(newOutgoingCookieHandler()); return httpRouterContext; @@ -196,12 +197,11 @@ public class BaseApplication implements Application { () -> RandomUtil.randomString(16)); } - protected HttpHandler newOutgoingSessionHandler(Codec sessionCodec) { + protected HttpHandler newOutgoingSessionHandler() { return new OutgoingSessionHandler( getSecret(), "HmacSHA1", sessionName, - sessionCodec, getStaticFileSuffixes(), "user_id", "e_user_id", @@ -212,6 +212,10 @@ public class BaseApplication implements Application { ); } + protected HttpHandler newPersistSessionHandler(Codec sessionCodec) { + return new PersistSessionHandler(sessionCodec); + } + @Override public void onCreated(Session session) { logger.log(Level.FINER, "session name = " + sessionName + " created = " + session); diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouter.java b/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouter.java index 4bf465e..a79f7fd 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouter.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouter.java @@ -108,8 +108,6 @@ public class BaseHttpRouter implements HttpRouter { try { route(application, httpRouterContext, httpRouteResolverResults); } finally { - application.onClose(httpRouterContext); - // outgoing cookie/session httpRouterContext.getCloseHandlers().forEach(h -> { try { h.handle(httpRouterContext); @@ -117,6 +115,14 @@ public class BaseHttpRouter implements HttpRouter { routeToErrorHandler(httpRouterContext, e); } }); + application.onClose(httpRouterContext); + httpRouterContext.getReleaseHandlers().forEach(h -> { + try { + h.handle(httpRouterContext); + } catch (Exception e) { + routeToErrorHandler(httpRouterContext, e); + } + }); application.releaseContext(httpRouterContext); } } diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouterContext.java b/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouterContext.java index 6d858b1..f06ae73 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouterContext.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/route/BaseHttpRouterContext.java @@ -42,6 +42,8 @@ public class BaseHttpRouterContext implements HttpRouterContext { private final List closeHandlers; + private final List releaseeHandlers; + private String contextPath; private URL contextURL; @@ -63,6 +65,7 @@ public class BaseHttpRouterContext implements HttpRouterContext { this.httpResponseBuilder = httpResponseBuilder; this.openHandlers = new LinkedList<>(); this.closeHandlers = new LinkedList<>(); + this.releaseeHandlers = new LinkedList<>(); this.attributes = new BaseAttributes(); this.attributes.put("application", application); this.attributes.put("domain", domain); @@ -91,6 +94,16 @@ public class BaseHttpRouterContext implements HttpRouterContext { return closeHandlers; } + @Override + public void addReleaseHandler(HttpHandler handler) { + this.releaseeHandlers.add(handler); + } + + @Override + public List getReleaseHandlers() { + return releaseeHandlers; + } + @Override public Application getApplication() { return application; diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/route/HttpRouterContext.java b/net-http-server/src/main/java/org/xbib/net/http/server/route/HttpRouterContext.java index d2d7406..6548cb0 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/route/HttpRouterContext.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/route/HttpRouterContext.java @@ -17,7 +17,6 @@ import org.xbib.net.http.cookie.Cookie; import org.xbib.net.http.server.HttpHandler; import org.xbib.net.http.server.HttpRequest; import org.xbib.net.http.server.HttpRequestBuilder; -import org.xbib.net.http.server.HttpResponseBuilder; import org.xbib.net.http.server.application.Application; public interface HttpRouterContext { @@ -32,14 +31,16 @@ public interface HttpRouterContext { List getCloseHandlers(); + void addReleaseHandler(HttpHandler handler); + + List getReleaseHandlers(); + HttpRequestBuilder getRequestBuilder(); void setRequest(HttpRequest httpRequest); HttpRequest getRequest(); - //HttpResponseBuilder getResponseBuilder(); - Attributes getAttributes(); void done(); diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/session/IncomingSessionHandler.java b/net-http-server/src/main/java/org/xbib/net/http/server/session/IncomingSessionHandler.java index 34d93ca..633ab68 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/session/IncomingSessionHandler.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/session/IncomingSessionHandler.java @@ -71,6 +71,7 @@ public class IncomingSessionHandler implements HttpHandler { if (suffix != null && suffixes.contains(suffix)) { return; } + Map payload = null; Session session = null; CookieBox cookieBox = context.getAttributes().get(CookieBox.class, "incomingcookies"); if (cookieBox != null) { @@ -78,13 +79,8 @@ public class IncomingSessionHandler implements HttpHandler { if (cookie.name().equals(sessionCookieName)) { if (session == null) { try { - Map payload = decodeCookie(cookie); - logger.log(Level.FINER, "cookie decoded"); + payload = decodeCookie(cookie); session = toSession(payload); - UserProfile userProfile = newUserProfile(payload, session); - if (userProfile != null) { - context.getAttributes().put("userprofile", userProfile); - } } catch (CookieSignatureException e) { // set exception in context to discard broken cookie later and render exception message context.getAttributes().put("_throwable", e); @@ -93,7 +89,7 @@ public class IncomingSessionHandler implements HttpHandler { throw new HttpException("unable to create session", context, HttpResponseStatus.INTERNAL_SERVER_ERROR); } } else { - logger.log(Level.WARNING, "received extra session cookie, something is wrong, ignoring"); + logger.log(Level.WARNING, "received extra session cookie of same name, something is wrong, ignoring"); } } } @@ -106,8 +102,14 @@ public class IncomingSessionHandler implements HttpHandler { throw new HttpException("unable to create session", context, HttpResponseStatus.INTERNAL_SERVER_ERROR); } } + if (payload != null) { + UserProfile userProfile = newUserProfile(payload, session); + if (userProfile != null) { + context.getAttributes().put("userprofile", userProfile); + } + } + logger.log(Level.FINEST, "incoming session ID = " + session.id() + " keys = " + session.keySet()); context.getAttributes().put("session", session); - logger.log(Level.FINEST, "incoming session " + session.id()); } private Map decodeCookie(Cookie cookie) throws IOException, diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/session/OutgoingSessionHandler.java b/net-http-server/src/main/java/org/xbib/net/http/server/session/OutgoingSessionHandler.java index 9addec9..0616874 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/session/OutgoingSessionHandler.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/session/OutgoingSessionHandler.java @@ -24,7 +24,6 @@ import org.xbib.net.http.server.route.HttpRouterContext; import org.xbib.net.http.server.application.Application; import org.xbib.net.http.server.cookie.CookieSignatureException; import org.xbib.net.http.server.cookie.CookieSignatureUtil; -import org.xbib.net.http.server.persist.Codec; public class OutgoingSessionHandler implements HttpHandler { @@ -38,8 +37,6 @@ public class OutgoingSessionHandler implements HttpHandler { private final Duration sessionDuration; - private final Codec sessionCodec; - private final Set suffixes; private final String sessionUserName; @@ -55,7 +52,6 @@ public class OutgoingSessionHandler implements HttpHandler { public OutgoingSessionHandler(String sessionSecret, String sessionCookieAlgorithm, String sessionCookieName, - Codec sessionCodec, Set suffixes, String sessionUserName, String sessionEffectiveUserName, @@ -66,7 +62,6 @@ public class OutgoingSessionHandler implements HttpHandler { this.sessionSecret = sessionSecret; this.sessionCookieAlgorithm = sessionCookieAlgorithm; this.sessionCookieName = sessionCookieName; - this.sessionCodec = sessionCodec; this.suffixes = suffixes; this.sessionUserName = sessionUserName; this.sessionEffectiveUserName = sessionEffectiveUserName; @@ -113,17 +108,16 @@ public class OutgoingSessionHandler implements HttpHandler { session.put(sessionEffectiveUserName, userProfile.getEffectiveUserId()); } } - sessionCodec.write(session.id(), session); Cookie cookie = encodeCookie(session, host, path); if (cookie != null) { cookieBox.add(cookie); } } catch (Exception e) { logger.log(Level.SEVERE, e.getMessage(), e); - throw new HttpException("unable to create session cookie", context, HttpResponseStatus.INTERNAL_SERVER_ERROR); + throw new HttpException("unable to create session data for cookie", context, HttpResponseStatus.INTERNAL_SERVER_ERROR); } } - logger.log(Level.FINEST, "outgoing cookies = " + cookieBox); + logger.log(Level.FINEST, "prepared outgoing cookies = " + cookieBox); context.getAttributes().put("outgoingcookies", cookieBox); } diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/session/PersistSessionHandler.java b/net-http-server/src/main/java/org/xbib/net/http/server/session/PersistSessionHandler.java new file mode 100644 index 0000000..2097633 --- /dev/null +++ b/net-http-server/src/main/java/org/xbib/net/http/server/session/PersistSessionHandler.java @@ -0,0 +1,35 @@ +package org.xbib.net.http.server.session; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.xbib.net.http.HttpResponseStatus; +import org.xbib.net.http.server.HttpException; +import org.xbib.net.http.server.HttpHandler; +import org.xbib.net.http.server.persist.Codec; +import org.xbib.net.http.server.route.HttpRouterContext; + +public class PersistSessionHandler implements HttpHandler { + + private static final Logger logger = Logger.getLogger(PersistSessionHandler.class.getName()); + + private final Codec sessionCodec; + + public PersistSessionHandler(Codec sessionCodec) { + this.sessionCodec = sessionCodec; + } + + @Override + public void handle(HttpRouterContext context) throws IOException { + Session session = context.getAttributes().get(Session.class, "session"); + if (session != null) { + try { + logger.log(Level.FINEST, "writing session id " + session.id() + " keys = " + session.keySet()); + sessionCodec.write(session.id(), session); + } catch (Exception e) { + logger.log(Level.SEVERE, e.getMessage(), e); + throw new HttpException("unable to create session data for cookie", context, HttpResponseStatus.INTERNAL_SERVER_ERROR); + } + } + } +} diff --git a/net-http-server/src/test/java/org/xbib/net/http/server/session/JsonSessionTest.java b/net-http-server/src/test/java/org/xbib/net/http/server/session/JsonSessionTest.java new file mode 100644 index 0000000..2376943 --- /dev/null +++ b/net-http-server/src/test/java/org/xbib/net/http/server/session/JsonSessionTest.java @@ -0,0 +1,36 @@ +package org.xbib.net.http.server.session; + +import java.io.IOException; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.function.Supplier; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.junit.jupiter.api.Test; +import org.xbib.net.http.server.persist.Codec; +import org.xbib.net.http.server.session.file.FileJsonSessionCodec; +import org.xbib.net.util.RandomUtil; + +public class JsonSessionTest { + + private static final Logger logger = Logger.getLogger(JsonSessionTest.class.getName()); + + @Test + void testJsonSession() throws IOException { + Codec sessionCodec = newSessionCodec(); + Session session = create(sessionCodec, () -> RandomUtil.randomString(16)); + session.put("a", "b"); + sessionCodec.write(session.id(), session); + Session session1 = sessionCodec.read(session.id()); + logger.log(Level.INFO, session1.get("a").toString()); + } + + private Codec newSessionCodec() { + return new FileJsonSessionCodec("SESSION-TEST", null, 1024, + Duration.ofDays(1), Paths.get("/var/tmp/session-test")); + } + + private Session create(Codec sessionCodec, Supplier sessionIdGenerator) throws IOException { + return sessionCodec.create(sessionIdGenerator.get()); + } +}