diff --git a/net-http-j2html/build.gradle b/net-http-j2html/build.gradle new file mode 100644 index 0000000..f76160e --- /dev/null +++ b/net-http-j2html/build.gradle @@ -0,0 +1,4 @@ +dependencies { + api project(':net-http-server') + api libs.j2html +} diff --git a/net-http-j2html/src/main/java/module-info.java b/net-http-j2html/src/main/java/module-info.java new file mode 100644 index 0000000..f0bf7d7 --- /dev/null +++ b/net-http-j2html/src/main/java/module-info.java @@ -0,0 +1,9 @@ +module org.xbib.net.http.j2html { + exports org.xbib.net.http.j2html; + requires org.xbib.net; + requires org.xbib.net.http; + requires org.xbib.net.http.server; + requires org.xbib.config; + requires com.j2html; + requires java.logging; +} diff --git a/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResourceHandler.java b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResourceHandler.java new file mode 100644 index 0000000..74d51dc --- /dev/null +++ b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlResourceHandler.java @@ -0,0 +1,68 @@ +package org.xbib.net.http.j2html; + +import org.xbib.net.Attributes; +import org.xbib.net.Resource; +import org.xbib.net.buffer.DataBuffer; +import org.xbib.net.http.HttpResponseStatus; +import org.xbib.net.http.server.application.Application; +import org.xbib.net.http.server.resource.HtmlTemplateResource; +import org.xbib.net.http.server.resource.HtmlTemplateResourceHandler; +import org.xbib.net.http.server.route.HttpRouterContext; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static j2html.TagCreator.body; +import static j2html.TagCreator.h1; +import static org.xbib.net.http.HttpHeaderNames.CONTENT_TYPE; + +public class J2HtmlResourceHandler extends HtmlTemplateResourceHandler { + + public J2HtmlResourceHandler() { + this(null, "java", "index.java"); + } + + public J2HtmlResourceHandler(Path root, String suffix, String indexFileName) { + super(root, suffix, indexFileName); + } + + @Override + protected Resource createResource(HttpRouterContext httpRouterContext) throws IOException { + return new J2HtmlResource(this, httpRouterContext); + } + + protected static class J2HtmlResource extends HtmlTemplateResource { + + private static final Logger logger = Logger.getLogger(J2HtmlResource.class.getName()); + + protected J2HtmlResource(HtmlTemplateResourceHandler templateResourceHandler, HttpRouterContext httpRouterContext) throws IOException { + super(templateResourceHandler, httpRouterContext); + } + + @Override + public void render(HttpRouterContext context) throws IOException { + Application application = context.getAttributes().get(Application.class, "application"); + if (application == null) { + logger.log(Level.WARNING, "application is null"); + return; + } + DataBuffer dataBuffer = context.getDataBufferFactory().allocateBuffer(); + dataBuffer.write(render(application, context.getAttributes()), StandardCharsets.UTF_8); + 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()) + .body(dataBuffer); + } + + protected String render(Application application, Attributes attributes) { + return body( + h1("Hello World") + ).render(); + } + } +} diff --git a/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlService.java b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlService.java new file mode 100644 index 0000000..c4df8bc --- /dev/null +++ b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlService.java @@ -0,0 +1,15 @@ +package org.xbib.net.http.j2html; + +import org.xbib.net.http.server.service.BaseHttpService; +import org.xbib.net.http.server.service.BaseHttpServiceBuilder; + +public class J2HtmlService extends BaseHttpService { + + protected J2HtmlService(BaseHttpServiceBuilder builder) { + super(builder); + } + + public static J2HtmlServiceBuilder builder() { + return new J2HtmlServiceBuilder(); + } +} diff --git a/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlServiceBuilder.java b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlServiceBuilder.java new file mode 100644 index 0000000..288c17e --- /dev/null +++ b/net-http-j2html/src/main/java/org/xbib/net/http/j2html/J2HtmlServiceBuilder.java @@ -0,0 +1,69 @@ +package org.xbib.net.http.j2html; + +import org.xbib.net.ParameterDefinition; +import org.xbib.net.http.HttpMethod; +import org.xbib.net.http.server.HttpHandler; +import org.xbib.net.http.server.domain.HttpSecurityDomain; +import org.xbib.net.http.server.service.BaseHttpServiceBuilder; + +import java.nio.file.Path; + +public class J2HtmlServiceBuilder extends BaseHttpServiceBuilder { + + protected Path prefix; + + protected String suffix; + + public J2HtmlServiceBuilder() { + super(); + this.prefix = null; + } + + @Override + public J2HtmlServiceBuilder setPath(String path) { + super.setPath(path); + return this; + } + + @Override + public J2HtmlServiceBuilder setMethod(HttpMethod... httpMethod) { + super.setMethod(httpMethod); + return this; + } + + @Override + public J2HtmlServiceBuilder setHandler(HttpHandler... handler) { + super.setHandler(handler); + return this; + } + + @Override + public J2HtmlServiceBuilder setParameterDefinition(ParameterDefinition... parameterDefinition) { + super.setParameterDefinition(parameterDefinition); + return this; + } + + @Override + public J2HtmlServiceBuilder setSecurityDomain(HttpSecurityDomain securityDomain) { + super.setSecurityDomain(securityDomain); + return this; + } + + public J2HtmlServiceBuilder setPrefix(Path prefix) { + this.prefix = prefix; + return this; + } + + public J2HtmlServiceBuilder setSuffix(String suffix) { + this.suffix = suffix; + return this; + } + + public J2HtmlService build() { + if (this.handlers == null) { + HttpHandler httpHandler = new J2HtmlResourceHandler(prefix, suffix, "index.java"); + setHandler(httpHandler); + } + return new J2HtmlService(this); + } +} diff --git a/net-http-server-application-web/build.gradle b/net-http-server-application-web/build.gradle index 7e745ed..dbab609 100644 --- a/net-http-server-application-web/build.gradle +++ b/net-http-server-application-web/build.gradle @@ -4,6 +4,7 @@ dependencies { api project(':net-http-server-netty-secure') api project(':net-http-server-application-config') api project(':net-http-template-groovy') + api project(':net-http-j2html') api libs.jdbc.query implementation libs.webjars.bootstrap implementation libs.webjars.jquery @@ -12,7 +13,7 @@ dependencies { } application { - mainClass.set('org.xbib.net.http.server.application.web.Bootstrap') + mainClass.set('org.xbib.net.http.server.application.web.j2html.Bootstrap') applicationDefaultJvmArgs = [ '-Dfile.encoding=UTF-8', '-Duser.language=de', diff --git a/net-http-server-application-web/src/main/java/module-info.java b/net-http-server-application-web/src/main/java/module-info.java index 1e104ce..932bc12 100644 --- a/net-http-server-application-web/src/main/java/module-info.java +++ b/net-http-server-application-web/src/main/java/module-info.java @@ -1,6 +1,7 @@ module org.xbib.net.http.server.application.web { uses org.xbib.config.ConfigLogger; exports org.xbib.net.http.server.application.web; + exports org.xbib.net.http.server.application.web.groovy; requires org.xbib.net; requires org.xbib.net.mime; requires org.xbib.net.http; @@ -8,8 +9,10 @@ module org.xbib.net.http.server.application.web { requires org.xbib.net.http.server.netty; requires org.xbib.net.http.server.netty.secure; requires org.xbib.net.http.template.groovy; + requires org.xbib.net.http.j2html; requires org.xbib.datastructures.tiny; requires org.xbib.jdbc.query; requires org.xbib.config; requires java.logging; + requires com.j2html; } diff --git a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/Bootstrap.java b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/groovy/Bootstrap.java similarity index 68% rename from net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/Bootstrap.java rename to net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/groovy/Bootstrap.java index a066618..8e9733f 100644 --- a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/Bootstrap.java +++ b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/groovy/Bootstrap.java @@ -1,8 +1,9 @@ -package org.xbib.net.http.server.application.web; +package org.xbib.net.http.server.application.web.groovy; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.ServiceLoader; import org.xbib.config.ConfigLoader; @@ -14,6 +15,9 @@ import org.xbib.net.http.HttpHeaderNames; import org.xbib.net.http.HttpHeaderValues; import org.xbib.net.http.HttpResponseStatus; import org.xbib.net.http.HttpVersion; +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.domain.BaseHttpDomain; import org.xbib.net.http.server.domain.BaseHttpSecurityDomain; import org.xbib.net.http.server.domain.HttpSecurityDomain; @@ -39,7 +43,6 @@ import org.xbib.net.http.template.groovy.GroovyInternalServerErrorHandler; import org.xbib.net.http.template.groovy.GroovyHttpStatusHandler; import org.xbib.net.http.template.groovy.GroovyTemplateResourceHandler; import org.xbib.net.http.template.groovy.GroovyTemplateService; -import org.xbib.net.mime.stream.Hex; import org.xbib.settings.Settings; public final class Bootstrap { @@ -164,7 +167,7 @@ public final class Bootstrap { .setHandler(ctx -> { ctx.status(HttpResponseStatus.OK) .header(HttpHeaderNames.CONTENT_TYPE, "image/x-icon") - .body(NettyDataBufferFactory.getInstance().wrap(Hex.fromHex(hexFavIcon))) + .body(NettyDataBufferFactory.getInstance().wrap(fromHex(hexFavIcon))) .done(); }) .build()) @@ -173,6 +176,11 @@ public final class Bootstrap { .setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/")) .build()) .addService(httpService) + .addService(J2HtmlService.builder() + .setPrefix("/j2html") + .setPath("glob:**") + .setHandler(new J2HtmlResourceHandler()) + .build()) .addService(GroovyTemplateService.builder() .setTemplateName("index.gtpl") .setSecurityDomain(securityDomain) @@ -205,5 +213,66 @@ public final class Bootstrap { private static final String hexFavIcon = - "00000100010010100000010020006804000016000000280000001000000020000000010020000000000040040000130b0000130b0000000000000000000000000000000000000000000000000000000000005140322f62524a536050475f5140322f5140320c000000000000000000000000000000000000000000000000000000000000000000000000fffffe40b1a9a5c76f605bff6f605bff6f605bff6f605bff6b5b55f38f7d71ff978473bf877465100000000000000000000000000000000000000000fffffe8ff2eeeafff2f0eeffb7b0adff786a65ff6f605bff6f605bff6f605bff6f605bff72625af77f6d60bf00000000000000000000000000000000fffffe8fd9ccc0fffbfaf9fffbfaf8fffbfaf8fff6f5f5ff9c928eff6f605bff786a65ff6f605bff6f605bff716159ff6d5d4f9f0000000000000000fffffe40e7e0d8ffcfbfb2ffc4b2a0ffcebdafffccbbabffe1d6ceffe7ded7ffd2cdccff786a65ff6f605bff6f605bff6c5c54ff6a5a4ffb6050425000000000faf9f6afd3c5b8ffd4c6baffcfc1b2ffd5c7bbffcab9a9ffe7ded7fff2f0efff786a65ff6f605bff6f605bff6f605bff6c5c53ff6b5b53fb5d4d3fdf00000000e9e2dbffd8ccc0ffdcd1c5ffdcd1c6ffd4c7b9ffdacec3ffffffffffd2cdccff6f605bff6f605bff6f605bff6f605bff6a5a4fff6b5b52ff5b4b3eef53443660ece7e0ffdbd0c5ffe0d6ccffe9e2d9ffe3dad1fff1ece8ffffffffffc0b9b7ff6f605bff6f605bff6f605bff6f605bff685749ff6a5a4fff5e4e43cf5142348fefe9e3ffe0d7ccffe9e1d9ffeae3dbffe3dbd0fff4f0ecffffffffffdbd7d6ff6f605bff6f605bff6f605bff6d5e56ff675648ff6a5a4fff5e4f44af514233bfefeae4ffe8e0d7ffebe5ddffede8dfffebe5dcfff2eee8ffffffffffffffffff817470ff6f605bff6f605aff69594dff685749ff6b5b52ff5c4d429f5142338ff9f7f4afeae3dbffe7e1d6ffece7ddffefebe3fff1ece7ffffffffffffffffffdbd7d6ff6f605bff6a5a4fff68574bff6a5a50ff6b5b54f7554638574f403150fffffe40f1ede7ffece7dfffebe5dcffeae3dbfffbf9f7fffffffffffffffffff8f5f3ff83756dff69584dff69584cff6b5b52ff675750af000000004e3f304000000000fcfbf99fede7e0ffe4dbd1fff6f3effffffffffff9f8f6ffe1d7ceffdbcfc5ff9b8a7cff68574aff6a594eff6b5b54e751403218000000004e3f30100000000000000000fbfaf89fece6dfffeee9e3ffe4dbd2ffd8cbbfffd8ccc0ffcdbcadff6f5e52ff6a5a50ff6b5b54e75d4d4328000000000000000000000000000000000000000000000000fffffe60f6f4f0cff1ece7ffe2d9d0ffdbcec3ffccc1b7ff6a5a52f7675750af51403218000000000000000000000000000000000000000000000000000000000000000000000000fffffe10fffffe40fffffe40fffffe205140320c000000000000000000000000000000000000000000000000f83f9407e0070000c0079807800300000001603f0001603f0000603f0000603f0000e13e0000e23e0000e23e0002e23e8002e23ec007e33ee00fe33ef83fe33"; + "000001000100101000000100200068040000160000002800000010000000" + + "200000000100200000000000000000000000000000000000000000000000" + + "000099330000993300009933000099330000993300009933000099330000" + + "993300009933000099330000993300009933000099330000993300009933" + + "00009933000099330000993300009933000099330000993300ff993300ff" + + "993300ff993300ff993300ff993300ff993300ff993300ff993300ff9933" + + "0000993300009933000099330000993300009933000099330000993300ff" + + "993300ff993300ff993300ff993300ff993300ff993300ff993300ff9933" + + "00ff99330000993300009933000099330000993300009933000099330000" + + "993300ff9933000099330000993300009933000099330000993300009933" + + "00009933000099330000993300009933000099330000993300ff993300ff" + + "99330000993300ff99330000993300ff993300ff993300ff993300ff9933" + + "00ff993300ff993300ff993300ff993300ff9933000099330000993300ff" + + "993300ff99330000993300ff99330000993300ff993300ff993300ff9933" + + "00ff993300ff993300ff993300ff993300ff993300ff9933000000000000" + + "993300ff993300ff00000000993300ff0000000000000000000000000000" + + "00000000000000000000000000000000000099330000993300ff99330000" + + "00000000993300ff993300ff00000000993300ff00000000000000000000" + + "0000000000000000000000000000993300ff993300ff00000000993300ff" + + "0000000000000000993300ff993300ff00000000993300ff993300009933" + + "000099330000993300009933000099330000993300ff993300ff99330000" + + "993300ff9933000000000000993300ff993300ff00000000993300ff9933" + + "00009933000099330000993300009933000099330000993300ff993300ff" + + "99330000993300ff9933000000000000993300ff993300ff000000009933" + + "00ff993300009933000099330000993300009933000099330000993300ff" + + "993300ff99330000993300ff9933000000000000993300ff993300ff0000" + + "000099330000993300009933000099330000993300009933000099330000" + + "993300ff993300ff99330000993300ff9933000000000000993300ff9933" + + "00ff00000000993300ff993300ff993300ff993300ff993300ff993300ff" + + "993300ff993300ff993300ff99330000993300ff99330000000000009933" + + "00ff993300ff000000009933000099330000993300009933000099330000" + + "993300009933000099330000993300009933000099330000993300000000" + + "0000993300ff993300ff993300ff993300ff993300ff993300ff993300ff" + + "993300ff993300ff993300ff993300ff993300ff99330000993300009933" + + "000000000000000000000000000000000000993300009933000099330000" + + "993300009933000099330000993300009933000099330000993300009933" + + "000099330000ffff0000f0070000f0070000f7ff00009401000094010000" + + "97fd000097e5000097e5000097e5000097e500009fe50000900500009fff" + + "000080070000ffff0000"; + + + private static byte[] fromHex(String hex) { + Objects.requireNonNull(hex); + int len = hex.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) fromHex(hex.charAt(i), hex.charAt(i + 1)); + } + return data; + } + + private static int fromHex(int b1, int b2) { + int i1 = Character.digit(b1, 16); + if (i1 == -1) { + throw new IllegalArgumentException("invalid character in hexadecimal: " + b1); + } + int i2 = Character.digit(b2, 16); + if (i2 == -1) { + throw new IllegalArgumentException("invalid character in hexadecimal: " + b2); + } + return (i1 << 4) + i2; + } } diff --git a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/Bootstrap.java b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/Bootstrap.java new file mode 100644 index 0000000..d9b3bdc --- /dev/null +++ b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/Bootstrap.java @@ -0,0 +1,264 @@ +package org.xbib.net.http.server.application.web.j2html; + +import org.xbib.config.ConfigLoader; +import org.xbib.config.ConfigLogger; +import org.xbib.config.ConfigParams; +import org.xbib.config.SystemConfigLogger; +import org.xbib.net.NetworkClass; +import org.xbib.net.http.HttpHeaderNames; +import org.xbib.net.http.HttpHeaderValues; +import org.xbib.net.http.HttpResponseStatus; +import org.xbib.net.http.HttpVersion; +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; +import org.xbib.net.http.server.auth.FormAuthenticationHandler; +import org.xbib.net.http.server.domain.BaseHttpDomain; +import org.xbib.net.http.server.domain.BaseHttpSecurityDomain; +import org.xbib.net.http.server.domain.HttpSecurityDomain; +import org.xbib.net.http.server.executor.BaseExecutor; +import org.xbib.net.http.server.executor.Executor; +import org.xbib.net.http.server.ldap.LdapContextFactory; +import org.xbib.net.http.server.ldap.LdapGroupMapping; +import org.xbib.net.http.server.ldap.LdapRealm; +import org.xbib.net.http.server.ldap.LdapUserMapping; +import org.xbib.net.http.server.netty.NettyHttpServer; +import org.xbib.net.http.server.netty.buffer.NettyDataBufferFactory; +import org.xbib.net.http.server.netty.secure.HttpsAddress; +import org.xbib.net.http.server.netty.secure.HttpsRequest; +import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig; +import org.xbib.net.http.server.resource.ClassLoaderResourceHandler; +import org.xbib.net.http.server.route.BaseHttpRouter; +import org.xbib.net.http.server.route.HttpRouter; +import org.xbib.net.http.server.service.BaseHttpService; +import org.xbib.net.http.server.service.HttpService; +import org.xbib.settings.Settings; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.ServiceLoader; + +public final class Bootstrap { + + private static final ConfigLogger bootLogger; + + static { + // early loading of boot logger during static initialization block + ServiceLoader serviceLoader = ServiceLoader.load(ConfigLogger.class); + Optional optionalBootLogger = serviceLoader.findFirst(); + bootLogger = optionalBootLogger.orElse(new SystemConfigLogger()); + } + + private Bootstrap() { + } + + public static void main(String[] args) throws Exception { + String profile = args.length > 0 ? args[0] : System.getProperty("application.profile"); + ConfigParams configParams; + ConfigLoader configLoader; + Settings settings; + configParams = new ConfigParams() + .withArgs(args) + .withDirectoryName("application") + .withFileNamesWithoutSuffix(profile) + .withSystemEnvironment() + .withSystemProperties(); + configLoader = ConfigLoader.getInstance() + .withLogger(bootLogger); + settings = configLoader.load(configParams); + int rc = 1; + try { + rc = runApplication(settings); + } catch (Throwable t) { + t.printStackTrace(); + } finally { + // always hard-exit the JVM, maybe there are threads hanging + System.exit(rc); + } + } + + private static int runApplication(Settings settings) throws Exception { + HttpsAddress httpsAddress = HttpsAddress.builder() + .setSecure(true) + .setVersion(HttpVersion.HTTP_2_0) + .setHost("localhost") + .setPort(8443) + .setSelfCert("localhost") + .build(); + NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig(); + serverConfig.setServerName("WebApplication", Bootstrap.class.getPackage().getImplementationVersion()); + serverConfig.setNetworkClass(NetworkClass.SITE); + serverConfig.setDebug(true); + + Map contextFactories = new HashMap<>(); + LdapContextFactory contextFactory = new LdapContextFactory("simple", + "com.sun.jndi.ldap.LdapCtxFactory", + null, + "ldap://localhost:1389", + false, + null, + null, + "follow" + ); + contextFactories.put("default", contextFactory); + Map userMappings = new HashMap<>(); + LdapUserMapping userMapping = new LdapUserMapping("ou=People,dc=example.org", + "(&(objectclass=posixAccount)(uid:caseExactMatch:={0}))", + "uid", + "cn" + ); + userMappings.put("default", userMapping); + Map groupMappings = new HashMap<>(); + LdapGroupMapping groupMapping = new LdapGroupMapping("ou=group,dc=example.org", + "cn", + "(&(objectclass=posixGroup)(memberUid:caseExactMatch:={0}))", + new String[] { "uid" } + ); + groupMappings.put("default", groupMapping); + LdapRealm ldapRealm = new LdapRealm("Web Application Realm", contextFactories, userMappings, groupMappings); + + BasicAuthenticationHandler basicAuthenticationHandler = + new BasicAuthenticationHandler(ldapRealm); + + FormAuthenticationHandler formAuthenticationHandler = + new FormAuthenticationHandler("j_username", "j_password", "j_remember", + "demo/auth/form/index.gtpl", ldapRealm); + + HttpSecurityDomain securityDomain = BaseHttpSecurityDomain.builder() + .setSecurityRealm(ldapRealm) + .setHandlers(formAuthenticationHandler) + .build(); + + HttpService httpService = BaseHttpService.builder() + .setPath("/secure") + .setHandler(ctx -> { + ctx.status(HttpResponseStatus.OK) + .header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN) + .charset(StandardCharsets.UTF_8) + .body("secure domain: " + + " SNI host = " + ctx.getRequest().as(HttpsRequest.class).getSNIHost() + + " SSL session = " + ctx.getRequest().as(HttpsRequest.class).getSSLSession() + + " base URL = " + ctx.getRequest().getBaseURL() + + " parameter = " + ctx.getRequest().getParameter().allToString() + + " attributes = " + ctx.getAttributes() + + " local address = " + ctx.getRequest().getLocalAddress() + + " remote address = " + ctx.getRequest().getRemoteAddress()); + ctx.done(); + }) + .build(); + + HttpRouter httpRouter = BaseHttpRouter.builder() + .setHandler(500, new InternalServerErrorHandler()) + .addDomain(BaseHttpDomain.builder() + .setHttpAddress(httpsAddress) + .addService(BaseHttpService.builder() + .setPath("/favicon.ico") + .setHandler(ctx -> { + ctx.status(HttpResponseStatus.OK) + .header(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/")) + .build()) + .addService(httpService) + .addService(J2HtmlService.builder() + .setPath("glob:**") + .setHandler(new J2HtmlResourceHandler()) + .build()) + .build()) + .build(); + + Executor executor = BaseExecutor.builder() + .build(); + + WebApplication application = WebApplication.builder() + .setSettings(settings) + .setSecret("1088e6b7ad58d64d09961e1357bf95544447051c6ad1332cd626e3a33bb5786b") + .setExecutor(executor) + .setRouter(httpRouter) + .build(); + + try (NettyHttpServer server = NettyHttpServer.builder() + .setHttpServerConfig(serverConfig) + .setApplication(application) + .build()) { + server.bind(); + server.loop(); + } + + return 0; + } + + + private static final String hexFavIcon = + "000001000100101000000100200068040000160000002800000010000000" + + "200000000100200000000000000000000000000000000000000000000000" + + "000099330000993300009933000099330000993300009933000099330000" + + "993300009933000099330000993300009933000099330000993300009933" + + "00009933000099330000993300009933000099330000993300ff993300ff" + + "993300ff993300ff993300ff993300ff993300ff993300ff993300ff9933" + + "0000993300009933000099330000993300009933000099330000993300ff" + + "993300ff993300ff993300ff993300ff993300ff993300ff993300ff9933" + + "00ff99330000993300009933000099330000993300009933000099330000" + + "993300ff9933000099330000993300009933000099330000993300009933" + + "00009933000099330000993300009933000099330000993300ff993300ff" + + "99330000993300ff99330000993300ff993300ff993300ff993300ff9933" + + "00ff993300ff993300ff993300ff993300ff9933000099330000993300ff" + + "993300ff99330000993300ff99330000993300ff993300ff993300ff9933" + + "00ff993300ff993300ff993300ff993300ff993300ff9933000000000000" + + "993300ff993300ff00000000993300ff0000000000000000000000000000" + + "00000000000000000000000000000000000099330000993300ff99330000" + + "00000000993300ff993300ff00000000993300ff00000000000000000000" + + "0000000000000000000000000000993300ff993300ff00000000993300ff" + + "0000000000000000993300ff993300ff00000000993300ff993300009933" + + "000099330000993300009933000099330000993300ff993300ff99330000" + + "993300ff9933000000000000993300ff993300ff00000000993300ff9933" + + "00009933000099330000993300009933000099330000993300ff993300ff" + + "99330000993300ff9933000000000000993300ff993300ff000000009933" + + "00ff993300009933000099330000993300009933000099330000993300ff" + + "993300ff99330000993300ff9933000000000000993300ff993300ff0000" + + "000099330000993300009933000099330000993300009933000099330000" + + "993300ff993300ff99330000993300ff9933000000000000993300ff9933" + + "00ff00000000993300ff993300ff993300ff993300ff993300ff993300ff" + + "993300ff993300ff993300ff99330000993300ff99330000000000009933" + + "00ff993300ff000000009933000099330000993300009933000099330000" + + "993300009933000099330000993300009933000099330000993300000000" + + "0000993300ff993300ff993300ff993300ff993300ff993300ff993300ff" + + "993300ff993300ff993300ff993300ff993300ff99330000993300009933" + + "000000000000000000000000000000000000993300009933000099330000" + + "993300009933000099330000993300009933000099330000993300009933" + + "000099330000ffff0000f0070000f0070000f7ff00009401000094010000" + + "97fd000097e5000097e5000097e5000097e500009fe50000900500009fff" + + "000080070000ffff0000"; + + + private static byte[] fromHex(String hex) { + Objects.requireNonNull(hex); + int len = hex.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) fromHex(hex.charAt(i), hex.charAt(i + 1)); + } + return data; + } + + private static int fromHex(int b1, int b2) { + int i1 = Character.digit(b1, 16); + if (i1 == -1) { + throw new IllegalArgumentException("invalid character in hexadecimal: " + b1); + } + int i2 = Character.digit(b2, 16); + if (i2 == -1) { + throw new IllegalArgumentException("invalid character in hexadecimal: " + b2); + } + return (i1 << 4) + i2; + } +} diff --git a/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/InternalServerErrorHandler.java b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/InternalServerErrorHandler.java new file mode 100644 index 0000000..17a29e3 --- /dev/null +++ b/net-http-server-application-web/src/main/java/org/xbib/net/http/server/application/web/j2html/InternalServerErrorHandler.java @@ -0,0 +1,37 @@ +package org.xbib.net.http.server.application.web.j2html; + +import org.xbib.net.Attributes; +import org.xbib.net.http.j2html.J2HtmlResourceHandler; +import org.xbib.net.http.server.application.Application; +import org.xbib.net.http.server.resource.HtmlTemplateResourceHandler; +import org.xbib.net.http.server.route.HttpRouterContext; +import org.xbib.net.util.ExceptionFormatter; + +import java.io.IOException; + +import static j2html.TagCreator.body; +import static j2html.TagCreator.code; +import static j2html.TagCreator.div; +import static j2html.TagCreator.h1; +import static j2html.TagCreator.pre; + +public class InternalServerErrorHandler extends J2HtmlResourceHandler { + + protected static class InternalServerErrorResource extends J2HtmlResource { + + protected InternalServerErrorResource(HtmlTemplateResourceHandler templateResourceHandler, HttpRouterContext httpRouterContext) throws IOException { + super(templateResourceHandler, httpRouterContext); + } + + protected String render(Application application, Attributes attributes) { + String message = attributes.get(String.class, "_message"); + Throwable throwable = attributes.get(Throwable.class, "_throwable"); + String stackTrace = ExceptionFormatter.format(throwable); + return body( + h1("Internal Server Error"), + div(message), + pre(code(stackTrace)) + ).render(); + } + } +} diff --git a/settings.gradle b/settings.gradle index 9616a27..cdbda91 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,6 +28,7 @@ dependencyResolutionManagement { library('webjars-bootstrap', 'org.webjars', 'bootstrap').version('5.2.3') library('webjars-jquery', 'org.webjars', 'jquery').version('3.6.4') library('webjars-fontawesome', 'org.webjars', 'font-awesome').version('6.3.0') + library('j2html', 'com.j2html', 'j2html').version('1.6.0') library('net', 'org.xbib', 'net').versionRef('net') library('net-mime', 'org.xbib', 'net-mime').versionRef('net') library('net-security', 'org.xbib', 'net-security').versionRef('net') @@ -61,6 +62,7 @@ include 'net-http-server-nio' include 'net-http-server-simple' include 'net-http-server-simple-secure' include 'net-http-template-groovy' +include 'net-http-j2html' include 'net-http-server-application-web' include 'net-http-server-application-config' include 'net-http-server-application-database'