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 group = org.xbib
name = net-http name = net-http
version = 3.2.0 version = 3.3.0
org.gradle.warning.mode = ALL org.gradle.warning.mode = ALL

View file

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

View file

@ -16,6 +16,7 @@ public class HttpsRequestBuilder extends HttpRequestBuilder {
} }
public HttpsRequest build() { 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 @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof ByteBuf) { if (msg instanceof ByteBuf byteBuf) {
ByteBuf byteBuf = (ByteBuf) msg;
if (byteBuf.isReadable()) { if (byteBuf.isReadable()) {
msg = new DefaultHttpContent(byteBuf); 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.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.PooledByteBufAllocator;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; 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.BackOff;
import org.xbib.net.http.client.ExceptionListener; import org.xbib.net.http.client.ExceptionListener;
import org.xbib.net.http.client.HttpResponse; 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.ResponseListener;
import org.xbib.net.http.client.TimeoutListener; import org.xbib.net.http.client.TimeoutListener;
import org.xbib.net.http.cookie.Cookie; 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 HttpRequestBuilder builder;
private final HttpHeaders headers;
private CompletableFuture<HttpRequest> completableFuture; private CompletableFuture<HttpRequest> completableFuture;
private int redirectCount; private int redirectCount;
protected HttpRequest(HttpRequestBuilder builder, HttpHeaders headers) { protected HttpRequest(HttpRequestBuilder builder) {
this.builder = builder; this.builder = builder;
this.headers = headers;
} }
@Override @Override
@ -62,7 +59,7 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
@Override @Override
public HttpHeaders getHeaders() { public HttpHeaders getHeaders() {
return headers; return builder.headers;
} }
@Override @Override
@ -95,11 +92,11 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
@Override @Override
public CharBuffer getBodyAsChars(Charset charset) { public CharBuffer getBodyAsChars(Charset charset) {
return charset.decode(getBody()); return charset.decode(builder.body);
} }
public CharBuffer getBodyAsChars(Charset charset, int offset, int size) { 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); slicedBuffer.limit(size);
return charset.decode(slicedBuffer); return charset.decode(slicedBuffer);
} }
@ -110,8 +107,8 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
return (R) this; return (R) this;
} }
public List<InterfaceHttpData> getBodyData() { public List<Part> getParts() {
return builder.bodyData; return builder.parts;
} }
public boolean isFollowRedirect() { public boolean isFollowRedirect() {
@ -151,7 +148,7 @@ public class HttpRequest implements org.xbib.net.http.client.HttpRequest, Closea
return "HttpNettyRequest[url=" + builder.url + return "HttpNettyRequest[url=" + builder.url +
",version=" + builder.httpVersion + ",version=" + builder.httpVersion +
",method=" + builder.httpMethod + ",method=" + builder.httpMethod +
",headers=" + headers.entries() + ",headers=" + builder.headers.entries() +
",content=" + (builder.body != null && builder.body.remaining() >= 16 ? ",content=" + (builder.body != null && builder.body.remaining() >= 16 ?
getBodyAsChars(StandardCharsets.UTF_8, 0, 16) + "..." : getBodyAsChars(StandardCharsets.UTF_8, 0, 16) + "..." :
builder.body != null ? getBodyAsChars(StandardCharsets.UTF_8) : "") + 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) return builder(PooledByteBufAllocator.DEFAULT, httpMethod)
.setVersion(httpRequest.builder.httpVersion) .setVersion(httpRequest.builder.httpVersion)
.setURL(httpRequest.builder.url) .setURL(httpRequest.builder.url)
.setHeaders(httpRequest.headers) .setHeaders(httpRequest.builder.headers)
.content(httpRequest.builder.body) .content(httpRequest.builder.body)
.setResponseListener(httpRequest.builder.responseListener) .setResponseListener(httpRequest.builder.responseListener)
.setTimeoutListener(httpRequest.builder.timeoutListener, httpRequest.builder.timeoutMillis) .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.buffer.ByteBufAllocator;
import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http2.HttpConversionUtil; import io.netty.handler.codec.http2.HttpConversionUtil;
import java.io.IOException; import java.io.IOException;
import java.io.UncheckedIOException; 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.BackOff;
import org.xbib.net.http.client.ExceptionListener; import org.xbib.net.http.client.ExceptionListener;
import org.xbib.net.http.client.HttpResponse; 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.ResponseListener;
import org.xbib.net.http.client.TimeoutListener; import org.xbib.net.http.client.TimeoutListener;
import org.xbib.net.http.cookie.Cookie; 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"; 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() { protected HttpRequestBuilder() {
this(ByteBufAllocator.DEFAULT); this(ByteBufAllocator.DEFAULT);
@ -108,10 +108,10 @@ public class HttpRequestBuilder implements org.xbib.net.http.client.HttpRequestB
this.headers = new HttpHeaders(); this.headers = new HttpHeaders();
this.removeHeaders = new ArrayList<>(); this.removeHeaders = new ArrayList<>();
this.cookies = new HashSet<>(); this.cookies = new HashSet<>();
this.bodyData = new ArrayList<>();
this.contentType = DEFAULT_FORM_CONTENT_TYPE; this.contentType = DEFAULT_FORM_CONTENT_TYPE;
this.parameterBuilder = Parameter.builder(); this.parameterBuilder = Parameter.builder();
this.timeoutMillis = 0L; this.timeoutMillis = 0L;
this.parts = new ArrayList<>();
} }
@Override @Override
@ -245,14 +245,9 @@ public class HttpRequestBuilder implements org.xbib.net.http.client.HttpRequestB
return this; return this;
} }
/** @Override
* For multipart MIME body data. public HttpRequestBuilder addPart(Part part) {
* parts.add(part);
* @param data a mime body
* @return this
*/
public HttpRequestBuilder addBodyData(InterfaceHttpData data) {
bodyData.add(data);
return this; return this;
} }
@ -382,10 +377,11 @@ public class HttpRequestBuilder implements org.xbib.net.http.client.HttpRequestB
} }
public HttpRequest build() { 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(); Parameter parameter = parameterBuilder.build();
HttpHeaders validatedHeaders = HttpHeaders.of(headers); HttpHeaders validatedHeaders = HttpHeaders.of(headers);
if (url != null) { if (url != null) {

View file

@ -26,7 +26,7 @@ public class NettyHttpClientConfig {
*/ */
private LogLevel debugLogLevel = LogLevel.DEBUG; private LogLevel debugLogLevel = LogLevel.DEBUG;
SocketConfig socketConfig = new SocketConfig(); protected SocketConfig socketConfig = new SocketConfig();
private String transportProviderName = null; private String transportProviderName = null;
@ -67,7 +67,7 @@ public class NettyHttpClientConfig {
/** /**
* Default for gzip codec is false * Default for gzip codec is false
*/ */
private boolean gzipEnabled = false; private boolean isGzipEnabled = false;
private ByteBufAllocator byteBufAllocator; private ByteBufAllocator byteBufAllocator;
@ -95,6 +95,10 @@ public class NettyHttpClientConfig {
private BackOff backOff = BackOff.ZERO_BACKOFF; private BackOff backOff = BackOff.ZERO_BACKOFF;
private Boolean isChunkWriteEnabled = true;
private Boolean isObjectAggregationEnabled = true;
public NettyHttpClientConfig() { public NettyHttpClientConfig() {
this.byteBufAllocator = ByteBufAllocator.DEFAULT; this.byteBufAllocator = ByteBufAllocator.DEFAULT;
} }
@ -208,12 +212,12 @@ public class NettyHttpClientConfig {
} }
public NettyHttpClientConfig setGzipEnabled(boolean gzipEnabled) { public NettyHttpClientConfig setGzipEnabled(boolean gzipEnabled) {
this.gzipEnabled = gzipEnabled; this.isGzipEnabled = gzipEnabled;
return this; return this;
} }
public boolean isGzipEnabled() { public boolean isGzipEnabled() {
return gzipEnabled; return isGzipEnabled;
} }
public NettyHttpClientConfig setHttp2Settings(Http2Settings http2Settings) { public NettyHttpClientConfig setHttp2Settings(Http2Settings http2Settings) {
@ -330,4 +334,21 @@ public class NettyHttpClientConfig {
return backOff; 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 { Interaction interaction) throws IOException {
NettyHttpClientConfig nettyHttpClientConfig = nettyHttpClient.getClientConfig(); NettyHttpClientConfig nettyHttpClientConfig = nettyHttpClient.getClientConfig();
ChannelPipeline pipeline = channel.pipeline(); ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("http-client-chunk-writer", pipeline.addLast("http-client-codec", new HttpClientCodec(nettyHttpClientConfig.getMaxInitialLineLength(),
new ChunkedWriteHandler());
pipeline.addLast("http-client-codec", new HttpClientCodec(nettyHttpClientConfig.getMaxInitialLineLength(),
nettyHttpClientConfig.getMaxHeadersSize(), nettyHttpClientConfig.getMaxChunkSize())); nettyHttpClientConfig.getMaxHeadersSize(), nettyHttpClientConfig.getMaxChunkSize()));
if (nettyHttpClientConfig.isGzipEnabled()) { if (nettyHttpClientConfig.isGzipEnabled()) {
pipeline.addLast("http-client-decompressor", new HttpContentDecompressor()); pipeline.addLast("http-client-decompressor", new HttpContentDecompressor());
} }
HttpObjectAggregator httpObjectAggregator = if (nettyHttpClientConfig.isObjectAggregationEnabled()) {
new HttpObjectAggregator(nettyHttpClientConfig.getMaxContentLength(), false); HttpObjectAggregator httpObjectAggregator =
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpClientConfig.getMaxCompositeBufferComponents()); new HttpObjectAggregator(nettyHttpClientConfig.getMaxContentLength(), false);
pipeline.addLast("http-client-aggregator", httpObjectAggregator); 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)); pipeline.addLast("http-client-response", new Http1Handler(interaction));
interaction.settingsReceived(null); interaction.settingsReceived(null);
} }

View file

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

View file

@ -1,19 +1,26 @@
package org.xbib.net.http.client.netty.http1; 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.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; 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.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; 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.HttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder; import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
import io.netty.handler.codec.http2.Http2Headers; import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2Settings; import io.netty.handler.codec.http2.Http2Settings;
import java.io.IOException; import java.io.IOException;
import java.net.ConnectException; 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.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.CompletableFuture; 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.HttpAddress;
import org.xbib.net.http.HttpHeaders; import org.xbib.net.http.HttpHeaders;
import org.xbib.net.http.HttpResponseStatus; 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.cookie.Cookie;
import org.xbib.net.http.client.cookie.CookieDecoder; import org.xbib.net.http.client.cookie.CookieDecoder;
import org.xbib.net.http.client.cookie.CookieEncoder; 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 static final Logger logger = Logger.getLogger(Http1Interaction.class.getName());
private final HttpDataFactory httpDataFactory;
public Http1Interaction(NettyHttpClient nettyHttpClient, HttpAddress httpAddress) { public Http1Interaction(NettyHttpClient nettyHttpClient, HttpAddress httpAddress) {
super(nettyHttpClient, httpAddress); super(nettyHttpClient, httpAddress);
this.httpDataFactory = new DefaultHttpDataFactory();
} }
@Override @Override
@ -72,6 +77,10 @@ public class Http1Interaction extends BaseInteraction {
} }
public Interaction executeRequest(HttpRequest request, Channel channel) throws IOException { 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(); final String channelId = channel.id().toString();
streamIds.putIfAbsent(channelId, new StreamIds()); streamIds.putIfAbsent(channelId, new StreamIds());
// Some HTTP 1 servers do not understand URIs in HTTP command line in spite of RFC 7230. // 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 ? DefaultFullHttpRequest fullHttpRequest = request.getBody() == null ?
new DefaultFullHttpRequest(httpVersion, httpMethod, uri) : new DefaultFullHttpRequest(httpVersion, httpMethod, uri) :
new DefaultFullHttpRequest(httpVersion, httpMethod, uri, Unpooled.wrappedBuffer(request.getBody())); new DefaultFullHttpRequest(httpVersion, httpMethod, uri, Unpooled.wrappedBuffer(request.getBody()));
HttpPostRequestEncoder httpPostRequestEncoder = null;
final Integer streamId = streamIds.get(channelId).nextStreamId(); final Integer streamId = streamIds.get(channelId).nextStreamId();
if (streamId == null) { if (streamId == null) {
throw new IllegalStateException("stream id is null"); throw new IllegalStateException("stream id is null");
@ -96,27 +104,52 @@ public class Http1Interaction extends BaseInteraction {
if (!cookies.isEmpty()) { if (!cookies.isEmpty()) {
request.getHeaders().set(HttpHeaderNames.COOKIE, CookieEncoder.STRICT.encode(cookies)); request.getHeaders().set(HttpHeaderNames.COOKIE, CookieEncoder.STRICT.encode(cookies));
} }
// headers
request.getHeaders().entries().forEach(p -> fullHttpRequest.headers().add(p.getKey(), p.getValue())); request.getHeaders().entries().forEach(p -> fullHttpRequest.headers().add(p.getKey(), p.getValue()));
if (request.getBody() == null && !request.getBodyData().isEmpty()) { // file upload
try { HttpDataFactory httpDataFactory = new DefaultHttpDataFactory();
httpPostRequestEncoder = new HttpPostRequestEncoder(httpDataFactory, fullHttpRequest, true); HttpPostRequestEncoder httpPostRequestEncoder = null;
httpPostRequestEncoder.setBodyHttpDatas(request.getBodyData()); try {
httpPostRequestEncoder.finalizeRequest(); if (!request.getParts().isEmpty()) {
} catch (HttpPostRequestEncoder.ErrorDataEncoderException e) { httpPostRequestEncoder = new HttpPostRequestEncoder(httpDataFactory,
throw new IOException(e); 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 (httpPostRequestEncoder != null && httpPostRequestEncoder.isChunked()) {
if (!channel.isWritable()) { logger.log(Level.FINEST, "finish chunked HTTP POST encoder");
logger.log(Level.WARNING, "channel not writable"); channel.write(httpPostRequestEncoder);
return this; } else {
} logger.log(Level.FINEST, "HTTP POST encoder not chunked");
channel.write(fullHttpRequest); }
if (httpPostRequestEncoder != null && httpPostRequestEncoder.isChunked()) { channel.flush();
channel.write(httpPostRequestEncoder); } catch (HttpPostRequestEncoder.ErrorDataEncoderException e) {
} throw new IOException(e);
channel.flush(); } finally {
if (httpPostRequestEncoder != null) { if (httpPostRequestEncoder != null) {
httpPostRequestEncoder.cleanFiles(); logger.log(Level.FINEST, "cleaning files of HTTP POST encoder");
//httpPostRequestEncoder.cleanFiles();
}
logger.log(Level.FINEST, "clean all http data");
//httpDataFactory.cleanAllHttpData();
} }
return this; return this;
} }
@ -241,7 +274,7 @@ public class Http1Interaction extends BaseInteraction {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
httpDataFactory.cleanAllHttpData(); //httpDataFactory.cleanAllHttpData();
super.close(); 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.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http2.Http2StreamFrameToHttpObjectCodec; 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.Interaction;
import org.xbib.net.http.client.netty.NettyHttpClientConfig; import org.xbib.net.http.client.netty.NettyHttpClientConfig;
public class Http2ChildChannelInitializer extends ChannelInitializer<Channel> { public class Http2ChildChannelInitializer extends ChannelInitializer<Channel> {
private final NettyHttpClientConfig clientConfig; private final NettyHttpClientConfig nettyHttpClientConfig;
private final Interaction interaction; private final Interaction interaction;
protected final Channel parentChannel; protected final Channel parentChannel;
public Http2ChildChannelInitializer(NettyHttpClientConfig clientConfig, Interaction interaction, Channel parentChannel) { public Http2ChildChannelInitializer(NettyHttpClientConfig nettyHttpClientConfig, Interaction interaction, Channel parentChannel) {
this.clientConfig = clientConfig; this.nettyHttpClientConfig = nettyHttpClientConfig;
this.interaction = interaction; this.interaction = interaction;
this.parentChannel = parentChannel; this.parentChannel = parentChannel;
} }
@ -30,8 +31,13 @@ public class Http2ChildChannelInitializer extends ChannelInitializer<Channel> {
new Http2StreamFrameToHttpObjectCodec(false)); new Http2StreamFrameToHttpObjectCodec(false));
p.addLast("child-client-decompressor", p.addLast("child-client-decompressor",
new HttpContentDecompressor()); new HttpContentDecompressor());
p.addLast("child-client-chunk-aggregator", if (nettyHttpClientConfig.isChunkWriteEnabled()) {
new HttpObjectAggregator(clientConfig.getMaxContentLength())); 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", p.addLast("child-client-response-handler",
new Http2Handler(interaction)); new Http2Handler(interaction));
} }

View file

@ -19,5 +19,7 @@ public interface HttpRequestBuilder {
HttpRequestBuilder setBody(ByteBuffer byteBuffer); HttpRequestBuilder setBody(ByteBuffer byteBuffer);
HttpRequestBuilder addPart(Part part);
HttpRequest build() throws UnmappableCharacterException, MalformedInputException; 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.BaseHttpDomain;
import org.xbib.net.http.server.domain.BaseHttpSecurityDomain; import org.xbib.net.http.server.domain.BaseHttpSecurityDomain;
import org.xbib.net.http.server.domain.HttpSecurityDomain; 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.service.HttpService;
import org.xbib.net.http.server.auth.BasicAuthenticationHandler; import org.xbib.net.http.server.auth.BasicAuthenticationHandler;
import org.xbib.net.http.server.auth.FormAuthenticationHandler; import org.xbib.net.http.server.auth.FormAuthenticationHandler;
@ -120,6 +123,7 @@ public final class Bootstrap {
BasicAuthenticationHandler basicAuthenticationHandler = BasicAuthenticationHandler basicAuthenticationHandler =
new BasicAuthenticationHandler(ldapRealm); new BasicAuthenticationHandler(ldapRealm);
FormAuthenticationHandler formAuthenticationHandler = FormAuthenticationHandler formAuthenticationHandler =
new FormAuthenticationHandler("j_username", "j_password", "j_remember", new FormAuthenticationHandler("j_username", "j_password", "j_remember",
"demo/auth/form/index.gtpl", ldapRealm); "demo/auth/form/index.gtpl", ldapRealm);
@ -148,48 +152,57 @@ public final class Bootstrap {
}) })
.build(); .build();
try (NettyHttpServer server = NettyHttpServer.builder() HttpRouter httpRouter = BaseHttpRouter.builder()
.setHttpServerConfig(serverConfig) .setHandler(400, new GroovyHttpStatusHandler(HttpResponseStatus.BAD_REQUEST, "Bad request", "400.gtpl"))
.setApplication(WebApplication.builder() .setHandler(401, new GroovyHttpStatusHandler(HttpResponseStatus.UNAUTHORIZED, "Unauthorized", "401.gtpl"))
.setSettings(settings) .setHandler(403, new GroovyHttpStatusHandler(HttpResponseStatus.FORBIDDEN, "Forbidden", "403.gtpl"))
.setSecret("1088e6b7ad58d64d09961e1357bf95544447051c6ad1332cd626e3a33bb5786b") .setHandler(404, new GroovyHttpStatusHandler(HttpResponseStatus.NOT_FOUND, "Not found", "404.gtpl"))
.setRouter(BaseHttpRouter.builder() .setHandler(500, new GroovyInternalServerErrorHandler("500.gtpl"))
.setHandler(400, new GroovyHttpStatusHandler(HttpResponseStatus.BAD_REQUEST, "Bad request", "400.gtpl")) .addDomain(BaseHttpDomain.builder()
.setHandler(401, new GroovyHttpStatusHandler(HttpResponseStatus.UNAUTHORIZED, "Unauthorized", "401.gtpl")) .setHttpAddress(httpsAddress)
.setHandler(403, new GroovyHttpStatusHandler(HttpResponseStatus.FORBIDDEN, "Forbidden", "403.gtpl")) .addService(BaseHttpService.builder()
.setHandler(404, new GroovyHttpStatusHandler(HttpResponseStatus.NOT_FOUND, "Not found", "404.gtpl")) .setPath("/favicon.ico")
.setHandler(500, new GroovyInternalServerErrorHandler("500.gtpl")) .setHandler(ctx -> {
.addDomain(BaseHttpDomain.builder() ctx.response()
.setHttpAddress(httpsAddress) .setResponseStatus(HttpResponseStatus.OK)
.addService(BaseHttpService.builder() .setHeader(HttpHeaderNames.CONTENT_TYPE, "image/x-icon")
.setPath("/favicon.ico") .write(NettyDataBufferFactory.getInstance().wrap(Hex.fromHex(hexFavIcon)))
.setHandler(ctx -> { .build();
ctx.response() ctx.done();
.setResponseStatus(HttpResponseStatus.OK) })
.setHeader(HttpHeaderNames.CONTENT_TYPE, "image/x-icon") .build())
.write(NettyDataBufferFactory.getInstance().wrap(Hex.fromHex(hexFavIcon))) .addService(BaseHttpService.builder()
.build(); .setPath("/webjars/**")
ctx.done(); .setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/"))
}) .build())
.build()) .addService(httpService)
.addService(BaseHttpService.builder() .addService(GroovyTemplateService.builder()
.setPath("/webjars/**") .setTemplateName("index.gtpl")
.setHandler(new ClassLoaderResourceHandler(Bootstrap.class.getClassLoader(), "META-INF/resources/")) .setSecurityDomain(securityDomain)
.build()) .setPath("glob:**")
.addService(httpService) .setHandler(new GroovyTemplateResourceHandler())
.addService(GroovyTemplateService.builder()
.setTemplateName("index.gtpl")
.setSecurityDomain(securityDomain)
.setPath("glob:**")
.setHandler(new GroovyTemplateResourceHandler())
.build())
.build())
.build()) .build())
.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()) { .build()) {
server.bind(); server.bind();
server.loop(); server.loop();
} }
return 0; 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.application.BaseApplicationBuilder;
import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.settings.Settings; import org.xbib.settings.Settings;
public class WebApplicationBuilder extends BaseApplicationBuilder { public class WebApplicationBuilder extends BaseApplicationBuilder {
@ -12,14 +14,8 @@ public class WebApplicationBuilder extends BaseApplicationBuilder {
protected WebApplicationBuilder() { protected WebApplicationBuilder() {
super(); super();
this.profile = System.getProperty("application.profile");
this.name = System.getProperty("application.name"); this.name = System.getProperty("application.name");
} this.profile = System.getProperty("application.profile");
@Override
public WebApplicationBuilder setSettings(Settings settings) {
super.setSettings(settings);
return this;
} }
public WebApplicationBuilder setProfile(String profile) { public WebApplicationBuilder setProfile(String profile) {
@ -32,10 +28,32 @@ public class WebApplicationBuilder extends BaseApplicationBuilder {
return this; 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 @Override
public WebApplication build() { public WebApplication build() {
WebApplication webApplication = new WebApplication(this); return new WebApplication(this);
setupApplication(webApplication);
return webApplication;
} }
} }

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

View file

@ -89,7 +89,7 @@ public class Https1Handler extends ChannelDuplexHandler {
serverRequestBuilder.setSNIHost(serverNameIndicationHandler.hostname()); serverRequestBuilder.setSNIHost(serverNameIndicationHandler.hostname());
serverRequestBuilder.setSSLSession(serverNameIndicationHandler.getSslHandler().engine().getSession()); serverRequestBuilder.setSSLSession(serverNameIndicationHandler.getSslHandler().engine().getSession());
} }
nettyHttpServer.getApplication().dispatch(serverRequestBuilder, serverResponseBuilder); nettyHttpServer.dispatch(serverRequestBuilder, serverResponseBuilder);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e); logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e);
DefaultFullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(io.netty.handler.codec.http.HttpVersion.valueOf(httpAddress.getVersion().text()), 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.Http2ServerUpgradeCodec;
import io.netty.handler.codec.http2.Http2Settings; import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LogLevel;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; 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.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig; import org.xbib.net.http.server.netty.NettyHttpServerConfig;
import org.xbib.net.http.server.netty.TrafficLoggingHandler; 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.NettyHttpsServerConfig;
import org.xbib.net.http.server.netty.secure.ServerNameIndicationHandler; 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 NettyHttpsServerConfig nettyHttpsServerConfig = (NettyHttpsServerConfig) nettyHttpServer.getNettyHttpServerConfig();
final ServerNameIndicationHandler serverNameIndicationHandler = final ServerNameIndicationHandler serverNameIndicationHandler =
new ServerNameIndicationHandler(nettyHttpsServerConfig, httpAddress, new ServerNameIndicationHandler(nettyHttpsServerConfig, httpAddress,
nettyHttpsServerConfig.getDomainNameMapping(nettyHttpServer.getApplication().getDomains())); nettyHttpsServerConfig.getDomainNameMapping(nettyHttpServer.getDomains()));
channel.attr(NettyHttpsServerConfig.ATTRIBUTE_KEY_SNI_HANDLER).set(serverNameIndicationHandler); channel.attr(NettyHttpsServerConfig.ATTRIBUTE_KEY_SNI_HANDLER).set(serverNameIndicationHandler);
ChannelPipeline pipeline = channel.pipeline(); ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("server-sni", serverNameIndicationHandler); 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-logger", new TrafficLoggingHandler(LogLevel.DEBUG));
} }
pipeline.addLast("server-upgrade", createUpgradeHandler(nettyHttpServer, httpAddress, serverNameIndicationHandler)); pipeline.addLast("server-upgrade", createUpgradeHandler(nettyHttpServer, httpAddress, serverNameIndicationHandler));
// handler for HTTP1 if (nettyHttpsServerConfig.isObjectAggregationEnabled()) {
pipeline.addLast("server-object-aggregator", new HttpObjectAggregator(nettyHttpsServerConfig.getMaxContentLength())); 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-requests", new Https2Handler(nettyHttpServer));
pipeline.addLast("server-messages", new Https2Messages()); pipeline.addLast("server-messages", new Https2Messages());
pipeline.addLast("server-idle-timeout", new IdleTimeoutHandler(nettyHttpsServerConfig.getTimeoutMillis())); pipeline.addLast("server-idle-timeout", new IdleTimeoutHandler(nettyHttpsServerConfig.getTimeoutMillis()));
@ -77,6 +87,7 @@ public class Https2ChannelInitializer implements HttpChannelInitializer {
NettyHttpServerConfig nettyHttpServerConfig = nettyHttpServer.getNettyHttpServerConfig(); NettyHttpServerConfig nettyHttpServerConfig = nettyHttpServer.getNettyHttpServerConfig();
Https2ChildChannelInitializer childHandler = Https2ChildChannelInitializer childHandler =
new Https2ChildChannelInitializer(nettyHttpServer, httpAddress, serverNameIndicationHandler); new Https2ChildChannelInitializer(nettyHttpServer, httpAddress, serverNameIndicationHandler);
// TODO replace Http2MultiplexCodecBuilder
Http2MultiplexCodecBuilder multiplexCodecBuilder = Http2MultiplexCodecBuilder.forServer(childHandler) Http2MultiplexCodecBuilder multiplexCodecBuilder = Http2MultiplexCodecBuilder.forServer(childHandler)
.initialSettings(Http2Settings.defaultSettings()); .initialSettings(Http2Settings.defaultSettings());
if (nettyHttpServerConfig.isDebug()) { if (nettyHttpServerConfig.isDebug()) {
@ -94,6 +105,10 @@ public class Https2ChannelInitializer implements HttpChannelInitializer {
return new CleartextHttp2ServerUpgradeHandler(serverCodec, upgradeHandler, multiplexCodec); return new CleartextHttp2ServerUpgradeHandler(serverCodec, upgradeHandler, multiplexCodec);
} }
/**
* A new upgrade handler.
* Sadly, this does not work.
*/
protected CleartextHttp2ServerUpgradeHandler createNewUpgradeHandler(NettyHttpServer nettyHttpServer, protected CleartextHttp2ServerUpgradeHandler createNewUpgradeHandler(NettyHttpServer nettyHttpServer,
HttpAddress httpAddress, HttpAddress httpAddress,
ServerNameIndicationHandler serverNameIndicationHandler) { ServerNameIndicationHandler serverNameIndicationHandler) {

View file

@ -55,7 +55,7 @@ public class Https2Handler extends ChannelDuplexHandler {
.setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress()) .setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress())
.setStreamId(streamId); .setStreamId(streamId);
if ("PRI".equals(fullHttpRequest.method().name())) { 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; return;
} }
httpsResponseBuilder.shouldClose("close".equalsIgnoreCase(fullHttpRequest.headers().get(HttpHeaderNames.CONNECTION))); httpsResponseBuilder.shouldClose("close".equalsIgnoreCase(fullHttpRequest.headers().get(HttpHeaderNames.CONNECTION)));
@ -65,7 +65,7 @@ public class Https2Handler extends ChannelDuplexHandler {
httpsRequestBuilder.setSNIHost(serverNameIndicationHandler.hostname()); httpsRequestBuilder.setSNIHost(serverNameIndicationHandler.hostname());
httpsRequestBuilder.setSSLSession(serverNameIndicationHandler.getSslHandler().engine().getSession()); httpsRequestBuilder.setSSLSession(serverNameIndicationHandler.getSslHandler().engine().getSession());
} }
nettyHttpServer.getApplication().dispatch(httpsRequestBuilder, httpsResponseBuilder); nettyHttpServer.dispatch(httpsRequestBuilder, httpsResponseBuilder);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e); logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e);
DefaultFullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(io.netty.handler.codec.http.HttpVersion.valueOf(httpAddress.getVersion().text()), 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.client.netty.secure.NettyHttpsClientConfig;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer; 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.HttpsAddress;
@ -46,36 +49,43 @@ public class NettyHttps2ServerMultiRequestLoadTest {
NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig(); NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig();
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion()); serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig) .setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); 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.client.netty.secure.NettyHttpsClientConfig;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer; 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.HttpsAddress;
@ -47,36 +50,43 @@ public class NettyHttps2ServerTest {
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion()); serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK); serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
serverConfig.setDebug(true); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig) .setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); 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.client.netty.secure.NettyHttpsClientConfig;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer; 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.HttpsAddress;
@ -48,39 +51,46 @@ public class NettyHttpsServerMultiRequestLoadTest {
NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig(); NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig();
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion()); serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig) .setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); server.bind();
@ -133,38 +143,45 @@ public class NettyHttpsServerMultiRequestLoadTest {
NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig(); NettyHttpsServerConfig serverConfig = new NettyHttpsServerConfig();
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion()); serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig) .setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); 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.client.netty.secure.NettyHttpsClientConfig;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.NettyHttpServer;
import org.xbib.net.http.server.netty.secure.HttpsAddress; 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.HttpsRequest;
import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig; import org.xbib.net.http.server.netty.secure.NettyHttpsServerConfig;
import org.xbib.net.http.server.route.BaseHttpRouter; import org.xbib.net.http.server.route.BaseHttpRouter;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.http.server.service.BaseHttpService; import org.xbib.net.http.server.service.BaseHttpService;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -52,38 +55,45 @@ public class NettyHttpsServerTest {
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion()); serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK); serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
serverConfig.setDebug(true); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig) .setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); server.bind();
@ -127,38 +137,45 @@ public class NettyHttpsServerTest {
Bootstrap.class.getPackage().getImplementationVersion()); Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK); serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
serverConfig.setDebug(true); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig) .setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); server.bind();
@ -201,37 +218,44 @@ public class NettyHttpsServerTest {
serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion()); serverConfig.setServerName("NettySecureHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK); serverConfig.setNetworkClass(NetworkClass.LOOPBACK);
serverConfig.setDebug(true); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig) .setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); server.bind();

