refactoring code for easier API, update to Netty 4.1.92, begin of file upload work
This commit is contained in:
parent
43558479e7
commit
3749b9ba3a
67 changed files with 1903 additions and 917 deletions
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
|
||||||
new ChunkedWriteHandler());
|
|
||||||
pipeline.addLast("http-client-codec", new HttpClientCodec(nettyHttpClientConfig.getMaxInitialLineLength(),
|
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());
|
||||||
}
|
}
|
||||||
|
if (nettyHttpClientConfig.isObjectAggregationEnabled()) {
|
||||||
HttpObjectAggregator httpObjectAggregator =
|
HttpObjectAggregator httpObjectAggregator =
|
||||||
new HttpObjectAggregator(nettyHttpClientConfig.getMaxContentLength(), false);
|
new HttpObjectAggregator(nettyHttpClientConfig.getMaxContentLength(), false);
|
||||||
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpClientConfig.getMaxCompositeBufferComponents());
|
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpClientConfig.getMaxCompositeBufferComponents());
|
||||||
pipeline.addLast("http-client-aggregator", httpObjectAggregator);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
HttpDataFactory httpDataFactory = new DefaultHttpDataFactory();
|
||||||
|
HttpPostRequestEncoder httpPostRequestEncoder = null;
|
||||||
try {
|
try {
|
||||||
httpPostRequestEncoder = new HttpPostRequestEncoder(httpDataFactory, fullHttpRequest, true);
|
if (!request.getParts().isEmpty()) {
|
||||||
httpPostRequestEncoder.setBodyHttpDatas(request.getBodyData());
|
httpPostRequestEncoder = new HttpPostRequestEncoder(httpDataFactory,
|
||||||
httpPostRequestEncoder.finalizeRequest();
|
fullHttpRequest,
|
||||||
} catch (HttpPostRequestEncoder.ErrorDataEncoderException e) {
|
true,
|
||||||
throw new IOException(e);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!channel.isWritable()) {
|
io.netty.handler.codec.http.HttpRequest httpRequest = httpPostRequestEncoder.finalizeRequest();
|
||||||
logger.log(Level.WARNING, "channel not writable");
|
channel.write(httpRequest);
|
||||||
return this;
|
} else {
|
||||||
}
|
|
||||||
channel.write(fullHttpRequest);
|
channel.write(fullHttpRequest);
|
||||||
|
}
|
||||||
if (httpPostRequestEncoder != null && httpPostRequestEncoder.isChunked()) {
|
if (httpPostRequestEncoder != null && httpPostRequestEncoder.isChunked()) {
|
||||||
|
logger.log(Level.FINEST, "finish chunked HTTP POST encoder");
|
||||||
channel.write(httpPostRequestEncoder);
|
channel.write(httpPostRequestEncoder);
|
||||||
|
} else {
|
||||||
|
logger.log(Level.FINEST, "HTTP POST encoder not chunked");
|
||||||
}
|
}
|
||||||
channel.flush();
|
channel.flush();
|
||||||
|
} catch (HttpPostRequestEncoder.ErrorDataEncoderException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
} 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,12 +152,7 @@ public final class Bootstrap {
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
HttpRouter httpRouter = BaseHttpRouter.builder()
|
||||||
.setHttpServerConfig(serverConfig)
|
|
||||||
.setApplication(WebApplication.builder()
|
|
||||||
.setSettings(settings)
|
|
||||||
.setSecret("1088e6b7ad58d64d09961e1357bf95544447051c6ad1332cd626e3a33bb5786b")
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.setHandler(400, new GroovyHttpStatusHandler(HttpResponseStatus.BAD_REQUEST, "Bad request", "400.gtpl"))
|
.setHandler(400, new GroovyHttpStatusHandler(HttpResponseStatus.BAD_REQUEST, "Bad request", "400.gtpl"))
|
||||||
.setHandler(401, new GroovyHttpStatusHandler(HttpResponseStatus.UNAUTHORIZED, "Unauthorized", "401.gtpl"))
|
.setHandler(401, new GroovyHttpStatusHandler(HttpResponseStatus.UNAUTHORIZED, "Unauthorized", "401.gtpl"))
|
||||||
.setHandler(403, new GroovyHttpStatusHandler(HttpResponseStatus.FORBIDDEN, "Forbidden", "403.gtpl"))
|
.setHandler(403, new GroovyHttpStatusHandler(HttpResponseStatus.FORBIDDEN, "Forbidden", "403.gtpl"))
|
||||||
|
@ -184,12 +183,26 @@ public final class Bootstrap {
|
||||||
.setHandler(new GroovyTemplateResourceHandler())
|
.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
if (nettyHttpsServerConfig.isObjectAggregationEnabled()) {
|
||||||
HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpsServerConfig.getMaxContentLength());
|
HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpsServerConfig.getMaxContentLength());
|
||||||
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpsServerConfig.getMaxCompositeBufferComponents());
|
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpsServerConfig.getMaxCompositeBufferComponents());
|
||||||
pipeline.addLast("server-aggregator", httpObjectAggregator);
|
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()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()),
|
||||||
|
|
|
@ -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,10 +49,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpsAddress)
|
.setHttpAddress(httpsAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -75,7 +76,16 @@ public class NettyHttps2ServerMultiRequestLoadTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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,10 +50,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpsAddress)
|
.setHttpAddress(httpsAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -76,7 +77,16 @@ public class NettyHttps2ServerTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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,10 +51,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpsAddress)
|
.setHttpAddress(httpsAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -80,7 +81,16 @@ public class NettyHttpsServerMultiRequestLoadTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
@ -133,10 +143,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpsAddress)
|
.setHttpAddress(httpsAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -164,7 +172,16 @@ public class NettyHttpsServerMultiRequestLoadTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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,10 +55,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpsAddress)
|
.setHttpAddress(httpsAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -83,7 +84,16 @@ public class NettyHttpsServerTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
@ -127,10 +137,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpsAddress)
|
.setHttpAddress(httpsAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -158,7 +166,16 @@ public class NettyHttpsServerTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
@ -201,10 +218,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpsAddress)
|
.setHttpAddress(httpsAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -231,7 +246,16 @@ public class NettyHttpsServerTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
|
||||||
// retain request, so we can read the body later without refCnt=0 error
|
|
||||||
this.fullHttpRequest = fullHttpRequest.retain();
|
|
||||||
setVersion(HttpVersion.valueOf(fullHttpRequest.protocolVersion().text()));
|
setVersion(HttpVersion.valueOf(fullHttpRequest.protocolVersion().text()));
|
||||||
setMethod(HttpMethod.valueOf(fullHttpRequest.method().name()));
|
setMethod(HttpMethod.valueOf(fullHttpRequest.method().name()));
|
||||||
setRequestURI(fullHttpRequest.uri());
|
setRequestURI(fullHttpRequest.uri());
|
||||||
fullHttpRequest.headers().entries().forEach(e -> addHeader(e.getKey(), e.getValue()));
|
fullHttpRequest.headers().entries().forEach(e -> addHeader(e.getKey(), e.getValue()));
|
||||||
|
// read all bytes from request into a JDK ByteBuffer. This might be expensive.
|
||||||
|
if (fullHttpRequest.content() != null) {
|
||||||
|
byteBuffer = ByteBuffer.wrap(ByteBufUtil.getBytes(fullHttpRequest.content()));
|
||||||
}
|
}
|
||||||
return this;
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
if (nettyHttpServerConfig.isObjectAggregationEnabled()) {
|
||||||
HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength());
|
HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength());
|
||||||
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpServerConfig.getMaxCompositeBufferComponents());
|
httpObjectAggregator.setMaxCumulationBufferComponents(nettyHttpServerConfig.getMaxCompositeBufferComponents());
|
||||||
pipeline.addLast("server-aggregator", httpObjectAggregator);
|
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()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
if (nettyHttpServerConfig.isObjectAggregationEnabled()) {
|
||||||
pipeline.addLast("server-object-aggregator",
|
pipeline.addLast("server-object-aggregator",
|
||||||
new HttpObjectAggregator(nettyHttpServerConfig.getMaxContentLength()));
|
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-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()));
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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,10 +40,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpAddress)
|
.setHttpAddress(httpAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -59,7 +60,16 @@ public class NettyHttp2ServerMultiRequestLoadTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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,10 +41,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(nettyHttpServerConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpAddress1)
|
.setHttpAddress(httpAddress1)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -58,7 +59,16 @@ public class NettyHttp2ServerTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(nettyHttpServerConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,11 +45,8 @@ public class NettyHttpServerFailureTest {
|
||||||
nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL);
|
nettyHttpServerConfig.setNetworkClass(NetworkClass.LOCAL);
|
||||||
nettyHttpServerConfig.setDebug(true);
|
nettyHttpServerConfig.setDebug(true);
|
||||||
nettyHttpServerConfig.setPipelining(false);
|
nettyHttpServerConfig.setPipelining(false);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(nettyHttpServerConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setHome(Paths.get("."))
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpAddress1)
|
.setHttpAddress(httpAddress1)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -65,7 +65,17 @@ public class NettyHttpServerFailureTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(nettyHttpServerConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setHome(Paths.get("."))
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,10 +40,8 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpAddress)
|
.setHttpAddress(httpAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -59,7 +60,16 @@ public class NettyHttpServerMultiRequestLoadTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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,14 +36,13 @@ 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);
|
||||||
try (NettyHttpServer server = NettyHttpServer.builder()
|
|
||||||
.setHttpServerConfig(nettyHttpServerConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpAddress1)
|
.setHttpAddress(httpAddress1)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -59,7 +61,16 @@ public class NettyHttpServerTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (NettyHttpServer server = NettyHttpServer.builder()
|
||||||
|
.setHttpServerConfig(nettyHttpServerConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,13 +27,8 @@ 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())
|
|
||||||
.setNetworkClass(NetworkClass.SITE)
|
|
||||||
)
|
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpAddress1)
|
.setHttpAddress(httpAddress1)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -63,10 +61,20 @@ public class NioHttpServerTest {
|
||||||
})
|
})
|
||||||
.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);
|
||||||
|
|
|
@ -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,10 +36,8 @@ 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);
|
||||||
try (SimpleHttpServer server = SimpleHttpsServer.builder()
|
|
||||||
.setHttpServerConfig(serverConfig)
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpsAddress)
|
.setHttpAddress(httpsAddress)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -63,7 +64,16 @@ public class SimpleHttpsServerTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (SimpleHttpServer server = SimpleHttpsServer.builder()
|
||||||
|
.setHttpServerConfig(serverConfig)
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build()) {
|
.build()) {
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,13 +27,8 @@ 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);
|
||||||
SimpleHttpServer server = SimpleHttpServer.builder()
|
|
||||||
.setHttpServerConfig(new HttpServerConfig()
|
HttpRouter router = BaseHttpRouter.builder()
|
||||||
.setServerName("SimpleHttpServer", SimpleHttpServer.class.getPackage().getImplementationVendor())
|
|
||||||
.setNetworkClass(NetworkClass.SITE)
|
|
||||||
)
|
|
||||||
.setApplication(BaseApplication.builder()
|
|
||||||
.setRouter(BaseHttpRouter.builder()
|
|
||||||
.addDomain(BaseHttpDomain.builder()
|
.addDomain(BaseHttpDomain.builder()
|
||||||
.setHttpAddress(httpAddress1)
|
.setHttpAddress(httpAddress1)
|
||||||
.addService(BaseHttpService.builder()
|
.addService(BaseHttpService.builder()
|
||||||
|
@ -67,7 +65,18 @@ public class SimpleHttpServerTest {
|
||||||
})
|
})
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build())
|
||||||
.build())
|
.build();
|
||||||
|
|
||||||
|
Executor executor = BaseExecutor.builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
SimpleHttpServer server = SimpleHttpServer.builder()
|
||||||
|
.setHttpServerConfig(new HttpServerConfig()
|
||||||
|
.setServerName("SimpleHttpServer", SimpleHttpServer.class.getPackage().getImplementationVendor())
|
||||||
|
.setNetworkClass(NetworkClass.SITE))
|
||||||
|
.setApplication(BaseApplication.builder()
|
||||||
|
.setExecutor(executor)
|
||||||
|
.setRouter(router)
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
server.bind();
|
server.bind();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) : "") + "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
|
||||||
}
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,11 +11,11 @@ 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,
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in a new issue