refactoring code for easier API, update to Netty 4.1.92, begin of file upload work

This commit is contained in:
Jörg Prante 2023-04-07 22:35:24 +02:00
parent 43558479e7
commit 3749b9ba3a
67 changed files with 1903 additions and 917 deletions

View file

@ -1,5 +1,5 @@
group = org.xbib
name = net-http
version = 3.2.0
version = 3.3.0
org.gradle.warning.mode = ALL

View file

@ -1,15 +1,14 @@
package org.xbib.net.http.client.netty.secure;
import javax.net.ssl.SSLSession;
import org.xbib.net.http.HttpHeaders;
import org.xbib.net.http.client.netty.HttpRequest;
public class HttpsRequest extends HttpRequest {
private final HttpsRequestBuilder builder;
protected HttpsRequest(HttpsRequestBuilder builder, HttpHeaders headers) {
super(builder, headers);
protected HttpsRequest(HttpsRequestBuilder builder) {
super(builder);
this.builder = builder;
}

View file

@ -16,6 +16,7 @@ public class HttpsRequestBuilder extends HttpRequestBuilder {
}
public HttpsRequest build() {
return new HttpsRequest(this, validateHeaders());
this.headers = validateHeaders(headers);
return new HttpsRequest(this);
}
}

View file

@ -16,8 +16,7 @@ public class HttpChunkContentCompressor extends HttpContentCompressor {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof ByteBuf) {
ByteBuf byteBuf = (ByteBuf) msg;
if (msg instanceof ByteBuf byteBuf) {
if (byteBuf.isReadable()) {
msg = new DefaultHttpContent(byteBuf);
}

View file

@ -2,7 +2,6 @@ package org.xbib.net.http.client.netty;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
@ -23,6 +22,7 @@ import org.xbib.net.http.HttpVersion;
import org.xbib.net.http.client.BackOff;
import org.xbib.net.http.client.ExceptionListener;
import org.xbib.net.http.client.HttpResponse;
import org.xbib.net.http.client.Part;
import org.xbib.net.http.client.ResponseListener;
import org.xbib.net.http.client.TimeoutListener;
import org.xbib.net.http.cookie.Cookie;
@ -34,15 +34,12 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
private final HttpRequestBuilder builder;
private final HttpHeaders headers;
private CompletableFuture<HttpRequest> completableFuture;
private int redirectCount;
protected HttpRequest(HttpRequestBuilder builder, HttpHeaders headers) {
protected HttpRequest(HttpRequestBuilder builder) {
this.builder = builder;
this.headers = headers;
}
@Override
@ -62,7 +59,7 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
@Override
public HttpHeaders getHeaders() {
return headers;
return builder.headers;
}
@Override
@ -95,11 +92,11 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
@Override
public CharBuffer getBodyAsChars(Charset charset) {
return charset.decode(getBody());
return charset.decode(builder.body);
}
public CharBuffer getBodyAsChars(Charset charset, int offset, int size) {
ByteBuffer slicedBuffer = (getBody().duplicate().position(offset)).slice();
ByteBuffer slicedBuffer = (builder.body.duplicate().position(offset)).slice();
slicedBuffer.limit(size);
return charset.decode(slicedBuffer);
}
@ -110,8 +107,8 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
return (R) this;
}
public List<InterfaceHttpData> getBodyData() {
return builder.bodyData;
public List<Part> getParts() {
return builder.parts;
}
public boolean isFollowRedirect() {
@ -151,7 +148,7 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
return "HttpNettyRequest[url=" + builder.url +
",version=" + builder.httpVersion +
",method=" + builder.httpMethod +
",headers=" + headers.entries() +
",headers=" + builder.headers.entries() +
",content=" + (builder.body != null && builder.body.remaining() >= 16 ?
getBodyAsChars(StandardCharsets.UTF_8, 0, 16) + "..." :
builder.body != null ? getBodyAsChars(StandardCharsets.UTF_8) : "") +
@ -254,7 +251,7 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
return builder(PooledByteBufAllocator.DEFAULT, httpMethod)
.setVersion(httpRequest.builder.httpVersion)
.setURL(httpRequest.builder.url)
.setHeaders(httpRequest.headers)
.setHeaders(httpRequest.builder.headers)
.content(httpRequest.builder.body)
.setResponseListener(httpRequest.builder.responseListener)
.setTimeoutListener(httpRequest.builder.timeoutListener, httpRequest.builder.timeoutMillis)

View file

@ -2,7 +2,6 @@ package org.xbib.net.http.client.netty;
import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http2.HttpConversionUtil;
import java.io.IOException;
import java.io.UncheckedIOException;
@ -33,6 +32,7 @@ import org.xbib.net.http.HttpVersion;
import org.xbib.net.http.client.BackOff;
import org.xbib.net.http.client.ExceptionListener;
import org.xbib.net.http.client.HttpResponse;
import org.xbib.net.http.client.Part;
import org.xbib.net.http.client.ResponseListener;
import org.xbib.net.http.client.TimeoutListener;
import org.xbib.net.http.cookie.Cookie;
@ -43,53 +43,53 @@ public class HttpRequestBuilder implements org.xbib.net.http.client.HttpRequestB
private static final String DEFAULT_FORM_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=utf-8";
final ByteBufAllocator allocator;
protected final ByteBufAllocator allocator;
HttpAddress httpAddress;
protected HttpAddress httpAddress;
URL url;
protected URL url;
String requestPath;
protected String requestPath;
final Collection<Cookie> cookies;
protected final Collection<Cookie> cookies;
HttpMethod httpMethod;
protected HttpMethod httpMethod;
HttpHeaders headers;
protected HttpHeaders headers;
HttpVersion httpVersion;
protected HttpVersion httpVersion;
final List<String> removeHeaders;
protected final List<String> removeHeaders;
String userAgent;
protected String userAgent;
boolean keepalive;
protected boolean keepalive;
boolean gzip;
protected boolean gzip;
String contentType;
protected String contentType;
ParameterBuilder parameterBuilder;
protected ParameterBuilder parameterBuilder;
ByteBuffer body;
protected ByteBuffer body;
final List<InterfaceHttpData> bodyData;
protected boolean followRedirect;
boolean followRedirect;
protected int maxRedirects;
int maxRedirects;
protected boolean enableBackOff;
boolean enableBackOff;
protected BackOff backOff;
BackOff backOff;
protected ResponseListener<HttpResponse> responseListener;
ResponseListener<HttpResponse> responseListener;
protected ExceptionListener exceptionListener;
ExceptionListener exceptionListener;
protected TimeoutListener timeoutListener;
TimeoutListener timeoutListener;
protected long timeoutMillis;
long timeoutMillis;
protected final List<Part> parts;
protected HttpRequestBuilder() {
this(ByteBufAllocator.DEFAULT);
@ -108,10 +108,10 @@ public class HttpRequestBuilder implements org.xbib.net.http.client.HttpRequestB
this.headers = new HttpHeaders();
this.removeHeaders = new ArrayList<>();
this.cookies = new HashSet<>();
this.bodyData = new ArrayList<>();
this.contentType = DEFAULT_FORM_CONTENT_TYPE;
this.parameterBuilder = Parameter.builder();
this.timeoutMillis = 0L;
this.parts = new ArrayList<>();
}
@Override
@ -245,14 +245,9 @@ public class HttpRequestBuilder implements org.xbib.net.http.client.HttpRequestB
return this;
}
/**
* For multipart MIME body data.
*
* @param data a mime body
* @return this
*/
public HttpRequestBuilder addBodyData(InterfaceHttpData data) {
bodyData.add(data);
@Override
public HttpRequestBuilder addPart(Part part) {
parts.add(part);
return this;
}
@ -382,10 +377,11 @@ public class HttpRequestBuilder implements org.xbib.net.http.client.HttpRequestB
}
public HttpRequest build() {
return new HttpRequest(this, validateHeaders());
this.headers = validateHeaders(headers);
return new HttpRequest(this);
}
protected HttpHeaders validateHeaders() {
protected HttpHeaders validateHeaders(HttpHeaders httpHeaders) {
Parameter parameter = parameterBuilder.build();
HttpHeaders validatedHeaders = HttpHeaders.of(headers);
if (url != null) {

View file

@ -26,7 +26,7 @@ public class NettyHttpClientConfig {
*/
private LogLevel debugLogLevel = LogLevel.DEBUG;
SocketConfig socketConfig = new SocketConfig();
protected SocketConfig socketConfig = new SocketConfig();
private String transportProviderName = null;
@ -67,7 +67,7 @@ public class NettyHttpClientConfig {
/**
* Default for gzip codec is false
*/
private boolean gzipEnabled = false;
private boolean isGzipEnabled = false;
private ByteBufAllocator byteBufAllocator;
@ -95,6 +95,10 @@ public class NettyHttpClientConfig {
private BackOff backOff = BackOff.ZERO_BACKOFF;
private Boolean isChunkWriteEnabled = true;
private Boolean isObjectAggregationEnabled = true;
public NettyHttpClientConfig() {
this.byteBufAllocator = ByteBufAllocator.DEFAULT;
}
@ -208,12 +212,12 @@ public class NettyHttpClientConfig {
}
public NettyHttpClientConfig setGzipEnabled(boolean gzipEnabled) {
this.gzipEnabled = gzipEnabled;
this.isGzipEnabled = gzipEnabled;
return this;
}
public boolean isGzipEnabled() {
return gzipEnabled;
return isGzipEnabled;
}
public NettyHttpClientConfig setHttp2Settings(Http2Settings http2Settings) {
@ -330,4 +334,21 @@ public class NettyHttpClientConfig {
return backOff;
}
public NettyHttpClientConfig setChunkWriteEnabled(boolean isChunkWriteEnabled) {
this.isChunkWriteEnabled = isChunkWriteEnabled;
return this;
}
public Boolean isChunkWriteEnabled() {
return isChunkWriteEnabled;
}
public NettyHttpClientConfig setObjectAggregationEnabled(boolean isObjectAggregationEnabled) {
this.isObjectAggregationEnabled = isObjectAggregationEnabled;
return this;
}
public Boolean isObjectAggregationEnabled() {
return isObjectAggregationEnabled;
}
}

View file

@ -85,17 +85,21 @@ public class Http1ChannelInitializer implements HttpChannelInitializer {
Interaction interaction) throws IOException {
NettyHttpClientConfig nettyHttpClientConfig = nettyHttpClient.getClientConfig();
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("http-client-chunk-writer",
new ChunkedWriteHandler());
pipeline.addLast("http-client-codec", new HttpClientCodec(nettyHttpClientConfig.getMaxInitialLineLength(),
nettyHttpClientConfig.getMaxHeadersSize(), nettyHttpClientConfig.getMaxChunkSize()));
if (nettyHttpClientConfig.isGzipEnabled()) {
pipeline.addLast("http-client-decompressor", new HttpContentDecompressor());
}
HttpObjectAggregator httpObjectAggregator =
new HttpObjectAggregator(nettyHttpClientConfig.getMaxContentLength(), false);
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpClientConfig.getMaxCompositeBufferComponents());
pipeline.addLast("http-client-aggregator", httpObjectAggregator);
if (nettyHttpClientConfig.isObjectAggregationEnabled()) {
HttpObjectAggregator httpObjectAggregator =
new HttpObjectAggregator(nettyHttpClientConfig.getMaxContentLength(), false);
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpClientConfig.getMaxCompositeBufferComponents());
pipeline.addLast("http-client-aggregator", httpObjectAggregator);
}
if (nettyHttpClientConfig.isChunkWriteEnabled()) {
//pipeline.addLast("http-client-chunk-content-compressor", new HttpChunkContentCompressor());
pipeline.addLast("http-client-chunked-writer", new ChunkedWriteHandler());
}
pipeline.addLast("http-client-response", new Http1Handler(interaction));
interaction.settingsReceived(null);
}

View file

@ -21,8 +21,7 @@ public class Http1Handler extends ChannelDuplexHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpResponse) {
FullHttpResponse httpResponse = (FullHttpResponse) msg;
if (msg instanceof FullHttpResponse httpResponse) {
try {
interaction.responseReceived(ctx.channel(), null, httpResponse);
} finally {
@ -35,6 +34,7 @@ public class Http1Handler extends ChannelDuplexHandler {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
logger.log(Level.FINEST, "event " + evt.getClass().getName());
ctx.fireUserEventTriggered(evt);
}

View file

@ -1,19 +1,26 @@
package org.xbib.net.http.client.netty.http1;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2Settings;
import java.io.IOException;
import java.net.ConnectException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
@ -26,6 +33,7 @@ import org.xbib.net.URLSyntaxException;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpHeaders;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.client.Part;
import org.xbib.net.http.cookie.Cookie;
import org.xbib.net.http.client.cookie.CookieDecoder;
import org.xbib.net.http.client.cookie.CookieEncoder;
@ -42,11 +50,8 @@ public class Http1Interaction extends BaseInteraction {
private static final Logger logger = Logger.getLogger(Http1Interaction.class.getName());
private final HttpDataFactory httpDataFactory;
public Http1Interaction(NettyHttpClient nettyHttpClient, HttpAddress httpAddress) {
super(nettyHttpClient, httpAddress);
this.httpDataFactory = new DefaultHttpDataFactory();
}
@Override
@ -72,6 +77,10 @@ public class Http1Interaction extends BaseInteraction {
}
public Interaction executeRequest(HttpRequest request, Channel channel) throws IOException {
if (!channel.isWritable()) {
logger.log(Level.WARNING, "sorry, channel not writable");
return this;
}
final String channelId = channel.id().toString();
streamIds.putIfAbsent(channelId, new StreamIds());
// Some HTTP 1 servers do not understand URIs in HTTP command line in spite of RFC 7230.
@ -84,7 +93,6 @@ public class Http1Interaction extends BaseInteraction {
DefaultFullHttpRequest fullHttpRequest = request.getBody() == null ?
new DefaultFullHttpRequest(httpVersion, httpMethod, uri) :
new DefaultFullHttpRequest(httpVersion, httpMethod, uri, Unpooled.wrappedBuffer(request.getBody()));
HttpPostRequestEncoder httpPostRequestEncoder = null;
final Integer streamId = streamIds.get(channelId).nextStreamId();
if (streamId == null) {
throw new IllegalStateException("stream id is null");
@ -96,27 +104,52 @@ public class Http1Interaction extends BaseInteraction {
if (!cookies.isEmpty()) {
request.getHeaders().set(HttpHeaderNames.COOKIE, CookieEncoder.STRICT.encode(cookies));
}
// headers
request.getHeaders().entries().forEach(p -> fullHttpRequest.headers().add(p.getKey(), p.getValue()));
if (request.getBody() == null && !request.getBodyData().isEmpty()) {
try {
httpPostRequestEncoder = new HttpPostRequestEncoder(httpDataFactory, fullHttpRequest, true);
httpPostRequestEncoder.setBodyHttpDatas(request.getBodyData());
httpPostRequestEncoder.finalizeRequest();
} catch (HttpPostRequestEncoder.ErrorDataEncoderException e) {
throw new IOException(e);
// file upload
HttpDataFactory httpDataFactory = new DefaultHttpDataFactory();
HttpPostRequestEncoder httpPostRequestEncoder = null;
try {
if (!request.getParts().isEmpty()) {
httpPostRequestEncoder = new HttpPostRequestEncoder(httpDataFactory,
fullHttpRequest,
true,
StandardCharsets.UTF_8,
HttpPostRequestEncoder.EncoderMode.RFC1738);
for (Part part : request.getParts()) {
Path path = part.getPath();
if (Files.exists(path)) {
FileUpload fileUpload = httpDataFactory.createFileUpload(fullHttpRequest, part.getName(),
path.toFile().getName(), part.getContentType(), part.getContentTransferEncoding(),
part.getCharset(), Files.size(path));
fileUpload.setContent(path.toFile());
logger.log(Level.FINEST, "HTTP FORM file upload = " + fileUpload);
httpPostRequestEncoder.addBodyHttpData(fileUpload);
} else {
logger.log(Level.WARNING, " does not exist : " + path);
}
}
io.netty.handler.codec.http.HttpRequest httpRequest = httpPostRequestEncoder.finalizeRequest();
channel.write(httpRequest);
} else {
channel.write(fullHttpRequest);
}
}
if (!channel.isWritable()) {
logger.log(Level.WARNING, "channel not writable");
return this;
}
channel.write(fullHttpRequest);
if (httpPostRequestEncoder != null && httpPostRequestEncoder.isChunked()) {
channel.write(httpPostRequestEncoder);
}
channel.flush();
if (httpPostRequestEncoder != null) {
httpPostRequestEncoder.cleanFiles();
if (httpPostRequestEncoder != null && httpPostRequestEncoder.isChunked()) {
logger.log(Level.FINEST, "finish chunked HTTP POST encoder");
channel.write(httpPostRequestEncoder);
} else {
logger.log(Level.FINEST, "HTTP POST encoder not chunked");
}
channel.flush();
} catch (HttpPostRequestEncoder.ErrorDataEncoderException e) {
throw new IOException(e);
} finally {
if (httpPostRequestEncoder != null) {
logger.log(Level.FINEST, "cleaning files of HTTP POST encoder");
//httpPostRequestEncoder.cleanFiles();
}
logger.log(Level.FINEST, "clean all http data");
//httpDataFactory.cleanAllHttpData();
}
return this;
}
@ -241,7 +274,7 @@ public class Http1Interaction extends BaseInteraction {
@Override
public void close() throws IOException {
httpDataFactory.cleanAllHttpData();
//httpDataFactory.cleanAllHttpData();
super.close();
}

View file

@ -6,19 +6,20 @@ import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.xbib.net.http.client.netty.Interaction;
import org.xbib.net.http.client.netty.NettyHttpClientConfig;
public class Http2ChildChannelInitializer extends ChannelInitializer<Channel> {
private final NettyHttpClientConfig clientConfig;
private final NettyHttpClientConfig nettyHttpClientConfig;
private final Interaction interaction;
protected final Channel parentChannel;
public Http2ChildChannelInitializer(NettyHttpClientConfig clientConfig, Interaction interaction, Channel parentChannel) {
this.clientConfig = clientConfig;
public Http2ChildChannelInitializer(NettyHttpClientConfig nettyHttpClientConfig, Interaction interaction, Channel parentChannel) {
this.nettyHttpClientConfig = nettyHttpClientConfig;
this.interaction = interaction;
this.parentChannel = parentChannel;
}
@ -30,8 +31,13 @@ public class Http2ChildChannelInitializer extends ChannelInitializer<Channel> {
new Http2StreamFrameToHttpObjectCodec(false));
p.addLast("child-client-decompressor",
new HttpContentDecompressor());
p.addLast("child-client-chunk-aggregator",
new HttpObjectAggregator(clientConfig.getMaxContentLength()));
if (nettyHttpClientConfig.isChunkWriteEnabled()) {
p.addLast("child-chunk-write", new ChunkedWriteHandler());
}
if (nettyHttpClientConfig.isObjectAggregationEnabled()) {
p.addLast("child-client-object-aggregator",
new HttpObjectAggregator(nettyHttpClientConfig.getMaxContentLength()));
}
p.addLast("child-client-response-handler",
new Http2Handler(interaction));
}

View file

@ -19,5 +19,7 @@ public interface HttpRequestBuilder {
HttpRequestBuilder setBody(ByteBuffer byteBuffer);
HttpRequestBuilder addPart(Part part);
HttpRequest build() throws UnmappableCharacterException, MalformedInputException;
}

View file

@ -0,0 +1,49 @@
package org.xbib.net.http.client;
import java.nio.charset.Charset;
import java.nio.file.Path;
public class Part {
private final String contentType;
private final String contentTransferEncoding;
private final String name;
private final Path path;
private final Charset charset;
public Part(String contentType,
String contentTransferEncoding,
String name,
Path path,
Charset charset) {
this.contentType = contentType;
this.contentTransferEncoding = contentTransferEncoding;
this.name = name;
this.path = path;
this.charset = charset;
}
public String getContentType() {
return contentType;
}
public String getContentTransferEncoding() {
return contentTransferEncoding;
}
public String getName() {
return name;
}
public Path getPath() {
return path;
}
public Charset getCharset() {
return charset;
}
}

View file

@ -17,6 +17,9 @@ import org.xbib.net.http.HttpVersion;
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.route.HttpRouter;
import org.xbib.net.http.server.service.HttpService;
import org.xbib.net.http.server.auth.BasicAuthenticationHandler;
import org.xbib.net.http.server.auth.FormAuthenticationHandler;
@ -120,6 +123,7 @@ public final class Bootstrap {
BasicAuthenticationHandler basicAuthenticationHandler =
new BasicAuthenticationHandler(ldapRealm);
FormAuthenticationHandler formAuthenticationHandler =
new FormAuthenticationHandler("j_username", "j_password", "j_remember",
"demo/auth/form/index.gtpl", ldapRealm);
@ -148,48 +152,57 @@ public final class Bootstrap {
})
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(WebApplication.builder()
.setSettings(settings)
.setSecret("1088e6b7ad58d64d09961e1357bf95544447051c6ad1332cd626e3a33bb5786b")
.setRouter(BaseHttpRouter.builder()
.setHandler(400, new GroovyHttpStatusHandler(HttpResponseStatus.BAD_REQUEST, "Bad request", "400.gtpl"))
.setHandler(401, new GroovyHttpStatusHandler(HttpResponseStatus.UNAUTHORIZED, "Unauthorized", "401.gtpl"))
.setHandler(403, new GroovyHttpStatusHandler(HttpResponseStatus.FORBIDDEN, "Forbidden", "403.gtpl"))
.setHandler(404, new GroovyHttpStatusHandler(HttpResponseStatus.NOT_FOUND, "Not found", "404.gtpl"))
.setHandler(500, new GroovyInternalServerErrorHandler("500.gtpl"))
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, "image/x-icon")
.write(NettyDataBufferFactory.getInstance().wrap(Hex.fromHex(hexFavIcon)))
.build();
ctx.done();
})
.build())
.addService(BaseHttpService.builder()
.setPath("/webjars/**")
.setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/"))
.build())
.addService(httpService)
.addService(GroovyTemplateService.builder()
.setTemplateName("index.gtpl")
.setSecurityDomain(securityDomain)
.setPath("glob:**")
.setHandler(new GroovyTemplateResourceHandler())
.build())
.build())
HttpRouter httpRouter = BaseHttpRouter.builder()
.setHandler(400, new GroovyHttpStatusHandler(HttpResponseStatus.BAD_REQUEST, "Bad request", "400.gtpl"))
.setHandler(401, new GroovyHttpStatusHandler(HttpResponseStatus.UNAUTHORIZED, "Unauthorized", "401.gtpl"))
.setHandler(403, new GroovyHttpStatusHandler(HttpResponseStatus.FORBIDDEN, "Forbidden", "403.gtpl"))
.setHandler(404, new GroovyHttpStatusHandler(HttpResponseStatus.NOT_FOUND, "Not found", "404.gtpl"))
.setHandler(500, new GroovyInternalServerErrorHandler("500.gtpl"))
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, "image/x-icon")
.write(NettyDataBufferFactory.getInstance().wrap(Hex.fromHex(hexFavIcon)))
.build();
ctx.done();
})
.build())
.addService(BaseHttpService.builder()
.setPath("/webjars/**")
.setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/"))
.build())
.addService(httpService)
.addService(GroovyTemplateService.builder()
.setTemplateName("index.gtpl")
.setSecurityDomain(securityDomain)
.setPath("glob:**")
.setHandler(new GroovyTemplateResourceHandler())
.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;
}

View file

@ -2,6 +2,8 @@ package org.xbib.net.http.server.application.web;
import org.xbib.net.http.server.application.BaseApplicationBuilder;
import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.settings.Settings;
public class WebApplicationBuilder extends BaseApplicationBuilder {
@ -12,14 +14,8 @@ public class WebApplicationBuilder extends BaseApplicationBuilder {
protected WebApplicationBuilder() {
super();
this.profile = System.getProperty("application.profile");
this.name = System.getProperty("application.name");
}
@Override
public WebApplicationBuilder setSettings(Settings settings) {
super.setSettings(settings);
return this;
this.profile = System.getProperty("application.profile");
}
public WebApplicationBuilder setProfile(String profile) {
@ -32,10 +28,32 @@ public class WebApplicationBuilder extends BaseApplicationBuilder {
return this;
}
@Override
public WebApplicationBuilder setSettings(Settings settings) {
this.settings = settings;
return this;
}
@Override
public WebApplicationBuilder setSecret(String secret) {
super.setSecret(secret);
return this;
}
@Override
public WebApplicationBuilder setExecutor(Executor executor) {
super.setExecutor(executor);
return this;
}
@Override
public WebApplicationBuilder setRouter(HttpRouter router) {
super.setRouter(router);
return this;
}
@Override
public WebApplication build() {
WebApplication webApplication = new WebApplication(this);
setupApplication(webApplication);
return webApplication;
return new WebApplication(this);
}
}

View file

@ -25,6 +25,7 @@ import org.xbib.net.http.server.netty.HttpChannelInitializer;
import org.xbib.net.http.server.netty.NettyCustomizer;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
import org.xbib.net.http.server.netty.http1.HttpFileUploadHandler;
import org.xbib.net.http.server.netty.http1.HttpPipeliningHandler;
import org.xbib.net.http.server.netty.IdleTimeoutHandler;
import org.xbib.net.http.server.netty.TrafficLoggingHandler;
@ -50,13 +51,12 @@ public class Https1ChannelInitializer implements HttpChannelInitializer {
final NettyHttpsServerConfig nettyHttpsServerConfig = (NettyHttpsServerConfig) nettyHttpServer.getNettyHttpServerConfig();
final ServerNameIndicationHandler serverNameIndicationHandler =
new ServerNameIndicationHandler(nettyHttpsServerConfig, httpAddress,
nettyHttpsServerConfig.getDomainNameMapping(nettyHttpServer.getApplication().getDomains()));
nettyHttpsServerConfig.getDomainNameMapping(nettyHttpServer.getDomains()));
channel.attr(NettyHttpsServerConfig.ATTRIBUTE_KEY_SNI_HANDLER).set(serverNameIndicationHandler);
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("server-sni", serverNameIndicationHandler);
HttpServerCodec httpServerCodec = new HttpServerCodec(nettyHttpsServerConfig.getMaxInitialLineLength(),
nettyHttpsServerConfig.getMaxHeadersSize(), nettyHttpsServerConfig.getMaxChunkSize());
pipeline.addLast("server-chunked-write", new ChunkedWriteHandler());
pipeline.addLast("server-codec", httpServerCodec);
if (nettyHttpsServerConfig.isCompressionEnabled()) {
pipeline.addLast("server-compressor", new HttpContentCompressor());
@ -64,9 +64,18 @@ public class Https1ChannelInitializer implements HttpChannelInitializer {
if (nettyHttpsServerConfig.isDecompressionEnabled()) {
pipeline.addLast("server-decompressor", new HttpContentDecompressor());
}
HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpsServerConfig.getMaxContentLength());
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpsServerConfig.getMaxCompositeBufferComponents());
pipeline.addLast("server-aggregator", httpObjectAggregator);
if (nettyHttpsServerConfig.isObjectAggregationEnabled()) {
HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpsServerConfig.getMaxContentLength());
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpsServerConfig.getMaxCompositeBufferComponents());
pipeline.addLast("server-aggregator", httpObjectAggregator);
}
if (nettyHttpsServerConfig.isFileUploadEnabled()) {
HttpFileUploadHandler httpFileUploadHandler = new HttpFileUploadHandler(nettyHttpServer);
pipeline.addLast("server-file-upload", httpFileUploadHandler);
}
if (nettyHttpsServerConfig.isChunkedWriteEnabled()) {
pipeline.addLast("server-chunked-write", new ChunkedWriteHandler());
}
if (nettyHttpsServerConfig.isPipeliningEnabled()) {
pipeline.addLast("server-pipelining", new HttpPipeliningHandler(nettyHttpsServerConfig.getPipeliningCapacity()));
}

View file

@ -89,7 +89,7 @@ public class Https1Handler extends ChannelDuplexHandler {
serverRequestBuilder.setSNIHost(serverNameIndicationHandler.hostname());
serverRequestBuilder.setSSLSession(serverNameIndicationHandler.getSslHandler().engine().getSession());
}
nettyHttpServer.getApplication().dispatch(serverRequestBuilder, serverResponseBuilder);
nettyHttpServer.dispatch(serverRequestBuilder, serverResponseBuilder);
} catch (Exception e) {
logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e);
DefaultFullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(io.netty.handler.codec.http.HttpVersion.valueOf(httpAddress.getVersion().text()),

View file

@ -16,6 +16,7 @@ import io.netty.handler.codec.http2.Http2MultiplexHandler;
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.AsciiString;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -26,6 +27,7 @@ import org.xbib.net.http.server.netty.NettyCustomizer;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
import org.xbib.net.http.server.netty.TrafficLoggingHandler;
import org.xbib.net.http.server.netty.http1.HttpFileUploadHandler;
import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig;
import org.xbib.net.http.server.netty.secure.ServerNameIndicationHandler;
@ -47,7 +49,7 @@ public class Https2ChannelInitializer implements HttpChannelInitializer {
final NettyHttpsServerConfig nettyHttpsServerConfig = (NettyHttpsServerConfig) nettyHttpServer.getNettyHttpServerConfig();
final ServerNameIndicationHandler serverNameIndicationHandler =
new ServerNameIndicationHandler(nettyHttpsServerConfig, httpAddress,
nettyHttpsServerConfig.getDomainNameMapping(nettyHttpServer.getApplication().getDomains()));
nettyHttpsServerConfig.getDomainNameMapping(nettyHttpServer.getDomains()));
channel.attr(NettyHttpsServerConfig.ATTRIBUTE_KEY_SNI_HANDLER).set(serverNameIndicationHandler);
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("server-sni", serverNameIndicationHandler);
@ -55,8 +57,16 @@ public class Https2ChannelInitializer implements HttpChannelInitializer {
pipeline.addLast("server-logger", new TrafficLoggingHandler(LogLevel.DEBUG));
}
pipeline.addLast("server-upgrade", createUpgradeHandler(nettyHttpServer, httpAddress, serverNameIndicationHandler));
// handler for HTTP1
pipeline.addLast("server-object-aggregator", new HttpObjectAggregator(nettyHttpsServerConfig.getMaxContentLength()));
if (nettyHttpsServerConfig.isObjectAggregationEnabled()) {
pipeline.addLast("server-object-aggregator", new HttpObjectAggregator(nettyHttpsServerConfig.getMaxContentLength()));
}
if (nettyHttpsServerConfig.isFileUploadEnabled()) {
HttpFileUploadHandler httpFileUploadHandler = new HttpFileUploadHandler(nettyHttpServer);
pipeline.addLast("server-file-upload", httpFileUploadHandler);
}
if (nettyHttpsServerConfig.isChunkedWriteEnabled()) {
pipeline.addLast("server-chunked-write", new ChunkedWriteHandler());
}
pipeline.addLast("server-requests", new Https2Handler(nettyHttpServer));
pipeline.addLast("server-messages", new Https2Messages());
pipeline.addLast("server-idle-timeout", new IdleTimeoutHandler(nettyHttpsServerConfig.getTimeoutMillis()));
@ -77,6 +87,7 @@ public class Https2ChannelInitializer implements HttpChannelInitializer {
NettyHttpServerConfig nettyHttpServerConfig = nettyHttpServer.getNettyHttpServerConfig();
Https2ChildChannelInitializer childHandler =
new Https2ChildChannelInitializer(nettyHttpServer, httpAddress, serverNameIndicationHandler);
// TODO replace Http2MultiplexCodecBuilder
Http2MultiplexCodecBuilder multiplexCodecBuilder = Http2MultiplexCodecBuilder.forServer(childHandler)
.initialSettings(Http2Settings.defaultSettings());
if (nettyHttpServerConfig.isDebug()) {
@ -94,6 +105,10 @@ public class Https2ChannelInitializer implements HttpChannelInitializer {
return new CleartextHttp2ServerUpgradeHandler(serverCodec, upgradeHandler, multiplexCodec);
}
/**
* A new upgrade handler.
* Sadly, this does not work.
*/
protected CleartextHttp2ServerUpgradeHandler createNewUpgradeHandler(NettyHttpServer nettyHttpServer,
HttpAddress httpAddress,
ServerNameIndicationHandler serverNameIndicationHandler) {

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.getApplication().dispatch(httpsRequestBuilder, httpsResponseBuilder, HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED);
nettyHttpServer.dispatch(httpsRequestBuilder, httpsResponseBuilder, HttpResponseStatus.HTTP_VERSION_NOT_SUPPORTED);
return;
}
httpsResponseBuilder.shouldClose("close".equalsIgnoreCase(fullHttpRequest.headers().get(HttpHeaderNames.CONNECTION)));
@ -65,7 +65,7 @@ public class Https2Handler extends ChannelDuplexHandler {
httpsRequestBuilder.setSNIHost(serverNameIndicationHandler.hostname());
httpsRequestBuilder.setSSLSession(serverNameIndicationHandler.getSslHandler().engine().getSession());
}
nettyHttpServer.getApplication().dispatch(httpsRequestBuilder, httpsResponseBuilder);
nettyHttpServer.dispatch(httpsRequestBuilder, httpsResponseBuilder);
} catch (Exception e) {
logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e);
DefaultFullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(io.netty.handler.codec.http.HttpVersion.valueOf(httpAddress.getVersion().text()),

View file

@ -19,6 +19,9 @@ import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.client.netty.secure.NettyHttpsClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
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.http.server.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.secure.HttpsAddress;
@ -46,36 +49,43 @@ public class NettyHttps2ServerMultiRequestLoadTest {
NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig();
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> { ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain " +
" SNI host " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() + " " +
" SSL peer host " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() + " " +
" base URL = " + ctx.request().getBaseURL() + " " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> { ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain " +
" SNI host " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() + " " +
" SSL peer host " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() + " " +
" base URL = " + ctx.request().getBaseURL() + " " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();

View file

@ -19,6 +19,9 @@ import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.client.netty.secure.NettyHttpsClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
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.http.server.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.secure.HttpsAddress;
@ -47,36 +50,43 @@ public class NettyHttps2ServerTest {
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
serverConfig.setDebug(true);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> { ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain " +
" SNI host " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() + " " +
" SSL peer host " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() + " " +
" base URL = " + ctx.request().getBaseURL() + " " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> { ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain " +
" SNI host " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() + " " +
" SSL peer host " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() + " " +
" base URL = " + ctx.request().getBaseURL() + " " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();

View file

@ -15,6 +15,9 @@ import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.client.netty.secure.NettyHttpsClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
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.http.server.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.secure.HttpsAddress;
@ -48,39 +51,46 @@ public class NettyHttpsServerMultiRequestLoadTest {
NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig();
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
logger.log(Level.INFO, "executing /secure");
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL peer host = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
logger.log(Level.INFO, "executing /secure");
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL peer host = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();
@ -133,38 +143,45 @@ public class NettyHttpsServerMultiRequestLoadTest {
NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig();
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL session = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL session = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();

View file

@ -22,11 +22,14 @@ import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.client.netty.secure.NettyHttpsClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
import org.xbib.net.http.server.executor.BaseExecutor;
import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.netty.NettyHttpServer;
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.route.BaseHttpRouter;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.http.server.service.BaseHttpService;
import java.nio.charset.StandardCharsets;
@ -52,38 +55,45 @@ public class NettyHttpsServerTest {
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
serverConfig.setDebug(true);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL peer host = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL peer host = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();
@ -127,38 +137,45 @@ public class NettyHttpsServerTest {
Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
serverConfig.setDebug(true);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL session = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL session = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();
@ -201,37 +218,44 @@ public class NettyHttpsServerTest {
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
serverConfig.setDebug(true);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain " +
" SNI host " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() + " " +
" SSL peer host " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() + " " +
" base URL = " + ctx.httpRequest().getBaseURL() + " " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain " +
" SNI host " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() + " " +
" SSL peer host " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() + " " +
" base URL = " + ctx.httpRequest().getBaseURL() + " " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();

View file

@ -1,7 +1,5 @@
package org.xbib.net.http.server.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import org.xbib.net.Request;
import org.xbib.net.http.server.BaseHttpRequest;
@ -9,8 +7,8 @@ import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import org.xbib.net.util.ByteBufferInputStream;
public class HttpRequest extends BaseHttpRequest {
@ -27,7 +25,8 @@ public class HttpRequest extends BaseHttpRequest {
@Override
public InputStream getInputStream() {
return new ByteBufInputStream(builder.fullHttpRequest.content());
//return new ByteBufInputStream(builder.fullHttpRequest.content());
return builder.byteBuffer != null ? new ByteBufferInputStream(builder.byteBuffer) : null;
}
@Override
@ -48,13 +47,10 @@ public class HttpRequest extends BaseHttpRequest {
@Override
public String toString() {
return "HttpRequest[request=" + builder.fullHttpRequest +
return "HttpRequest[method=" + builder.getMethod() +
",version=" + builder.getVersion() +
",parameter=" + builder.getParameter() +
",body=" + builder.fullHttpRequest.content().toString(StandardCharsets.UTF_8) +
",body=" + (builder.byteBuffer != null) +
"]";
}
public ByteBuf getByteBuf() {
return builder.fullHttpRequest.content();
}
}

View file

@ -2,6 +2,10 @@ package org.xbib.net.http.server.netty;
import io.netty.buffer.ByteBufUtil;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.multipart.FileUpload;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.Parameter;
import org.xbib.net.URL;
import org.xbib.net.http.HttpAddress;
@ -13,44 +17,47 @@ import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import org.xbib.net.http.server.Part;
public class HttpRequestBuilder extends BaseHttpRequestBuilder {
protected FullHttpRequest fullHttpRequest;
private static final Logger logger = Logger.getLogger(HttpRequestBuilder.class.getName());
protected ByteBuffer byteBuffer;
protected HttpRequestBuilder() {
}
public HttpRequestBuilder setHttpRequest(io.netty.handler.codec.http.HttpRequest httpRequest) {
if (httpRequest != null) {
setVersion(HttpVersion.valueOf(httpRequest.protocolVersion().text()));
setMethod(HttpMethod.valueOf(httpRequest.method().name()));
setRequestURI(httpRequest.uri());
httpRequest.headers().entries().forEach(e -> addHeader(e.getKey(), e.getValue()));
}
return this;
}
public HttpRequestBuilder setFullHttpRequest(FullHttpRequest fullHttpRequest) {
if (fullHttpRequest != null) {
// retain request, so we can read the body later without refCnt=0 error
this.fullHttpRequest = fullHttpRequest.retain();
setVersion(HttpVersion.valueOf(fullHttpRequest.protocolVersion().text()));
setMethod(HttpMethod.valueOf(fullHttpRequest.method().name()));
setRequestURI(fullHttpRequest.uri());
fullHttpRequest.headers().entries().forEach(e -> addHeader(e.getKey(), e.getValue()));
setVersion(HttpVersion.valueOf(fullHttpRequest.protocolVersion().text()));
setMethod(HttpMethod.valueOf(fullHttpRequest.method().name()));
setRequestURI(fullHttpRequest.uri());
fullHttpRequest.headers().entries().forEach(e -> addHeader(e.getKey(), e.getValue()));
// read all bytes from request into a JDK ByteBuffer. This might be expensive.
if (fullHttpRequest.content() != null) {
byteBuffer = ByteBuffer.wrap(ByteBufUtil.getBytes(fullHttpRequest.content()));
}
return this;
}
@Override
public ByteBuffer getBody() {
if (byteBuffer != null) {
return byteBuffer;
}
// read all bytes from request into a JDK ByteBuffer. This might be expensive.
if (fullHttpRequest.content() != null) {
byteBuffer = ByteBuffer.wrap(ByteBufUtil.getBytes(fullHttpRequest.content()));
}
return byteBuffer;
}
@Override
public CharBuffer getBodyAsChars(Charset charset) {
return fullHttpRequest.content() != null ?
CharBuffer.wrap(fullHttpRequest.content().toString(charset)) : null;
return byteBuffer != null ? charset.decode(byteBuffer) : null;
}
@Override
@ -101,6 +108,17 @@ public class HttpRequestBuilder extends BaseHttpRequestBuilder {
return this;
}
public HttpRequestBuilder addFileUpload(FileUpload fileUpload) throws IOException {
logger.log(Level.FINE, "add file upload = " + fileUpload);
Part part = new Part(fileUpload.getContentType(),
fileUpload.getContentTransferEncoding(),
fileUpload.getFilename(),
fileUpload.isInMemory() ? null : fileUpload.getFile().toPath(),
ByteBuffer.wrap(fileUpload.get()));
super.parts.add(part);
return this;
}
protected Parameter getParameter() {
return super.parameter;
}
@ -112,8 +130,6 @@ public class HttpRequestBuilder extends BaseHttpRequestBuilder {
@Override
public void release() {
if (fullHttpRequest != null) {
fullHttpRequest.release();
}
}
}

View file

@ -11,13 +11,6 @@ import io.netty.channel.socket.ServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.AttributeKey;
import org.xbib.net.NetworkClass;
import org.xbib.net.NetworkUtils;
import org.xbib.net.SocketConfig;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.server.application.Application;
import org.xbib.net.http.server.HttpServer;
import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
@ -28,6 +21,18 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.NetworkClass;
import org.xbib.net.NetworkUtils;
import org.xbib.net.SocketConfig;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.server.HttpRequestBuilder;
import org.xbib.net.http.server.HttpResponseBuilder;
import org.xbib.net.http.server.HttpServerContext;
import org.xbib.net.http.server.HttpServer;
import org.xbib.net.http.server.domain.HttpDomain;
import org.xbib.net.http.server.executor.CallableReleasable;
import org.xbib.net.http.server.route.HttpRouter;
/**
* Netty HTTP server.
@ -161,8 +166,50 @@ public class NettyHttpServer implements HttpServer {
}
@Override
public Application getApplication() {
return builder.application;
public void dispatch(HttpRequestBuilder requestBuilder,
HttpResponseBuilder responseBuilder) {
CallableReleasable<?> callableReleasable = new CallableReleasable<>() {
@Override
public Object call() {
HttpRouter router = builder.application.getRouter();
router.route(builder.application, requestBuilder, responseBuilder);
return true;
}
@Override
public void release() {
requestBuilder.release();
responseBuilder.release();
}
};
builder.application.getExecutor().execute(callableReleasable);
}
@Override
public void dispatch(HttpRequestBuilder requestBuilder,
HttpResponseBuilder responseBuilder,
HttpResponseStatus responseStatus) {
CallableReleasable<?> callableReleasable = new CallableReleasable<>() {
@Override
public Object call() {
HttpRouter router = builder.application.getRouter();
HttpServerContext httpServerContext = builder.application.createContext(null, requestBuilder, responseBuilder);
router.routeStatus(responseStatus, httpServerContext);
return true;
}
@Override
public void release() {
requestBuilder.release();
responseBuilder.release();
}
};
builder.application.getExecutor().execute(callableReleasable);
}
@Override
public Collection<HttpDomain> getDomains() {
return builder.application.getDomains();
}
@Override

View file

@ -56,20 +56,37 @@ public class NettyHttpServerConfig extends HttpServerConfig {
*/
private int pipeliningCapacity = 1024;
/**
* HTTP object aggregation is enabled by default.
*/
private boolean isObjectAggregationEnabled = true;
/**
* This is Netty's default.
*/
private int maxCompositeBufferComponents = 1024;
/**
* Default for compression.
* Do not write chunks by default.
*/
private boolean enableCompression = true;
private boolean isChunkedWriteEnabled = false;
/**
* Default for decompression.
* Compression is enabled by default.
*/
private boolean enableDecompression = true;
private boolean isCompressionEnabled = true;
/**
* Decompression is enabled by default.
*/
private boolean isDecompressionEnabled = true;
/**
* Disable file upload (POST) by default.
*/
private boolean isFileUploadEnabled = false;
private int fileUploadDiskThreshold = 1 *1024 * 1024;
public NettyHttpServerConfig() {
}
@ -154,6 +171,15 @@ public class NettyHttpServerConfig extends HttpServerConfig {
return pipeliningCapacity;
}
public NettyHttpServerConfig setObjectAggregationEnabled(boolean isObjectAgregationEnabled) {
this.isObjectAggregationEnabled = isObjectAgregationEnabled;
return this;
}
public boolean isObjectAggregationEnabled() {
return isObjectAggregationEnabled;
}
public NettyHttpServerConfig setMaxCompositeBufferComponents(int maxCompositeBufferComponents) {
this.maxCompositeBufferComponents = maxCompositeBufferComponents;
return this;
@ -163,22 +189,48 @@ public class NettyHttpServerConfig extends HttpServerConfig {
return maxCompositeBufferComponents;
}
public NettyHttpServerConfig setCompression(boolean enabled) {
this.enableCompression = enabled;
public NettyHttpServerConfig setCompression(boolean isCompressionEnabled) {
this.isCompressionEnabled = isCompressionEnabled;
return this;
}
public boolean isCompressionEnabled() {
return enableCompression;
return isCompressionEnabled;
}
public NettyHttpServerConfig setDecompression(boolean enabled) {
this.enableDecompression = enabled;
public NettyHttpServerConfig setDecompression(boolean isDecompressionEnabled) {
this.isDecompressionEnabled = isDecompressionEnabled;
return this;
}
public boolean isDecompressionEnabled() {
return enableDecompression;
return isDecompressionEnabled;
}
public NettyHttpServerConfig setChunkWriteEnabled(boolean isChunkedWriteEnabled) {
this.isChunkedWriteEnabled = isChunkedWriteEnabled;
return this;
}
public boolean isChunkedWriteEnabled() {
return isChunkedWriteEnabled;
}
public NettyHttpServerConfig setFileUploadEnabled(boolean isFileUploadEnabled) {
this.isFileUploadEnabled = isFileUploadEnabled;
return this;
}
public boolean isFileUploadEnabled() {
return isFileUploadEnabled;
}
public NettyHttpServerConfig setFileUploadDiskThreshold(int fileUploadDiskThreshold) {
this.fileUploadDiskThreshold = fileUploadDiskThreshold;
return this;
}
public int getFileUploadDiskThreshold() {
return fileUploadDiskThreshold;
}
}

View file

@ -41,7 +41,7 @@ public class Http1ChannelInitializer implements HttpChannelInitializer {
if (nettyHttpServerConfig.isDebug()) {
pipeline.addLast("server-logging", new TrafficLoggingHandler(LogLevel.DEBUG));
}
//pipeline.addLast("server-chunked-write", new ChunkedWriteHandler());
// always use the server codec or we won't be able to handle HTTP
pipeline.addLast("server-codec", new HttpServerCodec(nettyHttpServerConfig.getMaxInitialLineLength(),
nettyHttpServerConfig.getMaxHeadersSize(), nettyHttpServerConfig.getMaxChunkSize()));
if (nettyHttpServerConfig.isCompressionEnabled()) {
@ -50,9 +50,18 @@ public class Http1ChannelInitializer implements HttpChannelInitializer {
if (nettyHttpServerConfig.isDecompressionEnabled()) {
pipeline.addLast("server-decompressor", new HttpContentDecompressor());
}
HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength());
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpServerConfig.getMaxCompositeBufferComponents());
pipeline.addLast("server-aggregator", httpObjectAggregator);
if (nettyHttpServerConfig.isObjectAggregationEnabled()) {
HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength());
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpServerConfig.getMaxCompositeBufferComponents());
pipeline.addLast("server-aggregator", httpObjectAggregator);
}
if (nettyHttpServerConfig.isChunkedWriteEnabled()) {
pipeline.addLast("server-chunked-write", new ChunkedWriteHandler());
}
if (nettyHttpServerConfig.isFileUploadEnabled()) {
HttpFileUploadHandler httpFileUploadHandler = new HttpFileUploadHandler(server);
pipeline.addLast("server-file-upload", httpFileUploadHandler);
}
if (nettyHttpServerConfig.isPipeliningEnabled()) {
pipeline.addLast("server-pipelining", new HttpPipeliningHandler(nettyHttpServerConfig.getPipeliningCapacity()));
}

View file

@ -34,11 +34,9 @@ class Http1Handler extends ChannelDuplexHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpPipelinedRequest) {
HttpPipelinedRequest httpPipelinedRequest = (HttpPipelinedRequest) msg;
if (msg instanceof HttpPipelinedRequest httpPipelinedRequest) {
try {
if (httpPipelinedRequest.getRequest() instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) httpPipelinedRequest.getRequest();
if (httpPipelinedRequest.getRequest() instanceof FullHttpRequest fullHttpRequest) {
requestReceived(ctx, fullHttpRequest, httpPipelinedRequest.getSequenceId());
}
} finally {
@ -73,7 +71,9 @@ class Http1Handler extends ChannelDuplexHandler {
ctx.close();
}
protected void requestReceived(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest, Integer sequenceId) {
protected void requestReceived(ChannelHandlerContext ctx,
FullHttpRequest fullHttpRequest,
Integer sequenceId) {
HttpAddress httpAddress = ctx.channel().attr(NettyHttpServerConfig.ATTRIBUTE_KEY_HTTP_ADDRESS).get();
try {
HttpResponseBuilder serverResponseBuilder = HttpResponse.builder()
@ -82,7 +82,7 @@ class Http1Handler extends ChannelDuplexHandler {
serverResponseBuilder.setSequenceId(sequenceId);
}
serverResponseBuilder.shouldClose("close".equalsIgnoreCase(fullHttpRequest.headers().get(HttpHeaderNames.CONNECTION)));
// the base URL construction may fail with exception. In hat case, we return a built-in 400 Bad Request.
// the base URL construction may fail with exception. In that case, we return a built-in 400 Bad Request.
HttpRequestBuilder serverRequestBuilder = HttpRequest.builder()
.setFullHttpRequest(fullHttpRequest)
.setBaseURL(httpAddress,
@ -91,7 +91,7 @@ class Http1Handler extends ChannelDuplexHandler {
.setLocalAddress((InetSocketAddress) ctx.channel().localAddress())
.setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress())
.setSequenceId(sequenceId);
nettyHttpServer.getApplication().dispatch(serverRequestBuilder, serverResponseBuilder);
nettyHttpServer.dispatch(serverRequestBuilder, serverResponseBuilder);
} catch (Exception e) {
logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e);
DefaultFullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,

View file

@ -0,0 +1,120 @@
package org.xbib.net.http.server.netty.http1;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import java.net.InetSocketAddress;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.server.netty.HttpRequestBuilder;
import org.xbib.net.http.server.netty.HttpResponse;
import org.xbib.net.http.server.netty.HttpResponseBuilder;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
public class HttpFileUploadHandler extends SimpleChannelInboundHandler<HttpObject> {
private static final Logger logger = Logger.getLogger(HttpFileUploadHandler.class.getName());
private final NettyHttpServer nettyHttpServer;
public HttpFileUploadHandler(NettyHttpServer nettyHttpServer) {
this.nettyHttpServer = nettyHttpServer;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject httpObject) {
logger.log(Level.FINEST, "checking file upload");
HttpRequest httpRequest = null;
HttpPostRequestDecoder httpDecoder = null;
if (httpObject instanceof HttpRequest) {
httpRequest = (HttpRequest) httpObject;
// peek into request if we have a POST request
if (httpRequest.method() == HttpMethod.POST) {
logger.log(Level.FINEST, "checking HTTP POST: success");
HttpDataFactory factory = new DefaultHttpDataFactory(nettyHttpServer.getNettyHttpServerConfig().getFileUploadDiskThreshold());
httpDecoder = new HttpPostRequestDecoder(factory, httpRequest);
}
}
if (httpDecoder != null) {
if (httpObject instanceof HttpContent chunk) {
logger.log(Level.FINEST, "got chunk");
httpDecoder.offer(chunk);
try {
while (httpDecoder.hasNext()) {
InterfaceHttpData data = httpDecoder.next();
logger.log(Level.FINEST, "got data");
if (data != null) {
try {
if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {
logger.log(Level.FINEST, "got file upload");
FileUpload fileUpload = (FileUpload) data;
requestReceived(ctx, httpRequest, fileUpload);
} else {
logger.log(Level.FINEST, "got HTTP data type = " + data.getHttpDataType());
}
} finally {
data.release();
}
}
}
} catch (HttpPostRequestDecoder.EndOfDataDecoderException e) {
logger.log(Level.FINEST, "end of data decoder exception");
}
if (chunk instanceof LastHttpContent) {
//httpDecoder.destroy();
}
} else {
logger.log(Level.FINEST, "not a HttpContent: " );
}
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.log(Level.SEVERE, cause.getMessage(), cause);
ctx.close();
}
protected void requestReceived(ChannelHandlerContext ctx,
HttpRequest httpRequest,
FileUpload fileUpload) {
HttpAddress httpAddress = ctx.channel().attr(NettyHttpServerConfig.ATTRIBUTE_KEY_HTTP_ADDRESS).get();
try {
HttpResponseBuilder serverResponseBuilder = HttpResponse.builder()
.setChannelHandlerContext(ctx);
serverResponseBuilder.shouldClose("close".equalsIgnoreCase(httpRequest.headers().get(HttpHeaderNames.CONNECTION)));
// the base URL construction may fail with exception. In that case, we return a built-in 400 Bad Request.
HttpRequestBuilder httpRequestBuilder = org.xbib.net.http.server.netty.HttpRequest.builder()
.setHttpRequest(httpRequest)
.addFileUpload(fileUpload)
.setBaseURL(httpAddress,
httpRequest.uri(),
httpRequest.headers().get(HttpHeaderNames.HOST))
.setLocalAddress((InetSocketAddress) ctx.channel().localAddress())
.setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress());
nettyHttpServer.dispatch(httpRequestBuilder, serverResponseBuilder);
} catch (Exception e) {
logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e);
DefaultFullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.BAD_REQUEST);
ctx.writeAndFlush(fullHttpResponse);
ctx.close();
}
}
}

View file

@ -28,6 +28,7 @@ import org.xbib.net.http.server.netty.TrafficLoggingHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.http.server.netty.http1.HttpFileUploadHandler;
/**
* Insecure HTTP/2 server channel initializer.
@ -63,9 +64,17 @@ public class Http2ChannelInitializer implements HttpChannelInitializer {
if (nettyHttpServerConfig.isDecompressionEnabled()) {
pipeline.addLast("server-decompressor", new HttpContentDecompressor());
}
pipeline.addLast("server-object-aggregator",
new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength()));
pipeline.addLast("server-chunked-write", new ChunkedWriteHandler());
if (nettyHttpServerConfig.isObjectAggregationEnabled()) {
pipeline.addLast("server-object-aggregator",
new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength()));
}
if (nettyHttpServerConfig.isFileUploadEnabled()) {
HttpFileUploadHandler httpFileUploadHandler = new HttpFileUploadHandler(nettyHttpServer);
pipeline.addLast("server-file-upload", httpFileUploadHandler);
}
if (nettyHttpServerConfig.isChunkedWriteEnabled()) {
pipeline.addLast("server-chunked-write", new ChunkedWriteHandler());
}
pipeline.addLast("server-request", new Http2Handler(nettyHttpServer));
pipeline.addLast("server-messages", new Http2Messages());
pipeline.addLast("server-idle-timeout", new IdleTimeoutHandler(nettyHttpServerConfig.getTimeoutMillis()));

View file

@ -55,7 +55,7 @@ public class Http2Handler extends ChannelDuplexHandler {
.setLocalAddress((InetSocketAddress) ctx.channel().localAddress())
.setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress())
.setStreamId(streamId);
nettyHttpServer.getApplication().dispatch(serverRequestBuilder, httpResponseBuilder);
nettyHttpServer.dispatch(serverRequestBuilder, httpResponseBuilder);
} catch (Exception e) {
logger.log(Level.SEVERE, "bad request:" + e.getMessage(), e);
DefaultFullHttpResponse fullHttpResponse =

View file

@ -19,6 +19,9 @@ import org.xbib.net.http.client.netty.NettyHttpClient;
import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
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.http.server.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -37,29 +40,36 @@ public class NettyHttp2ServerMultiRequestLoadTest {
NettyHttpServerConfig serverConfig = new NettyHttpServerConfig();
serverConfig.setServerName("NettyHttp2CleartextServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain: " +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain: " +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();

View file

@ -18,6 +18,9 @@ import org.xbib.net.http.client.netty.NettyHttpClient;
import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
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.http.server.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -38,27 +41,34 @@ public class NettyHttp2ServerTest {
Bootstrap.class.getPackage().getImplementationVersion());
nettyHttpServerConfig.setNetworkClass(NetworkClass.ANY);
nettyHttpServerConfig.setDebug(true);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(nettyHttpServerConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();

View file

@ -0,0 +1,100 @@
package org.xbib.net.http.netty.test;
import io.netty.bootstrap.Bootstrap;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.jupiter.api.Test;
import org.xbib.net.NetworkClass;
import org.xbib.net.URL;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpHeaderNames;
import org.xbib.net.http.HttpHeaderValues;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.client.netty.HttpRequest;
import org.xbib.net.http.client.netty.NettyHttpClient;
import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
import org.xbib.net.http.server.executor.BaseExecutor;
import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
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 static org.junit.jupiter.api.Assertions.assertTrue;
public class NettyHttpServerBodyTest {
private static final Logger logger = Logger.getLogger(NettyHttpServerBodyTest.class.getName());
@Test
public void testHttp() throws Exception {
URL url = URL.from("http://localhost:8008/domain/");
HttpAddress httpAddress1 = HttpAddress.http1(url);
NettyHttpServerConfig nettyHttpServerConfig = new NettyHttpServerConfig();
nettyHttpServerConfig.setServerName("NettyHttpServer",
Bootstrap.class.getPackage().getImplementationVersion());
nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL);
nettyHttpServerConfig.setDebug(true);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
String body = ctx.request().getBodyAsChars(StandardCharsets.UTF_8).toString();
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("parameter = " + ctx.httpRequest().getParameter().allToString() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress() +
" attributes = " + ctx.getAttributes() +
" body = " + body
);
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(nettyHttpServerConfig)
.setApplication(BaseApplication.builder()
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();
NettyHttpClientConfig config = new NettyHttpClientConfig()
.setDebug(true);
AtomicBoolean received = new AtomicBoolean();
try (NettyHttpClient client = NettyHttpClient.builder()
.setConfig(config)
.build()) {
HttpRequest request = HttpRequest.post()
.setURL(url)
.content("Hello, I'm a simple POST body for Jörg",
"text/plain",
StandardCharsets.UTF_8)
.setResponseListener(resp -> {
logger.log(Level.INFO, "got response:" +
" status = " + resp.getStatus() +
" header = " + resp.getHeaders() +
" body = " + resp.getBodyAsChars(StandardCharsets.UTF_8));
received.set(true);
})
.build();
client.execute(request).get().close();
}
assertTrue(received.get());
}
}
}

View file

@ -23,6 +23,9 @@ import org.xbib.net.http.client.netty.NettyHttpClient;
import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
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.http.server.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -42,30 +45,37 @@ public class NettyHttpServerFailureTest {
nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL);
nettyHttpServerConfig.setDebug(true);
nettyHttpServerConfig.setPipelining(false);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain" +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress() +
" attributes = " + ctx.getAttributes()
);
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(nettyHttpServerConfig)
.setApplication(BaseApplication.builder()
.setHome(Paths.get("."))
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain" +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress() +
" attributes = " + ctx.getAttributes()
);
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();

View file

@ -0,0 +1,110 @@
package org.xbib.net.http.netty.test;
import io.netty.bootstrap.Bootstrap;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.jupiter.api.Test;
import org.xbib.net.NetworkClass;
import org.xbib.net.URL;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpHeaderNames;
import org.xbib.net.http.HttpHeaderValues;
import org.xbib.net.http.HttpMethod;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.client.Part;
import org.xbib.net.http.client.netty.HttpRequest;
import org.xbib.net.http.client.netty.NettyHttpClient;
import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
import org.xbib.net.http.server.executor.BaseExecutor;
import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
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 static org.junit.jupiter.api.Assertions.assertTrue;
public class NettyHttpServerFileUploadTest {
private static final Logger logger = Logger.getLogger(NettyHttpServerFileUploadTest.class.getName());
@Test
public void testFileUpload() throws Exception {
URL url = URL.from("http://localhost:8008/domain/");
HttpAddress httpAddress1 = HttpAddress.http1(url);
NettyHttpServerConfig nettyHttpServerConfig = new NettyHttpServerConfig();
nettyHttpServerConfig.setServerName("NettyHttpServer",
Bootstrap.class.getPackage().getImplementationVersion());
nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL);
nettyHttpServerConfig.setDebug(true);
nettyHttpServerConfig.setChunkWriteEnabled(true);
nettyHttpServerConfig.setFileUploadEnabled(true);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setMethod(HttpMethod.POST)
.setHandler(ctx -> {
logger.log(Level.FINEST, "handler starting");
List<org.xbib.net.http.server.Part> parts = ctx.httpRequest().getParts();
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("parameter = " + ctx.httpRequest().getParameter().allToString() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress() +
" attributes = " + ctx.getAttributes() +
" parts = " + parts
);
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(nettyHttpServerConfig)
.setApplication(BaseApplication.builder()
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();
NettyHttpClientConfig config = new NettyHttpClientConfig()
.setGzipEnabled(false)
.setChunkWriteEnabled(true)
.setObjectAggregationEnabled(true)
.setDebug(true);
AtomicBoolean received = new AtomicBoolean();
try (NettyHttpClient client = NettyHttpClient.builder()
.setConfig(config)
.build()) {
HttpRequest request = HttpRequest.post()
.setURL(url)
.addPart(new Part("text/plain", "base64",
"test", Paths.get("build.gradle"), StandardCharsets.UTF_8))
.setResponseListener(resp -> {
logger.log(Level.INFO, "got response:" +
" status = " + resp.getStatus() +
" header = " + resp.getHeaders() +
" body = " + resp.getBodyAsChars(StandardCharsets.UTF_8));
received.set(true);
})
.build();
client.execute(request).get().close();
}
assertTrue(received.get());
}
}
}

View file

@ -19,6 +19,9 @@ import org.xbib.net.http.client.netty.NettyHttpClient;
import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
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.http.server.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -37,29 +40,36 @@ public class NettyHttpServerMultiRequestLoadTest {
NettyHttpServerConfig serverConfig = new NettyHttpServerConfig();
serverConfig.setServerName("NettyHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain: " +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain: " +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" attributes = " + ctx.getAttributes() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();

View file

@ -15,7 +15,10 @@ import org.xbib.net.http.client.netty.NettyHttpClient;
import org.xbib.net.http.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
import org.xbib.net.http.server.executor.BaseExecutor;
import org.xbib.net.http.server.executor.Executor;
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.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -33,34 +36,42 @@ public class NettyHttpServerTest {
URL url = URL.from("http://localhost:8008/domain/");
HttpAddress httpAddress1 = HttpAddress.http1(url);
NettyHttpServerConfig nettyHttpServerConfig = new NettyHttpServerConfig();
nettyHttpServerConfig.setServerName("NettyHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
nettyHttpServerConfig.setServerName("NettyHttpServer",
Bootstrap.class.getPackage().getImplementationVersion());
nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL);
nettyHttpServerConfig.setDebug(true);
nettyHttpServerConfig.setPipelining(false);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain" +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress() +
" attributes = " + ctx.getAttributes()
);
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(nettyHttpServerConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain" +
" parameter = " + ctx.httpRequest().getParameter().allToString() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress() +
" attributes = " + ctx.getAttributes()
);
})
.build())
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();
NettyHttpClientConfig config = new NettyHttpClientConfig()

View file

@ -1,11 +1,13 @@
package org.xbib.net.http.server.nio;
import java.util.Collection;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpHeaderNames;
import org.xbib.net.http.HttpHeaders;
import org.xbib.net.http.HttpMethod;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.HttpVersion;
import org.xbib.net.http.server.application.Application;
import org.xbib.net.http.server.HttpServerContext;
import org.xbib.net.http.server.HttpServer;
import java.io.IOException;
@ -30,6 +32,9 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.xbib.net.http.server.domain.HttpDomain;
import org.xbib.net.http.server.executor.CallableReleasable;
import org.xbib.net.http.server.route.HttpRouter;
public class NioHttpServer implements HttpServer {
@ -101,7 +106,7 @@ public class NioHttpServer implements HttpServer {
httpAddress,
(InetSocketAddress) socketChannel.getLocalAddress(),
(InetSocketAddress) socketChannel.getRemoteAddress());
builder.application.dispatch(requestBuilder, responseBuilder);
dispatch(requestBuilder, responseBuilder);
socketChannel.close();
} catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage(), e);
@ -128,8 +133,50 @@ public class NioHttpServer implements HttpServer {
}
@Override
public Application getApplication() {
return builder.application;
public void dispatch(org.xbib.net.http.server.HttpRequestBuilder requestBuilder,
org.xbib.net.http.server.HttpResponseBuilder responseBuilder) {
CallableReleasable<?> callableReleasable = new CallableReleasable<>() {
@Override
public Object call() {
HttpRouter router = builder.application.getRouter();
router.route(builder.application, requestBuilder, responseBuilder);
return true;
}
@Override
public void release() {
requestBuilder.release();
responseBuilder.release();
}
};
builder.application.getExecutor().execute(callableReleasable);
}
@Override
public void dispatch(org.xbib.net.http.server.HttpRequestBuilder requestBuilder,
org.xbib.net.http.server.HttpResponseBuilder responseBuilder,
HttpResponseStatus responseStatus) {
HttpServerContext httpServerContext = builder.application.createContext(null, requestBuilder, responseBuilder);
CallableReleasable<?> callableReleasable = new CallableReleasable<>() {
@Override
public Object call() {
HttpRouter router = builder.application.getRouter();
router.routeStatus(responseStatus, httpServerContext);
return true;
}
@Override
public void release() {
requestBuilder.release();
responseBuilder.release();
}
};
builder.application.getExecutor().execute(callableReleasable);
}
@Override
public Collection<HttpDomain> getDomains() {
return builder.application.getDomains();
}
@Override

View file

@ -9,6 +9,9 @@ import org.xbib.net.http.HttpHeaderValues;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
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.http.server.service.BaseHttpService;
import org.xbib.net.http.server.route.BaseHttpRouter;
import org.xbib.net.http.server.HttpServerConfig;
@ -24,49 +27,54 @@ public class NioHttpServerTest {
public void nioServerTest() throws Exception {
HttpAddress httpAddress1 = HttpAddress.http1("localhost", 8008);
HttpAddress httpAddress2 = HttpAddress.http1("localhost", 8009);
NioHttpServer server = NioHttpServer.builder()
.setHttpServerConfig(new HttpServerConfig()
.setServerName("NioHttpServer", NioHttpServer.class.getPackage().getImplementationVendor())
.setNetworkClass(NetworkClass.SITE)
)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain1")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain1 " +
ctx.httpRequest().getParameter().toString() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress2)
.addService(BaseHttpService.builder()
.setPath("/domain2")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain2 " +
ctx.httpRequest().getParameter().toString() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain1")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain1 " +
ctx.httpRequest().getParameter().toString() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress2)
.addService(BaseHttpService.builder()
.setPath("/domain2")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain2 " +
ctx.httpRequest().getParameter().toString() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.build();
try {
Executor executor = BaseExecutor.builder()
.build();
try(NioHttpServer server = NioHttpServer.builder()
.setHttpServerConfig(new HttpServerConfig()
.setServerName("NioHttpServer", NioHttpServer.class.getPackage().getImplementationVendor())
.setNetworkClass(NetworkClass.SITE))
.setApplication(BaseApplication.builder()
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();
} catch (BindException e) {
throw new RuntimeException(e);

View file

@ -9,7 +9,10 @@ import org.xbib.net.http.HttpHeaderValues;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
import org.xbib.net.http.server.executor.BaseExecutor;
import org.xbib.net.http.server.executor.Executor;
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.HttpServerConfig;
import org.xbib.net.http.server.simple.SimpleHttpServer;
@ -33,37 +36,44 @@ public class SimpleHttpsServerTest {
serverConfig.setServerName("SimpleSecureHttpServer", SimpleHttpServer.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
serverConfig.setDebug(true);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL peer host = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
try (SimpleHttpServer server = SimpleHttpsServer.builder()
.setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpsAddress)
.addService(BaseHttpService.builder()
.setPath("/favicon.ico")
.setHandler(ctx -> ctx.response()
.setResponseStatus(HttpResponseStatus.NOT_FOUND)
.build()
.flush())
.build())
.addService(BaseHttpService.builder()
.setPath("/secure")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("secure domain: " +
" SNI host = " + ctx.httpRequest().as(HttpsRequest.class).getSNIHost() +
" SSL peer host = " + ctx.httpRequest().as(HttpsRequest.class).getSSLSession() +
" base URL = " + ctx.httpRequest().getBaseURL() +
" parameter = " + ctx.httpRequest().getParameter() +
" local address = " + ctx.httpRequest().getLocalAddress() +
" remote address = " + ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build()) {
server.bind();

View file

@ -1,11 +1,13 @@
package org.xbib.net.http.server.simple;
import java.util.Collection;
import org.xbib.net.NetworkClass;
import org.xbib.net.NetworkUtils;
import org.xbib.net.SocketConfig;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpHeaderNames;
import org.xbib.net.http.server.application.Application;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.server.HttpServerContext;
import org.xbib.net.http.HttpHeaders;
import org.xbib.net.http.HttpMethod;
import org.xbib.net.http.HttpVersion;
@ -31,6 +33,9 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.xbib.net.http.server.domain.HttpDomain;
import org.xbib.net.http.server.executor.CallableReleasable;
import org.xbib.net.http.server.route.HttpRouter;
public class SimpleHttpServer implements HttpServer {
@ -108,7 +113,7 @@ public class SimpleHttpServer implements HttpServer {
httpAddress,
(InetSocketAddress) socket.getLocalSocketAddress(),
(InetSocketAddress) socket.getRemoteSocketAddress());
builder.application.dispatch(httpRequestBuilder, httpResponseBuilder);
dispatch(httpRequestBuilder, httpResponseBuilder);
} catch (Throwable t) {
logger.log(Level.SEVERE, t.getMessage(), t);
} finally {
@ -129,6 +134,53 @@ public class SimpleHttpServer implements HttpServer {
}
}
@Override
public void dispatch(org.xbib.net.http.server.HttpRequestBuilder requestBuilder,
org.xbib.net.http.server.HttpResponseBuilder responseBuilder) {
CallableReleasable<?> callableReleasable = new CallableReleasable<>() {
@Override
public Object call() {
HttpRouter router = builder.application.getRouter();
router.route(builder.application, requestBuilder, responseBuilder);
return true;
}
@Override
public void release() {
requestBuilder.release();
responseBuilder.release();
}
};
builder.application.getExecutor().execute(callableReleasable);
}
@Override
public void dispatch(org.xbib.net.http.server.HttpRequestBuilder requestBuilder,
org.xbib.net.http.server.HttpResponseBuilder responseBuilder,
HttpResponseStatus responseStatus) {
HttpServerContext httpServerContext = builder.application.createContext(null, requestBuilder, responseBuilder);
CallableReleasable<?> callableReleasable = new CallableReleasable<>() {
@Override
public Object call() {
HttpRouter router = builder.application.getRouter();
router.routeStatus(responseStatus, httpServerContext);
return true;
}
@Override
public void release() {
requestBuilder.release();
responseBuilder.release();
}
};
builder.application.getExecutor().execute(callableReleasable);
}
@Override
public Collection<HttpDomain> getDomains() {
return builder.application.getDomains();
}
@Override
public void loop() throws IOException {
CountDownLatch latch = new CountDownLatch(1);
@ -139,11 +191,6 @@ public class SimpleHttpServer implements HttpServer {
}
}
@Override
public Application getApplication() {
return builder.application;
}
@Override
public void close() throws IOException {
logger.log(Level.INFO, "closing");
@ -248,5 +295,4 @@ public class SimpleHttpServer implements HttpServer {
}
return result.toString();
}
}

View file

@ -9,7 +9,10 @@ import org.xbib.net.http.HttpHeaderValues;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain;
import org.xbib.net.http.server.executor.BaseExecutor;
import org.xbib.net.http.server.executor.Executor;
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.HttpServerConfig;
import org.xbib.net.http.server.resource.FileResourceHandler;
@ -24,50 +27,56 @@ public class SimpleHttpServerTest {
public void simpleServerTest() throws Exception {
HttpAddress httpAddress1 = HttpAddress.http1("localhost", 8008);
HttpAddress httpAddress2 = HttpAddress.http1("localhost", 8008);
HttpRouter router = BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain1")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain1 " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.addService(BaseHttpService.builder()
.setPath("/file1/*")
.setHandler(new FileResourceHandler())
.build())
.build())
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress2)
.addService(BaseHttpService.builder()
.setPath("/domain2")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain2 " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build();
Executor executor = BaseExecutor.builder()
.build();
SimpleHttpServer server = SimpleHttpServer.builder()
.setHttpServerConfig(new HttpServerConfig()
.setServerName("SimpleHttpServer", SimpleHttpServer.class.getPackage().getImplementationVendor())
.setNetworkClass(NetworkClass.SITE)
)
.setNetworkClass(NetworkClass.SITE))
.setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder()
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress1)
.addService(BaseHttpService.builder()
.setPath("/domain1")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain1 " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.addService(BaseHttpService.builder()
.setPath("/file1/*")
.setHandler(new FileResourceHandler())
.build())
.build())
.addDomain(BaseHttpDomain.builder()
.setHttpAddress(httpAddress2)
.addService(BaseHttpService.builder()
.setPath("/domain2")
.setHandler(ctx -> {
ctx.response()
.setResponseStatus(HttpResponseStatus.OK)
.setHeader(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
.setCharset(StandardCharsets.UTF_8);
ctx.write("domain2 " +
ctx.httpRequest().getParameter() + " " +
ctx.httpRequest().getLocalAddress() + " " +
ctx.httpRequest().getRemoteAddress());
})
.build())
.build())
.build())
.setExecutor(executor)
.setRouter(router)
.build())
.build();
server.bind();

View file

@ -23,6 +23,7 @@ module org.xbib.net.http.server {
exports org.xbib.net.http.server.session.file;
exports org.xbib.net.http.server.session.memory;
exports org.xbib.net.http.server.validate;
exports org.xbib.net.http.server.executor;
requires org.xbib.net;
requires org.xbib.net.mime;
requires org.xbib.net.http;

View file

@ -1,6 +1,7 @@
package org.xbib.net.http.server;
import java.net.InetSocketAddress;
import java.util.List;
import org.xbib.net.Attributes;
import org.xbib.net.Parameter;
import org.xbib.net.URL;
@ -85,6 +86,11 @@ public abstract class BaseHttpRequest implements HttpRequest {
return builder.requestId;
}
@Override
public List<Part> getParts() {
return builder.parts;
}
@Override
public HttpServerContext getContext() {
return builder.httpServerContext;

View file

@ -4,6 +4,8 @@ import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.xbib.net.Parameter;
import org.xbib.net.URL;
@ -49,8 +51,11 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
protected boolean done;
protected List<Part> parts;
protected BaseHttpRequestBuilder() {
this.httpHeaders = new HttpHeaders();
this.parts = new ArrayList<>();
}
@Override
@ -264,6 +269,14 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
return this;
}
public BaseHttpRequestBuilder addPart(Part part) {
if (done) {
return this;
}
this.parts.add(part);
return this;
}
@Override
public void done() {
this.done = true;

View file

@ -2,6 +2,8 @@ package org.xbib.net.http.server;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.List;
import org.xbib.net.Attributes;
import org.xbib.net.Parameter;
import org.xbib.net.Request;
@ -38,5 +40,7 @@ public interface HttpRequest extends Request {
InputStream getInputStream();
List<Part> getParts();
Attributes getAttributes();
}

View file

@ -31,6 +31,8 @@ public interface HttpRequestBuilder {
HttpRequestBuilder addHeader(String name, String value);
HttpRequestBuilder addPart(Part part);
URL getBaseURL();
HttpMethod getMethod();

View file

@ -3,7 +3,9 @@ package org.xbib.net.http.server;
import java.io.Closeable;
import java.io.IOException;
import java.net.BindException;
import org.xbib.net.http.server.application.Application;
import java.util.Collection;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.server.domain.HttpDomain;
public interface HttpServer extends Closeable {
@ -11,5 +13,13 @@ public interface HttpServer extends Closeable {
void loop() throws IOException;
Application getApplication();
void dispatch(HttpRequestBuilder requestBuilder,
HttpResponseBuilder responseBuilder);
void dispatch(HttpRequestBuilder requestBuilder,
HttpResponseBuilder responseBuilder,
HttpResponseStatus responseStatus);
Collection<HttpDomain> getDomains();
}

View file

@ -0,0 +1,55 @@
package org.xbib.net.http.server;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import static java.nio.charset.StandardCharsets.UTF_8;
public class Part {
private final String contentType;
private final String contentTransferEncoding;
private final String name;
private final Path path;
private final ByteBuffer byteBuffer;
public Part(String contentType,
String contentTransferEncoding,
String name,
Path path,
ByteBuffer byteBuffer) {
this.contentType = contentType;
this.contentTransferEncoding = contentTransferEncoding;
this.name = name;
this.path = path;
this.byteBuffer = byteBuffer;
}
public String getContentType() {
return contentType;
}
public String getContentTransferEncoding() {
return contentTransferEncoding;
}
public String getName() {
return name;
}
public Path getPath() {
return path;
}
public ByteBuffer getByteBuffer() {
return byteBuffer;
}
@Override
public String toString() {
return "Part[name=" + name + ",path=" + path + ",bytebuffer=" + (byteBuffer != null ? UTF_8.decode(byteBuffer) : "") + "]";
}
}

View file

@ -8,11 +8,12 @@ import java.util.Collection;
import java.util.Locale;
import java.util.Set;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.server.HttpRequestBuilder;
import org.xbib.net.http.server.HttpResponseBuilder;
import org.xbib.net.http.server.HttpServerContext;
import org.xbib.net.http.server.domain.HttpDomain;
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;
@ -35,26 +36,10 @@ public interface Application extends SessionListener, Resolver<Path>, Closeable
Settings getSettings();
void addModule(ApplicationModule applicationModule);
Collection<ApplicationModule> getModules();
/**
* Dispatch a request.
* @param requestBuilder the request
* @param responseBuilder the response
*/
void dispatch(HttpRequestBuilder requestBuilder,
HttpResponseBuilder responseBuilder);
/**
* Dispatch a status.
* @param requestBuilder the request
* @param responseBuilder the response
* @param httpResponseStatus the status
*/
void dispatch(HttpRequestBuilder requestBuilder,
HttpResponseBuilder responseBuilder,
HttpResponseStatus httpResponseStatus);
HttpServerContext createContext(HttpDomain domain,
HttpRequestBuilder httpRequestBuilder,
HttpResponseBuilder httpResponseBuilder);
@ -63,5 +48,9 @@ public interface Application extends SessionListener, Resolver<Path>, Closeable
void onClose(HttpServerContext httpServerContext);
Executor getExecutor();
HttpRouter getRouter();
void close() throws IOException;
}

View file

@ -3,23 +3,12 @@ package org.xbib.net.http.server.application;
import java.nio.file.Path;
import java.time.ZoneId;
import java.util.Locale;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.mime.MimeTypeService;
public interface ApplicationBuilder {
ApplicationBuilder setThreadCount(int blockingThreadCount);
ApplicationBuilder setQueueCount(int blockingThreadQueueCount);
ApplicationBuilder setKeepAliveTime(int keepAliveTime);
ApplicationBuilder setKeepAliveTimeUnit(TimeUnit keepAliveTimeUnit);
ApplicationBuilder setExecutor(ThreadPoolExecutor executor);
ApplicationBuilder setHome(Path home);
ApplicationBuilder setContextPath(String contextPath);
@ -28,8 +17,6 @@ public interface ApplicationBuilder {
ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled);
ApplicationBuilder setRouter(HttpRouter router);
ApplicationBuilder setLocale(Locale locale);
ApplicationBuilder setZoneId(ZoneId zoneId);
@ -38,7 +25,9 @@ public interface ApplicationBuilder {
ApplicationBuilder setStaticSuffixes(String... suffixes);
ApplicationBuilder registerModule(ApplicationModule applicationModule);
ApplicationBuilder setExecutor(Executor executor);
ApplicationBuilder setRouter(HttpRouter httpRouter);
Application build();
}

View file

@ -1,7 +0,0 @@
package org.xbib.net.http.server.application;
import java.util.concurrent.Callable;
import org.xbib.net.buffer.Releasable;
public interface ApplicationCallable<T> extends Callable<T>, Releasable {
}

View file

@ -6,16 +6,15 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.ZoneId;
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.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.cookie.SameSite;
import org.xbib.net.http.server.BaseHttpServerContext;
import org.xbib.net.http.server.HttpException;
@ -26,6 +25,7 @@ import org.xbib.net.http.server.HttpServerContext;
import org.xbib.net.http.server.cookie.IncomingCookieHandler;
import org.xbib.net.http.server.cookie.OutgoingCookieHandler;
import org.xbib.net.http.server.domain.HttpDomain;
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;
@ -60,6 +60,8 @@ public class BaseApplication implements Application {
private HttpHandler outgoingSessionHandler;
protected List<ApplicationModule> applicationModuleList;
protected BaseApplication(BaseApplicationBuilder builder) {
this.builder = builder;
this.sessionName = getSettings().get("session.name", "SESS");
@ -67,12 +69,38 @@ public class BaseApplication implements Application {
this.incomingCookieHandler = newIncomingCookieHandler();
this.outgoingCookieHandler = newOutgoingCookieHandler();
this.httpResponseRenderer = newResponseRenderer();
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);
}
}
}
public static BaseApplicationBuilder builder() {
return new BaseApplicationBuilder();
}
@Override
public void addModule(ApplicationModule applicationModule) {
applicationModuleList.add(applicationModule);
}
@Override
public Locale getLocale() {
return builder.locale;
@ -88,10 +116,12 @@ public class BaseApplication implements Application {
return builder.mimeTypeService;
}
@Override
public Path getHome() {
return builder.home;
}
@Override
public String getContextPath() {
return builder.contextPath;
}
@ -101,10 +131,6 @@ public class BaseApplication implements Application {
return builder.settings;
}
public HttpRouter getRouter() {
return builder.router;
}
public String getSecret() {
return builder.secret;
}
@ -115,60 +141,17 @@ public class BaseApplication implements Application {
@Override
public Collection<ApplicationModule> getModules() {
return builder.applicationModuleList;
return applicationModuleList;
}
@Override
public Collection<HttpDomain> getDomains() {
return getRouter().getDomains();
return builder.httpRouter.getDomains();
}
@Override
public Set<HttpAddress> getAddresses() {
return getRouter().getDomainsByAddress().keySet();
}
@Override
public void dispatch(HttpRequestBuilder httpRequestBuilder,
HttpResponseBuilder httpResponseBuilder) {
final Application application = this;
ApplicationCallable<?> applicationCallable = new ApplicationCallable<>() {
@Override
public Object call() {
getRouter().route(application, httpRequestBuilder, httpResponseBuilder);
return true;
}
@Override
public void release() {
httpRequestBuilder.release();
httpResponseBuilder.release();
}
};
Future<?> future = builder.executor.submit(applicationCallable);
logger.log(Level.FINEST, "dispatched " + future);
}
@Override
public void dispatch(HttpRequestBuilder httpRequestBuilder,
HttpResponseBuilder httpResponseBuilder,
HttpResponseStatus httpResponseStatus) {
HttpServerContext httpServerContext = createContext(null, httpRequestBuilder, httpResponseBuilder);
ApplicationCallable<?> applicationCallable = new ApplicationCallable<>() {
@Override
public Object call() {
getRouter().routeStatus(httpResponseStatus, httpServerContext);
return true;
}
@Override
public void release() {
httpRequestBuilder.release();
httpResponseBuilder.release();
}
};
Future<?> future = builder.executor.submit(applicationCallable);
logger.log(Level.FINEST, "dispatched status " + future);
return builder.httpRouter.getDomainsByAddress().keySet();
}
@Override
@ -242,13 +225,13 @@ public class BaseApplication implements Application {
@Override
public void onCreated(Session session) {
logger.log(Level.FINER, "session name = " + sessionName + " created = " + session);
builder.applicationModuleList.forEach(module -> module.onOpen(session));
applicationModuleList.forEach(module -> module.onOpen(session));
}
@Override
public void onDestroy(Session session) {
logger.log(Level.FINER, "session name = " + sessionName + " destroyed = " + session);
builder.applicationModuleList.forEach(module -> module.onClose(session));
applicationModuleList.forEach(module -> module.onClose(session));
}
@Override
@ -264,12 +247,12 @@ public class BaseApplication implements Application {
incomingSessionHandler.handle(httpServerContext);
}
// call modules after request/cookie/session setup
builder.applicationModuleList.forEach(module -> module.onOpen(httpServerContext));
applicationModuleList.forEach(module -> module.onOpen(httpServerContext));
} catch (HttpException e) {
getRouter().routeException(e);
builder.httpRouter.routeException(e);
httpServerContext.fail();
} catch (Throwable t) {
getRouter().routeToErrorHandler(httpServerContext, t);
builder.httpRouter.routeToErrorHandler(httpServerContext, t);
httpServerContext.fail();
}
}
@ -278,7 +261,7 @@ public class BaseApplication implements Application {
public void onClose(HttpServerContext httpServerContext) {
try {
// call modules before session/cookie
builder.applicationModuleList.forEach(module -> module.onClose(httpServerContext));
applicationModuleList.forEach(module -> module.onClose(httpServerContext));
if (builder.sessionsEnabled && outgoingSessionHandler != null) {
outgoingSessionHandler.handle(httpServerContext);
}
@ -286,9 +269,9 @@ public class BaseApplication implements Application {
outgoingCookieHandler.handle(httpServerContext);
}
} catch (HttpException e) {
getRouter().routeException(e);
builder.httpRouter.routeException(e);
} catch (Throwable t) {
getRouter().routeToErrorHandler(httpServerContext, t);
builder.httpRouter.routeToErrorHandler(httpServerContext, t);
} finally {
try {
if (httpResponseRenderer != null) {
@ -300,6 +283,16 @@ public class BaseApplication implements Application {
}
}
@Override
public Executor getExecutor() {
return builder.executor;
}
@Override
public HttpRouter getRouter() {
return builder.httpRouter;
}
@Override
public Path resolve(String string) {
if (string == null) {
@ -321,18 +314,9 @@ public class BaseApplication implements Application {
@Override
public void close() throws IOException {
logger.log(Level.INFO, "application closing");
// stop dispatching and stop dispatched requests
builder.executor.shutdown();
try {
if (!builder.executor.awaitTermination(10, TimeUnit.SECONDS)) {
List<Runnable> list = builder.executor.shutdownNow();
logger.log(Level.WARNING, "unable to stop runnables " + list);
}
} catch (InterruptedException e) {
List<Runnable> list = builder.executor.shutdownNow();
logger.log(Level.WARNING, "unable to stop runnables " + list);
}
builder.applicationModuleList.forEach(module -> {
// stop dispatching and stop dispatched requests
applicationModuleList.forEach(module -> {
logger.log(Level.FINE, "application closing module " + module);
module.onClose();
});

View file

@ -3,30 +3,23 @@ package org.xbib.net.http.server.application;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.config.ConfigLoader;
import org.xbib.config.ConfigLogger;
import org.xbib.config.ConfigParams;
import org.xbib.config.SystemConfigLogger;
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.net.util.NamedThreadFactory;
import org.xbib.settings.Settings;
public class BaseApplicationBuilder implements ApplicationBuilder {
private static final Logger logger = Logger.getLogger(BaseApplicationBuilder.class.getName());
private static final ConfigLogger bootLogger;
static {
@ -36,18 +29,11 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
bootLogger = optionalBootLogger.orElse(new SystemConfigLogger());
}
private static final Set<String> DEFAULT_SUFFIXES =
Set.of("css", "js", "ico", "png", "jpg", "jpeg", "gif", "woff2");
protected ClassLoader classLoader;
protected int blockingThreadCount;
protected int blockingThreadQueueCount;
protected int blockingThreadKeepAliveTime;
protected TimeUnit blockingThreadKeepAliveTimeUnit;
protected ThreadPoolExecutor executor;
protected Path home;
protected String contextPath;
@ -56,8 +42,6 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
protected boolean sessionsEnabled;
protected HttpRouter router;
protected Locale locale;
protected ZoneId zoneId;
@ -72,14 +56,12 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
protected Settings settings;
protected List<ApplicationModule> applicationModuleList;
protected Executor executor;
protected HttpRouter httpRouter;
protected BaseApplicationBuilder() {
this.classLoader = getClass().getClassLoader();
this.blockingThreadCount = Runtime.getRuntime().availableProcessors();
this.blockingThreadQueueCount = 0; // use fair synchronous queue
this.blockingThreadKeepAliveTime = 60;
this.blockingThreadKeepAliveTimeUnit = TimeUnit.SECONDS;
this.home = Paths.get(System.getProperties().containsKey("application.home") ? System.getProperty("application.home") : ".");
this.contextPath = "/";
this.secret = "secret";
@ -87,7 +69,26 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
this.locale = Locale.getDefault();
this.zoneId = ZoneId.systemDefault();
this.mimeTypeService = new MimeTypeService();
this.applicationModuleList = new ArrayList<>();
String name = System.getProperty("application.name");
if (name == null) {
name = "application";
}
String profile = System.getProperty("application.profile");
if (profile == null) {
profile = "developer";
}
this.configParams = new ConfigParams()
.withDirectoryName(name)
.withFileNamesWithoutSuffix(profile)
.withSystemEnvironment()
.withSystemProperties();
this.configLoader = ConfigLoader.getInstance()
.withLogger(bootLogger);
this.settings = configLoader.load(configParams);
if (staticFileSuffixes == null) {
staticFileSuffixes = DEFAULT_SUFFIXES;
}
this.executor = BaseExecutor.builder().build();
}
public BaseApplicationBuilder setSettings(Settings settings) {
@ -95,36 +96,6 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this;
}
@Override
public BaseApplicationBuilder setThreadCount(int blockingThreadCount) {
this.blockingThreadCount = blockingThreadCount;
return this;
}
@Override
public BaseApplicationBuilder setQueueCount(int blockingThreadQueueCount) {
this.blockingThreadQueueCount = blockingThreadQueueCount;
return this;
}
@Override
public BaseApplicationBuilder setKeepAliveTime(int blockingThreadKeepAliveTime) {
this.blockingThreadKeepAliveTime = blockingThreadKeepAliveTime;
return this;
}
@Override
public BaseApplicationBuilder setKeepAliveTimeUnit(TimeUnit blockingThreadKeepAliveTimeUnit) {
this.blockingThreadKeepAliveTimeUnit = blockingThreadKeepAliveTimeUnit;
return this;
}
@Override
public BaseApplicationBuilder setExecutor(ThreadPoolExecutor executor) {
this.executor = executor;
return this;
}
@Override
public BaseApplicationBuilder setHome(Path home) {
this.home = home;
@ -149,12 +120,6 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this;
}
@Override
public ApplicationBuilder setRouter(HttpRouter router) {
this.router = router;
return this;
}
@Override
public ApplicationBuilder setLocale(Locale locale) {
this.locale = locale;
@ -180,73 +145,20 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
}
@Override
public ApplicationBuilder registerModule(ApplicationModule applicationModule) {
applicationModuleList.add(applicationModule);
public ApplicationBuilder setExecutor(Executor executor) {
this.executor = executor;
return this;
}
@Override
public ApplicationBuilder setRouter(HttpRouter httpRouter) {
this.httpRouter = httpRouter;
return this;
}
@Override
public Application build() {
prepareApplication();
Application application = new BaseApplication(this);
setupApplication(application);
return application;
Objects.requireNonNull(httpRouter);
return new BaseApplication(this);
}
protected void prepareApplication() {
String name = System.getProperty("application.name");
if (name == null) {
name = "application";
}
String profile = System.getProperty("application.profile");
if (profile == null) {
profile = "developer";
}
String[] args = profile.split(";");
this.configParams = new ConfigParams()
.withArgs(args)
.withDirectoryName(name)
.withFileNamesWithoutSuffix(args[0])
.withSystemEnvironment()
.withSystemProperties();
this.configLoader = ConfigLoader.getInstance()
.withLogger(bootLogger);
this.settings = configLoader.load(configParams);
if (staticFileSuffixes == null) {
staticFileSuffixes = DEFAULT_SUFFIXES;
}
if (this.executor == null) {
this.executor = new ApplicationThreadPoolExecutor(blockingThreadCount, blockingThreadQueueCount,
blockingThreadKeepAliveTime, blockingThreadKeepAliveTimeUnit,
new NamedThreadFactory("org-xbib-net-http-server-application"));
this.executor.setRejectedExecutionHandler((runnable, threadPoolExecutor) ->
logger.log(Level.SEVERE, "rejected " + runnable + " for thread pool executor = " + threadPoolExecutor));
}
}
protected void setupApplication(Application application) {
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, classLoader);
ApplicationModule applicationModule = clazz.getConstructor(Application.class, String.class, Settings.class)
.newInstance(application, 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);
}
}
}
private static final Set<String> DEFAULT_SUFFIXES =
Set.of("css", "js", "ico", "png", "jpg", "jpeg", "gif", "woff2");
}

View file

@ -0,0 +1,41 @@
package org.xbib.net.http.server.executor;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class BaseExecutor implements Executor {
private static final Logger logger = Logger.getLogger(BaseExecutor.class.getName());
private final BaseExecutorBuilder builder;
protected BaseExecutor(BaseExecutorBuilder builder) {
this.builder = builder;
}
public static BaseExecutorBuilder builder() {
return new BaseExecutorBuilder();
}
@Override
public void execute(CallableReleasable<?> callableReleasable) {
builder.executor.submit(callableReleasable);
}
@Override
public void shutdown() throws IOException {
builder.executor.shutdown();
try {
if (!builder.executor.awaitTermination(builder.threadKeepAliveTime, builder.threadKeepAliveTimeUnit)) {
List<Runnable> list = builder.executor.shutdownNow();
logger.log(Level.WARNING, "unable to stop runnables " + list);
}
} catch (InterruptedException e) {
List<Runnable> list = builder.executor.shutdownNow();
logger.log(Level.WARNING, "unable to stop runnables " + list);
throw new IOException(e);
}
}
}

View file

@ -0,0 +1,80 @@
package org.xbib.net.http.server.executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.util.NamedThreadFactory;
public class BaseExecutorBuilder implements ExecutorBuilder {
private static final Logger logger = Logger.getLogger(BaseExecutorBuilder.class.getName());
protected String threadPrefix;
protected int threadCount;
protected int threadQueueCount;
protected int threadKeepAliveTime;
protected TimeUnit threadKeepAliveTimeUnit;
protected ThreadPoolExecutor executor;
protected BaseExecutorBuilder() {
this.threadPrefix = "org-xbib-net-server-executor";
this.threadCount = Runtime.getRuntime().availableProcessors();
this.threadQueueCount = 0; // use fair synchronous queue
this.threadKeepAliveTime = 10;
this.threadKeepAliveTimeUnit = TimeUnit.SECONDS;
}
@Override
public ExecutorBuilder setThreadPrefix(String threadPrefix) {
this.threadPrefix = threadPrefix;
return this;
}
@Override
public ExecutorBuilder setThreadCount(int threadCount) {
this.threadCount = threadCount;
return this;
}
@Override
public ExecutorBuilder setQueueCount(int threadQueueCount) {
this.threadQueueCount = threadQueueCount;
return this;
}
@Override
public ExecutorBuilder setKeepAliveTime(int keepAliveTime) {
this.threadKeepAliveTime = keepAliveTime;
return this;
}
@Override
public ExecutorBuilder setKeepAliveTimeUnit(TimeUnit keepAliveTimeUnit) {
this.threadKeepAliveTimeUnit = keepAliveTimeUnit;
return this;
}
@Override
public ExecutorBuilder setExecutor(ThreadPoolExecutor executor) {
this.executor = executor;
return this;
}
@Override
public Executor build() {
if (executor == null) {
this.executor = new BaseThreadPoolExecutor(threadCount, threadQueueCount,
threadKeepAliveTime, threadKeepAliveTimeUnit,
new NamedThreadFactory(threadPrefix));
this.executor.setRejectedExecutionHandler((runnable, threadPoolExecutor) ->
logger.log(Level.SEVERE, "rejected " + runnable + " for thread pool executor = " + threadPoolExecutor));
}
return new BaseExecutor(this);
}
}

View file

@ -1,4 +1,4 @@
package org.xbib.net.http.server.application;
package org.xbib.net.http.server.executor;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@ -11,15 +11,15 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ApplicationThreadPoolExecutor extends ThreadPoolExecutor {
public class BaseThreadPoolExecutor extends ThreadPoolExecutor {
private final Logger logger = Logger.getLogger(ApplicationThreadPoolExecutor.class.getName());
private final Logger logger = Logger.getLogger(BaseThreadPoolExecutor.class.getName());
public ApplicationThreadPoolExecutor(int nThreads,
int maxQueue,
long keepAliveTime,
TimeUnit timeUnit,
ThreadFactory threadFactory) {
public BaseThreadPoolExecutor(int nThreads,
int maxQueue,
long keepAliveTime,
TimeUnit timeUnit,
ThreadFactory threadFactory) {
super(nThreads, nThreads, keepAliveTime, timeUnit, createBlockingQueue(maxQueue), threadFactory);
logger.log(Level.FINE, () -> "threadpool executor up with nThreads = " + nThreads +
" keepAliveTime = " + keepAliveTime +
@ -34,7 +34,7 @@ public class ApplicationThreadPoolExecutor extends ThreadPoolExecutor {
@Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new ApplicationTask<>(callable);
return new Task<>(callable);
}
@Override
@ -45,10 +45,10 @@ public class ApplicationThreadPoolExecutor extends ThreadPoolExecutor {
logger.log(Level.SEVERE, terminationCause.getMessage(), terminationCause);
return;
}
if (runnable instanceof ApplicationTask<?> applicationTask) {
ApplicationCallable<?> applicationCallable = (ApplicationCallable<?>) applicationTask.getCallable();
logger.log(Level.FINEST, () -> "releasing " + applicationCallable);
applicationCallable.release();
if (runnable instanceof Task<?> task) {
CallableReleasable<?> callableReleasable = (CallableReleasable<?>) task.getCallable();
logger.log(Level.FINEST, () -> "releasing " + callableReleasable);
callableReleasable.release();
}
}
}

View file

@ -0,0 +1,7 @@
package org.xbib.net.http.server.executor;
import java.util.concurrent.Callable;
import org.xbib.net.buffer.Releasable;
public interface CallableReleasable<T> extends Callable<T>, Releasable {
}

View file

@ -0,0 +1,13 @@
package org.xbib.net.http.server.executor;
import java.io.IOException;
public interface Executor {
/**
* Execute a task that must be released after execution.
*/
void execute(CallableReleasable<?> callableReleasable);
void shutdown() throws IOException;
}

View file

@ -0,0 +1,21 @@
package org.xbib.net.http.server.executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public interface ExecutorBuilder {
ExecutorBuilder setThreadPrefix(String prefix);
ExecutorBuilder setThreadCount(int threadCount);
ExecutorBuilder setQueueCount(int threadQueueCount);
ExecutorBuilder setKeepAliveTime(int keepAliveTime);
ExecutorBuilder setKeepAliveTimeUnit(TimeUnit keepAliveTimeUnit);
ExecutorBuilder setExecutor(ThreadPoolExecutor executor);
Executor build();
}

View file

@ -1,13 +1,13 @@
package org.xbib.net.http.server.application;
package org.xbib.net.http.server.executor;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ApplicationTask<T> extends FutureTask<T> {
public class Task<T> extends FutureTask<T> {
private final Callable<T> callable;
public ApplicationTask(Callable<T> callable) {
public Task(Callable<T> callable) {
super(callable);
this.callable = callable;
}

View file

@ -37,24 +37,8 @@ public class BaseHttpRouter implements HttpRouter {
private final DomainsByAddress domainsByAddress;
private final HttpRouteResolver<HttpService> httpRouteResolver;
protected BaseHttpRouter(BaseHttpRouterBuilder builder) {
this.builder = builder;
HttpRouteResolver.Builder<HttpService> httpRouteResolverBuilder = newHttpRouteResolverBuilder();
for (HttpDomain domain : builder.domains) {
for (HttpService httpService : domain.getServices()) {
logger.log(Level.FINER, "adding " + domain.getAddress() + " " + httpService.getMethods() +
" prefix = " + httpService.getPrefix() +
" path = " + httpService.getPathSpecification() + " " + httpService);
HttpRoute httpRoute = new BaseHttpRoute(domain.getAddress(),
httpService.getMethods(),
httpService.getPrefix(),
httpService.getPathSpecification(), false);
httpRouteResolverBuilder.add(httpRoute, httpService);
}
}
this.httpRouteResolver = httpRouteResolverBuilder.build();
this.domains = createDomains(builder.domains);
this.domainsByAddress = createAddresses(builder.domains);
}
@ -63,10 +47,6 @@ public class BaseHttpRouter implements HttpRouter {
return new BaseHttpRouterBuilder();
}
public HttpRouteResolver.Builder<HttpService> newHttpRouteResolverBuilder() {
return BaseHttpRouteResolver.builder();
}
@Override
public Collection<HttpDomain> getDomains() {
return builder.domains;
@ -95,7 +75,7 @@ public class BaseHttpRouter implements HttpRouter {
builder.prefix,
requestBuilder.getRequestPath(),
true);
httpRouteResolver.resolve(httpRoute, httpRouteResolverResults::add);
builder.httpRouteResolver.resolve(httpRoute, httpRouteResolverResults::add);
HttpServerContext httpServerContext = application.createContext(httpDomain, requestBuilder, responseBuilder);
application.onOpen(httpServerContext);
try {

View file

@ -5,6 +5,8 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.http.server.HttpHandler;
import org.xbib.net.http.server.domain.HttpDomain;
import org.xbib.net.http.server.handler.BadRequestHandler;
@ -14,15 +16,20 @@ 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.HttpService;
public class BaseHttpRouterBuilder implements HttpRouterBuilder {
private static final Logger logger = Logger.getLogger(BaseHttpRouterBuilder.class.getName());
protected String prefix;
protected final Collection<HttpDomain> domains;
protected final Map<Integer, HttpHandler> handlers;
protected HttpRouteResolver<HttpService> httpRouteResolver;
protected BaseHttpRouterBuilder() {
prefix = "";
domains = new ArrayList<>();
@ -58,11 +65,34 @@ public class BaseHttpRouterBuilder implements HttpRouterBuilder {
return this;
}
@Override
public BaseHttpRouterBuilder setRouteResolver(HttpRouteResolver<HttpService> httpRouteResolver) {
this.httpRouteResolver = httpRouteResolver;
return this;
}
@Override
public BaseHttpRouter build() {
if (domains.isEmpty()) {
throw new IllegalArgumentException("no domain configured, unable to continue");
}
if (httpRouteResolver == null) {
HttpRouteResolver.Builder<HttpService> httpRouteResolverBuilder = BaseHttpRouteResolver.builder();
for (HttpDomain domain : domains) {
for (HttpService httpService : domain.getServices()) {
logger.log(Level.FINER, "adding " + domain.getAddress() + " " + httpService.getMethods() +
" prefix = " + httpService.getPrefix() +
" path = " + httpService.getPathSpecification() + " " + httpService);
HttpRoute httpRoute = new BaseHttpRoute(domain.getAddress(),
httpService.getMethods(),
httpService.getPrefix(),
httpService.getPathSpecification(),
false);
httpRouteResolverBuilder.add(httpRoute, httpService);
}
}
this.httpRouteResolver = httpRouteResolverBuilder.build();
}
return new BaseHttpRouter(this);
}
}

View file

@ -2,6 +2,7 @@ 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.service.HttpService;
public interface HttpRouterBuilder {
@ -11,5 +12,7 @@ public interface HttpRouterBuilder {
HttpRouterBuilder addDomain(HttpDomain domain);
HttpRouterBuilder setRouteResolver(HttpRouteResolver<HttpService> httpRouteResolver);
HttpRouter build();
}

View file

@ -4,7 +4,7 @@ dependencyResolutionManagement {
version('gradle', '8.0.2')
version('junit', '5.9.2')
version('groovy', '4.0.11')
version('netty', '4.1.90.Final')
version('netty', '4.1.91.Final')
version('netty-tcnative', '2.0.59.Final')
version('datastructures', '2.0.0')
version('config', '5.0.2')