View file

@ -1,7 +1,5 @@
package org.xbib.net.http.server.netty; 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.Request;
import org.xbib.net.http.server.BaseHttpRequest; import org.xbib.net.http.server.BaseHttpRequest;
@ -9,8 +7,8 @@ import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Objects; import java.util.Objects;
import org.xbib.net.util.ByteBufferInputStream;
public class HttpRequest extends BaseHttpRequest { public class HttpRequest extends BaseHttpRequest {
@ -27,7 +25,8 @@ public class HttpRequest extends BaseHttpRequest {
@Override @Override
public InputStream getInputStream() { 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 @Override
@ -48,13 +47,10 @@ public class HttpRequest extends BaseHttpRequest {
@Override @Override
public String toString() { public String toString() {
return "HttpRequest[request=" + builder.fullHttpRequest + return "HttpRequest[method=" + builder.getMethod() +
",version=" + builder.getVersion() +
",parameter=" + builder.getParameter() + ",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.buffer.ByteBufUtil;
import io.netty.handler.codec.http.FullHttpRequest; 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.Parameter;
import org.xbib.net.URL; import org.xbib.net.URL;
import org.xbib.net.http.HttpAddress; import org.xbib.net.http.HttpAddress;
@ -13,44 +17,47 @@ import java.net.InetSocketAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import org.xbib.net.http.server.Part;
public class HttpRequestBuilder extends BaseHttpRequestBuilder { public class HttpRequestBuilder extends BaseHttpRequestBuilder {
protected FullHttpRequest fullHttpRequest; private static final Logger logger = Logger.getLogger(HttpRequestBuilder.class.getName());
protected ByteBuffer byteBuffer; protected ByteBuffer byteBuffer;
protected HttpRequestBuilder() { 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) { public HttpRequestBuilder setFullHttpRequest(FullHttpRequest fullHttpRequest) {
if (fullHttpRequest != null) { setVersion(HttpVersion.valueOf(fullHttpRequest.protocolVersion().text()));
// retain request, so we can read the body later without refCnt=0 error setMethod(HttpMethod.valueOf(fullHttpRequest.method().name()));
this.fullHttpRequest = fullHttpRequest.retain(); setRequestURI(fullHttpRequest.uri());
setVersion(HttpVersion.valueOf(fullHttpRequest.protocolVersion().text())); fullHttpRequest.headers().entries().forEach(e -> addHeader(e.getKey(), e.getValue()));
setMethod(HttpMethod.valueOf(fullHttpRequest.method().name())); // read all bytes from request into a JDK ByteBuffer. This might be expensive.
setRequestURI(fullHttpRequest.uri()); if (fullHttpRequest.content() != null) {
fullHttpRequest.headers().entries().forEach(e -> addHeader(e.getKey(), e.getValue())); byteBuffer = ByteBuffer.wrap(ByteBufUtil.getBytes(fullHttpRequest.content()));
} }
return this; return this;
} }
@Override @Override
public ByteBuffer getBody() { 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; return byteBuffer;
} }
@Override @Override
public CharBuffer getBodyAsChars(Charset charset) { public CharBuffer getBodyAsChars(Charset charset) {
return fullHttpRequest.content() != null ? return byteBuffer != null ? charset.decode(byteBuffer) : null;
CharBuffer.wrap(fullHttpRequest.content().toString(charset)) : null;
} }
@Override @Override
@ -101,6 +108,17 @@ public class HttpRequestBuilder extends BaseHttpRequestBuilder {
return this; 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() { protected Parameter getParameter() {
return super.parameter; return super.parameter;
} }
@ -112,8 +130,6 @@ public class HttpRequestBuilder extends BaseHttpRequestBuilder {
@Override @Override
public void release() { 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.LogLevel;
import io.netty.handler.logging.LoggingHandler; import io.netty.handler.logging.LoggingHandler;
import io.netty.util.AttributeKey; 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.io.IOException;
import java.net.BindException; import java.net.BindException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -28,6 +21,18 @@ import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; 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. * Netty HTTP server.
@ -161,8 +166,50 @@ public class NettyHttpServer implements HttpServer {
} }
@Override @Override
public Application getApplication() { public void dispatch(HttpRequestBuilder requestBuilder,
return builder.application; 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 @Override

View file

@ -56,20 +56,37 @@ public class NettyHttpServerConfig extends HttpServerConfig {
*/ */
private int pipeliningCapacity = 1024; private int pipeliningCapacity = 1024;
/**
* HTTP object aggregation is enabled by default.
*/
private boolean isObjectAggregationEnabled = true;
/** /**
* This is Netty's default. * This is Netty's default.
*/ */
private int maxCompositeBufferComponents = 1024; 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() { public NettyHttpServerConfig() {
} }
@ -154,6 +171,15 @@ public class NettyHttpServerConfig extends HttpServerConfig {
return pipeliningCapacity; return pipeliningCapacity;
} }
public NettyHttpServerConfig setObjectAggregationEnabled(boolean isObjectAgregationEnabled) {
this.isObjectAggregationEnabled = isObjectAgregationEnabled;
return this;
}
public boolean isObjectAggregationEnabled() {
return isObjectAggregationEnabled;
}
public NettyHttpServerConfig setMaxCompositeBufferComponents(int maxCompositeBufferComponents) { public NettyHttpServerConfig setMaxCompositeBufferComponents(int maxCompositeBufferComponents) {
this.maxCompositeBufferComponents = maxCompositeBufferComponents; this.maxCompositeBufferComponents = maxCompositeBufferComponents;
return this; return this;
@ -163,22 +189,48 @@ public class NettyHttpServerConfig extends HttpServerConfig {
return maxCompositeBufferComponents; return maxCompositeBufferComponents;
} }
public NettyHttpServerConfig setCompression(boolean enabled) { public NettyHttpServerConfig setCompression(boolean isCompressionEnabled) {
this.enableCompression = enabled; this.isCompressionEnabled = isCompressionEnabled;
return this; return this;
} }
public boolean isCompressionEnabled() { public boolean isCompressionEnabled() {
return enableCompression; return isCompressionEnabled;
} }
public NettyHttpServerConfig setDecompression(boolean enabled) { public NettyHttpServerConfig setDecompression(boolean isDecompressionEnabled) {
this.enableDecompression = enabled; this.isDecompressionEnabled = isDecompressionEnabled;
return this; return this;
} }
public boolean isDecompressionEnabled() { 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()) { if (nettyHttpServerConfig.isDebug()) {
pipeline.addLast("server-logging", new TrafficLoggingHandler(LogLevel.DEBUG)); 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(), pipeline.addLast("server-codec", new HttpServerCodec(nettyHttpServerConfig.getMaxInitialLineLength(),
nettyHttpServerConfig.getMaxHeadersSize(), nettyHttpServerConfig.getMaxChunkSize())); nettyHttpServerConfig.getMaxHeadersSize(), nettyHttpServerConfig.getMaxChunkSize()));
if (nettyHttpServerConfig.isCompressionEnabled()) { if (nettyHttpServerConfig.isCompressionEnabled()) {
@ -50,9 +50,18 @@ public class Http1ChannelInitializer implements HttpChannelInitializer {
if (nettyHttpServerConfig.isDecompressionEnabled()) { if (nettyHttpServerConfig.isDecompressionEnabled()) {
pipeline.addLast("server-decompressor", new HttpContentDecompressor()); pipeline.addLast("server-decompressor", new HttpContentDecompressor());
} }
HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength()); if (nettyHttpServerConfig.isObjectAggregationEnabled()) {
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpServerConfig.getMaxCompositeBufferComponents()); HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength());
pipeline.addLast("server-aggregator", httpObjectAggregator); 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()) { if (nettyHttpServerConfig.isPipeliningEnabled()) {
pipeline.addLast("server-pipelining", new HttpPipeliningHandler(nettyHttpServerConfig.getPipeliningCapacity())); pipeline.addLast("server-pipelining", new HttpPipeliningHandler(nettyHttpServerConfig.getPipeliningCapacity()));
} }

View file

@ -34,11 +34,9 @@ class Http1Handler extends ChannelDuplexHandler {
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpPipelinedRequest) { if (msg instanceof HttpPipelinedRequest httpPipelinedRequest) {
HttpPipelinedRequest httpPipelinedRequest = (HttpPipelinedRequest) msg;
try { try {
if (httpPipelinedRequest.getRequest() instanceof FullHttpRequest) { if (httpPipelinedRequest.getRequest() instanceof FullHttpRequest fullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) httpPipelinedRequest.getRequest();
requestReceived(ctx, fullHttpRequest, httpPipelinedRequest.getSequenceId()); requestReceived(ctx, fullHttpRequest, httpPipelinedRequest.getSequenceId());
} }
} finally { } finally {
@ -73,7 +71,9 @@ class Http1Handler extends ChannelDuplexHandler {
ctx.close(); 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(); HttpAddress httpAddress = ctx.channel().attr(NettyHttpServerConfig.ATTRIBUTE_KEY_HTTP_ADDRESS).get();
try { try {
HttpResponseBuilder serverResponseBuilder = HttpResponse.builder() HttpResponseBuilder serverResponseBuilder = HttpResponse.builder()
@ -82,7 +82,7 @@ class Http1Handler extends ChannelDuplexHandler {
serverResponseBuilder.setSequenceId(sequenceId); serverResponseBuilder.setSequenceId(sequenceId);
} }
serverResponseBuilder.shouldClose("close".equalsIgnoreCase(fullHttpRequest.headers().get(HttpHeaderNames.CONNECTION))); 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() HttpRequestBuilder serverRequestBuilder = HttpRequest.builder()
.setFullHttpRequest(fullHttpRequest) .setFullHttpRequest(fullHttpRequest)
.setBaseURL(httpAddress, .setBaseURL(httpAddress,
@ -91,7 +91,7 @@ class Http1Handler extends ChannelDuplexHandler {
.setLocalAddress((InetSocketAddress) ctx.channel().localAddress()) .setLocalAddress((InetSocketAddress) ctx.channel().localAddress())
.setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress()) .setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress())
.setSequenceId(sequenceId); .setSequenceId(sequenceId);
nettyHttpServer.getApplication().dispatch(serverRequestBuilder, serverResponseBuilder); nettyHttpServer.dispatch(serverRequestBuilder, serverResponseBuilder);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e); logger.log(Level.SEVERE, "bad request: " + e.getMessage(), e);
DefaultFullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, 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.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.xbib.net.http.server.netty.http1.HttpFileUploadHandler;
/** /**
* Insecure HTTP/2 server channel initializer. * Insecure HTTP/2 server channel initializer.
@ -63,9 +64,17 @@ public class Http2ChannelInitializer implements HttpChannelInitializer {
if (nettyHttpServerConfig.isDecompressionEnabled()) { if (nettyHttpServerConfig.isDecompressionEnabled()) {
pipeline.addLast("server-decompressor", new HttpContentDecompressor()); pipeline.addLast("server-decompressor", new HttpContentDecompressor());
} }
pipeline.addLast("server-object-aggregator", if (nettyHttpServerConfig.isObjectAggregationEnabled()) {
new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength())); pipeline.addLast("server-object-aggregator",
pipeline.addLast("server-chunked-write", new ChunkedWriteHandler()); 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-request", new Http2Handler(nettyHttpServer));
pipeline.addLast("server-messages", new Http2Messages()); pipeline.addLast("server-messages", new Http2Messages());
pipeline.addLast("server-idle-timeout", new IdleTimeoutHandler(nettyHttpServerConfig.getTimeoutMillis())); 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()) .setLocalAddress((InetSocketAddress) ctx.channel().localAddress())
.setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress()) .setRemoteAddress((InetSocketAddress) ctx.channel().remoteAddress())
.setStreamId(streamId); .setStreamId(streamId);
nettyHttpServer.getApplication().dispatch(serverRequestBuilder, httpResponseBuilder); nettyHttpServer.dispatch(serverRequestBuilder, httpResponseBuilder);
} catch (Exception e) { } catch (Exception e) {
logger.log(Level.SEVERE, "bad request:" + e.getMessage(), e); logger.log(Level.SEVERE, "bad request:" + e.getMessage(), e);
DefaultFullHttpResponse fullHttpResponse = 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.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer; import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig; import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -37,29 +40,36 @@ public class NettyHttp2ServerMultiRequestLoadTest {
NettyHttpServerConfig serverConfig = new NettyHttpServerConfig(); NettyHttpServerConfig serverConfig = new NettyHttpServerConfig();
serverConfig.setServerName("NettyHttp2CleartextServer", Bootstrap.class.getPackage().getImplementationVersion()); serverConfig.setServerName("NettyHttp2CleartextServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig) .setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); 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.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer; import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig; import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -38,27 +41,34 @@ public class NettyHttp2ServerTest {
Bootstrap.class.getPackage().getImplementationVersion()); Bootstrap.class.getPackage().getImplementationVersion());
nettyHttpServerConfig.setNetworkClass(NetworkClass.ANY); nettyHttpServerConfig.setNetworkClass(NetworkClass.ANY);
nettyHttpServerConfig.setDebug(true); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(nettyHttpServerConfig) .setHttpServerConfig(nettyHttpServerConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); 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.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer; import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig; import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -42,30 +45,37 @@ public class NettyHttpServerFailureTest {
nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL); nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL);
nettyHttpServerConfig.setDebug(true); nettyHttpServerConfig.setDebug(true);
nettyHttpServerConfig.setPipelining(false); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(nettyHttpServerConfig) .setHttpServerConfig(nettyHttpServerConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setHome(Paths.get(".")) .setHome(Paths.get("."))
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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()) .build())
.build()) { .build()) {
server.bind(); 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.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer; import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig; import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -37,29 +40,36 @@ public class NettyHttpServerMultiRequestLoadTest {
NettyHttpServerConfig serverConfig = new NettyHttpServerConfig(); NettyHttpServerConfig serverConfig = new NettyHttpServerConfig();
serverConfig.setServerName("NettyHttpServer", Bootstrap.class.getPackage().getImplementationVersion()); serverConfig.setServerName("NettyHttpServer", Bootstrap.class.getPackage().getImplementationVersion());
serverConfig.setNetworkClass(NetworkClass.LOOPBACK); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(serverConfig) .setHttpServerConfig(serverConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build()) { .build()) {
server.bind(); 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.client.netty.NettyHttpClientConfig;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.BaseHttpRouter;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.http.server.service.BaseHttpService; import org.xbib.net.http.server.service.BaseHttpService;
import org.xbib.net.http.server.netty.NettyHttpServer; import org.xbib.net.http.server.netty.NettyHttpServer;
import org.xbib.net.http.server.netty.NettyHttpServerConfig; import org.xbib.net.http.server.netty.NettyHttpServerConfig;
@ -33,34 +36,42 @@ public class NettyHttpServerTest {
URL url = URL.from("http://localhost:8008/domain/"); URL url = URL.from("http://localhost:8008/domain/");
HttpAddress httpAddress1 = HttpAddress.http1(url); HttpAddress httpAddress1 = HttpAddress.http1(url);
NettyHttpServerConfig nettyHttpServerConfig = new NettyHttpServerConfig(); NettyHttpServerConfig nettyHttpServerConfig = new NettyHttpServerConfig();
nettyHttpServerConfig.setServerName("NettyHttpServer", Bootstrap.class.getPackage().getImplementationVersion()); nettyHttpServerConfig.setServerName("NettyHttpServer",
Bootstrap.class.getPackage().getImplementationVersion());
nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL); nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL);
nettyHttpServerConfig.setDebug(true); nettyHttpServerConfig.setDebug(true);
nettyHttpServerConfig.setPipelining(false); 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() try (NettyHttpServer server = NettyHttpServer.builder()
.setHttpServerConfig(nettyHttpServerConfig) .setHttpServerConfig(nettyHttpServerConfig)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.setHttpAddress(httpAddress1) .build())
.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())
.build()) { .build()) {
server.bind(); server.bind();
NettyHttpClientConfig config = new NettyHttpClientConfig() NettyHttpClientConfig config = new NettyHttpClientConfig()

View file

@ -1,11 +1,13 @@
package org.xbib.net.http.server.nio; package org.xbib.net.http.server.nio;
import java.util.Collection;
import org.xbib.net.http.HttpAddress; import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpHeaderNames; import org.xbib.net.http.HttpHeaderNames;
import org.xbib.net.http.HttpHeaders; import org.xbib.net.http.HttpHeaders;
import org.xbib.net.http.HttpMethod; import org.xbib.net.http.HttpMethod;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.HttpVersion; 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 org.xbib.net.http.server.HttpServer;
import java.io.IOException; import java.io.IOException;
@ -30,6 +32,9 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; 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 { public class NioHttpServer implements HttpServer {
@ -101,7 +106,7 @@ public class NioHttpServer implements HttpServer {
httpAddress, httpAddress,
(InetSocketAddress) socketChannel.getLocalAddress(), (InetSocketAddress) socketChannel.getLocalAddress(),
(InetSocketAddress) socketChannel.getRemoteAddress()); (InetSocketAddress) socketChannel.getRemoteAddress());
builder.application.dispatch(requestBuilder, responseBuilder); dispatch(requestBuilder, responseBuilder);
socketChannel.close(); socketChannel.close();
} catch (IOException e) { } catch (IOException e) {
logger.log(Level.SEVERE, e.getMessage(), e); logger.log(Level.SEVERE, e.getMessage(), e);
@ -128,8 +133,50 @@ public class NioHttpServer implements HttpServer {
} }
@Override @Override
public Application getApplication() { public void dispatch(org.xbib.net.http.server.HttpRequestBuilder requestBuilder,
return builder.application; 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 @Override

View file

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

View file

@ -1,11 +1,13 @@
package org.xbib.net.http.server.simple; package org.xbib.net.http.server.simple;
import java.util.Collection;
import org.xbib.net.NetworkClass; import org.xbib.net.NetworkClass;
import org.xbib.net.NetworkUtils; import org.xbib.net.NetworkUtils;
import org.xbib.net.SocketConfig; import org.xbib.net.SocketConfig;
import org.xbib.net.http.HttpAddress; import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpHeaderNames; 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.HttpHeaders;
import org.xbib.net.http.HttpMethod; import org.xbib.net.http.HttpMethod;
import org.xbib.net.http.HttpVersion; import org.xbib.net.http.HttpVersion;
@ -31,6 +33,9 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; 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 { public class SimpleHttpServer implements HttpServer {
@ -108,7 +113,7 @@ public class SimpleHttpServer implements HttpServer {
httpAddress, httpAddress,
(InetSocketAddress) socket.getLocalSocketAddress(), (InetSocketAddress) socket.getLocalSocketAddress(),
(InetSocketAddress) socket.getRemoteSocketAddress()); (InetSocketAddress) socket.getRemoteSocketAddress());
builder.application.dispatch(httpRequestBuilder, httpResponseBuilder); dispatch(httpRequestBuilder, httpResponseBuilder);
} catch (Throwable t) { } catch (Throwable t) {
logger.log(Level.SEVERE, t.getMessage(), t); logger.log(Level.SEVERE, t.getMessage(), t);
} finally { } 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 @Override
public void loop() throws IOException { public void loop() throws IOException {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
@ -139,11 +191,6 @@ public class SimpleHttpServer implements HttpServer {
} }
} }
@Override
public Application getApplication() {
return builder.application;
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
logger.log(Level.INFO, "closing"); logger.log(Level.INFO, "closing");
@ -248,5 +295,4 @@ public class SimpleHttpServer implements HttpServer {
} }
return result.toString(); 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.HttpResponseStatus;
import org.xbib.net.http.server.application.BaseApplication; import org.xbib.net.http.server.application.BaseApplication;
import org.xbib.net.http.server.domain.BaseHttpDomain; 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.BaseHttpRouter;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.http.server.service.BaseHttpService; import org.xbib.net.http.server.service.BaseHttpService;
import org.xbib.net.http.server.HttpServerConfig; import org.xbib.net.http.server.HttpServerConfig;
import org.xbib.net.http.server.resource.FileResourceHandler; import org.xbib.net.http.server.resource.FileResourceHandler;
@ -24,50 +27,56 @@ public class SimpleHttpServerTest {
public void simpleServerTest() throws Exception { public void simpleServerTest() throws Exception {
HttpAddress httpAddress1 = HttpAddress.http1("localhost", 8008); HttpAddress httpAddress1 = HttpAddress.http1("localhost", 8008);
HttpAddress httpAddress2 = 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() SimpleHttpServer server = SimpleHttpServer.builder()
.setHttpServerConfig(new HttpServerConfig() .setHttpServerConfig(new HttpServerConfig()
.setServerName("SimpleHttpServer", SimpleHttpServer.class.getPackage().getImplementationVendor()) .setServerName("SimpleHttpServer", SimpleHttpServer.class.getPackage().getImplementationVendor())
.setNetworkClass(NetworkClass.SITE) .setNetworkClass(NetworkClass.SITE))
)
.setApplication(BaseApplication.builder() .setApplication(BaseApplication.builder()
.setRouter(BaseHttpRouter.builder() .setExecutor(executor)
.addDomain(BaseHttpDomain.builder() .setRouter(router)
.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())
.build()) .build())
.build(); .build();
server.bind(); 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.file;
exports org.xbib.net.http.server.session.memory; exports org.xbib.net.http.server.session.memory;
exports org.xbib.net.http.server.validate; exports org.xbib.net.http.server.validate;
exports org.xbib.net.http.server.executor;
requires org.xbib.net; requires org.xbib.net;
requires org.xbib.net.mime; requires org.xbib.net.mime;
requires org.xbib.net.http; requires org.xbib.net.http;

View file

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

View file

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

View file

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

View file

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

View file

@ -3,7 +3,9 @@ package org.xbib.net.http.server;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.net.BindException; 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 { public interface HttpServer extends Closeable {
@ -11,5 +13,13 @@ public interface HttpServer extends Closeable {
void loop() throws IOException; 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.Locale;
import java.util.Set; import java.util.Set;
import org.xbib.net.http.HttpAddress; 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.HttpRequestBuilder;
import org.xbib.net.http.server.HttpResponseBuilder; import org.xbib.net.http.server.HttpResponseBuilder;
import org.xbib.net.http.server.HttpServerContext; import org.xbib.net.http.server.HttpServerContext;
import org.xbib.net.http.server.domain.HttpDomain; 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.http.server.session.SessionListener;
import org.xbib.net.mime.MimeTypeService; import org.xbib.net.mime.MimeTypeService;
import org.xbib.settings.Settings; import org.xbib.settings.Settings;
@ -35,26 +36,10 @@ public interface Application extends SessionListener, Resolver<Path>, Closeable
Settings getSettings(); Settings getSettings();
void addModule(ApplicationModule applicationModule);
Collection<ApplicationModule> getModules(); 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, HttpServerContext createContext(HttpDomain domain,
HttpRequestBuilder httpRequestBuilder, HttpRequestBuilder httpRequestBuilder,
HttpResponseBuilder httpResponseBuilder); HttpResponseBuilder httpResponseBuilder);
@ -63,5 +48,9 @@ public interface Application extends SessionListener, Resolver<Path>, Closeable
void onClose(HttpServerContext httpServerContext); void onClose(HttpServerContext httpServerContext);
Executor getExecutor();
HttpRouter getRouter();
void close() throws IOException; void close() throws IOException;
} }

View file

@ -3,23 +3,12 @@ package org.xbib.net.http.server.application;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.ThreadPoolExecutor; import org.xbib.net.http.server.executor.Executor;
import java.util.concurrent.TimeUnit;
import org.xbib.net.http.server.route.HttpRouter; import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.mime.MimeTypeService; import org.xbib.net.mime.MimeTypeService;
public interface ApplicationBuilder { 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 setHome(Path home);
ApplicationBuilder setContextPath(String contextPath); ApplicationBuilder setContextPath(String contextPath);
@ -28,8 +17,6 @@ public interface ApplicationBuilder {
ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled); ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled);
ApplicationBuilder setRouter(HttpRouter router);
ApplicationBuilder setLocale(Locale locale); ApplicationBuilder setLocale(Locale locale);
ApplicationBuilder setZoneId(ZoneId zoneId); ApplicationBuilder setZoneId(ZoneId zoneId);
@ -38,7 +25,9 @@ public interface ApplicationBuilder {
ApplicationBuilder setStaticSuffixes(String... suffixes); ApplicationBuilder setStaticSuffixes(String... suffixes);
ApplicationBuilder registerModule(ApplicationModule applicationModule); ApplicationBuilder setExecutor(Executor executor);
ApplicationBuilder setRouter(HttpRouter httpRouter);
Application build(); 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.nio.file.Path;
import java.time.Duration; import java.time.Duration;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.xbib.net.http.HttpAddress; import org.xbib.net.http.HttpAddress;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.cookie.SameSite; import org.xbib.net.http.cookie.SameSite;
import org.xbib.net.http.server.BaseHttpServerContext; import org.xbib.net.http.server.BaseHttpServerContext;
import org.xbib.net.http.server.HttpException; 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.IncomingCookieHandler;
import org.xbib.net.http.server.cookie.OutgoingCookieHandler; import org.xbib.net.http.server.cookie.OutgoingCookieHandler;
import org.xbib.net.http.server.domain.HttpDomain; 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.persist.Codec;
import org.xbib.net.http.server.render.HttpResponseRenderer; import org.xbib.net.http.server.render.HttpResponseRenderer;
import org.xbib.net.http.server.route.HttpRouter; import org.xbib.net.http.server.route.HttpRouter;
@ -60,6 +60,8 @@ public class BaseApplication implements Application {
private HttpHandler outgoingSessionHandler; private HttpHandler outgoingSessionHandler;
protected List<ApplicationModule> applicationModuleList;
protected BaseApplication(BaseApplicationBuilder builder) { protected BaseApplication(BaseApplicationBuilder builder) {
this.builder = builder; this.builder = builder;
this.sessionName = getSettings().get("session.name", "SESS"); this.sessionName = getSettings().get("session.name", "SESS");
@ -67,12 +69,38 @@ public class BaseApplication implements Application {
this.incomingCookieHandler = newIncomingCookieHandler(); this.incomingCookieHandler = newIncomingCookieHandler();
this.outgoingCookieHandler = newOutgoingCookieHandler(); this.outgoingCookieHandler = newOutgoingCookieHandler();
this.httpResponseRenderer = newResponseRenderer(); 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() { public static BaseApplicationBuilder builder() {
return new BaseApplicationBuilder(); return new BaseApplicationBuilder();
} }
@Override
public void addModule(ApplicationModule applicationModule) {
applicationModuleList.add(applicationModule);
}
@Override @Override
public Locale getLocale() { public Locale getLocale() {
return builder.locale; return builder.locale;
@ -88,10 +116,12 @@ public class BaseApplication implements Application {
return builder.mimeTypeService; return builder.mimeTypeService;
} }
@Override
public Path getHome() { public Path getHome() {
return builder.home; return builder.home;
} }
@Override
public String getContextPath() { public String getContextPath() {
return builder.contextPath; return builder.contextPath;
} }
@ -101,10 +131,6 @@ public class BaseApplication implements Application {
return builder.settings; return builder.settings;
} }
public HttpRouter getRouter() {
return builder.router;
}
public String getSecret() { public String getSecret() {
return builder.secret; return builder.secret;
} }
@ -115,60 +141,17 @@ public class BaseApplication implements Application {
@Override @Override
public Collection<ApplicationModule> getModules() { public Collection<ApplicationModule> getModules() {
return builder.applicationModuleList; return applicationModuleList;
} }
@Override @Override
public Collection<HttpDomain> getDomains() { public Collection<HttpDomain> getDomains() {
return getRouter().getDomains(); return builder.httpRouter.getDomains();
} }
@Override @Override
public Set<HttpAddress> getAddresses() { public Set<HttpAddress> getAddresses() {
return getRouter().getDomainsByAddress().keySet(); return builder.httpRouter.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);
} }
@Override @Override
@ -242,13 +225,13 @@ public class BaseApplication implements Application {
@Override @Override
public void onCreated(Session session) { public void onCreated(Session session) {
logger.log(Level.FINER, "session name = " + sessionName + " created = " + session); logger.log(Level.FINER, "session name = " + sessionName + " created = " + session);
builder.applicationModuleList.forEach(module -> module.onOpen(session)); applicationModuleList.forEach(module -> module.onOpen(session));
} }
@Override @Override
public void onDestroy(Session session) { public void onDestroy(Session session) {
logger.log(Level.FINER, "session name = " + sessionName + " destroyed = " + session); logger.log(Level.FINER, "session name = " + sessionName + " destroyed = " + session);
builder.applicationModuleList.forEach(module -> module.onClose(session)); applicationModuleList.forEach(module -> module.onClose(session));
} }
@Override @Override
@ -264,12 +247,12 @@ public class BaseApplication implements Application {
incomingSessionHandler.handle(httpServerContext); incomingSessionHandler.handle(httpServerContext);
} }
// call modules after request/cookie/session setup // call modules after request/cookie/session setup
builder.applicationModuleList.forEach(module -> module.onOpen(httpServerContext)); applicationModuleList.forEach(module -> module.onOpen(httpServerContext));
} catch (HttpException e) { } catch (HttpException e) {
getRouter().routeException(e); builder.httpRouter.routeException(e);
httpServerContext.fail(); httpServerContext.fail();
} catch (Throwable t) { } catch (Throwable t) {
getRouter().routeToErrorHandler(httpServerContext, t); builder.httpRouter.routeToErrorHandler(httpServerContext, t);
httpServerContext.fail(); httpServerContext.fail();
} }
} }
@ -278,7 +261,7 @@ public class BaseApplication implements Application {
public void onClose(HttpServerContext httpServerContext) { public void onClose(HttpServerContext httpServerContext) {
try { try {
// call modules before session/cookie // call modules before session/cookie
builder.applicationModuleList.forEach(module -> module.onClose(httpServerContext)); applicationModuleList.forEach(module -> module.onClose(httpServerContext));
if (builder.sessionsEnabled && outgoingSessionHandler != null) { if (builder.sessionsEnabled && outgoingSessionHandler != null) {
outgoingSessionHandler.handle(httpServerContext); outgoingSessionHandler.handle(httpServerContext);
} }
@ -286,9 +269,9 @@ public class BaseApplication implements Application {
outgoingCookieHandler.handle(httpServerContext); outgoingCookieHandler.handle(httpServerContext);
} }
} catch (HttpException e) { } catch (HttpException e) {
getRouter().routeException(e); builder.httpRouter.routeException(e);
} catch (Throwable t) { } catch (Throwable t) {
getRouter().routeToErrorHandler(httpServerContext, t); builder.httpRouter.routeToErrorHandler(httpServerContext, t);
} finally { } finally {
try { try {
if (httpResponseRenderer != null) { 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 @Override
public Path resolve(String string) { public Path resolve(String string) {
if (string == null) { if (string == null) {
@ -321,18 +314,9 @@ public class BaseApplication implements Application {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
logger.log(Level.INFO, "application closing"); logger.log(Level.INFO, "application closing");
// stop dispatching and stop dispatched requests
builder.executor.shutdown(); builder.executor.shutdown();
try { // stop dispatching and stop dispatched requests
if (!builder.executor.awaitTermination(10, TimeUnit.SECONDS)) { applicationModuleList.forEach(module -> {
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 -> {
logger.log(Level.FINE, "application closing module " + module); logger.log(Level.FINE, "application closing module " + module);
module.onClose(); module.onClose();
}); });

View file

@ -3,30 +3,23 @@ package org.xbib.net.http.server.application;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.Set; 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.ConfigLoader;
import org.xbib.config.ConfigLogger; import org.xbib.config.ConfigLogger;
import org.xbib.config.ConfigParams; import org.xbib.config.ConfigParams;
import org.xbib.config.SystemConfigLogger; 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.http.server.route.HttpRouter;
import org.xbib.net.mime.MimeTypeService; import org.xbib.net.mime.MimeTypeService;
import org.xbib.net.util.NamedThreadFactory;
import org.xbib.settings.Settings; import org.xbib.settings.Settings;
public class BaseApplicationBuilder implements ApplicationBuilder { public class BaseApplicationBuilder implements ApplicationBuilder {
private static final Logger logger = Logger.getLogger(BaseApplicationBuilder.class.getName());
private static final ConfigLogger bootLogger; private static final ConfigLogger bootLogger;
static { static {
@ -36,18 +29,11 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
bootLogger = optionalBootLogger.orElse(new SystemConfigLogger()); 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 ClassLoader classLoader;
protected int blockingThreadCount;
protected int blockingThreadQueueCount;
protected int blockingThreadKeepAliveTime;
protected TimeUnit blockingThreadKeepAliveTimeUnit;
protected ThreadPoolExecutor executor;
protected Path home; protected Path home;
protected String contextPath; protected String contextPath;
@ -56,8 +42,6 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
protected boolean sessionsEnabled; protected boolean sessionsEnabled;
protected HttpRouter router;
protected Locale locale; protected Locale locale;
protected ZoneId zoneId; protected ZoneId zoneId;
@ -72,14 +56,12 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
protected Settings settings; protected Settings settings;
protected List<ApplicationModule> applicationModuleList; protected Executor executor;
protected HttpRouter httpRouter;
protected BaseApplicationBuilder() { protected BaseApplicationBuilder() {
this.classLoader = getClass().getClassLoader(); 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.home = Paths.get(System.getProperties().containsKey("application.home") ? System.getProperty("application.home") : ".");
this.contextPath = "/"; this.contextPath = "/";
this.secret = "secret"; this.secret = "secret";
@ -87,7 +69,26 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
this.locale = Locale.getDefault(); this.locale = Locale.getDefault();
this.zoneId = ZoneId.systemDefault(); this.zoneId = ZoneId.systemDefault();
this.mimeTypeService = new MimeTypeService(); 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) { public BaseApplicationBuilder setSettings(Settings settings) {
@ -95,36 +96,6 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this; 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 @Override
public BaseApplicationBuilder setHome(Path home) { public BaseApplicationBuilder setHome(Path home) {
this.home = home; this.home = home;
@ -149,12 +120,6 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this; return this;
} }
@Override
public ApplicationBuilder setRouter(HttpRouter router) {
this.router = router;
return this;
}
@Override @Override
public ApplicationBuilder setLocale(Locale locale) { public ApplicationBuilder setLocale(Locale locale) {
this.locale = locale; this.locale = locale;
@ -180,73 +145,20 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
} }
@Override @Override
public ApplicationBuilder registerModule(ApplicationModule applicationModule) { public ApplicationBuilder setExecutor(Executor executor) {
applicationModuleList.add(applicationModule); this.executor = executor;
return this;
}
@Override
public ApplicationBuilder setRouter(HttpRouter httpRouter) {
this.httpRouter = httpRouter;
return this; return this;
} }
@Override @Override
public Application build() { public Application build() {
prepareApplication(); Objects.requireNonNull(httpRouter);
Application application = new BaseApplication(this); return new BaseApplication(this);
setupApplication(application);
return application;
} }
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.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
@ -11,15 +11,15 @@ import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; 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, public BaseThreadPoolExecutor(int nThreads,
int maxQueue, int maxQueue,
long keepAliveTime, long keepAliveTime,
TimeUnit timeUnit, TimeUnit timeUnit,
ThreadFactory threadFactory) { ThreadFactory threadFactory) {
super(nThreads, nThreads, keepAliveTime, timeUnit, createBlockingQueue(maxQueue), threadFactory); super(nThreads, nThreads, keepAliveTime, timeUnit, createBlockingQueue(maxQueue), threadFactory);
logger.log(Level.FINE, () -> "threadpool executor up with nThreads = " + nThreads + logger.log(Level.FINE, () -> "threadpool executor up with nThreads = " + nThreads +
" keepAliveTime = " + keepAliveTime + " keepAliveTime = " + keepAliveTime +
@ -34,7 +34,7 @@ public class ApplicationThreadPoolExecutor extends ThreadPoolExecutor {
@Override @Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new ApplicationTask<>(callable); return new Task<>(callable);
} }
@Override @Override
@ -45,10 +45,10 @@ public class ApplicationThreadPoolExecutor extends ThreadPoolExecutor {
logger.log(Level.SEVERE, terminationCause.getMessage(), terminationCause); logger.log(Level.SEVERE, terminationCause.getMessage(), terminationCause);
return; return;
} }
if (runnable instanceof ApplicationTask<?> applicationTask) { if (runnable instanceof Task<?> task) {
ApplicationCallable<?> applicationCallable = (ApplicationCallable<?>) applicationTask.getCallable(); CallableReleasable<?> callableReleasable = (CallableReleasable<?>) task.getCallable();
logger.log(Level.FINEST, () -> "releasing " + applicationCallable); logger.log(Level.FINEST, () -> "releasing " + callableReleasable);
applicationCallable.release(); 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.Callable;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
public class ApplicationTask<T> extends FutureTask<T> { public class Task<T> extends FutureTask<T> {
private final Callable<T> callable; private final Callable<T> callable;
public ApplicationTask(Callable<T> callable) { public Task(Callable<T> callable) {
super(callable); super(callable);
this.callable = callable; this.callable = callable;
} }

View file

@ -37,24 +37,8 @@ public class BaseHttpRouter implements HttpRouter {
private final DomainsByAddress domainsByAddress; private final DomainsByAddress domainsByAddress;
private final HttpRouteResolver<HttpService> httpRouteResolver;
protected BaseHttpRouter(BaseHttpRouterBuilder builder) { protected BaseHttpRouter(BaseHttpRouterBuilder builder) {
this.builder = 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.domains = createDomains(builder.domains);
this.domainsByAddress = createAddresses(builder.domains); this.domainsByAddress = createAddresses(builder.domains);
} }
@ -63,10 +47,6 @@ public class BaseHttpRouter implements HttpRouter {
return new BaseHttpRouterBuilder(); return new BaseHttpRouterBuilder();
} }
public HttpRouteResolver.Builder<HttpService> newHttpRouteResolverBuilder() {
return BaseHttpRouteResolver.builder();
}
@Override @Override
public Collection<HttpDomain> getDomains() { public Collection<HttpDomain> getDomains() {
return builder.domains; return builder.domains;
@ -95,7 +75,7 @@ public class BaseHttpRouter implements HttpRouter {
builder.prefix, builder.prefix,
requestBuilder.getRequestPath(), requestBuilder.getRequestPath(),
true); true);
httpRouteResolver.resolve(httpRoute, httpRouteResolverResults::add); builder.httpRouteResolver.resolve(httpRoute, httpRouteResolverResults::add);
HttpServerContext httpServerContext = application.createContext(httpDomain, requestBuilder, responseBuilder); HttpServerContext httpServerContext = application.createContext(httpDomain, requestBuilder, responseBuilder);
application.onOpen(httpServerContext); application.onOpen(httpServerContext);
try { try {

View file

@ -5,6 +5,8 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; 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.HttpHandler;
import org.xbib.net.http.server.domain.HttpDomain; import org.xbib.net.http.server.domain.HttpDomain;
import org.xbib.net.http.server.handler.BadRequestHandler; 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.NotImplementedHandler;
import org.xbib.net.http.server.handler.UnauthorizedHandler; import org.xbib.net.http.server.handler.UnauthorizedHandler;
import org.xbib.net.http.server.handler.VersionNotSupportedHandler; import org.xbib.net.http.server.handler.VersionNotSupportedHandler;
import org.xbib.net.http.server.service.HttpService;
public class BaseHttpRouterBuilder implements HttpRouterBuilder { public class BaseHttpRouterBuilder implements HttpRouterBuilder {
private static final Logger logger = Logger.getLogger(BaseHttpRouterBuilder.class.getName());
protected String prefix; protected String prefix;
protected final Collection<HttpDomain> domains; protected final Collection<HttpDomain> domains;
protected final Map<Integer, HttpHandler> handlers; protected final Map<Integer, HttpHandler> handlers;
protected HttpRouteResolver<HttpService> httpRouteResolver;
protected BaseHttpRouterBuilder() { protected BaseHttpRouterBuilder() {
prefix = ""; prefix = "";
domains = new ArrayList<>(); domains = new ArrayList<>();
@ -58,11 +65,34 @@ public class BaseHttpRouterBuilder implements HttpRouterBuilder {
return this; return this;
} }
@Override
public BaseHttpRouterBuilder setRouteResolver(HttpRouteResolver<HttpService> httpRouteResolver) {
this.httpRouteResolver = httpRouteResolver;
return this;
}
@Override @Override
public BaseHttpRouter build() { public BaseHttpRouter build() {
if (domains.isEmpty()) { if (domains.isEmpty()) {
throw new IllegalArgumentException("no domain configured, unable to continue"); 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); 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.HttpHandler;
import org.xbib.net.http.server.domain.HttpDomain; import org.xbib.net.http.server.domain.HttpDomain;
import org.xbib.net.http.server.service.HttpService;
public interface HttpRouterBuilder { public interface HttpRouterBuilder {
@ -11,5 +12,7 @@ public interface HttpRouterBuilder {
HttpRouterBuilder addDomain(HttpDomain domain); HttpRouterBuilder addDomain(HttpDomain domain);
HttpRouterBuilder setRouteResolver(HttpRouteResolver<HttpService> httpRouteResolver);
HttpRouter build(); HttpRouter build();
} }

View file

@ -4,7 +4,7 @@ dependencyResolutionManagement {
version('gradle', '8.0.2') version('gradle', '8.0.2')
version('junit', '5.9.2') version('junit', '5.9.2')
version('groovy', '4.0.11') 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('netty-tcnative', '2.0.59.Final')
version('datastructures', '2.0.0') version('datastructures', '2.0.0')
version('config', '5.0.2') version('config', '5.0.2')