working on forcing cookies, better header setting/adding, session/userprofile persisting
This commit is contained in:
parent
e444bfca5a
commit
715ee9cd2a
49 changed files with 384 additions and 399 deletions
|
@ -1,34 +0,0 @@
|
|||
package org.xbib.net.http.j2html;
|
||||
|
||||
import org.xbib.net.Resource;
|
||||
import org.xbib.net.http.server.resource.HtmlTemplateResourceHandler;
|
||||
import org.xbib.net.http.server.route.HttpRouterContext;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class J2HtmlAuthResourceHandler extends HtmlTemplateResourceHandler {
|
||||
|
||||
public J2HtmlAuthResourceHandler() {
|
||||
this(null, "java", "index.java");
|
||||
}
|
||||
|
||||
public J2HtmlAuthResourceHandler(Path root, String suffix, String indexFileName) {
|
||||
super(root, suffix, indexFileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Resource createResource(HttpRouterContext httpRouterContext) throws IOException {
|
||||
return httpRouterContext.isAuthenticated() ?
|
||||
createAuthenticatedResource(httpRouterContext) :
|
||||
createUnauthenticatedResource(httpRouterContext);
|
||||
}
|
||||
|
||||
protected Resource createAuthenticatedResource(HttpRouterContext httpRouterContext) throws IOException {
|
||||
return new J2HtmlResource(this, httpRouterContext);
|
||||
}
|
||||
|
||||
protected Resource createUnauthenticatedResource(HttpRouterContext httpRouterContext) throws IOException {
|
||||
return new UnauthorizedHandler.UnauthorizedResource(this, httpRouterContext);
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.Objects;
|
||||
|
||||
import static org.xbib.j2html.TagCreator.body;
|
||||
import static org.xbib.j2html.TagCreator.document;
|
||||
import static org.xbib.j2html.TagCreator.h1;
|
||||
import static org.xbib.j2html.TagCreator.html;
|
||||
import static org.xbib.net.http.HttpHeaderNames.CONTENT_TYPE;
|
||||
|
@ -37,17 +38,13 @@ public class J2HtmlResource extends HtmlTemplateResource {
|
|||
httpResponseStatus = getResponseStatus();
|
||||
}
|
||||
context.status(httpResponseStatus)
|
||||
.header("cache-control", cacheControl()) // override default must-revalidate behavior
|
||||
.header("content-length", Integer.toString(dataBuffer.writePosition()))
|
||||
.header(CONTENT_TYPE, "text/html; charset=" + getCharset().displayName())
|
||||
.setHeader("cache-control", "no-cache")
|
||||
.setHeader("content-length", Integer.toString(dataBuffer.writePosition()))
|
||||
.setHeader(CONTENT_TYPE, "text/html; charset=" + getCharset().displayName())
|
||||
.body(dataBuffer)
|
||||
.done();
|
||||
}
|
||||
|
||||
protected String cacheControl() {
|
||||
return "no-cache";
|
||||
}
|
||||
|
||||
protected HttpResponseStatus getResponseStatus() {
|
||||
return HttpResponseStatus.OK;
|
||||
}
|
||||
|
@ -65,6 +62,6 @@ public class J2HtmlResource extends HtmlTemplateResource {
|
|||
* @return the body string fo the HTTP response
|
||||
*/
|
||||
protected String renderHtml(HttpRouterContext context) {
|
||||
return html(body(h1("Hello World"))).render();
|
||||
return document(html(body(h1("Hello World"))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,12 @@ public class J2HtmlResourceHandler extends HtmlTemplateResourceHandler {
|
|||
|
||||
@Override
|
||||
protected Resource createResource(HttpRouterContext httpRouterContext) throws IOException {
|
||||
return httpRouterContext.isAuthenticated() ?
|
||||
new J2HtmlResource(this, httpRouterContext) :
|
||||
createUnauthenticatedResource(httpRouterContext);
|
||||
}
|
||||
|
||||
protected Resource createUnauthenticatedResource(HttpRouterContext httpRouterContext) throws IOException {
|
||||
return new J2HtmlResource(this, httpRouterContext);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,14 +61,9 @@ public class J2HtmlServiceBuilder extends BaseHttpServiceBuilder {
|
|||
|
||||
public J2HtmlService build() {
|
||||
if (handlers == null) {
|
||||
if (securityDomain != null) {
|
||||
HttpHandler httpHandler = new J2HtmlAuthResourceHandler(prefix, suffix, "index.java");
|
||||
setHandler(httpHandler);
|
||||
} else {
|
||||
HttpHandler httpHandler = new J2HtmlResourceHandler(prefix, suffix, "index.java");
|
||||
setHandler(httpHandler);
|
||||
}
|
||||
}
|
||||
return new J2HtmlService(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ public final class Bootstrap {
|
|||
.setPath("/secure")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain: " +
|
||||
" SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() +
|
||||
|
@ -165,14 +165,14 @@ public final class Bootstrap {
|
|||
.setPath("/favicon.ico")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, "image/x-icon")
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, "image/x-icon")
|
||||
.body(NettyDataBufferFactory.getInstance().wrap(fromHex(hexFavIcon)))
|
||||
.done();
|
||||
})
|
||||
.build())
|
||||
.addService(BaseHttpService.builder()
|
||||
.setPath("/webjars/**")
|
||||
.setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/"))
|
||||
.setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/", 24 + 2600))
|
||||
.build())
|
||||
.addService(httpService)
|
||||
.addService(GroovyTemplateService.builder()
|
||||
|
|
|
@ -11,8 +11,8 @@ import org.xbib.net.http.HttpHeaderValues;
|
|||
import org.xbib.net.http.HttpResponseStatus;
|
||||
import org.xbib.net.http.HttpVersion;
|
||||
import org.xbib.net.http.j2html.InternalServerErrorHandler;
|
||||
import org.xbib.net.http.j2html.J2HtmlAuthResourceHandler;
|
||||
import org.xbib.net.http.j2html.J2HtmlResource;
|
||||
import org.xbib.net.http.j2html.J2HtmlResourceHandler;
|
||||
import org.xbib.net.http.j2html.J2HtmlService;
|
||||
import org.xbib.net.http.server.application.web.WebApplication;
|
||||
import org.xbib.net.http.server.auth.BasicAuthenticationHandler;
|
||||
|
@ -148,20 +148,20 @@ public final class Bootstrap {
|
|||
.setPath("/favicon.ico")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, "image/x-icon")
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, "image/x-icon")
|
||||
.body(NettyDataBufferFactory.getInstance().wrap(fromHex(hexFavIcon)))
|
||||
.done();
|
||||
})
|
||||
.build())
|
||||
.addService(BaseHttpService.builder()
|
||||
.setPath("/webjars/**")
|
||||
.setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/"))
|
||||
.setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/", 24 * 3600))
|
||||
.build())
|
||||
.addService(BaseHttpService.builder()
|
||||
.setPath("/insecure")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain: " +
|
||||
" SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() +
|
||||
|
@ -203,10 +203,10 @@ public final class Bootstrap {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static class MyResourceHandler extends J2HtmlAuthResourceHandler {
|
||||
static class MyResourceHandler extends J2HtmlResourceHandler {
|
||||
|
||||
@Override
|
||||
protected Resource createAuthenticatedResource(HttpRouterContext httpRouterContext) throws IOException {
|
||||
protected Resource createResource(HttpRouterContext httpRouterContext) throws IOException {
|
||||
return new DemoResource(this, httpRouterContext);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ public class NettyHttps2ServerMultiRequestLoadTest {
|
|||
.setPath("/secure")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain " +
|
||||
" SNI host " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() + " " +
|
||||
|
|
|
@ -62,7 +62,7 @@ public class NettyHttps2ServerTest {
|
|||
.setPath("/secure")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain " +
|
||||
" SNI host " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() + " " +
|
||||
|
|
|
@ -64,7 +64,7 @@ public class NettyHttpsServerMultiRequestLoadTest {
|
|||
.setHandler(ctx -> {
|
||||
logger.log(Level.INFO, "executing /secure");
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain: " +
|
||||
" SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() +
|
||||
|
@ -151,7 +151,7 @@ public class NettyHttpsServerMultiRequestLoadTest {
|
|||
.setPath("/secure")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain: " +
|
||||
" SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() +
|
||||
|
|
|
@ -68,7 +68,7 @@ public class NettyHttpsServerTest {
|
|||
.setPath("/secure")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain: " +
|
||||
" SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() +
|
||||
|
@ -146,7 +146,7 @@ public class NettyHttpsServerTest {
|
|||
.setPath("/secure")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain: " +
|
||||
" SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() +
|
||||
|
@ -223,7 +223,7 @@ public class NettyHttpsServerTest {
|
|||
.setPath("/secure")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain " +
|
||||
" SNI host " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() + " " +
|
||||
|
|
|
@ -51,7 +51,7 @@ public class NettyHttp2ServerMultiRequestLoadTest {
|
|||
.setPath("/domain")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("domain: " +
|
||||
" base URL = " + ctx.getRequest().getBaseURL() +
|
||||
|
|
|
@ -49,7 +49,7 @@ public class NettyHttp2ServerTest {
|
|||
.setPath("/domain")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("Hello, here is my response: " +
|
||||
ctx.getRequest().asJson())
|
||||
|
|
|
@ -48,7 +48,7 @@ public class NettyHttpServerBodyTest {
|
|||
.setHandler(ctx -> {
|
||||
String body = ctx.getRequestBuilder().getBodyAsChars(StandardCharsets.UTF_8).toString();
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("parameter = " + ctx.getRequest().getParameter().toString() +
|
||||
" local address = " + ctx.getRequest().getLocalAddress() +
|
||||
|
|
|
@ -58,7 +58,7 @@ class NettyHttpServerByteOrderMarkTest {
|
|||
String content = ctx.getRequestBuilder().getBodyAsChars(StandardCharsets.UTF_8).toString();
|
||||
logger.log(Level.FINEST, "got content = " + content);
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("parameter = " + ctx.getRequest().getParameter().toString() +
|
||||
" local address = " + ctx.getRequest().getLocalAddress() +
|
||||
|
|
|
@ -53,7 +53,7 @@ public class NettyHttpServerFailureTest {
|
|||
.setPath("/domain")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("domain" +
|
||||
" parameter = " + ctx.getRequest().getParameter().toString() +
|
||||
|
|
|
@ -82,7 +82,7 @@ public class NettyHttpServerFileUploadTest {
|
|||
.map(m -> StandardCharsets.UTF_8.decode(m.getByteBuffer()))
|
||||
.collect(Collectors.joining());
|
||||
ctx.status(httpResponseStatus)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("parameter = " + ctx.getRequest().getParameter().toString() +
|
||||
" local address = " + ctx.getRequest().getLocalAddress() +
|
||||
|
@ -175,7 +175,7 @@ public class NettyHttpServerFileUploadTest {
|
|||
}
|
||||
}
|
||||
ctx.status(httpResponseStatus)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("parameter = " + ctx.getRequest().getParameter().toString() +
|
||||
" local address = " + ctx.getRequest().getLocalAddress() +
|
||||
|
|
|
@ -51,7 +51,7 @@ public class NettyHttpServerMultiRequestLoadTest {
|
|||
.setPath("/domain")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("domain: " +
|
||||
" base URL = " + ctx.getRequest().getBaseURL() +
|
||||
|
|
|
@ -55,7 +55,7 @@ public class NettyHttpServerRequestTest {
|
|||
try {
|
||||
String value = ctx.getRequest().getParameter().getAsString("value", Parameter.Domain.QUERY);
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("local address = " + ctx.getRequest().getLocalAddress() +
|
||||
" remote address = " + ctx.getRequest().getRemoteAddress() +
|
||||
|
|
|
@ -52,7 +52,7 @@ public class NettyHttpServerTest {
|
|||
.setPath("/domain")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body(ctx.getRequest().asJson())
|
||||
.done();
|
||||
|
|
|
@ -35,7 +35,7 @@ public class NioHttpServerTest {
|
|||
.setPath("/domain1")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("domain1 " +
|
||||
ctx.getRequest().getParameter().toString() + " " +
|
||||
|
@ -50,7 +50,7 @@ public class NioHttpServerTest {
|
|||
.setPath("/domain2")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("domain2 " +
|
||||
ctx.getRequest().getParameter().toString() + " " +
|
||||
|
|
|
@ -54,7 +54,7 @@ public class SimpleHttpsServerTest {
|
|||
.setPath("/secure")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("secure domain: " +
|
||||
" SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() +
|
||||
|
|
|
@ -41,7 +41,7 @@ public class HttpRouterTest {
|
|||
.setHandler(ctx -> {
|
||||
Logger.getAnonymousLogger().log(Level.INFO, "got request: " + ctx.getRequestBuilder().getRequestURI());
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body(ctx.getRequestBuilder().getRequestURI());
|
||||
})
|
||||
|
|
|
@ -35,7 +35,7 @@ public class SimpleHttpServerTest {
|
|||
.setPath("/domain1")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("domain1 " +
|
||||
ctx.getRequest().getParameter() + " " +
|
||||
|
@ -54,7 +54,7 @@ public class SimpleHttpServerTest {
|
|||
.setPath("/domain2")
|
||||
.setHandler(ctx -> {
|
||||
ctx.status(HttpResponseStatus.OK)
|
||||
.header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
|
||||
.charset(StandardCharsets.UTF_8)
|
||||
.body("domain2 " +
|
||||
ctx.getRequest().getParameter() + " " +
|
||||
|
|
|
@ -155,14 +155,17 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
|
|||
if (done) {
|
||||
return this;
|
||||
}
|
||||
if (name == null || value == null) {
|
||||
return this;
|
||||
}
|
||||
if (HttpHeaderNames.CONTENT_TYPE.equalsIgnoreCase(name.toString())) {
|
||||
if (value.startsWith("text") && charset != null) {
|
||||
value = value + "; charset=" + charset.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
setContentType(value);
|
||||
}
|
||||
if (headers.containsHeader(name)) {
|
||||
logger.log(Level.WARNING, "header already exist: " + headers.get(name) + " overwriting with " + value);
|
||||
if (headers.containsHeader(name) && !value.equals(headers.get(name))) {
|
||||
logger.log(Level.WARNING, "header '" + name + "' already exist, old value = '" + headers.get(name) + "', overwriting with '" + value + "'");
|
||||
}
|
||||
headers.set(name, value);
|
||||
return this;
|
||||
|
@ -173,8 +176,18 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
|
|||
if (done) {
|
||||
return this;
|
||||
}
|
||||
if (name == null) {
|
||||
return this;
|
||||
}
|
||||
if (HttpHeaderNames.CONTENT_TYPE.equalsIgnoreCase(name.toString())) {
|
||||
if (value.startsWith("text") && charset != null) {
|
||||
value = value + "; charset=" + charset.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
setContentType(value);
|
||||
}
|
||||
if (headers.containsHeader(name)) {
|
||||
logger.log(Level.WARNING, "header already exist: " + headers.get(name) + " adding " + value);
|
||||
logger.log(Level.WARNING, "when adding '" + value + "', the header already exist: " + name + " - ignoring");
|
||||
return this;
|
||||
}
|
||||
headers.add(name, value);
|
||||
return this;
|
||||
|
@ -315,11 +328,10 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
|
|||
|
||||
@Override
|
||||
public BaseHttpResponseBuilder addCookie(Cookie cookie) {
|
||||
if (done) {
|
||||
return this;
|
||||
}
|
||||
Objects.requireNonNull(cookie);
|
||||
headers.add(HttpHeaderNames.SET_COOKIE, CookieEncoder.STRICT.encode(cookie));
|
||||
// skip done check, we force cookie add
|
||||
Objects.requireNonNull(cookie, "cookie must not be null when adding");
|
||||
String cookieValue = CookieEncoder.STRICT.encode(cookie);
|
||||
headers.add(HttpHeaderNames.SET_COOKIE,cookieValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,8 @@ import org.xbib.net.http.server.executor.Executor;
|
|||
import org.xbib.net.http.server.persist.Codec;
|
||||
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.IncomingContextHandler;
|
||||
import org.xbib.net.http.server.session.OutgoingContextHandler;
|
||||
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;
|
||||
|
@ -171,9 +171,9 @@ public class BaseApplication implements Application {
|
|||
if (builder.sessionsEnabled) {
|
||||
Codec<Session> sessionCodec = newSessionCodec(httpRouterContext);
|
||||
httpRouterContext.getAttributes().put("sessioncodec", sessionCodec);
|
||||
httpRouterContext.addOpenHandler(newIncomingSessionHandler(sessionCodec));
|
||||
httpRouterContext.addCloseHandler(newOutgoingSessionHandler());
|
||||
httpRouterContext.addReleaseHandler(newPersistSessionHandler(sessionCodec));
|
||||
httpRouterContext.addOpenHandler(newIncomingContextHandler(sessionCodec));
|
||||
httpRouterContext.addCloseHandler(newOutgoingContextHandler());
|
||||
httpRouterContext.addCloseHandler(newPersistHandler(sessionCodec));
|
||||
}
|
||||
httpRouterContext.addCloseHandler(newOutgoingCookieHandler());
|
||||
return httpRouterContext;
|
||||
|
@ -212,26 +212,22 @@ public class BaseApplication implements Application {
|
|||
return new MemoryPropertiesSessionCodec(sessionName,this, 1024, Duration.ofDays(1));
|
||||
}
|
||||
|
||||
protected HttpHandler newIncomingSessionHandler(Codec<Session> sessionCodec) {
|
||||
return new IncomingSessionHandler(
|
||||
protected HttpHandler newIncomingContextHandler(Codec<Session> sessionCodec) {
|
||||
return new IncomingContextHandler(
|
||||
getSecret(),
|
||||
"HmacSHA1",
|
||||
sessionName,
|
||||
sessionCodec,
|
||||
getStaticFileSuffixes(),
|
||||
"user_id",
|
||||
"e_user_id",
|
||||
() -> RandomUtil.randomString(16));
|
||||
}
|
||||
|
||||
protected HttpHandler newOutgoingSessionHandler() {
|
||||
return new OutgoingSessionHandler(
|
||||
protected HttpHandler newOutgoingContextHandler() {
|
||||
return new OutgoingContextHandler(
|
||||
getSecret(),
|
||||
"HmacSHA1",
|
||||
sessionName,
|
||||
getStaticFileSuffixes(),
|
||||
"user_id",
|
||||
"e_user_id",
|
||||
Duration.ofDays(1),
|
||||
true,
|
||||
false,
|
||||
|
@ -239,7 +235,7 @@ public class BaseApplication implements Application {
|
|||
);
|
||||
}
|
||||
|
||||
protected HttpHandler newPersistSessionHandler(Codec<Session> sessionCodec) {
|
||||
protected HttpHandler newPersistHandler(Codec<Session> sessionCodec) {
|
||||
return new PersistSessionHandler(sessionCodec);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.xbib.net.http.server.auth;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.xbib.net.Attributes;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
|
@ -10,6 +12,10 @@ public class BaseAttributes extends LinkedHashMap<String, Object> implements Att
|
|||
super();
|
||||
}
|
||||
|
||||
public BaseAttributes(Map<String, Object> map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T get(Class<T> cl, String key) {
|
||||
|
|
|
@ -1,35 +1,33 @@
|
|||
package org.xbib.net.http.server.auth;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.xbib.datastructures.tiny.TinyMap;
|
||||
import org.xbib.net.Attributes;
|
||||
import org.xbib.net.UserProfile;
|
||||
|
||||
public class BaseUserProfile implements UserProfile {
|
||||
|
||||
private final Attributes attributes;
|
||||
|
||||
private final Attributes effectiveAttributes;
|
||||
|
||||
private final List<String> roles;
|
||||
|
||||
private final List<String> effectiveRoles;
|
||||
|
||||
private final List<String> permissions;
|
||||
|
||||
private final List<String> effectivePermissions;
|
||||
|
||||
private String uid;
|
||||
|
||||
private String euid;
|
||||
|
||||
private String name;
|
||||
|
||||
private boolean isRemembered;
|
||||
private Attributes attributes;
|
||||
|
||||
private Collection<String> roles;
|
||||
|
||||
private Collection<String> effectiveRoles;
|
||||
|
||||
private Collection<String> permissions;
|
||||
|
||||
private Collection<String> effectivePermissions;
|
||||
|
||||
public BaseUserProfile() {
|
||||
this.attributes = new BaseAttributes();
|
||||
this.effectiveAttributes = new BaseAttributes();
|
||||
this.roles = new ArrayList<>();
|
||||
this.effectiveRoles = new ArrayList<>();
|
||||
this.permissions = new ArrayList<>();
|
||||
|
@ -71,105 +69,64 @@ public class BaseUserProfile implements UserProfile {
|
|||
roles.add(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRoles(Collection<String> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEffectiveRole(String role) {
|
||||
effectiveRoles.add(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRoles() {
|
||||
return roles;
|
||||
public void setEffectiveRoles(Collection<String> effectiveRoles) {
|
||||
this.effectiveRoles = effectiveRoles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getEffectiveRoles() {
|
||||
public Collection<String> getEffectiveRoles() {
|
||||
return effectiveRoles;
|
||||
}
|
||||
|
||||
public boolean hasRole(String role) {
|
||||
return roles.contains(role);
|
||||
}
|
||||
|
||||
public boolean hasEffectiveRole(String role) {
|
||||
return effectiveRoles.contains(role);
|
||||
}
|
||||
|
||||
public boolean hasAccess(String requireAnyRole, String requireAllRoles) {
|
||||
boolean access = true;
|
||||
if (!requireAnyRole.isEmpty()) {
|
||||
String[] expectedRoles = requireAnyRole.split(",");
|
||||
if (!hasAnyRole(expectedRoles)) {
|
||||
access = false;
|
||||
}
|
||||
} else if (!requireAllRoles.isEmpty()) {
|
||||
String[] expectedRoles = requireAllRoles.split(",");
|
||||
if (!hasAllRoles(expectedRoles)) {
|
||||
access = false;
|
||||
}
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
public boolean hasAnyRole(String[] expectedRoles) {
|
||||
if (expectedRoles == null || expectedRoles.length == 0) {
|
||||
return true;
|
||||
}
|
||||
for (final String role : expectedRoles) {
|
||||
if (roles.contains(role)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasAnyEffectiveRole(String[] expectedRoles) {
|
||||
if (expectedRoles == null || expectedRoles.length == 0) {
|
||||
return true;
|
||||
}
|
||||
for (final String role : expectedRoles) {
|
||||
if (effectiveRoles.contains(role)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasAllRoles(String[] expectedRoles) {
|
||||
if (expectedRoles == null) {
|
||||
return true;
|
||||
}
|
||||
for (String role : expectedRoles) {
|
||||
if (!roles.contains(role)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasAllEffectiveRoles(String[] expectedRoles) {
|
||||
if (expectedRoles == null) {
|
||||
return true;
|
||||
}
|
||||
for (String role : expectedRoles) {
|
||||
if (!effectiveRoles.contains(role)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPermission(String permission) {
|
||||
permissions.add(permission);
|
||||
}
|
||||
|
||||
public void removePermission(String permission) {
|
||||
permissions.remove(permission);
|
||||
@Override
|
||||
public void setPermissions(Collection<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemembered(boolean remembered) {
|
||||
this.isRemembered = remembered;
|
||||
public Collection<String> getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEffectivePermission(String permission) {
|
||||
effectivePermissions.add(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEffectivePermissions(Collection<String> effectivePermissions) {
|
||||
this.effectivePermissions = effectivePermissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getEffectivePermissions() {
|
||||
return effectivePermissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttributes(Attributes attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -178,38 +135,74 @@ public class BaseUserProfile implements UserProfile {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Attributes getEffectiveAttributes() {
|
||||
return effectiveAttributes;
|
||||
public Map<String, Object> asMap() {
|
||||
TinyMap.Builder<String, Object> builder = TinyMap.builder();
|
||||
builder.put("name", getName());
|
||||
String userId = getUserId();
|
||||
if (userId == null) {
|
||||
userId = "";
|
||||
}
|
||||
builder.put("user_id", userId);
|
||||
String eUserId = getEffectiveUserId();
|
||||
if (eUserId == null) {
|
||||
eUserId = "";
|
||||
}
|
||||
builder.put("e_user_id", eUserId);
|
||||
if (getRoles() != null && !getRoles().isEmpty()) {
|
||||
builder.put("roles", getRoles());
|
||||
}
|
||||
if (getEffectiveRoles() != null && !getEffectiveRoles().isEmpty()) {
|
||||
builder.put("e_roles", getEffectiveRoles());
|
||||
}
|
||||
if (getPermissions() != null && !getPermissions().isEmpty()) {
|
||||
builder.put("perms", getPermissions());
|
||||
}
|
||||
if (getEffectivePermissions() != null && !getEffectivePermissions().isEmpty()) {
|
||||
builder.put("e_perms", getEffectivePermissions());
|
||||
}
|
||||
if (getAttributes() != null && !getAttributes().isEmpty()) {
|
||||
builder.put("attrs", getAttributes());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPermissions() {
|
||||
return permissions;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static UserProfile fromMap(Map<String, Object> map) {
|
||||
BaseUserProfile userProfile = new BaseUserProfile();
|
||||
if (map.containsKey("name")) {
|
||||
userProfile.setName((String) map.get("name"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEffectivePermission(String permission) {
|
||||
effectivePermissions.add(permission);
|
||||
if (map.containsKey("user_id")) {
|
||||
String userId = (String) map.get("user_id");
|
||||
// empty user ID for better map transport, change it to null
|
||||
if (userId != null && userId.isEmpty()) {
|
||||
userId = null;
|
||||
}
|
||||
@Override
|
||||
public List<String> getEffectivePermissions() {
|
||||
return effectivePermissions;
|
||||
userProfile.setUserId(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRemembered() {
|
||||
return isRemembered;
|
||||
if (map.containsKey("e_user_id")) {
|
||||
String eUserId = (String) map.get("e_user_id");
|
||||
// empty effective user ID for better map transport, change it to null
|
||||
if (eUserId != null && eUserId.isEmpty()) {
|
||||
eUserId = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "uid=" + uid +
|
||||
",roles=" + roles +
|
||||
",permissons=" + permissions +
|
||||
",attributes=" + attributes +
|
||||
",euid=" + euid +
|
||||
",eroles=" + effectiveRoles +
|
||||
",epermissions=" + effectivePermissions +
|
||||
",eattributes=" + effectiveAttributes;
|
||||
userProfile.setEffectiveUserId(eUserId);
|
||||
}
|
||||
if (map.containsKey("roles")) {
|
||||
userProfile.setRoles((Collection<String>) map.get("roles"));
|
||||
}
|
||||
if (map.containsKey("e_roles")) {
|
||||
userProfile.setEffectiveRoles((Collection<String>) map.get("e_roles"));
|
||||
}
|
||||
if (map.containsKey("perms")) {
|
||||
userProfile.setPermissions((Collection<String>) map.get("perms"));
|
||||
}
|
||||
if (map.containsKey("e_perms")) {
|
||||
userProfile.setEffectivePermissions((Collection<String>) map.get("e_perms"));
|
||||
}
|
||||
if (map.containsKey("attrs")) {
|
||||
userProfile.setAttributes(new BaseAttributes((Map<String, Object>) map.get("attrs")));
|
||||
}
|
||||
return userProfile;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,6 @@ public class BasicAuthenticationHandler extends LoginAuthenticationHandler imple
|
|||
}
|
||||
logger.log(Level.INFO, "unauthenticated");
|
||||
context.status(HttpResponseStatus.UNAUTHORIZED)
|
||||
.header("WWW-Authenticate", "Basic realm=\"" + getSecurityRealm().getName() + "\"");
|
||||
.setHeader("WWW-Authenticate", "Basic realm=\"" + getSecurityRealm().getName() + "\"");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public class FormAuthenticationHandler extends LoginAuthenticationHandler implem
|
|||
logger.log(Level.FINE, "request = " + context.getRequest());
|
||||
UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile");
|
||||
if (userProfile != null && userProfile.getUserId() != null) {
|
||||
logger.log(Level.FINE, "user id already set: " + userProfile.getUserId());
|
||||
logger.log(Level.FINE, "already authenticated, user id = " + userProfile.getUserId());
|
||||
context.setAuthenticated(true);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ public class OutgoingCookieHandler implements HttpHandler {
|
|||
CookieBox cookieBox = context.getAttributes().get(CookieBox.class, "outgoingcookies");
|
||||
if (cookieBox != null) {
|
||||
for (Cookie cookie : cookieBox) {
|
||||
// move cookie to http response
|
||||
context.cookie(cookie);
|
||||
logger.log(Level.FINEST, "cookie prepared for outgoing = " + cookie);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public class BadRequestHandler implements HttpErrorHandler {
|
|||
@Override
|
||||
public void handle(HttpRouterContext context) throws IOException {
|
||||
context.status(HttpResponseStatus.BAD_REQUEST)
|
||||
.header(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.setHeader(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.body("Bad request")
|
||||
.done();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public class ForbiddenHandler implements HttpErrorHandler {
|
|||
@Override
|
||||
public void handle(HttpRouterContext context) throws IOException {
|
||||
context.status(HttpResponseStatus.FORBIDDEN)
|
||||
.header(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.setHeader(CONTENT_TYPE, "text/plain; charset=utf-8")
|
||||
.body("Forbidden")
|
||||
.done();
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public class InternalServerErrorHandler implements HttpErrorHandler {
|
|||
message = throwable != null ? throwable.getMessage() : "";
|
||||
}
|
||||
context.status(status)
|
||||
.header(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.setHeader(CONTENT_TYPE, "text/plain; charset=utf-8")
|
||||
.body(message)
|
||||
.done();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public class NotFoundHandler implements HttpErrorHandler {
|
|||
@Override
|
||||
public void handle(HttpRouterContext context) throws IOException {
|
||||
context.status(HttpResponseStatus.NOT_FOUND)
|
||||
.header(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.setHeader(CONTENT_TYPE, "text/plain; charset=utf-8")
|
||||
.body("Not found")
|
||||
.done();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public class NotImplementedHandler implements HttpErrorHandler {
|
|||
@Override
|
||||
public void handle(HttpRouterContext context) throws IOException {
|
||||
context.status(HttpResponseStatus.NOT_IMPLEMENTED)
|
||||
.header(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.setHeader(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.body("Not implemented")
|
||||
.done();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public class UnauthorizedHandler implements HttpErrorHandler {
|
|||
@Override
|
||||
public void handle(HttpRouterContext context) throws IOException {
|
||||
context.status(HttpResponseStatus.UNAUTHORIZED)
|
||||
.header(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.setHeader(CONTENT_TYPE, "text/plain; charset=utf-8")
|
||||
.body("Unauthorized")
|
||||
.done();
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public class VersionNotSupportedHandler implements HttpErrorHandler {
|
|||
@Override
|
||||
public void handle(HttpRouterContext context) throws IOException {
|
||||
context.status(HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED)
|
||||
.header(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.setHeader(CONTENT_TYPE, "text/plain; charset=utf-8")
|
||||
.body("HTTP version not supported")
|
||||
.done();
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ public class LdapGroupsProvider extends GroupsProvider {
|
|||
* Get groups, or null if not possible.
|
||||
* @throws LdapException if unable to retrieve groups
|
||||
*/
|
||||
@Override
|
||||
public Collection<String> getGroups(String username) {
|
||||
if (userMappings == null) {
|
||||
return null;
|
||||
|
|
|
@ -46,13 +46,13 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
|
||||
protected abstract Resource createResource(HttpRouterContext httpRouterContext) throws IOException;
|
||||
|
||||
protected abstract boolean isETagResponseEnabled();
|
||||
protected abstract boolean isETagEnabled();
|
||||
|
||||
protected abstract boolean isCacheResponseEnabled();
|
||||
protected abstract boolean isRangeEnabled();
|
||||
|
||||
protected abstract boolean isRangeResponseEnabled();
|
||||
protected abstract int getCacheMaxAgeSeconds();
|
||||
|
||||
protected abstract int getMaxAgeSeconds();
|
||||
protected abstract String getCacheControl();
|
||||
|
||||
@Override
|
||||
public void handle(HttpRouterContext context) throws IOException {
|
||||
|
@ -75,7 +75,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
.build()
|
||||
.toString();
|
||||
logger.log(Level.FINEST, "client must add a /, external redirect to = " + loc);
|
||||
context.header(HttpHeaderNames.LOCATION, loc)
|
||||
context.setHeader(HttpHeaderNames.LOCATION, loc)
|
||||
.status(HttpResponseStatus.TEMPORARY_REDIRECT);
|
||||
} else if (resource.isExistsIndexFile()) {
|
||||
// internal redirect to default index file in this directory
|
||||
|
@ -102,7 +102,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
}
|
||||
HttpHeaders headers = context.getRequestBuilder().getHeaders();
|
||||
String contentType = resource.getMimeType();
|
||||
context.header(CONTENT_TYPE, contentType);
|
||||
context.setHeader(CONTENT_TYPE, contentType);
|
||||
// heuristic for inline disposition
|
||||
String disposition;
|
||||
if (!contentType.startsWith("text") && !contentType.startsWith("image") && !contentType.startsWith("font")) {
|
||||
|
@ -114,22 +114,26 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
if (resource.getBaseName() != null && resource.getSuffix() != null) {
|
||||
String contentDisposition = disposition + ";filename=\"" + resource.getBaseName() + '.' + resource.getSuffix() + '"';
|
||||
logger.log(Level.FINEST, () -> "content type = " + contentType + " content disposition = " + contentDisposition);
|
||||
context.header(HttpHeaderNames.CONTENT_DISPOSITION, contentDisposition);
|
||||
context.setHeader(HttpHeaderNames.CONTENT_DISPOSITION, contentDisposition);
|
||||
}
|
||||
long expirationMillis = System.currentTimeMillis() + 1000L * getMaxAgeSeconds();
|
||||
if (getCacheMaxAgeSeconds() > 0) {
|
||||
long expirationMillis = System.currentTimeMillis() + 1000L * getCacheMaxAgeSeconds();
|
||||
String expires = DateTimeUtil.formatRfc1123(expirationMillis);
|
||||
if (isCacheResponseEnabled()) {
|
||||
String cacheControl = "public, max-age=" + getMaxAgeSeconds();
|
||||
logger.log(Level.FINEST, () -> "cache response, expires = " + expires + " cache control = " + cacheControl);
|
||||
context.header(HttpHeaderNames.EXPIRES, expires)
|
||||
.header(HttpHeaderNames.CACHE_CONTROL, cacheControl);
|
||||
String cacheControl = "public, max-age=" + getCacheMaxAgeSeconds();
|
||||
logger.log(Level.FINEST, () -> "expires = " + expires + " cache control = " + cacheControl);
|
||||
context.setHeader(HttpHeaderNames.EXPIRES, expires)
|
||||
.setHeader(HttpHeaderNames.CACHE_CONTROL, cacheControl);
|
||||
} else {
|
||||
logger.log(Level.FINEST, () -> "uncached response");
|
||||
context.header(HttpHeaderNames.EXPIRES, "0")
|
||||
.header(HttpHeaderNames.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
|
||||
if (getCacheControl() == null) {
|
||||
context.setHeader(HttpHeaderNames.EXPIRES, "0")
|
||||
.setHeader(HttpHeaderNames.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
|
||||
} else {
|
||||
context.setHeader(HttpHeaderNames.EXPIRES, "0")
|
||||
.setHeader(HttpHeaderNames.CACHE_CONTROL, getCacheControl());
|
||||
}
|
||||
}
|
||||
boolean sent = false;
|
||||
if (isETagResponseEnabled()) {
|
||||
if (isETagEnabled()) {
|
||||
Instant lastModifiedInstant = resource.getLastModified();
|
||||
String eTag = Long.toHexString(resource.getResourcePath().hashCode() + lastModifiedInstant.toEpochMilli() + resource.getLength());
|
||||
logger.log(Level.FINEST, () -> "eTag = " + eTag);
|
||||
|
@ -149,7 +153,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
String ifNoneMatch = headers.get(HttpHeaderNames.IF_NONE_MATCH);
|
||||
if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) {
|
||||
logger.log(Level.FINEST, () -> "not modified, eTag = " + eTag);
|
||||
context.header(HttpHeaderNames.ETAG, eTag)
|
||||
context.setHeader(HttpHeaderNames.ETAG, eTag)
|
||||
.status(HttpResponseStatus.NOT_MODIFIED);
|
||||
return;
|
||||
}
|
||||
|
@ -157,15 +161,15 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
if (ifModifiedSinceInstant != null &&
|
||||
ifModifiedSinceInstant.plusMillis(1000L).isAfter(lastModifiedInstant)) {
|
||||
logger.log(Level.FINEST, () -> "not modified (after if-modified-since), eTag = " + eTag);
|
||||
context.header(HttpHeaderNames.ETAG, eTag)
|
||||
context.setHeader(HttpHeaderNames.ETAG, eTag)
|
||||
.status(HttpResponseStatus.NOT_MODIFIED);
|
||||
return;
|
||||
}
|
||||
String lastModified = DateTimeUtil.formatRfc1123(lastModifiedInstant);
|
||||
logger.log(Level.FINEST, () -> "sending resource, lastModified = " + lastModified);
|
||||
context.header(HttpHeaderNames.ETAG, eTag)
|
||||
.header(HttpHeaderNames.LAST_MODIFIED, lastModified);
|
||||
if (isRangeResponseEnabled()) {
|
||||
context.setHeader(HttpHeaderNames.ETAG, eTag)
|
||||
.setHeader(HttpHeaderNames.LAST_MODIFIED, lastModified);
|
||||
if (isRangeEnabled()) {
|
||||
performRangeResponse(context, resource, contentType, eTag, headers);
|
||||
sent = true;
|
||||
} else {
|
||||
|
@ -176,7 +180,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
long length = resource.getLength();
|
||||
if (length > 0L) {
|
||||
String string = Long.toString(resource.getLength());
|
||||
context.header(HttpHeaderNames.CONTENT_LENGTH, string);
|
||||
context.setHeader(HttpHeaderNames.CONTENT_LENGTH, string);
|
||||
logger.log(Level.FINEST, "length is known = " + resource.getLength());
|
||||
send(resource, HttpResponseStatus.OK, contentType, context, 0L, resource.getLength());
|
||||
} else {
|
||||
|
@ -194,13 +198,13 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
HttpHeaders headers) throws IOException {
|
||||
long length = resource.getLength();
|
||||
logger.log(Level.FINEST, "performing range response on resource = " + resource);
|
||||
context.header(HttpHeaderNames.ACCEPT_RANGES, "bytes");
|
||||
context.setHeader(HttpHeaderNames.ACCEPT_RANGES, "bytes");
|
||||
Range full = new Range(0, length - 1, length);
|
||||
List<Range> ranges = new ArrayList<>();
|
||||
String range = headers.get(HttpHeaderNames.RANGE);
|
||||
if (range != null) {
|
||||
if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) {
|
||||
context.header(HttpHeaderNames.CONTENT_RANGE, "bytes */" + length)
|
||||
context.setHeader(HttpHeaderNames.CONTENT_RANGE, "bytes */" + length)
|
||||
.status(HttpResponseStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
return;
|
||||
}
|
||||
|
@ -226,7 +230,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
end = length - 1;
|
||||
}
|
||||
if (start > end) {
|
||||
context.header(HttpHeaderNames.CONTENT_RANGE, "bytes */" + length)
|
||||
context.setHeader(HttpHeaderNames.CONTENT_RANGE, "bytes */" + length)
|
||||
.status(HttpResponseStatus.REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
return;
|
||||
}
|
||||
|
@ -235,16 +239,16 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
}
|
||||
}
|
||||
if (ranges.isEmpty() || ranges.getFirst() == full) {
|
||||
context.header(HttpHeaderNames.CONTENT_RANGE, "bytes " + full.start + '-' + full.end + '/' + full.total)
|
||||
.header(HttpHeaderNames.CONTENT_LENGTH, Long.toString(full.length));
|
||||
context.setHeader(HttpHeaderNames.CONTENT_RANGE, "bytes " + full.start + '-' + full.end + '/' + full.total)
|
||||
.setHeader(HttpHeaderNames.CONTENT_LENGTH, Long.toString(full.length));
|
||||
send(resource, HttpResponseStatus.OK, contentType, context, full.start, full.length);
|
||||
} else if (ranges.size() == 1) {
|
||||
Range r = ranges.getFirst();
|
||||
context.header(HttpHeaderNames.CONTENT_RANGE, "bytes " + r.start + '-' + r.end + '/' + r.total)
|
||||
.header(HttpHeaderNames.CONTENT_LENGTH, Long.toString(r.length));
|
||||
context.setHeader(HttpHeaderNames.CONTENT_RANGE, "bytes " + r.start + '-' + r.end + '/' + r.total)
|
||||
.setHeader(HttpHeaderNames.CONTENT_LENGTH, Long.toString(r.length));
|
||||
send(resource, HttpResponseStatus.PARTIAL_CONTENT, contentType, context, r.start, r.length);
|
||||
} else {
|
||||
context.header(CONTENT_TYPE, "multipart/byteranges; boundary=MULTIPART_BOUNDARY");
|
||||
context.setHeader(CONTENT_TYPE, "multipart/byteranges; boundary=MULTIPART_BOUNDARY");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Range r : ranges) {
|
||||
try {
|
||||
|
@ -262,7 +266,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
}
|
||||
}
|
||||
context.status(HttpResponseStatus.OK)
|
||||
.header(CONTENT_TYPE, contentType)
|
||||
.setHeader(CONTENT_TYPE, contentType)
|
||||
.body(CharBuffer.wrap(sb), StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +300,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
} else if (context.getRequestBuilder().getMethod() == HttpMethod.HEAD) {
|
||||
logger.log(Level.FINEST, "HEAD request, do not send body");
|
||||
context.status(HttpResponseStatus.OK)
|
||||
.header(CONTENT_TYPE, contentType);
|
||||
.setHeader(CONTENT_TYPE, contentType);
|
||||
} else {
|
||||
if ("file".equals(url.getScheme())) {
|
||||
Path path = resource.getPath();
|
||||
|
@ -338,7 +342,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
try (ReadableByteChannel channel = fileChannel) {
|
||||
DataBuffer dataBuffer = DataBufferUtil.readBuffer(context.getDataBufferFactory(), channel, size);
|
||||
context.status(httpResponseStatus)
|
||||
.header(CONTENT_TYPE, contentType)
|
||||
.setHeader(CONTENT_TYPE, contentType)
|
||||
.body(dataBuffer);
|
||||
}
|
||||
}
|
||||
|
@ -358,7 +362,7 @@ public abstract class AbstractResourceHandler implements HttpHandler {
|
|||
try (ReadableByteChannel channel = Channels.newChannel(inputStream)) {
|
||||
DataBuffer dataBuffer = DataBufferUtil.readBuffer(context.getDataBufferFactory(), channel, size);
|
||||
context.status(httpResponseStatus)
|
||||
.header(CONTENT_TYPE, contentType)
|
||||
.setHeader(CONTENT_TYPE, contentType)
|
||||
.body(dataBuffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,18 @@ public class ClassLoaderResourceHandler extends AbstractResourceHandler {
|
|||
|
||||
private final String resourcePrefix;
|
||||
|
||||
private final int cacheSeconds;
|
||||
|
||||
public ClassLoaderResourceHandler(ClassLoader classLoader) {
|
||||
this(classLoader, null);
|
||||
this(classLoader, null, 24 * 3600);
|
||||
}
|
||||
|
||||
public ClassLoaderResourceHandler(ClassLoader classLoader, String resourcePrefix) {
|
||||
public ClassLoaderResourceHandler(ClassLoader classLoader,
|
||||
String resourcePrefix,
|
||||
int cacheSeconds) {
|
||||
this.classLoader = classLoader;
|
||||
this.resourcePrefix = resourcePrefix;
|
||||
this.cacheSeconds = cacheSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,23 +39,23 @@ public class ClassLoaderResourceHandler extends AbstractResourceHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isETagResponseEnabled() {
|
||||
protected boolean isETagEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCacheResponseEnabled() {
|
||||
protected String getCacheControl() {
|
||||
return "public";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRangeEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRangeResponseEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMaxAgeSeconds() {
|
||||
return 24 * 3600;
|
||||
protected int getCacheMaxAgeSeconds() {
|
||||
return cacheSeconds;
|
||||
}
|
||||
|
||||
class ClassLoaderResource implements Resource {
|
||||
|
|
|
@ -24,14 +24,20 @@ public class FileResourceHandler extends AbstractResourceHandler {
|
|||
|
||||
private final String pathNameOfResource;
|
||||
|
||||
private final int cacheSeconds;
|
||||
|
||||
public FileResourceHandler() {
|
||||
this(null, "index.html", null);
|
||||
this(null, "index.html", null, 24 * 3600);
|
||||
}
|
||||
|
||||
public FileResourceHandler(String webRoot, String indexFileName, String pathNameOfResource) {
|
||||
public FileResourceHandler(String webRoot,
|
||||
String indexFileName,
|
||||
String pathNameOfResource,
|
||||
int cacheSeconds) {
|
||||
this.webRoot = webRoot;
|
||||
this.indexFileName = indexFileName;
|
||||
this.pathNameOfResource = pathNameOfResource;
|
||||
this.cacheSeconds = cacheSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,23 +64,23 @@ public class FileResourceHandler extends AbstractResourceHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isETagResponseEnabled() {
|
||||
protected boolean isETagEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCacheResponseEnabled() {
|
||||
protected boolean isRangeEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRangeResponseEnabled() {
|
||||
return true;
|
||||
protected int getCacheMaxAgeSeconds() {
|
||||
return cacheSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMaxAgeSeconds() {
|
||||
return 24 * 3600;
|
||||
public String getCacheControl() {
|
||||
return "public";
|
||||
}
|
||||
|
||||
protected class FileResource implements Resource {
|
||||
|
|
|
@ -27,25 +27,26 @@ public class HtmlTemplateResourceHandler extends AbstractResourceHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean isETagResponseEnabled() {
|
||||
protected boolean isETagEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCacheResponseEnabled() {
|
||||
protected boolean isRangeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRangeResponseEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMaxAgeSeconds() {
|
||||
protected int getCacheMaxAgeSeconds() {
|
||||
// disables caching
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCacheControl() {
|
||||
return "no-cache";
|
||||
}
|
||||
|
||||
public Path getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public class WebRootResourceResolver implements ResourceResolver {
|
|||
.build()
|
||||
.toString();
|
||||
httpRouterContext.status(HttpResponseStatus.TEMPORARY_REDIRECT)
|
||||
.header("location", loc);
|
||||
.setHeader("location", loc);
|
||||
}
|
||||
}
|
||||
return resource;
|
||||
|
|
|
@ -230,7 +230,13 @@ public class BaseHttpRouterContext implements HttpRouterContext {
|
|||
}
|
||||
|
||||
@Override
|
||||
public BaseHttpRouterContext header(String name, String value) {
|
||||
public BaseHttpRouterContext setHeader(String name, String value) {
|
||||
httpResponseBuilder.setHeader(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseHttpRouterContext addHeader(String name, String value) {
|
||||
httpResponseBuilder.addHeader(name, value);
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -79,7 +79,9 @@ public interface HttpRouterContext {
|
|||
|
||||
HttpRouterContext charset(Charset charset);
|
||||
|
||||
HttpRouterContext header(String name, String value);
|
||||
HttpRouterContext setHeader(String name, String value);
|
||||
|
||||
HttpRouterContext addHeader(String name, String value);
|
||||
|
||||
HttpRouterContext cookie(Cookie cookie);
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@ 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 IncomingSessionHandler implements HttpHandler {
|
||||
public class IncomingContextHandler implements HttpHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(IncomingSessionHandler.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(IncomingContextHandler.class.getName());
|
||||
|
||||
private final String sessionSecret;
|
||||
|
||||
|
@ -36,32 +36,21 @@ public class IncomingSessionHandler implements HttpHandler {
|
|||
|
||||
private final Codec<Session> sessionCodec;
|
||||
|
||||
private final String sessionUserName;
|
||||
|
||||
private final String sessionEffectiveUserName;
|
||||
|
||||
/**
|
||||
* These suffixes disable incoming session creation.
|
||||
*/
|
||||
private final Set<String> suffixes;
|
||||
|
||||
Supplier<String> sessionIdGenerator;
|
||||
|
||||
public IncomingSessionHandler(String sessionSecret,
|
||||
public IncomingContextHandler(String sessionSecret,
|
||||
String sessionCookieAlgorithm,
|
||||
String sessionCookieName,
|
||||
Codec<Session> sessionCodec,
|
||||
Set<String> suffixes,
|
||||
String sessionUserName,
|
||||
String sessionEffectiveUserName,
|
||||
Supplier<String> sessionIdGenerator) {
|
||||
this.sessionSecret = sessionSecret;
|
||||
this.sessionCookieAlgorithm = sessionCookieAlgorithm;
|
||||
this.sessionCookieName = sessionCookieName;
|
||||
this.sessionCodec = sessionCodec;
|
||||
this.suffixes = suffixes;
|
||||
this.sessionUserName = sessionUserName;
|
||||
this.sessionEffectiveUserName = sessionEffectiveUserName;
|
||||
this.sessionIdGenerator = sessionIdGenerator;
|
||||
}
|
||||
|
||||
|
@ -73,14 +62,20 @@ public class IncomingSessionHandler implements HttpHandler {
|
|||
}
|
||||
Map<String, Object> payload = null;
|
||||
Session session = null;
|
||||
UserProfile userProfile = null;
|
||||
CookieBox cookieBox = context.getAttributes().get(CookieBox.class, "incomingcookies");
|
||||
if (cookieBox != null) {
|
||||
for (Cookie cookie : cookieBox) {
|
||||
if (cookie.name().equals(sessionCookieName)) {
|
||||
logger.log(Level.FINE, "found our cookie " + sessionCookieName);
|
||||
if (session == null) {
|
||||
try {
|
||||
payload = decodeCookie(cookie);
|
||||
logger.log(Level.FINE, "payload from cookie = " + payload);
|
||||
session = toSession(payload);
|
||||
logger.log(Level.FINE, "session from payload = " + session);
|
||||
userProfile = toUserProfile(session);
|
||||
logger.log(Level.FINE, "userprofile from session = " + userProfile);
|
||||
} catch (CookieSignatureException e) {
|
||||
// set exception in context to discard broken cookie later and render exception message
|
||||
context.getAttributes().put("_throwable", e);
|
||||
|
@ -97,19 +92,63 @@ public class IncomingSessionHandler implements HttpHandler {
|
|||
if (session == null) {
|
||||
try {
|
||||
session = sessionCodec.create(sessionIdGenerator.get());
|
||||
logger.log(Level.FINE, "creating new session " + session.id());
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||
throw new HttpException("unable to create session", context, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
throw new HttpException("unable to create new session", context, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
if (payload != null) {
|
||||
UserProfile userProfile = newUserProfile(payload, session);
|
||||
if (userProfile != null) {
|
||||
if (userProfile == null) {
|
||||
logger.log(Level.FINE, "creating new user profile");
|
||||
userProfile = newUserProfile(payload);
|
||||
}
|
||||
context.getAttributes().put("session", session);
|
||||
context.getAttributes().put("userprofile", userProfile);
|
||||
}
|
||||
|
||||
protected Session toSession(Map<String, Object> map) {
|
||||
return toSession((String) map.get("id"), map);
|
||||
}
|
||||
logger.log(Level.FINEST, "incoming session ID = " + session.id() + " keys = " + session.keySet());
|
||||
context.getAttributes().put("session", session);
|
||||
|
||||
protected Session toSession(String id, Map<String, Object> map) {
|
||||
Session session = null;
|
||||
try {
|
||||
session = sessionCodec.read(id);
|
||||
if (session != null && map != null) {
|
||||
logger.log(Level.FINE, "session id " + id + " restored");
|
||||
session.putAll(map);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINEST, "unable to restore session, id = " + id, e);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected UserProfile toUserProfile(Session session) {
|
||||
Map<String, Object> map = (Map<String, Object>) session.get("userprofile");
|
||||
return map != null ? BaseUserProfile.fromMap(map) : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected UserProfile newUserProfile(Map<String, Object> cookieMap) {
|
||||
UserProfile userProfile = new BaseUserProfile();
|
||||
// user_id, e_user_id are in cookie map
|
||||
if (cookieMap != null) {
|
||||
Map<String, Object> m = (Map<String, Object>) cookieMap.get("map");
|
||||
if (m == null) {
|
||||
// nothing found
|
||||
return userProfile;
|
||||
}
|
||||
if (m.containsKey("user_id")) {
|
||||
userProfile.setUserId((String) m.get("user_id"));
|
||||
}
|
||||
if (m.containsKey("e_user_id")) {
|
||||
userProfile.setEffectiveUserId((String) m.get("e_user_id"));
|
||||
}
|
||||
// roles, permissions, attributes must be filled later
|
||||
}
|
||||
return userProfile;
|
||||
}
|
||||
|
||||
private Map<String, Object> decodeCookie(Cookie cookie) throws IOException,
|
||||
|
@ -127,48 +166,10 @@ public class IncomingSessionHandler implements HttpHandler {
|
|||
String sig = s[2];
|
||||
String mysig = CookieSignatureUtil.hmac(payload, sessionSecret, sessionCookieAlgorithm);
|
||||
if (!sig.equals(mysig)) {
|
||||
logger.log(Level.SEVERE, MessageFormat.format("signature in cookie does not match. algo={1} secret={2} payload={3} sig={4} mysig={5}",
|
||||
logger.log(Level.SEVERE, () -> MessageFormat.format("signature in cookie does not match. algo={0} secret={1} payload={2} sig={3} mysig={4}",
|
||||
sessionCookieAlgorithm, sessionSecret, payload, sig, mysig));
|
||||
throw new CookieSignatureException("cookie security problem");
|
||||
}
|
||||
Map<String, Object> map = CookieSignatureUtil.toMap(payload);
|
||||
return Map.of("id", id, "payload", payload, "map", map);
|
||||
}
|
||||
|
||||
protected Session toSession(Map<String, Object> map) {
|
||||
return toSession((String) map.get("id"), map);
|
||||
}
|
||||
|
||||
protected Session toSession(String id, Map<String, Object> map) {
|
||||
Session session = null;
|
||||
try {
|
||||
session = sessionCodec.read(id);
|
||||
if (session != null && map != null) {
|
||||
session.putAll(map);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.FINEST, "unable to read session, id = " + id, e);
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected UserProfile newUserProfile(Map<String, Object> map, Session session) {
|
||||
UserProfile userProfile = new BaseUserProfile();
|
||||
Map<String, Object> m = (Map<String, Object>) map.get("map");
|
||||
if (m == null) {
|
||||
return userProfile;
|
||||
}
|
||||
if (sessionUserName != null && m.containsKey(sessionUserName)) {
|
||||
userProfile.setUserId((String) m.get(sessionUserName));
|
||||
}
|
||||
if (sessionEffectiveUserName != null && m.containsKey(sessionEffectiveUserName)) {
|
||||
userProfile.setEffectiveUserId((String) m.get(sessionEffectiveUserName));
|
||||
}
|
||||
if (session != null && userProfile.getUserId() != null) {
|
||||
session.put("user_id", userProfile.getUserId());
|
||||
session.put("e_user_id", userProfile.getUserId());
|
||||
}
|
||||
return userProfile;
|
||||
return Map.of("id", id, "payload", payload, "map", CookieSignatureUtil.toMap(payload));
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
@ -25,9 +24,9 @@ import org.xbib.net.http.server.application.Application;
|
|||
import org.xbib.net.http.server.cookie.CookieSignatureException;
|
||||
import org.xbib.net.http.server.cookie.CookieSignatureUtil;
|
||||
|
||||
public class OutgoingSessionHandler implements HttpHandler {
|
||||
public class OutgoingContextHandler implements HttpHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(OutgoingSessionHandler.class.getName());
|
||||
private static final Logger logger = Logger.getLogger(OutgoingContextHandler.class.getName());
|
||||
|
||||
private final String sessionSecret;
|
||||
|
||||
|
@ -39,22 +38,16 @@ public class OutgoingSessionHandler implements HttpHandler {
|
|||
|
||||
private final Set<String> suffixes;
|
||||
|
||||
private final String sessionUserName;
|
||||
|
||||
private final String sessionEffectiveUserName;
|
||||
|
||||
private final boolean httpOnly;
|
||||
|
||||
private final boolean secure;
|
||||
|
||||
private final SameSite sameSite;
|
||||
|
||||
public OutgoingSessionHandler(String sessionSecret,
|
||||
public OutgoingContextHandler(String sessionSecret,
|
||||
String sessionCookieAlgorithm,
|
||||
String sessionCookieName,
|
||||
Set<String> suffixes,
|
||||
String sessionUserName,
|
||||
String sessionEffectiveUserName,
|
||||
Duration sessionDuration,
|
||||
boolean httpOnly,
|
||||
boolean secure,
|
||||
|
@ -63,8 +56,6 @@ public class OutgoingSessionHandler implements HttpHandler {
|
|||
this.sessionCookieAlgorithm = sessionCookieAlgorithm;
|
||||
this.sessionCookieName = sessionCookieName;
|
||||
this.suffixes = suffixes;
|
||||
this.sessionUserName = sessionUserName;
|
||||
this.sessionEffectiveUserName = sessionEffectiveUserName;
|
||||
this.sessionDuration = sessionDuration;
|
||||
this.httpOnly = httpOnly;
|
||||
this.secure = secure;
|
||||
|
@ -79,7 +70,6 @@ public class OutgoingSessionHandler implements HttpHandler {
|
|||
}
|
||||
String suffix = SessionUtil.extractExtension(context.getRequestBuilder().getRequestPath());
|
||||
if (suffix != null && suffixes.contains(suffix)) {
|
||||
logger.log(Level.FINEST, () -> "suffix " + suffix + " blocking outgoing session handling");
|
||||
return;
|
||||
}
|
||||
CookieBox cookieBox = context.getAttributes().get(CookieBox.class, "outgoingcookies");
|
||||
|
@ -87,7 +77,6 @@ public class OutgoingSessionHandler implements HttpHandler {
|
|||
cookieBox = new CookieBox();
|
||||
}
|
||||
Application application = context.getAttributes().get(Application.class, "application");
|
||||
UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile");
|
||||
String host = context.getContextURL().getHost();
|
||||
String path = application.getContextPath();
|
||||
Throwable throwable = context.getAttributes().get(Throwable.class, "_throwable");
|
||||
|
@ -99,16 +88,11 @@ public class OutgoingSessionHandler implements HttpHandler {
|
|||
Session session = context.getAttributes().get(Session.class, "session");
|
||||
if (session != null) {
|
||||
try {
|
||||
UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile");
|
||||
if (userProfile != null) {
|
||||
logger.log(Level.FINEST, () -> "user profile present: " + userProfile);
|
||||
if (sessionUserName != null) {
|
||||
session.put(sessionUserName, userProfile.getUserId());
|
||||
session.put("userprofile", userProfile.asMap());
|
||||
}
|
||||
if (sessionEffectiveUserName != null) {
|
||||
session.put(sessionEffectiveUserName, userProfile.getEffectiveUserId());
|
||||
}
|
||||
}
|
||||
Cookie cookie = encodeCookie(session, host, path);
|
||||
Cookie cookie = encodeCookie(session, userProfile, host, path);
|
||||
if (cookie != null) {
|
||||
cookieBox.add(cookie);
|
||||
}
|
||||
|
@ -117,12 +101,18 @@ public class OutgoingSessionHandler implements HttpHandler {
|
|||
throw new HttpException("unable to create session data for cookie", context, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
logger.log(Level.FINEST, "prepared outgoing cookies = " + cookieBox);
|
||||
context.getAttributes().put("outgoingcookies", cookieBox);
|
||||
}
|
||||
|
||||
private Cookie encodeCookie(Session session, String host, String path) throws IOException,
|
||||
private Cookie encodeCookie(Session session,
|
||||
UserProfile userProfile,
|
||||
String host,
|
||||
String path) throws IOException,
|
||||
NoSuchAlgorithmException, InvalidKeyException {
|
||||
if (session == null) {
|
||||
logger.log(Level.WARNING, "no session, no cookie");
|
||||
return null;
|
||||
}
|
||||
if (sessionSecret == null) {
|
||||
logger.log(Level.WARNING, "no secret, no cookie");
|
||||
return null;
|
||||
|
@ -137,13 +127,10 @@ public class OutgoingSessionHandler implements HttpHandler {
|
|||
session.invalidate();
|
||||
return createEmptyCookie(host, path);
|
||||
}
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
if (sessionUserName != null) {
|
||||
map.put(sessionUserName, session.get(sessionUserName));
|
||||
}
|
||||
if (sessionEffectiveUserName != null) {
|
||||
map.put(sessionEffectiveUserName, session.get(sessionEffectiveUserName));
|
||||
}
|
||||
Map<String, Object> map = userProfile != null ?
|
||||
Map.of("user_id", userProfile.getUserId() != null ? userProfile.getUserId() :"",
|
||||
"e_user_id", userProfile.getEffectiveUserId() != null ? userProfile.getEffectiveUserId() : "") :
|
||||
Map.of();
|
||||
String payload = CookieSignatureUtil.toString(map);
|
||||
String sig = CookieSignatureUtil.hmac(payload, sessionSecret, sessionCookieAlgorithm);
|
||||
String cookieValue = String.join(":", id, payload, sig);
|
|
@ -28,7 +28,7 @@ public class PersistSessionHandler implements HttpHandler {
|
|||
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);
|
||||
throw new HttpException("unable to write session data", context, HttpResponseStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,9 +30,9 @@ public class GroovyTemplateRenderer implements HttpHandler {
|
|||
}
|
||||
HttpResponseStatus httpResponseStatus = context.getAttributes().get(HttpResponseStatus.class, "_status", HttpResponseStatus.OK);
|
||||
context.status(httpResponseStatus)
|
||||
.header("cache-control", "no-cache") // override default must-revalidate behavior
|
||||
.header("content-length", Integer.toString(dataBuffer.writePosition()))
|
||||
.header(CONTENT_TYPE, "text/html; charset=" + StandardCharsets.UTF_8.displayName())
|
||||
.setHeader("cache-control", "no-cache")
|
||||
.setHeader("content-length", Integer.toString(dataBuffer.writePosition()))
|
||||
.setHeader(CONTENT_TYPE, "text/html; charset=" + StandardCharsets.UTF_8.displayName())
|
||||
.body(dataBuffer);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue