working on http router creation from settings

This commit is contained in:
Jörg Prante 2025-03-11 15:43:43 +01:00
parent 91fd31a85c
commit d4a8beaf65
64 changed files with 401 additions and 132 deletions

View file

@ -1,3 +1,3 @@
group = org.xbib
name = net-http
version = 4.9.0
version = 5.0.0

View file

@ -21,7 +21,7 @@ tasks.withType(JavaCompile) {
options.fork = true
options.forkOptions.jvmArgs += ['-Duser.language=en','-Duser.country=US']
options.encoding = 'UTF-8'
options.compilerArgs.add('-Xlint:all')
options.compilerArgs.add('-Xlint:all,-exports')
options.compilerArgs.add("--module-path")
options.compilerArgs.add(classpath.asPath)
classpath = files()

View file

@ -17,6 +17,9 @@ public class EventStreamTest {
private static final Logger logger = Logger.getLogger(EventStreamTest.class.getName());
public EventStreamTest() {
}
@Test
void testSimpleJournalGateway() throws URISyntaxException, IOException, InterruptedException {
var uri = new URI("http://localhost:19531/entries?follow");

View file

@ -229,12 +229,16 @@ public class Https1ChannelInitializer implements HttpChannelInitializer {
throw new IllegalStateException();
}
};
// TODO replace deprecation
Http2MultiplexCodecBuilder multiplexCodecBuilder = Http2MultiplexCodecBuilder.forClient(initializer)
.initialSettings(nettyHttpClientConfig.getHttp2Settings());
if (nettyHttpClientConfig.isDebug()) {
multiplexCodecBuilder.frameLogger(new Http2FrameLogger(LogLevel.DEBUG, "client-frame"));
}
Http2MultiplexCodec multiplexCodec = multiplexCodecBuilder.autoAckSettingsFrame(true).build();
// TODO replace deprecation
Http2MultiplexCodec multiplexCodec = multiplexCodecBuilder
.autoAckSettingsFrame(true)
.build();
pipeline.addLast("client-multiplex", multiplexCodec);
pipeline.addLast("client-messages", new Http2Messages(interaction));
// simulate we are ready for HTTP/2

View file

@ -170,11 +170,13 @@ public class Https2ChannelInitializer implements HttpChannelInitializer {
throw new IllegalStateException();
}
};
// TODO replace deprecation
Http2MultiplexCodecBuilder multiplexCodecBuilder = Http2MultiplexCodecBuilder.forClient(initializer)
.initialSettings(nettyHttpClientConfig.getHttp2Settings());
if (nettyHttpClientConfig.isDebug()) {
multiplexCodecBuilder.frameLogger(new Http2FrameLogger(LogLevel.DEBUG, "client-frame"));
}
// TODO replace deprecation
Http2MultiplexCodec multiplexCodec = multiplexCodecBuilder
.autoAckSettingsFrame(true)
.autoAckPingFrame(true)

View file

@ -15,6 +15,9 @@ public class AkamaiTest {
private static final Logger logger = Logger.getLogger(AkamaiTest.class.getName());
public AkamaiTest() {
}
/**
* Problems with akamai:
* failing: Cannot invoke "io.netty.handler.codec.http2.AbstractHttp2StreamChannel.fireChildRead(io.netty.handler.codec.http2.Http2Frame)" because "channel" is null * demo/h2_demo_frame.html sends no content, only a push promise, and does not continue

View file

@ -17,12 +17,17 @@ class ThreadLeakTest {
private static final Logger logger = Logger.getLogger(ThreadLeakTest.class.getName());
public ThreadLeakTest() {
}
@Test
void testForLeaks() throws IOException {
NettyHttpClientConfig config = new NettyHttpsClientConfig();
try (NettyHttpClient client = NettyHttpClient.builder()
.setConfig(config)
.build()) {
// do something with the client
client.getClientConfig();
}
}

View file

@ -66,11 +66,13 @@ public class Http2ChannelInitializer implements HttpChannelInitializer {
throw new IllegalStateException();
}
};
// TODO replace deprecation
Http2MultiplexCodecBuilder multiplexCodecBuilder = Http2MultiplexCodecBuilder.forClient(initializer)
.initialSettings(nettyHttpClientConfig.getHttp2Settings());
if (nettyHttpClientConfig.isDebug()) {
multiplexCodecBuilder.frameLogger(new Http2FrameLogger(LogLevel.DEBUG, "client-frame"));
}
// TODO replace deprecation
Http2MultiplexCodec multiplexCodec = multiplexCodecBuilder
.autoAckPingFrame(true)
.autoAckSettingsFrame(true)

View file

@ -278,7 +278,7 @@ public class ExponentialBackOff implements BackOff {
if (currentIntervalMillis >= maxIntervalMillis / multiplier) {
currentIntervalMillis = maxIntervalMillis;
} else {
currentIntervalMillis *= multiplier;
currentIntervalMillis = (int)(currentIntervalMillis * multiplier);
}
}

View file

@ -14,6 +14,9 @@ import static org.xbib.j2html.TagCreator.html;
public class BadRequestHandler extends J2HtmlResourceHandler {
public BadRequestHandler() {
}
@Override
protected Resource createResource(HttpRouterContext httpRouterContext)
throws IOException, ParameterException {

View file

@ -19,6 +19,9 @@ import static org.xbib.j2html.TagCreator.pre;
public class InternalServerErrorHandler extends J2HtmlResourceHandler {
public InternalServerErrorHandler() {
}
@Override
protected Resource createResource(HttpRouterContext httpRouterContext)
throws IOException, ParameterException {

View file

@ -14,6 +14,9 @@ import static org.xbib.j2html.TagCreator.html;
public class NotFoundHandler extends J2HtmlResourceHandler {
public NotFoundHandler() {
}
@Override
protected Resource createResource(HttpRouterContext httpRouterContext)
throws IOException, ParameterException {

View file

@ -14,6 +14,9 @@ import static org.xbib.j2html.TagCreator.html;
public class UnauthorizedHandler extends J2HtmlResourceHandler {
public UnauthorizedHandler() {
}
@Override
protected Resource createResource(HttpRouterContext httpRouterContext)
throws IOException, ParameterException {

View file

@ -13,6 +13,9 @@ public class Http1Test {
private static final Logger logger = Logger.getLogger(Http1Test.class.getName());
public Http1Test() {
}
@Test
void testGoogleConscrypt() throws Exception {

View file

@ -27,7 +27,7 @@ public class WebApplication extends BaseApplication {
@Override
protected Codec<Session> newSessionCodec(HttpRouterContext httpRouterContext) {
return new FileJsonSessionCodec(sessionName, this, 1024, Duration.ofDays(1),
return new FileJsonSessionCodec(getSessionName(), this, 1024, Duration.ofDays(1),
Paths.get("/var/tmp/session"));
}

View file

@ -8,10 +8,10 @@ import org.xbib.settings.Settings;
public class WebApplicationBuilder extends BaseApplicationBuilder {
protected String profile;
protected String name;
protected String profile;
protected WebApplicationBuilder() {
super();
this.name = System.getProperty("application.name");
@ -28,11 +28,11 @@ public class WebApplicationBuilder extends BaseApplicationBuilder {
return this;
}
@Override
/*@Override
public WebApplicationBuilder setSettings(Settings settings) {
this.settings = settings;
return this;
}
}*/
@Override
public WebApplicationBuilder setSecret(String secret) {

View file

@ -174,7 +174,7 @@ public final class Bootstrap {
.build();
WebApplication application = WebApplication.builder()
.setSettings(settings)
//.setSettings(settings)
.setSecret("1088e6b7ad58d64d09961e1357bf95544447051c6ad1332cd626e3a33bb5786b")
.setExecutor(executor)
.setRouter(httpRouter)

View file

@ -46,8 +46,6 @@ 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 static org.xbib.j2html.TagCreator.body;
import static org.xbib.j2html.TagCreator.h1;
@ -57,7 +55,7 @@ public final class Bootstrap {
private Bootstrap() {
}
public static void main(String[] args) throws Exception {
public static void main(String[] args) {
String profile = args.length > 0 ? args[0] : System.getProperty("application.profile");
ConfigParams configParams;
ConfigLoader configLoader;
@ -181,7 +179,7 @@ public final class Bootstrap {
.build();
WebApplication application = WebApplication.builder()
.setSettings(settings)
//.setSettings(settings)
.setSecret("1088e6b7ad58d64d09961e1357bf95544447051c6ad1332cd626e3a33bb5786b")
.setExecutor(executor)
.setRouter(httpRouter)

View file

@ -55,7 +55,7 @@ public class Https2Handler extends ChannelDuplexHandler {
.setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress())
.setStreamId(streamId);
if ("PRI".equals(fullHttpRequest.method().name())) {
nettyHttpServer.dispatch(httpsRequestBuilder, httpsResponseBuilder, HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED);
nettyHttpServer.dispatchError(httpsRequestBuilder, httpsResponseBuilder, HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED);
return;
}
httpsResponseBuilder.shouldClose("close".equalsIgnoreCase(fullHttpRequest.headers().get(HttpHeaderNames.CONNECTION)));

View file

@ -34,6 +34,9 @@ public class NettyHttps2ServerMultiRequestLoadTest {
private static final Logger logger = Logger.getLogger(NettyHttps2ServerMultiRequestLoadTest.class.getName());
public NettyHttps2ServerMultiRequestLoadTest() {
}
@Test
public void testHttps2Load() throws Exception {
// client HTTP 2.0, server HTTP 2.0

View file

@ -34,6 +34,9 @@ public class NettyHttps2ServerTest {
private static final Logger logger = Logger.getLogger(NettyHttps2ServerTest.class.getName());
public NettyHttps2ServerTest() {
}
@Test
public void testHttps2() throws Exception {
// client HTTP 2.0 + server HTTP 2.0 (no upgrade)

View file

@ -36,6 +36,9 @@ public class NettyHttpsServerMultiRequestLoadTest {
private static final Logger logger = Logger.getLogger(NettyHttpsServerMultiRequestLoadTest.class.getName());
public NettyHttpsServerMultiRequestLoadTest() {
}
@Test
public void testHttps1Load() throws Exception {
// client HTTP 1.1, server HTTP 1.1 (no upgrade)

View file

@ -39,6 +39,9 @@ public class NettyHttpsServerTest {
private static final Logger logger = Logger.getLogger(NettyHttpsServerTest.class.getName());
public NettyHttpsServerTest() {
}
@Test
public void testHttps1() throws Exception {
// client HTTP 1.1 + server HTTP 1.1 (no upgrade)

View file

@ -189,7 +189,7 @@ public class NettyHttpServer implements HttpServer {
}
@Override
public void dispatch(HttpRequestBuilder requestBuilder,
public void dispatchError(HttpRequestBuilder requestBuilder,
HttpResponseBuilder responseBuilder,
HttpResponseStatus responseStatus) {
Callable<?> callable = (Callable<Object>) () -> {

View file

@ -17,6 +17,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class HttpRequestTest {
public HttpRequestTest() {
}
@Test
public void testFormAsMultiMap() {
Parameter parameter = Parameter.builder().domain(Parameter.Domain.FORM)

View file

@ -32,6 +32,9 @@ public class NettyHttp2ServerMultiRequestLoadTest {
private static final Logger logger = Logger.getLogger(NettyHttp2ServerMultiRequestLoadTest.class.getName());
public NettyHttp2ServerMultiRequestLoadTest() {
}
@Test
public void testHttp2Multi() throws Exception {

View file

@ -31,6 +31,9 @@ public class NettyHttp2ServerTest {
private static final Logger logger = Logger.getLogger(NettyHttp2ServerTest.class.getName());
public NettyHttp2ServerTest() {
}
@Test
public void testHttp2() throws Exception {
// note that h2c in cleartext is very uncommon, browser do not support this.

View file

@ -30,6 +30,9 @@ public class NettyHttpServerBodyTest {
private static final Logger logger = Logger.getLogger(NettyHttpServerBodyTest.class.getName());
public NettyHttpServerBodyTest() {
}
@Test
public void testHttp() throws Exception {
URL url = URL.from("http://localhost:8008/domain/");

View file

@ -36,6 +36,9 @@ public class NettyHttpServerFailureTest {
private static final Logger logger = Logger.getLogger(NettyHttpServerTest.class.getName());
public NettyHttpServerFailureTest() {
}
@Test
public void testBadRequest() throws Exception {
URL url = URL.from("http://localhost:8008/domain/");

View file

@ -39,6 +39,9 @@ public class NettyHttpServerFileUploadTest {
private static final Logger logger = Logger.getLogger(NettyHttpServerFileUploadTest.class.getName());
public NettyHttpServerFileUploadTest() {
}
@Test
public void testSimpleFileUpload() throws Exception {
URL url = URL.from("http://localhost:8008/");

View file

@ -32,6 +32,9 @@ public class NettyHttpServerMultiRequestLoadTest {
private static final Logger logger = Logger.getLogger(NettyHttpServerMultiRequestLoadTest.class.getName());
public NettyHttpServerMultiRequestLoadTest() {
}
@Test
public void testHttp1Multi() throws Exception {

View file

@ -35,6 +35,9 @@ public class NettyHttpServerRequestTest {
private static final Logger logger = Logger.getLogger(NettyHttpServerRequestTest.class.getName());
public NettyHttpServerRequestTest() {
}
@Test
public void testHttpRequest() throws Exception {
URL url = URL.from("http://localhost:8008/test/");

View file

@ -35,6 +35,9 @@ public class NettyHttpServerTest {
private static final Logger logger = Logger.getLogger(NettyHttpServerTest.class.getName());
public NettyHttpServerTest() {
}
@Test
public void testHttp() throws Exception {
URL url = URL.from("http://localhost:8008/domain/");

View file

@ -144,7 +144,7 @@ public class NioHttpServer implements HttpServer {
}
@Override
public void dispatch(org.xbib.net.http.server.HttpRequestBuilder requestBuilder,
public void dispatchError(org.xbib.net.http.server.HttpRequestBuilder requestBuilder,
org.xbib.net.http.server.HttpResponseBuilder responseBuilder,
HttpResponseStatus responseStatus) {
HttpRouterContext httpRouterContext = builder.application.createContext(null, requestBuilder, responseBuilder);

View file

@ -8,6 +8,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ByteArrayTest {
public ByteArrayTest() {
}
@Test
public void should_return_inserted_data_when_insert_some_bytes_and_call_getCopyArray_given_a_ByteArray() {
ByteArray byteArray = new ByteArray();

View file

@ -17,6 +17,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpRequestParserTest {
public HttpRequestParserTest() {
}
@Test
public void should_parse_get_request_success() {
HttpRequestParser parser = new HttpRequestParser();

View file

@ -22,6 +22,9 @@ import java.nio.charset.StandardCharsets;
public class NioHttpServerTest {
public NioHttpServerTest() {
}
@Disabled
@Test
public void nioServerTest() throws Exception {

View file

@ -146,7 +146,7 @@ public class SimpleHttpServer implements HttpServer {
}
@Override
public void dispatch(org.xbib.net.http.server.HttpRequestBuilder requestBuilder,
public void dispatchError(org.xbib.net.http.server.HttpRequestBuilder requestBuilder,
org.xbib.net.http.server.HttpResponseBuilder responseBuilder,
HttpResponseStatus responseStatus) {
HttpRouterContext httpRouterContext = builder.application.createContext(null, requestBuilder, responseBuilder);

View file

@ -27,6 +27,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpRouterTest {
public HttpRouterTest() {
}
@Test
public void routerTest() throws Exception {
URL url = URL.http().host("localhost").port(8008).build();

View file

@ -22,6 +22,9 @@ import java.nio.charset.StandardCharsets;
public class SimpleHttpServerTest {
public SimpleHttpServerTest() {
}
@Disabled
@Test
public void simpleServerTest() throws Exception {

View file

@ -92,6 +92,7 @@ public abstract class BaseHttpResponseBuilder implements HttpResponseBuilder {
*/
protected Attributes attributes;
@SuppressWarnings("this-escape")
protected BaseHttpResponseBuilder() {
reset();
}

View file

@ -13,13 +13,12 @@ public interface HttpServer extends Closeable {
void loop() throws IOException;
Collection<HttpDomain> getDomains();
void dispatch(HttpRequestBuilder requestBuilder,
HttpResponseBuilder responseBuilder);
void dispatch(HttpRequestBuilder requestBuilder,
void dispatchError(HttpRequestBuilder requestBuilder,
HttpResponseBuilder responseBuilder,
HttpResponseStatus responseStatus);
Collection<HttpDomain> getDomains();
}

View file

@ -18,7 +18,6 @@ import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.http.server.session.SessionListener;
import org.xbib.net.mime.MimeTypeService;
import org.xbib.settings.Settings;
public interface Application extends SessionListener, Resolver<Path>, Closeable {
@ -28,10 +27,14 @@ public interface Application extends SessionListener, Resolver<Path>, Closeable
Path getHome();
String getPrefix();
Collection<HttpDomain> getDomains();
Set<HttpAddress> getAddresses();
boolean shouldNegotiateLocale();
Locale getLocale();
ZoneId getZoneId();
@ -40,8 +43,6 @@ public interface Application extends SessionListener, Resolver<Path>, Closeable
String getContextPath();
Settings getSettings();
void addModule(ApplicationModule applicationModule);
Collection<ApplicationModule> getModules();

View file

@ -13,6 +13,8 @@ public interface ApplicationBuilder {
ApplicationBuilder setHome(Path home);
ApplicationBuilder setPrefix(String prefix);
ApplicationBuilder setContextPath(String contextPath);
ApplicationBuilder setSecret(String hexSecret);
@ -21,6 +23,8 @@ public interface ApplicationBuilder {
ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled);
ApplicationBuilder shouldNegotiateLocale(boolean shouldNegotiate);
ApplicationBuilder setLocale(Locale locale);
ApplicationBuilder setZoneId(ZoneId zoneId);

View file

@ -9,7 +9,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -41,7 +40,6 @@ import org.xbib.net.http.server.auth.MemoryPropertiesUserProfileCodec;
import org.xbib.net.http.server.validate.HttpRequestValidator;
import org.xbib.net.mime.MimeTypeService;
import org.xbib.net.util.RandomUtil;
import org.xbib.settings.Settings;
public class BaseApplication implements Application {
@ -49,8 +47,6 @@ public class BaseApplication implements Application {
protected BaseApplicationBuilder builder;
protected final String sessionName;
private final HttpResponseRenderer httpResponseRenderer;
private final Attributes attributes;
@ -61,30 +57,9 @@ public class BaseApplication implements Application {
protected BaseApplication(BaseApplicationBuilder builder) {
this.builder = builder;
this.sessionName = builder.settings.get("session.name", "SESS");
this.httpResponseRenderer = newResponseRenderer();
this.attributes = newAttributes();
this.applicationModuleList = new ArrayList<>();
for (Map.Entry<String, Settings> entry : builder.settings.getGroups("module").entrySet()) {
String moduleName = entry.getKey();
Settings moduleSettings = entry.getValue();
if (moduleSettings.getAsBoolean("enabled", true)) {
try {
String className = moduleSettings.get("class");
@SuppressWarnings("unchecked")
Class<ApplicationModule> clazz =
(Class<ApplicationModule>) Class.forName(className, true, builder.classLoader);
ApplicationModule applicationModule = clazz.getConstructor(Application.class, String.class, Settings.class)
.newInstance(this, moduleName, moduleSettings);
applicationModuleList.add(applicationModule);
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
throw new IllegalArgumentException("class not found or not loadable: " + e.getMessage());
}
} else {
logger.log(Level.WARNING, "disabled module: " + moduleName);
}
}
this.httpResponseRenderer = newResponseRenderer();
}
public static BaseApplicationBuilder builder() {
@ -111,6 +86,16 @@ public class BaseApplication implements Application {
return builder.home;
}
@Override
public String getPrefix() {
return builder.prefix;
}
@Override
public boolean shouldNegotiateLocale() {
return builder.shouldNegotiateLocale;
}
@Override
public Locale getLocale() {
return builder.locale;
@ -131,19 +116,6 @@ public class BaseApplication implements Application {
return builder.contextPath;
}
@Override
public Settings getSettings() {
return builder.settings;
}
public String getSecret() {
return builder.secret;
}
public Set<String> getStaticFileSuffixes() {
return builder.staticFileSuffixes;
}
@Override
public Collection<ApplicationModule> getModules() {
return applicationModuleList;
@ -195,6 +167,18 @@ public class BaseApplication implements Application {
}
}
public String getSessionName() {
return "SESS_" + getProfile().toLowerCase(Locale.ROOT);
}
public String getSecret() {
return builder.secret;
}
public Set<String> getStaticFileSuffixes() {
return builder.staticFileSuffixes;
}
protected HttpRequestValidator newRequestValidator() {
return new HttpRequestValidator();
}
@ -216,7 +200,7 @@ public class BaseApplication implements Application {
}
protected Codec<Session> newSessionCodec(HttpRouterContext httpRouterContext) {
return new MemoryPropertiesSessionCodec(sessionName,this, 1024, Duration.ofDays(1));
return new MemoryPropertiesSessionCodec(getSessionName(),this, 1024, Duration.ofDays(1));
}
protected Codec<UserProfile> newUserProfileCodec(HttpRouterContext httpRouterContext) {
@ -227,7 +211,7 @@ public class BaseApplication implements Application {
return new IncomingContextHandler(userProfileCodec,
getSecret(),
"HmacSHA1",
sessionName,
getSessionName(),
sessionCodec,
getStaticFileSuffixes(),
() -> RandomUtil.randomString(16));
@ -237,7 +221,7 @@ public class BaseApplication implements Application {
return new OutgoingContextHandler(
getSecret(),
"HmacSHA1",
sessionName,
getSessionName(),
getStaticFileSuffixes(),
Duration.ofDays(1),
true,
@ -254,13 +238,13 @@ public class BaseApplication implements Application {
@Override
public void onCreated(Session session) {
logger.log(Level.FINER, "session name = " + sessionName + " created = " + session);
logger.log(Level.FINER, "session created = " + session);
applicationModuleList.forEach(module -> module.onOpen(session));
}
@Override
public void onDestroy(Session session) {
logger.log(Level.FINER, "session name = " + sessionName + " destroyed = " + session);
logger.log(Level.FINER, "session destroyed = " + session);
if (throwable != null) {
applicationModuleList.forEach(module -> module.onFail(session, throwable));
} else {

View file

@ -12,7 +12,6 @@ import org.xbib.net.http.server.executor.BaseExecutor;
import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.mime.MimeTypeService;
import org.xbib.settings.Settings;
public class BaseApplicationBuilder implements ApplicationBuilder {
@ -25,9 +24,9 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
protected Path home;
protected ClassLoader classLoader;
protected String prefix;
protected Settings settings;
protected ClassLoader classLoader;
protected String contextPath;
@ -37,6 +36,8 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
protected boolean sessionsEnabled;
protected boolean shouldNegotiateLocale;
protected Locale locale;
protected ZoneId zoneId;
@ -62,14 +63,16 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
this.profile = profile;
return this;
}
@Override
public BaseApplicationBuilder setHome(Path home) {
this.home = home;
return this;
}
public BaseApplicationBuilder setSettings(Settings settings) {
this.settings = settings;
@Override
public BaseApplicationBuilder setPrefix(String prefix) {
this.prefix = prefix;
return this;
}
@ -102,6 +105,12 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this;
}
@Override
public ApplicationBuilder shouldNegotiateLocale(boolean shouldNegotiateLocale) {
this.shouldNegotiateLocale = shouldNegotiateLocale;
return this;
}
@Override
public ApplicationBuilder setLocale(Locale locale) {
this.locale = locale;
@ -150,8 +159,8 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
if (this.home == null) {
this.home = Paths.get(System.getProperty("application.home", "."));
}
if (this.settings == null) {
this.settings = Settings.emptySettings();
if (this.prefix == null) {
this.prefix = "/";
}
if (this.classLoader == null) {
this.classLoader = getClass().getClassLoader();
@ -177,6 +186,26 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
if (executor == null) {
this.executor = BaseExecutor.builder().build();
}
/*for (Map.Entry<String, Settings> entry : settings.getGroups("module").entrySet()) {
String moduleName = entry.getKey();
Settings moduleSettings = entry.getValue();
if (moduleSettings.getAsBoolean("enabled", true)) {
try {
String className = moduleSettings.get("class");
@SuppressWarnings("unchecked")
Class<ApplicationModule> clazz =
(Class<ApplicationModule>) Class.forName(className, true, builder.classLoader);
ApplicationModule applicationModule = clazz.getConstructor(Application.class, String.class, Settings.class)
.newInstance(this, moduleName, moduleSettings);
applicationModuleList.add(applicationModule);
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
throw new IllegalArgumentException("class not found or not loadable: " + e.getMessage());
}
} else {
logger.log(Level.WARNING, "disabled module: " + moduleName);
}
}*/
return new BaseApplication(this);
}
}

View file

@ -1,8 +1,6 @@
package org.xbib.net.http.server.ldap;
import java.io.IOException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -12,7 +10,6 @@ import javax.naming.directory.InitialDirContext;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.StartTlsRequest;
import javax.naming.ldap.StartTlsResponse;
import javax.security.auth.Subject;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
@ -135,15 +132,13 @@ public class LdapContextFactory {
try {
LoginContext lc = new LoginContext(getClass().getName(), new CallbackHandlerImpl(principal, credentials));
lc.login();
initialDirContext = Subject.doAs(lc.getSubject(), (PrivilegedExceptionAction<InitialDirContext>) () -> {
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, factory);
env.put(Context.PROVIDER_URL, providerUrl);
env.put(Context.REFERRAL, referral);
logger.log(Level.FINE, "new initial LDAP context: " + env);
return new InitialLdapContext(env, null);
});
} catch (LoginException | PrivilegedActionException e) {
initialDirContext = new InitialLdapContext(env, null);
} catch (LoginException e) {
NamingException namingException = new NamingException(e.getMessage());
namingException.initCause(e);
throw namingException;

View file

@ -54,7 +54,7 @@ public class HtmlTemplateResource implements HttpServerResource {
this.templateResourceHandler = templateResourceHandler;
this.application = httpRouterContext.getAttributes().get(Application.class, "application");
Objects.requireNonNull(application);
this.shouldNegotiateLocale = application.getSettings().getAsBoolean("negotiateLocale", false);
this.shouldNegotiateLocale = application.shouldNegotiateLocale();
this.responseBuilder = httpRouterContext.getAttributes().get(HttpResponseBuilder.class, "responsebuilder");
Objects.requireNonNull(responseBuilder);
Path root = templateResourceHandler.getRoot();
@ -64,17 +64,18 @@ public class HtmlTemplateResource implements HttpServerResource {
}
this.resourcePath = httpRouterContext.getRequestBuilder().getRequestPath().substring(1);
this.path = !resourcePath.isEmpty() ? root.resolve(resourcePath) : root;
String indexFileName = templateResourceHandler.getIndexFileName();
logger.log(Level.FINEST, "class = " + getClass().getName() +
" root = " + root +
" resource path = " + resourcePath +
" path = " + path +
" index file name = " + templateResourceHandler.getIndexFileName());
" index file name = " + indexFileName);
this.name = path.getFileName().toString();
this.baseName = AbstractResourceHandler.basename(name);
this.suffix = AbstractResourceHandler.suffix(name);
if (Files.isDirectory(path)) {
if (getIndexFileName() != null) {
Path indexPath = path.resolve(templateResourceHandler.getIndexFileName());
if (indexFileName != null) {
Path indexPath = path.resolve(indexFileName);
logger.log(Level.FINEST, "resolved to index path = " + indexPath);
if (Files.exists(indexPath)) {
logger.log(Level.FINEST, "index path exists");
@ -191,11 +192,14 @@ public class HtmlTemplateResource implements HttpServerResource {
}
public String url(HttpRequest request, String rel, boolean absolute) {
String webPrefix = application.getSettings().get("web.prefix", "/");
if (!webPrefix.endsWith("/")) {
webPrefix = webPrefix + "/";
String prefix = application.getPrefix();
if (prefix == null) {
prefix = "/";
}
URL url = request.getBaseURL().resolve(webPrefix).resolve(rel);
if (!prefix.endsWith("/")) {
prefix = prefix + "/";
}
URL url = request.getBaseURL().resolve(prefix).resolve(rel);
return absolute ? url.toExternalForm() : toOrigin(url);
}

View file

@ -21,9 +21,9 @@ public class LocaleNegotiator {
}
lang = lang.trim();
if ((pos = lang.indexOf('-')) == -1) {
locale = new Locale(lang, Locale.getDefault().getCountry());
locale = Locale.of(lang, Locale.getDefault().getCountry());
} else {
locale = new Locale(lang.substring(0, pos), lang.substring(pos + 1));
locale = Locale.of(lang.substring(0, pos), lang.substring(pos + 1));
}
}
}

View file

@ -59,16 +59,17 @@ public class BaseHttpRoute implements HttpRoute {
}
@Override
public String getPrefix() {
public final String getPrefix() {
return prefix;
}
@Override
public String getPath() {
public final String getPath() {
return path;
}
public String getEffectivePath() {
@Override
public final String getEffectivePath() {
String path = getPath();
if (getPrefix() != null && !getPrefix().isEmpty()) {
path = path.replaceFirst(getPrefix(), "");

View file

@ -1,5 +1,6 @@
package org.xbib.net.http.server.route;
import java.security.KeyStoreException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@ -7,8 +8,17 @@ import java.util.Map;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.URL;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpMethod;
import org.xbib.net.http.HttpVersion;
import org.xbib.net.http.server.HttpHandler;
import org.xbib.net.http.server.HttpServiceBuilder;
import org.xbib.net.http.server.domain.BaseHttpDomain;
import org.xbib.net.http.server.domain.HttpDomain;
import org.xbib.net.http.server.domain.HttpDomainBuilder;
import org.xbib.net.http.server.domain.HttpSecurityDomain;
import org.xbib.net.http.server.handler.BadRequestHandler;
import org.xbib.net.http.server.handler.ForbiddenHandler;
import org.xbib.net.http.server.handler.InternalServerErrorHandler;
@ -16,7 +26,11 @@ import org.xbib.net.http.server.handler.NotFoundHandler;
import org.xbib.net.http.server.handler.NotImplementedHandler;
import org.xbib.net.http.server.handler.UnauthorizedHandler;
import org.xbib.net.http.server.handler.VersionNotSupportedHandler;
import org.xbib.net.http.server.service.BaseHttpService;
import org.xbib.net.http.server.service.HttpService;
import org.xbib.settings.Settings;
import javax.net.ssl.SSLException;
public class BaseHttpRouterBuilder implements HttpRouterBuilder {
@ -31,16 +45,17 @@ public class BaseHttpRouterBuilder implements HttpRouterBuilder {
protected HttpRouteResolver<HttpService> httpRouteResolver;
protected BaseHttpRouterBuilder() {
prefix = "";
domains = new ArrayList<>();
handlers = new HashMap<>();
handlers.put(400, new BadRequestHandler());
handlers.put(401, new UnauthorizedHandler());
handlers.put(403, new ForbiddenHandler());
handlers.put(404, new NotFoundHandler());
handlers.put(500, new InternalServerErrorHandler());
handlers.put(501, new NotImplementedHandler());
handlers.put(505, new VersionNotSupportedHandler());
this.prefix = "";
this.domains = new ArrayList<>();
this.handlers = new HashMap<>();
// default handlers
this.handlers.put(400, new BadRequestHandler());
this.handlers.put(401, new UnauthorizedHandler());
this.handlers.put(403, new ForbiddenHandler());
this.handlers.put(404, new NotFoundHandler());
this.handlers.put(500, new InternalServerErrorHandler());
this.handlers.put(501, new NotImplementedHandler());
this.handlers.put(505, new VersionNotSupportedHandler());
}
@Override
@ -55,7 +70,9 @@ public class BaseHttpRouterBuilder implements HttpRouterBuilder {
@Override
public BaseHttpRouterBuilder setHandler(Integer code, HttpHandler httpHandler) {
if (httpHandler != null) {
handlers.put(code, httpHandler);
}
return this;
}
@ -71,6 +88,31 @@ public class BaseHttpRouterBuilder implements HttpRouterBuilder {
return this;
}
@Override
public HttpRouterBuilder fromSettings(Settings settings) {
fromSettings(settings, null);
return this;
}
@Override
public HttpRouterBuilder fromSettings(Settings settings,
Map<String, HttpSecurityDomain> securityDomainMap) {
// global prefix
if (settings.containsSetting("prefix")) {
setPrefix(settings.get("prefix"));
}
// global handlers
for (Map.Entry<String, Settings> entry : settings.getGroups("handler").entrySet()) {
String codeString = entry.getKey();
Settings handlerSettings = entry.getValue();
setHandler(Integer.parseInt(codeString), createHttpHandler(handlerSettings));
}
// handler domains
for (Map.Entry<String, Settings> entry : settings.getGroups("domain").entrySet()) {
addDomain(createHttpDomain(entry.getValue(), securityDomainMap));
}
return this;
}
@Override
public BaseHttpRouter build() {
if (domains.isEmpty()) {
@ -95,4 +137,79 @@ public class BaseHttpRouterBuilder implements HttpRouterBuilder {
}
return new BaseHttpRouter(this);
}
private HttpDomain createHttpDomain(Settings settings,
Map<String, HttpSecurityDomain> securityDomainMap) {
if (settings.getAsBoolean("enabled", true)) {
try {
HttpDomainBuilder httpDomainBuilder = BaseHttpDomain.builder();
httpDomainBuilder.setHttpAddress(createHttpAddress(settings));
for (String name : settings.getAsArray("name")) {
httpDomainBuilder.addName(name);
}
for (Map.Entry<String, Settings> entry : settings.getGroups("service").entrySet()) {
httpDomainBuilder.addService(createHttpService(securityDomainMap, prefix, entry.getKey(), entry.getValue()));
}
return httpDomainBuilder.build();
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
throw new IllegalArgumentException("class not found or not loadable: " + e.getMessage());
}
} else {
logger.log(Level.WARNING, "disabled: " + settings.getAsMap());
}
return null;
}
private HttpAddress createHttpAddress(Settings settings) throws KeyStoreException, SSLException {
HttpAddress.Builder httpAddressBuilder = HttpAddress.builder();
if (settings.containsSetting("url")) {
URL url = URL.create(settings.get("url"));
httpAddressBuilder.setHost(url.getHost());
httpAddressBuilder.setPort(url.getPort());
httpAddressBuilder.setSecure("https".equals(url.getScheme()) || "h2".equals(url.getScheme()));
httpAddressBuilder.setVersion(url.getScheme().startsWith("http") ? HttpVersion.HTTP_1_1 : HttpVersion.HTTP_2_0);
} else {
httpAddressBuilder.setHost(settings.get("host", "localhost"));
boolean isSecure = settings.getAsBoolean("secure", false);
httpAddressBuilder.setSecure(isSecure);
httpAddressBuilder.setPort(settings.getAsInt("port", isSecure ? 443 : 80));
httpAddressBuilder.setVersion(HttpVersion.valueOf(settings.get("version", "HTTP_1_1")));
}
return httpAddressBuilder.build();
}
private HttpService createHttpService(Map<String, HttpSecurityDomain> securityDomainMap,
String prefix,
String path,
Settings settings) {
HttpServiceBuilder httpServiceBuilder = BaseHttpService.builder();
if (settings.containsSetting("securitydomain")) {
if (securityDomainMap != null) {
httpServiceBuilder.setSecurityDomain(securityDomainMap.get(settings.get("securitydomain")));
}
}
httpServiceBuilder.setPrefix(prefix);
httpServiceBuilder.setPath(path);
httpServiceBuilder.setMethod(HttpMethod.valueOf(settings.get("method", "GET")));
httpServiceBuilder.setHandler(createHttpHandler(settings));
return httpServiceBuilder.build();
}
private HttpHandler createHttpHandler(Settings settings) {
if (settings.getAsBoolean("enabled", true)) {
try {
String className = settings.get("class");
@SuppressWarnings("unchecked")
Class<HttpHandler> clazz = (Class<HttpHandler>) Class.forName(className, true, getClass().getClassLoader());
return clazz.getConstructor().newInstance();
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
throw new IllegalArgumentException("class not found or not loadable: " + e.getMessage());
}
} else {
logger.log(Level.WARNING, "disabled: " + settings.getAsMap());
}
return null;
}
}

View file

@ -2,7 +2,11 @@ package org.xbib.net.http.server.route;
import org.xbib.net.http.server.HttpHandler;
import org.xbib.net.http.server.domain.HttpDomain;
import org.xbib.net.http.server.domain.HttpSecurityDomain;
import org.xbib.net.http.server.service.HttpService;
import org.xbib.settings.Settings;
import java.util.Map;
public interface HttpRouterBuilder {
@ -14,5 +18,10 @@ public interface HttpRouterBuilder {
HttpRouterBuilder setRouteResolver(HttpRouteResolver<HttpService> httpRouteResolver);
HttpRouterBuilder fromSettings(Settings settings);
HttpRouterBuilder fromSettings(Settings settings,
Map<String, HttpSecurityDomain> securityDomainMap);
HttpRouter build();
}

View file

@ -131,7 +131,7 @@ public class BaseSession implements Session {
@SuppressWarnings("unchecked")
@Override
public Object put(String key, Object value) {
public final Object put(String key, Object value) {
Object v = value;
if (key.startsWith(CACHE_PREFIX)) {
if (value instanceof Map) {
@ -147,7 +147,7 @@ public class BaseSession implements Session {
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public void putAll(Map map) {
public final void putAll(Map map) {
if (map == null) {
throw new NullPointerException("unexpected null map for putAll");
}

View file

@ -3,6 +3,8 @@ module org.xbib.net.http.server.test {
requires org.xbib.net;
requires org.xbib.net.http;
requires org.xbib.net.http.server;
requires org.xbib.datastructures.json.tiny;
requires org.xbib.settings.api;
exports org.xbib.net.http.server.test.base;
exports org.xbib.net.http.server.test.ldap;
exports org.xbib.net.http.server.test.session;

View file

@ -14,6 +14,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class BaseHttpRouteResolverTest {
public BaseHttpRouteResolverTest() {
}
@Test
public void testEmptyRouteResolver() {
BaseHttpRouteResolver.Builder<Integer> builder = BaseHttpRouteResolver.builder();

View file

@ -0,0 +1,21 @@
package org.xbib.net.http.server.test.base;
import org.junit.jupiter.api.Test;
import org.xbib.net.http.server.route.BaseHttpRouter;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.settings.Settings;
public class RouterBuilderTest {
public RouterBuilderTest() {
}
@Test
void buildFromSettings() {
// java.lang.NullPointerException: Cannot invoke "org.xbib.settings.SettingsBuilder.build()" because the return value of "org.xbib.settings.Settings$Holder.createBuilder()" is null
Settings settings = Settings.emptySettings();
HttpRouter httpRouter = BaseHttpRouter.builder()
.fromSettings(settings)
.build();
}
}

View file

@ -20,6 +20,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class LdapRealmTest {
public LdapRealmTest() {
}
@Disabled
@Test

View file

@ -13,6 +13,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class LdapTest {
public LdapTest() {
}
@Disabled
@Test
public void testLdap() {

View file

@ -15,6 +15,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class JsonSessionTest {
public JsonSessionTest() {
}
@Test
void testJsonSession() throws IOException {
Codec<Session> sessionCodec = newSessionCodec();

View file

@ -1,6 +1,7 @@
package org.xbib.net.http.server.test.userprofile;
import org.junit.jupiter.api.Test;
import org.xbib.datastructures.json.tiny.JsonBuilder;
import org.xbib.net.UserProfile;
import org.xbib.net.http.server.persist.Codec;
import org.xbib.net.http.server.auth.FileJsonUserProfileCodec;
@ -12,6 +13,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class JsonUserProfileTest {
public JsonUserProfileTest() {
}
@Test
void testJsonUserProfile() throws IOException {

View file

@ -115,16 +115,19 @@ public abstract class DefaultMarkupTemplate extends BaseTemplate {
}
public String url(String rel, boolean absolute) {
String webPrefix = application.getSettings().get("web.prefix", "/");
if (!webPrefix.endsWith("/")) {
webPrefix = webPrefix + "/";
String prefix = application.getPrefix();
if (prefix == null) {
prefix = "/";
}
if (!prefix.endsWith("/")) {
prefix = prefix + "/";
}
if (request != null) {
URL url = request.getBaseURL().resolve(webPrefix).resolve(rel);
URL url = request.getBaseURL().resolve(prefix).resolve(rel);
return absolute ? url.toExternalForm() : toOrigin(url);
} else {
logger.log(Level.WARNING, "request is null, returning " + webPrefix + rel);
return webPrefix + rel;
logger.log(Level.WARNING, "request is null, returning " + prefix + rel);
return prefix + rel;
}
}

View file

@ -72,7 +72,7 @@ public class GroovyMarkupTemplateHandler implements HttpHandler {
}
}
protected TemplateConfiguration createConfiguration(Class<? extends BaseTemplate> templateClass,
protected final TemplateConfiguration createConfiguration(Class<? extends BaseTemplate> templateClass,
Locale locale,
boolean autoEscape,
boolean autoIndent,

View file

@ -7,8 +7,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
*/
public class HttpHeaders implements Headers {
private final List<Pair<String, String>> list;

View file

@ -35,7 +35,7 @@ public class DefaultCookie implements Cookie {
throw new IllegalArgumentException("empty name");
}
if (value != null) {
setValue(value);
this.value = Objects.requireNonNull(value, "value");
}
}