add persist session handler, remove hebis test code

This commit is contained in:
Jörg Prante 2023-06-09 11:59:47 +02:00
parent b1fb1cff50
commit 30926d6052
12 changed files with 118 additions and 98 deletions

View file

@ -1,5 +1,5 @@
group = org.xbib group = org.xbib
name = net-http name = net-http
version = 3.4.1 version = 3.4.2
org.gradle.warning.mode = ALL org.gradle.warning.mode = ALL

View file

@ -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 @Test
void testSequentialRequests() throws Exception { void testSequentialRequests() throws Exception {
NettyHttpClientConfig config = new NettyHttpsClientConfig() NettyHttpClientConfig config = new NettyHttpsClientConfig()

View file

@ -43,19 +43,6 @@ public class JdkClientTest {
logger.log(Level.INFO, response.body()); 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<String> 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<String, Object> data) { private static HttpRequest.BodyPublisher buildFormDataFromMap(Map<String, Object> data) {
var builder = new StringBuilder(); var builder = new StringBuilder();
for (Map.Entry<String, Object> entry : data.entrySet()) { for (Map.Entry<String, Object> entry : data.entrySet()) {

View file

@ -3,16 +3,11 @@ package org.xbib.net.http.server.application.web;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.Duration; 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.application.BaseApplication;
import org.xbib.net.http.server.HttpHandler;
import org.xbib.net.http.server.route.HttpRouterContext; import org.xbib.net.http.server.route.HttpRouterContext;
import org.xbib.net.http.server.persist.Codec; 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.Session;
import org.xbib.net.http.server.session.file.FileJsonSessionCodec; import org.xbib.net.http.server.session.file.FileJsonSessionCodec;
import org.xbib.net.util.RandomUtil;
public class WebApplication extends BaseApplication { public class WebApplication extends BaseApplication {
@ -28,39 +23,9 @@ public class WebApplication extends BaseApplication {
} }
} }
@Override
protected Codec<Session> newSessionCodec(HttpRouterContext httpRouterContext) { protected Codec<Session> newSessionCodec(HttpRouterContext httpRouterContext) {
return new FileJsonSessionCodec(sessionName, this, 1024, Duration.ofDays(1), return new FileJsonSessionCodec(sessionName, this, 1024, Duration.ofDays(1),
Paths.get("/var/tmp/session")); Paths.get("/var/tmp/session"));
} }
protected HttpHandler newIncomingSessionHandler(HttpRouterContext httpRouterContext) {
@SuppressWarnings("unchecked")
Codec<Session> 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<Session> 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);
}
} }

View file

@ -1,6 +1,5 @@
package org.xbib.net.http.server.application; package org.xbib.net.http.server.application;
import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; 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.route.HttpRouter;
import org.xbib.net.http.server.session.IncomingSessionHandler; import org.xbib.net.http.server.session.IncomingSessionHandler;
import org.xbib.net.http.server.session.OutgoingSessionHandler; 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.Session;
import org.xbib.net.http.server.session.memory.MemoryPropertiesSessionCodec; import org.xbib.net.http.server.session.memory.MemoryPropertiesSessionCodec;
import org.xbib.net.http.server.validate.HttpRequestValidator; import org.xbib.net.http.server.validate.HttpRequestValidator;
@ -149,7 +149,8 @@ public class BaseApplication implements Application {
Codec<Session> sessionCodec = newSessionCodec(httpRouterContext); Codec<Session> sessionCodec = newSessionCodec(httpRouterContext);
httpRouterContext.getAttributes().put("sessioncodec", sessionCodec); httpRouterContext.getAttributes().put("sessioncodec", sessionCodec);
httpRouterContext.addOpenHandler(newIncomingSessionHandler(sessionCodec)); httpRouterContext.addOpenHandler(newIncomingSessionHandler(sessionCodec));
httpRouterContext.addCloseHandler(newOutgoingSessionHandler(sessionCodec)); httpRouterContext.addCloseHandler(newOutgoingSessionHandler());
httpRouterContext.addReleaseHandler(newPersistSessionHandler(sessionCodec));
} }
httpRouterContext.addCloseHandler(newOutgoingCookieHandler()); httpRouterContext.addCloseHandler(newOutgoingCookieHandler());
return httpRouterContext; return httpRouterContext;
@ -196,12 +197,11 @@ public class BaseApplication implements Application {
() -> RandomUtil.randomString(16)); () -> RandomUtil.randomString(16));
} }
protected HttpHandler newOutgoingSessionHandler(Codec<Session> sessionCodec) { protected HttpHandler newOutgoingSessionHandler() {
return new OutgoingSessionHandler( return new OutgoingSessionHandler(
getSecret(), getSecret(),
"HmacSHA1", "HmacSHA1",
sessionName, sessionName,
sessionCodec,
getStaticFileSuffixes(), getStaticFileSuffixes(),
"user_id", "user_id",
"e_user_id", "e_user_id",
@ -212,6 +212,10 @@ public class BaseApplication implements Application {
); );
} }
protected HttpHandler newPersistSessionHandler(Codec<Session> sessionCodec) {
return new PersistSessionHandler(sessionCodec);
}
@Override @Override
public void onCreated(Session session) { public void onCreated(Session session) {
logger.log(Level.FINER, "session name = " + sessionName + " created = " + session); logger.log(Level.FINER, "session name = " + sessionName + " created = " + session);

View file

@ -108,8 +108,6 @@ public class BaseHttpRouter implements HttpRouter {
try { try {
route(application, httpRouterContext, httpRouteResolverResults); route(application, httpRouterContext, httpRouteResolverResults);
} finally { } finally {
application.onClose(httpRouterContext);
// outgoing cookie/session
httpRouterContext.getCloseHandlers().forEach(h -> { httpRouterContext.getCloseHandlers().forEach(h -> {
try { try {
h.handle(httpRouterContext); h.handle(httpRouterContext);
@ -117,6 +115,14 @@ public class BaseHttpRouter implements HttpRouter {
routeToErrorHandler(httpRouterContext, e); routeToErrorHandler(httpRouterContext, e);
} }
}); });
application.onClose(httpRouterContext);
httpRouterContext.getReleaseHandlers().forEach(h -> {
try {
h.handle(httpRouterContext);
} catch (Exception e) {
routeToErrorHandler(httpRouterContext, e);
}
});
application.releaseContext(httpRouterContext); application.releaseContext(httpRouterContext);
} }
} }

View file

@ -42,6 +42,8 @@ public class BaseHttpRouterContext implements HttpRouterContext {
private final List<HttpHandler> closeHandlers; private final List<HttpHandler> closeHandlers;
private final List<HttpHandler> releaseeHandlers;
private String contextPath; private String contextPath;
private URL contextURL; private URL contextURL;
@ -63,6 +65,7 @@ public class BaseHttpRouterContext implements HttpRouterContext {
this.httpResponseBuilder = httpResponseBuilder; this.httpResponseBuilder = httpResponseBuilder;
this.openHandlers = new LinkedList<>(); this.openHandlers = new LinkedList<>();
this.closeHandlers = new LinkedList<>(); this.closeHandlers = new LinkedList<>();
this.releaseeHandlers = new LinkedList<>();
this.attributes = new BaseAttributes(); this.attributes = new BaseAttributes();
this.attributes.put("application", application); this.attributes.put("application", application);
this.attributes.put("domain", domain); this.attributes.put("domain", domain);
@ -91,6 +94,16 @@ public class BaseHttpRouterContext implements HttpRouterContext {
return closeHandlers; return closeHandlers;
} }
@Override
public void addReleaseHandler(HttpHandler handler) {
this.releaseeHandlers.add(handler);
}
@Override
public List<HttpHandler> getReleaseHandlers() {
return releaseeHandlers;
}
@Override @Override
public Application getApplication() { public Application getApplication() {
return application; return application;

View file

@ -17,7 +17,6 @@ import org.xbib.net.http.cookie.Cookie;
import org.xbib.net.http.server.HttpHandler; import org.xbib.net.http.server.HttpHandler;
import org.xbib.net.http.server.HttpRequest; import org.xbib.net.http.server.HttpRequest;
import org.xbib.net.http.server.HttpRequestBuilder; import org.xbib.net.http.server.HttpRequestBuilder;
import org.xbib.net.http.server.HttpResponseBuilder;
import org.xbib.net.http.server.application.Application; import org.xbib.net.http.server.application.Application;
public interface HttpRouterContext { public interface HttpRouterContext {
@ -32,14 +31,16 @@ public interface HttpRouterContext {
List<HttpHandler> getCloseHandlers(); List<HttpHandler> getCloseHandlers();
void addReleaseHandler(HttpHandler handler);
List<HttpHandler> getReleaseHandlers();
HttpRequestBuilder getRequestBuilder(); HttpRequestBuilder getRequestBuilder();
void setRequest(HttpRequest httpRequest); void setRequest(HttpRequest httpRequest);
HttpRequest getRequest(); HttpRequest getRequest();
//HttpResponseBuilder getResponseBuilder();
Attributes getAttributes(); Attributes getAttributes();
void done(); void done();

View file

@ -71,6 +71,7 @@ public class IncomingSessionHandler implements HttpHandler {
if (suffix != null && suffixes.contains(suffix)) { if (suffix != null && suffixes.contains(suffix)) {
return; return;
} }
Map<String, Object> payload = null;
Session session = null; Session session = null;
CookieBox cookieBox = context.getAttributes().get(CookieBox.class, "incomingcookies"); CookieBox cookieBox = context.getAttributes().get(CookieBox.class, "incomingcookies");
if (cookieBox != null) { if (cookieBox != null) {
@ -78,13 +79,8 @@ public class IncomingSessionHandler implements HttpHandler {
if (cookie.name().equals(sessionCookieName)) { if (cookie.name().equals(sessionCookieName)) {
if (session == null) { if (session == null) {
try { try {
Map<String, Object> payload = decodeCookie(cookie); payload = decodeCookie(cookie);
logger.log(Level.FINER, "cookie decoded");
session = toSession(payload); session = toSession(payload);
UserProfile userProfile = newUserProfile(payload, session);
if (userProfile != null) {
context.getAttributes().put("userprofile", userProfile);
}
} catch (CookieSignatureException e) { } catch (CookieSignatureException e) {
// set exception in context to discard broken cookie later and render exception message // set exception in context to discard broken cookie later and render exception message
context.getAttributes().put("_throwable", e); 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); throw new HttpException("unable to create session", context, HttpResponseStatus.INTERNAL_SERVER_ERROR);
} }
} else { } 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); 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); context.getAttributes().put("session", session);
logger.log(Level.FINEST, "incoming session " + session.id());
} }
private Map<String, Object> decodeCookie(Cookie cookie) throws IOException, private Map<String, Object> decodeCookie(Cookie cookie) throws IOException,

View file

@ -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.application.Application;
import org.xbib.net.http.server.cookie.CookieSignatureException; import org.xbib.net.http.server.cookie.CookieSignatureException;
import org.xbib.net.http.server.cookie.CookieSignatureUtil; import org.xbib.net.http.server.cookie.CookieSignatureUtil;
import org.xbib.net.http.server.persist.Codec;
public class OutgoingSessionHandler implements HttpHandler { public class OutgoingSessionHandler implements HttpHandler {
@ -38,8 +37,6 @@ public class OutgoingSessionHandler implements HttpHandler {
private final Duration sessionDuration; private final Duration sessionDuration;
private final Codec<Session> sessionCodec;
private final Set<String> suffixes; private final Set<String> suffixes;
private final String sessionUserName; private final String sessionUserName;
@ -55,7 +52,6 @@ public class OutgoingSessionHandler implements HttpHandler {
public OutgoingSessionHandler(String sessionSecret, public OutgoingSessionHandler(String sessionSecret,
String sessionCookieAlgorithm, String sessionCookieAlgorithm,
String sessionCookieName, String sessionCookieName,
Codec<Session> sessionCodec,
Set<String> suffixes, Set<String> suffixes,
String sessionUserName, String sessionUserName,
String sessionEffectiveUserName, String sessionEffectiveUserName,
@ -66,7 +62,6 @@ public class OutgoingSessionHandler implements HttpHandler {
this.sessionSecret = sessionSecret; this.sessionSecret = sessionSecret;
this.sessionCookieAlgorithm = sessionCookieAlgorithm; this.sessionCookieAlgorithm = sessionCookieAlgorithm;
this.sessionCookieName = sessionCookieName; this.sessionCookieName = sessionCookieName;
this.sessionCodec = sessionCodec;
this.suffixes = suffixes; this.suffixes = suffixes;
this.sessionUserName = sessionUserName; this.sessionUserName = sessionUserName;
this.sessionEffectiveUserName = sessionEffectiveUserName; this.sessionEffectiveUserName = sessionEffectiveUserName;
@ -113,17 +108,16 @@ public class OutgoingSessionHandler implements HttpHandler {
session.put(sessionEffectiveUserName, userProfile.getEffectiveUserId()); session.put(sessionEffectiveUserName, userProfile.getEffectiveUserId());
} }
} }
sessionCodec.write(session.id(), session);
Cookie cookie = encodeCookie(session, host, path); Cookie cookie = encodeCookie(session, host, path);
if (cookie != null) { if (cookie != null) {
cookieBox.add(cookie); cookieBox.add(cookie);
} }
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), 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); context.getAttributes().put("outgoingcookies", cookieBox);
} }

View file

@ -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<Session> sessionCodec;
public PersistSessionHandler(Codec<Session> 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);
}
}
}
}

View file

@ -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<Session> 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<Session> newSessionCodec() {
return new FileJsonSessionCodec("SESSION-TEST", null, 1024,
Duration.ofDays(1), Paths.get("/var/tmp/session-test"));
}
private Session create(Codec<Session> sessionCodec, Supplier<String> sessionIdGenerator) throws IOException {
return sessionCodec.create(sessionIdGenerator.get());
}
}