add session name, fix netty request body retain, fix static suffixes, log throwables in the router dispatch

This commit is contained in:
Jörg Prante 2023-03-20 17:07:18 +01:00
parent 2f17616f1d
commit 1df5f884b1
13 changed files with 108 additions and 73 deletions

View file

@ -10,15 +10,11 @@ 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.settings.Settings;
public class WebApplication extends BaseApplication {
private final WebApplicationBuilder builder;
protected WebApplication(WebApplicationBuilder builder) {
super(builder);
this.builder = builder;
}
public static WebApplicationBuilder builder() {
@ -30,7 +26,7 @@ public class WebApplication extends BaseApplication {
}
protected Codec<Session> buildSessionCodec(HttpServerContext httpServerContext) {
return new FileJsonSessionCodec(this, 1024, Duration.ofDays(1),
return new FileJsonSessionCodec(sessionName, this, 1024, Duration.ofDays(1),
Paths.get("/var/tmp/session"));
}
@ -40,7 +36,7 @@ public class WebApplication extends BaseApplication {
return new IncomingSessionHandler(
getSecret(),
"HmacSHA1",
"SESSION",
sessionName,
sessionCodec,
getStaticFileSuffixes(),
"user_id",
@ -53,7 +49,7 @@ public class WebApplication extends BaseApplication {
return new OutgoingSessionHandler(
getSecret(),
"HmacSHA1",
"SESSION",
sessionName,
Duration.ofDays(1),
sessionCodec,
getStaticFileSuffixes(),

View file

@ -15,16 +15,17 @@ import java.nio.charset.Charset;
public class HttpRequestBuilder extends BaseHttpRequestBuilder {
FullHttpRequest fullHttpRequest;
protected FullHttpRequest fullHttpRequest;
ByteBuffer byteBuffer;
protected ByteBuffer byteBuffer;
protected HttpRequestBuilder() {
}
public HttpRequestBuilder setFullHttpRequest(FullHttpRequest fullHttpRequest) {
if (fullHttpRequest != null) {
this.fullHttpRequest = fullHttpRequest;
// retain request so we can read the body later without refCnt=0 error
this.fullHttpRequest = fullHttpRequest.retain();
setVersion(HttpVersion.valueOf(fullHttpRequest.protocolVersion().text()));
setMethod(HttpMethod.valueOf(fullHttpRequest.method().name()));
setRequestURI(fullHttpRequest.uri());

View file

@ -41,6 +41,8 @@ public class BaseApplication implements Application {
private final HttpRequestValidator httpRequestValidator;
protected final String sessionName;
private final HttpHandler incomingCookieHandler;
private final HttpHandler outgoingCookieHandler;
@ -59,6 +61,7 @@ public class BaseApplication implements Application {
new NamedThreadFactory("org-xbib-net-http-server-application"));
this.executor.setRejectedExecutionHandler((runnable, threadPoolExecutor) ->
logger.log(Level.SEVERE, "rejected " + runnable + " for thread pool executor = " + threadPoolExecutor));
this.sessionName = getSettings().get("session.name", "SESS");
this.httpRequestValidator = buildRequestValidator();
this.incomingCookieHandler = buildIncomingCookieHandler();
this.outgoingCookieHandler = buildOutgoingCookieHandler();
@ -121,7 +124,14 @@ public class BaseApplication implements Application {
@Override
public void dispatch(HttpRequestBuilder requestBuilder, HttpResponseBuilder responseBuilder) {
Future<?> future = executor.submit(() -> getRouter().route(requestBuilder, responseBuilder));
Future<?> future = executor.submit(() -> {
try {
getRouter().route(requestBuilder, responseBuilder);
} catch (Throwable t) {
logger.log(Level.SEVERE, t.getMessage(), t);
throw t;
}
});
logger.log(Level.FINE, "dispatching " + future);
}
@ -132,7 +142,12 @@ public class BaseApplication implements Application {
Future<?> future = executor.submit(() -> {
HttpServerContext httpServerContext = createContext(null, httpRequestBuilder, httpResponseBuilder);
httpServerContext.attributes().put("responsebuilder", httpResponseBuilder);
try {
getRouter().routeStatus(httpResponseStatus, httpServerContext);
} catch (Throwable t) {
logger.log(Level.SEVERE, t.getMessage(), t);
throw t;
}
});
logger.log(Level.FINE, "dispatching status " + future);
}
@ -166,7 +181,7 @@ public class BaseApplication implements Application {
}
protected Codec<Session> buildSessionCodec(HttpServerContext httpServerContext) {
return new MemoryPropertiesSessionCodec(this, 1024, Duration.ofDays(1));
return new MemoryPropertiesSessionCodec(sessionName,this, 1024, Duration.ofDays(1));
}
protected HttpHandler buildIncomingSessionHandler(HttpServerContext httpServerContext) {
@ -175,7 +190,7 @@ public class BaseApplication implements Application {
return new IncomingSessionHandler(
getSecret(),
"HmacSHA1",
"SESS",
sessionName,
sessionCodec,
getStaticFileSuffixes(),
"user_id",
@ -188,7 +203,7 @@ public class BaseApplication implements Application {
return new OutgoingSessionHandler(
getSecret(),
"HmacSHA1",
"SESS",
sessionName,
Duration.ofDays(1),
sessionCodec,
getStaticFileSuffixes(),
@ -202,13 +217,13 @@ public class BaseApplication implements Application {
@Override
public void onCreated(Session session) {
logger.log(Level.INFO, "session created = " + session);
logger.log(Level.FINE, "session name = " + sessionName + " created = " + session);
builder.applicationModuleList.forEach(module -> module.onOpen(this, session));
}
@Override
public void onDestroy(Session session) {
logger.log(Level.INFO, "session destroyed = " + session);
logger.log(Level.FINE, "session name = " + sessionName + " destroyed = " + session);
builder.applicationModuleList.forEach(module -> module.onClose(this, session));
}

View file

@ -4,7 +4,6 @@ import org.xbib.config.ConfigLoader;
import org.xbib.config.ConfigLogger;
import org.xbib.config.ConfigParams;
import org.xbib.config.SystemConfigLogger;
import org.xbib.datastructures.common.ImmutableSet;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.settings.Settings;
@ -136,12 +135,8 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this;
}
public ApplicationBuilder addStaticSuffixes(String... suffixes) {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (String suffix : suffixes) {
builder.add(suffix);
}
this.staticFileSuffixes = builder.build(new String[]{});
public ApplicationBuilder setStaticSuffixes(String... suffixes) {
this.staticFileSuffixes = Set.of(suffixes);
return this;
}
@ -152,12 +147,13 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
@Override
public Application build() {
prepareApplication();
Application application = new BaseApplication(this);
setupApplication(application);
return application;
}
protected void setupApplication(Application application) {
protected void prepareApplication() {
String name = System.getProperty("application.name");
if (name == null) {
name = "application";
@ -176,6 +172,15 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
this.configLoader = ConfigLoader.getInstance()
.withLogger(bootLogger);
this.settings = configLoader.load(configParams);
if (staticFileSuffixes == null) {
staticFileSuffixes = DEFAULT_SUFFIXES;
}
}
protected void setupApplication(Application application) {
if (router != null) {
router.setApplication(application);
}
for (Map.Entry<String, Settings> entry : settings.getGroups("module").entrySet()) {
String moduleName = entry.getKey();
Settings moduleSettings = entry.getValue();
@ -197,22 +202,8 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
logger.log(Level.WARNING, "disabled module: " + moduleName);
}
}
if (router != null) {
router.setApplication(application);
}
if (staticFileSuffixes == null) {
staticFileSuffixes = DEFAULT_SUFFIXES;
}
}
private static final Set<String> DEFAULT_SUFFIXES = ImmutableSet.<String>builder()
.add("css")
.add("js")
.add("ico")
.add("png")
.add("jpg")
.add("jpeg")
.add("gif")
.add("woff2")
.build(new String[]{});
private static final Set<String> DEFAULT_SUFFIXES =
Set.of("css", "js", "ico", "png", "jpg", "jpeg", "gif", "woff2");
}

View file

@ -59,8 +59,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
Resource resource = createResource(context);
logger.log(Level.FINE, "handle: resource = " + (resource != null ? resource.getClass().getName() + " " + resource : null));
if (resource instanceof HtmlTemplateResource) {
logger.log(Level.FINE, "handle: HTML template resource, generate cacheable resource, parameter = " +
context.httpRequest().getParameter());
logger.log(Level.FINE, "handle: HTML template resource, generate cacheable resource");
generateCacheableResource(context, resource);
logger.log(Level.FINE, "handle: done");
return;

View file

@ -49,23 +49,24 @@ public class HtmlTemplateResource implements HttpServerResource {
if (root == null) {
throw new IllegalArgumentException("no home path set for template resource resolving");
}
logger.log(Level.FINE, "class = " + getClass().getName());
logger.log(Level.FINE, "root = " + root);
this.resourcePath = httpServerContext.request().getRequestPath().substring(1);
logger.log(Level.FINE, "resource path = " + resourcePath);
this.path = resourcePath.length() > 0 ? root.resolve(resourcePath) : root;
logger.log(Level.FINE, "path = " + path);
logger.log(Level.FINE, "index file name = " + indexFileName);
this.url = URL.create(path.toUri().toString());
logger.log(Level.FINE, "uri = " + url);
this.name = path.getFileName().toString();
this.baseName = AbstractResourceHandler.basename(name);
this.suffix = AbstractResourceHandler.suffix(name);
if (Files.isDirectory(path)) {
if (getIndexFileName() != null) {
Path indexPath = path.resolve(indexFileName);
logger.log(Level.FINE, "index path = " + indexPath);
if (Files.exists(indexPath)) {
this.isExistsIndexFile = true;
this.path = indexPath;
logger.log(Level.FINE, "index file path found = " + path);
this.isDirectory = false;
} else {
this.isExistsIndexFile = false;
@ -82,6 +83,8 @@ public class HtmlTemplateResource implements HttpServerResource {
this.isExists = Files.exists(path);
logger.log(Level.FINE, "exists = " + isExists);
logger.log(Level.FINE, "isDirectory = " + isDirectory);
this.url = URL.create(path.toUri().toString());
logger.log(Level.FINE, "url = " + url);
if (isExists) {
this.lastModified = Files.getLastModifiedTime(path).toInstant();
} else {

View file

@ -18,6 +18,8 @@ public class BaseSession implements Session {
private final SessionListener sessionListener;
private final String name;
private final String id;
private final Duration lifetime;
@ -30,11 +32,13 @@ public class BaseSession implements Session {
public BaseSession(SessionListener sessionListener,
int cacheSize,
String name,
String id,
boolean create,
Duration lifetime) {
this.cacheSize = cacheSize;
this.sessionListener = sessionListener;
this.name = name;
this.id = id;
this.lifetime = lifetime;
this.map = new LinkedHashMap<>();
@ -47,6 +51,12 @@ public class BaseSession implements Session {
}
}
}
@Override
public String name() {
return name;
}
@Override
public String id() {
return id;

View file

@ -5,6 +5,8 @@ import java.util.Map;
public interface Session extends Map<String, Object> {
String name();
String id();
void invalidate();

View file

@ -27,6 +27,8 @@ public class FileJsonSessionCodec implements Codec<Session> {
private static final Logger logger = Logger.getLogger(FileJsonSessionCodec.class.getName());
private final String name;
private final ReentrantReadWriteLock lock;
private final SessionListener sessionListener;
@ -37,10 +39,13 @@ public class FileJsonSessionCodec implements Codec<Session> {
private final Duration sessionDuration;
public FileJsonSessionCodec(SessionListener sessionListener,
public FileJsonSessionCodec(String name,
SessionListener sessionListener,
int sessionCacheSize,
Duration sessionDuration,
Path path) {
Path path
) {
this.name = name;
this.sessionListener = sessionListener;
this.path = path;
this.sessionCacheSize = sessionCacheSize;
@ -56,7 +61,7 @@ public class FileJsonSessionCodec implements Codec<Session> {
@Override
public Session create(String key) throws IOException {
return new BaseSession(sessionListener, sessionCacheSize, key, true, sessionDuration);
return new BaseSession(sessionListener, sessionCacheSize, name, key, true, sessionDuration);
}
@Override
@ -66,7 +71,7 @@ public class FileJsonSessionCodec implements Codec<Session> {
try {
readLock.lock();
PercentEncoder percentEncoder = PercentEncoders.getUnreservedEncoder(StandardCharsets.UTF_8);
session = new BaseSession(sessionListener, sessionCacheSize, key, false, sessionDuration);
session = new BaseSession(sessionListener, sessionCacheSize, name, key, false, sessionDuration);
Map<String, Object> map = JsonUtil.toMap(Files.readString(path.resolve(percentEncoder.encode(key))));
session.putAll(map);
return session;

View file

@ -17,7 +17,6 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.xbib.net.http.server.HttpServerContext;
import org.xbib.net.http.server.persist.Codec;
import org.xbib.net.http.server.session.BaseSession;
import org.xbib.net.http.server.session.Session;
@ -26,6 +25,8 @@ import org.xbib.net.util.JsonUtil;
public class JdbcSessionCodec implements Codec<Session>, Closeable {
private final String name;
private final SessionListener sessionListener;
private final int sessionCacheSize;
@ -44,7 +45,7 @@ public class JdbcSessionCodec implements Codec<Session>, Closeable {
private final ScheduledExecutorService scheduledExecutorService;
public JdbcSessionCodec(HttpServerContext httpServerContext,
public JdbcSessionCodec(String name,
SessionListener sessionListener,
int sessionCacheSize,
Duration sessionDuration,
@ -53,6 +54,7 @@ public class JdbcSessionCodec implements Codec<Session>, Closeable {
String writeSessionStringStatement,
String deleteSessionStringStatement,
String purgeSessionStringStatement) {
this.name = name;
this.sessionListener = sessionListener;
this.sessionCacheSize = sessionCacheSize;
this.sessionDuration = sessionDuration;
@ -67,7 +69,7 @@ public class JdbcSessionCodec implements Codec<Session>, Closeable {
@Override
public Session create(String key) throws IOException {
return new BaseSession(sessionListener, sessionCacheSize, key, true, sessionDuration);
return new BaseSession(sessionListener, sessionCacheSize, name, key, true, sessionDuration);
}
@Override
@ -76,7 +78,7 @@ public class JdbcSessionCodec implements Codec<Session>, Closeable {
try {
Map<String, Object> map = JsonUtil.toMap(readString(key));
if (map != null) {
session = new BaseSession(sessionListener, sessionCacheSize, key, false, sessionDuration);
session = new BaseSession(sessionListener, sessionCacheSize, name, key, false, sessionDuration);
session.putAll(map);
return session;
}

View file

@ -14,28 +14,32 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
public class MemoryPropertiesSessionCodec implements Codec<Session> {
private final ReentrantReadWriteLock lock;
private static final Map<String, Object> store = new HashMap<>();
private final String name;
private final ReentrantReadWriteLock lock;
private final SessionListener sessionListener;
private final int sessionCacheSize;
private final Duration sessionDuration;
public MemoryPropertiesSessionCodec(SessionListener sessionListener,
public MemoryPropertiesSessionCodec(String name,
SessionListener sessionListener,
int sessionCacheSize,
Duration sessionDuration) {
this.lock = new ReentrantReadWriteLock();
this.name = name;
this.sessionListener = sessionListener;
this.sessionCacheSize = sessionCacheSize;
this.sessionDuration = sessionDuration;
this.lock = new ReentrantReadWriteLock();
}
@Override
public Session create(String key) throws IOException {
return new BaseSession(sessionListener, sessionCacheSize, key, true, sessionDuration);
return new BaseSession(sessionListener, sessionCacheSize, name, key, true, sessionDuration);
}
@Override
@ -66,7 +70,7 @@ public class MemoryPropertiesSessionCodec implements Codec<Session> {
}
private Session toSession(String key, Properties properties) {
Session session = new BaseSession(sessionListener, sessionCacheSize, key, false, sessionDuration);
Session session = new BaseSession(sessionListener, sessionCacheSize, name, key, false, sessionDuration);
properties.forEach((k, v) -> session.put(k.toString(), v));
return session;
}

View file

@ -5,6 +5,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
@ -26,7 +27,7 @@ public class BlockingThreadPoolExecutor extends ThreadPoolExecutor {
ThreadFactory threadFactory) {
super(nThreads, nThreads, keepAliveTime, timeUnit, createBlockingQueue(maxQueue), threadFactory);
logger.log(Level.INFO, "blocking threadpool executor up with nThreads = " + nThreads +
" keepALiveTime = " + keepAliveTime +
" keepAliveTime = " + keepAliveTime +
" time unit = " + timeUnit +
" maxQueue = " + maxQueue +
" thread factory = " + threadFactory);
@ -42,7 +43,7 @@ public class BlockingThreadPoolExecutor extends ThreadPoolExecutor {
@Override
protected void afterExecute(Runnable runnable, Throwable terminationCause) {
super.afterExecute(runnable, terminationCause);
logger.log(Level.FINE, "after dispatching " + runnable);
logger.log(Level.FINE, "after dispatching " + runnable + " terminationCause = " + terminationCause);
Throwable throwable = terminationCause;
if (throwable == null && runnable instanceof Future<?>) {
try {

View file

@ -3,6 +3,8 @@ package org.xbib.net.http.template.groovy;
import groovy.text.markup.BaseTemplate;
import groovy.text.markup.MarkupTemplateEngine;
import groovy.text.markup.TemplateConfiguration;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.URL;
@ -27,13 +29,13 @@ public abstract class DefaultMarkupTemplate extends BaseTemplate {
private static final Logger logger = Logger.getLogger(DefaultMarkupTemplate.class.getName());
private final Application application;
protected final Application application;
private final HttpRequest request;
protected final Session session;
private final HttpResponseBuilder responseBuilder;
protected final HttpRequest request;
private final Session session;
protected final HttpResponseBuilder responseBuilder;
public DefaultMarkupTemplate(MarkupTemplateEngine templateEngine,
Map<String,?> model,
@ -41,9 +43,13 @@ public abstract class DefaultMarkupTemplate extends BaseTemplate {
TemplateConfiguration configuration) {
super(templateEngine, model, modelTypes, configuration);
this.application = (Application) model.get("application");
this.request = (HttpRequest) model.get("request");
this.responseBuilder = (HttpResponseBuilder) model.get("responsebuilder");
Objects.requireNonNull(this.application, "application must not be null");
this.session = (Session) model.get("session");
Objects.requireNonNull(this.session, "session must not be null");
this.request = (HttpRequest) model.get("request");
Objects.requireNonNull(this.request, "request must not be null");
this.responseBuilder = (HttpResponseBuilder) model.get("responsebuilder");
Objects.requireNonNull(this.responseBuilder, "response must not be null");
}
public void responseStatus(HttpResponseStatus responseStatus) {
@ -67,23 +73,23 @@ public abstract class DefaultMarkupTemplate extends BaseTemplate {
responseBuilder.setHeader(HttpHeaderNames.CONTENT_LENGTH, Integer.toString(contentLength));
}
public void sendPermanentRedirect(String url) {
responseBuilder.setResponseStatus(HttpResponseStatus.MOVED_PERMANENTLY);
public void movedPermanently(String url) {
responseBuilder.setResponseStatus(HttpResponseStatus.MOVED_PERMANENTLY); // 301
responseBuilder.setHeader(HttpHeaderNames.LOCATION, url);
}
public void sendRedirect(String url) {
responseBuilder.setResponseStatus(HttpResponseStatus.FOUND);
public void found(String url) {
responseBuilder.setResponseStatus(HttpResponseStatus.FOUND); // 302
responseBuilder.setHeader(HttpHeaderNames.LOCATION, url);
}
public void seeOther(String url) {
responseBuilder.setResponseStatus(HttpResponseStatus.SEE_OTHER);
responseBuilder.setResponseStatus(HttpResponseStatus.SEE_OTHER); // 303
responseBuilder.setHeader(HttpHeaderNames.LOCATION, url);
}
public void temporaryRedirect(String url) {
responseBuilder.setResponseStatus(HttpResponseStatus.TEMPORARY_REDIRECT);
responseBuilder.setResponseStatus(HttpResponseStatus.TEMPORARY_REDIRECT); // 307
responseBuilder.setHeader(HttpHeaderNames.LOCATION, url);
}