fix done() in context to lock request/response, fix html template resource to redirect to index file resource

This commit is contained in:
Jörg Prante 2023-03-20 08:46:07 +01:00
parent cf259ee8fb
commit 2f17616f1d
12 changed files with 165 additions and 35 deletions

View file

@ -158,6 +158,10 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
} }
protected void setupApplication(Application application) { protected void setupApplication(Application application) {
String name = System.getProperty("application.name");
if (name == null) {
name = "application";
}
String profile = System.getProperty("application.profile"); String profile = System.getProperty("application.profile");
if (profile == null) { if (profile == null) {
profile = "developer"; profile = "developer";
@ -165,7 +169,7 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
String[] args = profile.split(";"); String[] args = profile.split(";");
this.configParams = new ConfigParams() this.configParams = new ConfigParams()
.withArgs(args) .withArgs(args)
.withDirectoryName("application") .withDirectoryName(name)
.withFileNamesWithoutSuffix(args[0]) .withFileNamesWithoutSuffix(args[0])
.withSystemEnvironment() .withSystemEnvironment()
.withSystemProperties(); .withSystemProperties();

View file

@ -16,37 +16,39 @@ import java.util.Objects;
public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder { public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
HttpServerContext httpServerContext; protected HttpServerContext httpServerContext;
HttpAddress httpAddress; protected HttpAddress httpAddress;
InetSocketAddress localAddress; protected InetSocketAddress localAddress;
InetSocketAddress remoteAddress; protected InetSocketAddress remoteAddress;
URL serverURL; protected URL serverURL;
URL baseURL; protected URL baseURL;
String requestPath; protected String requestPath;
Parameter parameter; protected Parameter parameter;
Integer sequenceId; protected Integer sequenceId;
Integer streamId; protected Integer streamId;
Long requestId; protected Long requestId;
HttpVersion httpVersion; protected HttpVersion httpVersion;
HttpMethod httpMethod; protected HttpMethod httpMethod;
HttpHeaders httpHeaders; protected HttpHeaders httpHeaders;
String requestURI; protected String requestURI;
ByteBuffer byteBuffer; protected ByteBuffer byteBuffer;
protected boolean done;
protected BaseHttpRequestBuilder() { protected BaseHttpRequestBuilder() {
this.httpHeaders = new HttpHeaders(); this.httpHeaders = new HttpHeaders();
@ -54,12 +56,18 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
@Override @Override
public BaseHttpRequestBuilder setContext(HttpServerContext httpServerContext) { public BaseHttpRequestBuilder setContext(HttpServerContext httpServerContext) {
if (done) {
return this;
}
this.httpServerContext = httpServerContext; this.httpServerContext = httpServerContext;
return this; return this;
} }
@Override @Override
public BaseHttpRequestBuilder setVersion(HttpVersion httpVersion) { public BaseHttpRequestBuilder setVersion(HttpVersion httpVersion) {
if (done) {
return this;
}
this.httpVersion = httpVersion; this.httpVersion = httpVersion;
return this; return this;
} }
@ -70,6 +78,9 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
@Override @Override
public BaseHttpRequestBuilder setMethod(HttpMethod httpMethod) { public BaseHttpRequestBuilder setMethod(HttpMethod httpMethod) {
if (done) {
return this;
}
this.httpMethod = httpMethod; this.httpMethod = httpMethod;
return this; return this;
} }
@ -80,6 +91,9 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
@Override @Override
public BaseHttpRequestBuilder setRequestURI(String requestURI) { public BaseHttpRequestBuilder setRequestURI(String requestURI) {
if (done) {
return this;
}
this.requestURI = requestURI; this.requestURI = requestURI;
return this; return this;
} }
@ -91,6 +105,9 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
@Override @Override
public BaseHttpRequestBuilder setHeaders(HttpHeaders httpHeaders) { public BaseHttpRequestBuilder setHeaders(HttpHeaders httpHeaders) {
if (done) {
return this;
}
this.httpHeaders = httpHeaders; this.httpHeaders = httpHeaders;
return this; return this;
} }
@ -102,11 +119,17 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
@Override @Override
public BaseHttpRequestBuilder addHeader(String key, String value) { public BaseHttpRequestBuilder addHeader(String key, String value) {
if (done) {
return this;
}
this.httpHeaders.add(key, value); this.httpHeaders.add(key, value);
return this; return this;
} }
public BaseHttpRequestBuilder setBody(ByteBuffer byteBuffer) { public BaseHttpRequestBuilder setBody(ByteBuffer byteBuffer) {
if (done) {
return this;
}
this.byteBuffer = byteBuffer; this.byteBuffer = byteBuffer;
return this; return this;
} }
@ -117,11 +140,17 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
@Override @Override
public BaseHttpRequestBuilder setBaseURL(URL baseURL) { public BaseHttpRequestBuilder setBaseURL(URL baseURL) {
if (done) {
return this;
}
this.baseURL = baseURL; this.baseURL = baseURL;
return this; return this;
} }
public BaseHttpRequestBuilder setBaseURL(HttpAddress httpAddress, String uri, String hostAndPort) { public BaseHttpRequestBuilder setBaseURL(HttpAddress httpAddress, String uri, String hostAndPort) {
if (done) {
return this;
}
Objects.requireNonNull(httpAddress); Objects.requireNonNull(httpAddress);
Objects.requireNonNull(uri); Objects.requireNonNull(uri);
String scheme = httpAddress.isSecure() ? "https" : "http"; String scheme = httpAddress.isSecure() ? "https" : "http";
@ -155,15 +184,21 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
return this; return this;
} }
@Override
public URL getBaseURL() { public URL getBaseURL() {
return baseURL; return baseURL;
} }
@Override
public BaseHttpRequestBuilder setRequestPath(String requestPath) { public BaseHttpRequestBuilder setRequestPath(String requestPath) {
if (done) {
return this;
}
this.requestPath = requestPath; this.requestPath = requestPath;
return this; return this;
} }
@Override
public String getRequestPath() { public String getRequestPath() {
return requestPath; return requestPath;
} }
@ -173,39 +208,66 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
return byteBuffer != null ? charset.decode(byteBuffer) : null; return byteBuffer != null ? charset.decode(byteBuffer) : null;
} }
@Override
public BaseHttpRequestBuilder setParameter(Parameter parameter) {
if (done) {
return this;
}
this.parameter = parameter;
return this;
}
public BaseHttpRequestBuilder setAddress(HttpAddress httpAddress) { public BaseHttpRequestBuilder setAddress(HttpAddress httpAddress) {
if (done) {
return this;
}
this.httpAddress = httpAddress; this.httpAddress = httpAddress;
return this; return this;
} }
public BaseHttpRequestBuilder setLocalAddress(InetSocketAddress localAddress) { public BaseHttpRequestBuilder setLocalAddress(InetSocketAddress localAddress) {
if (done) {
return this;
}
this.localAddress = localAddress; this.localAddress = localAddress;
return this; return this;
} }
public BaseHttpRequestBuilder setRemoteAddress(InetSocketAddress remoteAddress) { public BaseHttpRequestBuilder setRemoteAddress(InetSocketAddress remoteAddress) {
if (done) {
return this;
}
this.remoteAddress = remoteAddress; this.remoteAddress = remoteAddress;
return this; return this;
} }
public BaseHttpRequestBuilder setSequenceId(Integer sequenceId) { public BaseHttpRequestBuilder setSequenceId(Integer sequenceId) {
if (done) {
return this;
}
this.sequenceId = sequenceId; this.sequenceId = sequenceId;
return this; return this;
} }
public BaseHttpRequestBuilder setStreamId(Integer streamId) { public BaseHttpRequestBuilder setStreamId(Integer streamId) {
if (done) {
return this;
}
this.streamId = streamId; this.streamId = streamId;
return this; return this;
} }
public BaseHttpRequestBuilder setRequestId(Long requestId) { public BaseHttpRequestBuilder setRequestId(Long requestId) {
if (done) {
return this;
}
this.requestId = requestId; this.requestId = requestId;
return this; return this;
} }
public BaseHttpRequestBuilder setParameter(Parameter parameter) { @Override
this.parameter = parameter; public void done() {
return this; this.done = true;
} }
private static String stripPort(String hostMaybePort) { private static String stripPort(String hostMaybePort) {

View file

@ -74,6 +74,8 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
protected Long length; protected Long length;
protected boolean done;
protected BaseHttpResponseBuilder() { protected BaseHttpResponseBuilder() {
reset(); reset();
} }
@ -101,12 +103,18 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public BaseHttpResponseBuilder setVersion(HttpVersion version) { public BaseHttpResponseBuilder setVersion(HttpVersion version) {
if (done) {
return this;
}
this.version = version; this.version = version;
return this; return this;
} }
@Override @Override
public BaseHttpResponseBuilder setResponseStatus(HttpResponseStatus status) { public BaseHttpResponseBuilder setResponseStatus(HttpResponseStatus status) {
if (done) {
return this;
}
if (this.status == null) { if (this.status == null) {
this.status = status; this.status = status;
} else { } else {
@ -127,6 +135,9 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public BaseHttpResponseBuilder setHeader(CharSequence name, String value) { public BaseHttpResponseBuilder setHeader(CharSequence name, String value) {
if (done) {
return this;
}
if (HttpHeaderNames.CONTENT_TYPE.equalsIgnoreCase(name.toString())) { if (HttpHeaderNames.CONTENT_TYPE.equalsIgnoreCase(name.toString())) {
setContentType(value); setContentType(value);
} }
@ -139,6 +150,9 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public BaseHttpResponseBuilder addHeader(CharSequence name, String value) { public BaseHttpResponseBuilder addHeader(CharSequence name, String value) {
if (done) {
return this;
}
if (headers.containsHeader(name)) { if (headers.containsHeader(name)) {
logger.log(Level.WARNING, "header already exist: " + headers.get(name) + " adding " + value); logger.log(Level.WARNING, "header already exist: " + headers.get(name) + " adding " + value);
} }
@ -148,18 +162,27 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public BaseHttpResponseBuilder setTrailingHeader(CharSequence name, String value) { public BaseHttpResponseBuilder setTrailingHeader(CharSequence name, String value) {
if (done) {
return this;
}
trailingHeaders.set(name, value); trailingHeaders.set(name, value);
return this; return this;
} }
@Override @Override
public BaseHttpResponseBuilder setContentType(String contentType) { public BaseHttpResponseBuilder setContentType(String contentType) {
if (done) {
return this;
}
this.contentType = contentType; this.contentType = contentType;
return this; return this;
} }
@Override @Override
public BaseHttpResponseBuilder setCharset(Charset charset) { public BaseHttpResponseBuilder setCharset(Charset charset) {
if (done) {
return this;
}
this.charset = charset; this.charset = charset;
return this; return this;
} }
@ -167,6 +190,9 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public BaseHttpResponseBuilder shouldFlush(boolean shouldFlush) { public BaseHttpResponseBuilder shouldFlush(boolean shouldFlush) {
if (done) {
return this;
}
this.shouldFlush = shouldFlush; this.shouldFlush = shouldFlush;
return this; return this;
} }
@ -178,6 +204,9 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public BaseHttpResponseBuilder shouldClose(boolean shouldClose) { public BaseHttpResponseBuilder shouldClose(boolean shouldClose) {
if (done) {
return this;
}
this.shouldClose = shouldClose; this.shouldClose = shouldClose;
return this; return this;
} }
@ -189,18 +218,27 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public BaseHttpResponseBuilder setSequenceId(Integer sequenceId) { public BaseHttpResponseBuilder setSequenceId(Integer sequenceId) {
if (done) {
return this;
}
this.sequenceId = sequenceId; this.sequenceId = sequenceId;
return this; return this;
} }
@Override @Override
public BaseHttpResponseBuilder setStreamId(Integer streamId) { public BaseHttpResponseBuilder setStreamId(Integer streamId) {
if (done) {
return this;
}
this.streamId = streamId; this.streamId = streamId;
return this; return this;
} }
@Override @Override
public BaseHttpResponseBuilder setResponseId(Long responseId) { public BaseHttpResponseBuilder setResponseId(Long responseId) {
if (done) {
return this;
}
this.responseId = responseId; this.responseId = responseId;
return this; return this;
} }
@ -260,6 +298,9 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public BaseHttpResponseBuilder addCookie(Cookie cookie) { public BaseHttpResponseBuilder addCookie(Cookie cookie) {
if (done) {
return this;
}
Objects.requireNonNull(cookie); Objects.requireNonNull(cookie);
headers.add(HttpHeaderNames.SET_COOKIE, CookieEncoder.STRICT.encode(cookie)); headers.add(HttpHeaderNames.SET_COOKIE, CookieEncoder.STRICT.encode(cookie));
return this; return this;
@ -275,6 +316,12 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
@Override @Override
public abstract HttpResponse build(); public abstract HttpResponse build();
@Override
public void done() {
this.done = true;
logger.log(Level.FINER, "done");
}
public void buildHeaders(long contentLength) { public void buildHeaders(long contentLength) {
this.length = contentLength; this.length = contentLength;
if (!headers.containsHeader(HttpHeaderNames.CONTENT_TYPE)) { if (!headers.containsHeader(HttpHeaderNames.CONTENT_TYPE)) {

View file

@ -130,6 +130,8 @@ public class BaseHttpServerContext implements HttpServerContext {
@Override @Override
public void done() { public void done() {
this.done = true; this.done = true;
this.httpRequestBuilder.done();
this.httpResponseBuilder.done();
} }
@Override @Override

View file

@ -45,4 +45,6 @@ public interface HttpRequestBuilder {
CharBuffer getBodyAsChars(Charset charset); CharBuffer getBodyAsChars(Charset charset);
HttpRequest build(); HttpRequest build();
void done();
} }

View file

@ -68,4 +68,6 @@ public interface HttpResponseBuilder {
HttpResponse build(); HttpResponse build();
void done();
} }

View file

@ -98,6 +98,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
} else { } else {
logger.log(Level.FINE, "handle: generate cacheable resource"); logger.log(Level.FINE, "handle: generate cacheable resource");
generateCacheableResource(context, resource); generateCacheableResource(context, resource);
context.done();
} }
logger.log(Level.FINE, "handle: done"); logger.log(Level.FINE, "handle: done");
} }
@ -191,7 +192,6 @@ public abstract class AbstractResourceHandler implements HttpHandler {
send(resource, HttpResponseStatus.OK, contentType, context, 0L, -1L); send(resource, HttpResponseStatus.OK, contentType, context, 0L, -1L);
} }
} }
context.done();
} }
private void performRangeResponse(HttpServerContext context, private void performRangeResponse(HttpServerContext context,

View file

@ -97,7 +97,6 @@ public class ClassLoaderResourceHandler extends AbstractResourceHandler {
URLConnection urlConnection = url.openConnection(); URLConnection urlConnection = url.openConnection();
this.lastModified = Instant.ofEpochMilli(urlConnection.getLastModified()); this.lastModified = Instant.ofEpochMilli(urlConnection.getLastModified());
this.length = urlConnection.getContentLength(); this.length = urlConnection.getContentLength();
httpServerContext.done();
if (logger.isLoggable(Level.FINER)) { if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "success: path=[" + path + logger.log(Level.FINER, "success: path=[" + path +
"] -> url=" + url + " lastModified=" + lastModified + "length=" + length); "] -> url=" + url + " lastModified=" + lastModified + "length=" + length);

View file

@ -139,7 +139,6 @@ public class FileResourceHandler extends AbstractResourceHandler {
if (isExists) { if (isExists) {
this.lastModified = Files.getLastModifiedTime(path).toInstant(); this.lastModified = Files.getLastModifiedTime(path).toInstant();
this.length = Files.size(path); this.length = Files.size(path);
httpServerContext.done();
} else { } else {
this.lastModified = Instant.ofEpochMilli(0L); this.lastModified = Instant.ofEpochMilli(0L);
this.length = 0; this.length = 0;

View file

@ -60,20 +60,30 @@ public class HtmlTemplateResource implements HttpServerResource {
this.name = path.getFileName().toString(); this.name = path.getFileName().toString();
this.baseName = AbstractResourceHandler.basename(name); this.baseName = AbstractResourceHandler.basename(name);
this.suffix = AbstractResourceHandler.suffix(name); this.suffix = AbstractResourceHandler.suffix(name);
this.isExists = Files.exists(path); if (Files.isDirectory(path)) {
this.isDirectory = Files.isDirectory(path); if (getIndexFileName() != null) {
logger.log(Level.FINE, "exists = " + isExists); Path indexPath = path.resolve(indexFileName);
logger.log(Level.FINE, "isDirectory = " + isDirectory); if (Files.exists(indexPath)) {
if (isDirectory && getIndexFileName() != null) { this.isExistsIndexFile = true;
this.path = path.resolve(indexFileName); this.path = indexPath;
this.isExistsIndexFile = Files.exists(path); this.isDirectory = false;
httpServerContext.done(); } else {
this.isExistsIndexFile = false;
this.isDirectory = true;
}
} else {
this.isExistsIndexFile = false;
this.isDirectory = true;
}
} else { } else {
this.isExistsIndexFile = false; this.isExistsIndexFile = false;
this.isDirectory = false;
} }
this.isExists = Files.exists(path);
logger.log(Level.FINE, "exists = " + isExists);
logger.log(Level.FINE, "isDirectory = " + isDirectory);
if (isExists) { if (isExists) {
this.lastModified = Files.getLastModifiedTime(path).toInstant(); this.lastModified = Files.getLastModifiedTime(path).toInstant();
httpServerContext.done();
} else { } else {
this.lastModified = Instant.now(); this.lastModified = Instant.now();
} }

View file

@ -4,6 +4,8 @@ import groovy.text.TemplateEngine;
import groovy.text.markup.BaseTemplate; import groovy.text.markup.BaseTemplate;
import groovy.text.markup.MarkupTemplateEngine; import groovy.text.markup.MarkupTemplateEngine;
import groovy.text.markup.TemplateConfiguration; import groovy.text.markup.TemplateConfiguration;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.http.server.Application; import org.xbib.net.http.server.Application;
import org.xbib.net.http.server.HttpHandler; import org.xbib.net.http.server.HttpHandler;
import org.xbib.net.http.server.HttpServerContext; import org.xbib.net.http.server.HttpServerContext;
@ -15,6 +17,8 @@ import java.util.Locale;
public class GroovyMarkupTemplateHandler implements HttpHandler { public class GroovyMarkupTemplateHandler implements HttpHandler {
private static final Logger logger = Logger.getLogger(GroovyMarkupTemplateHandler.class.getName());
private final Resolver<Path> resolver; private final Resolver<Path> resolver;
private final ClassLoader classLoader; private final ClassLoader classLoader;
@ -59,10 +63,12 @@ public class GroovyMarkupTemplateHandler implements HttpHandler {
DefaultTemplateResolver templateResolver = context.attributes().get(DefaultTemplateResolver.class, "templateresolver"); DefaultTemplateResolver templateResolver = context.attributes().get(DefaultTemplateResolver.class, "templateresolver");
if (templateResolver == null) { if (templateResolver == null) {
context.attributes().put("templateresolver", this.templateResolver); context.attributes().put("templateresolver", this.templateResolver);
logger.log(Level.FINER, "setting templateresolver " + this.templateResolver);
} }
TemplateEngine templateEngine = context.attributes().get(TemplateEngine.class, "templateengine"); TemplateEngine templateEngine = context.attributes().get(TemplateEngine.class, "templateengine");
if (templateEngine == null) { if (templateEngine == null) {
context.attributes().put("templateengine", this.templateEngine); context.attributes().put("templateengine", this.templateEngine);
logger.log(Level.FINER, "setting templateengine " + this.templateEngine);
} }
} }

View file

@ -120,13 +120,11 @@ public class GroovyTemplateResource extends HtmlTemplateResource {
} }
Writable writable = template.make(binding.getVariables()); Writable writable = template.make(binding.getVariables());
httpServerContext.attributes().put("writable", writable); httpServerContext.attributes().put("writable", writable);
httpServerContext.done();
} catch (Exception e) { } catch (Exception e) {
// in case there is not template with negotiated locale // in case there is not template with negotiated locale
templateResolver.setLocale(application.getLocale()); templateResolver.setLocale(application.getLocale());
Writable writable = template.make(binding.getVariables()); Writable writable = template.make(binding.getVariables());
httpServerContext.attributes().put("writable", writable); httpServerContext.attributes().put("writable", writable);
httpServerContext.done();
} finally { } finally {
lock.unlock(); lock.unlock();
} }
@ -134,7 +132,6 @@ public class GroovyTemplateResource extends HtmlTemplateResource {
// for Groovy template engines without a resolver // for Groovy template engines without a resolver
Writable writable = template.make(binding.getVariables()); Writable writable = template.make(binding.getVariables());
httpServerContext.attributes().put("writable", writable); httpServerContext.attributes().put("writable", writable);
httpServerContext.done();
} }
logger.log(Level.FINER, "rendering done: " + httpServerContext.isDone()); logger.log(Level.FINER, "rendering done: " + httpServerContext.isDone());
} }