back to 4.1.16, fix SSL, fix pool, fix tests
This commit is contained in:
parent
5cadb716bc
commit
12a5f6418c
23 changed files with 343 additions and 215 deletions
|
@ -67,7 +67,7 @@ test {
|
||||||
jvmArgs "-javaagent:" + configurations.alpnagent.asPath
|
jvmArgs "-javaagent:" + configurations.alpnagent.asPath
|
||||||
}
|
}
|
||||||
testLogging {
|
testLogging {
|
||||||
showStandardStreams = true
|
showStandardStreams = false
|
||||||
exceptionFormat = 'full'
|
exceptionFormat = 'full'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
group = org.xbib
|
group = org.xbib
|
||||||
name = netty-http-client
|
name = netty-http-client
|
||||||
version = 4.1.22.2
|
version = 4.1.16.1
|
||||||
|
|
||||||
netty.version = 4.1.22.Final
|
netty.version = 4.1.16.Final
|
||||||
tcnative.version = 2.0.7.Final
|
tcnative.version = 2.0.7.Final
|
||||||
conscrypt.version = 1.0.1
|
conscrypt.version = 1.0.1
|
||||||
xbib-net-url.version = 1.1.0
|
xbib-net-url.version = 1.1.0
|
||||||
|
|
|
@ -40,6 +40,7 @@ import javax.net.ssl.SSLException;
|
||||||
import javax.net.ssl.SSLParameters;
|
import javax.net.ssl.SSLParameters;
|
||||||
import javax.net.ssl.TrustManagerFactory;
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -136,7 +137,7 @@ public final class Client {
|
||||||
this.http2SettingsHandler = new Http2SettingsHandler();
|
this.http2SettingsHandler = new Http2SettingsHandler();
|
||||||
this.http2ResponseHandler = new Http2ResponseHandler();
|
this.http2ResponseHandler = new Http2ResponseHandler();
|
||||||
this.transports = new CopyOnWriteArrayList<>();
|
this.transports = new CopyOnWriteArrayList<>();
|
||||||
if (hasPooledConnections()) {
|
if (!clientConfig.getPoolNodes().isEmpty()) {
|
||||||
List<HttpAddress> nodes = clientConfig.getPoolNodes();
|
List<HttpAddress> nodes = clientConfig.getPoolNodes();
|
||||||
Integer limit = clientConfig.getPoolNodeConnectionLimit();
|
Integer limit = clientConfig.getPoolNodeConnectionLimit();
|
||||||
if (limit == null || limit < 1) {
|
if (limit == null || limit < 1) {
|
||||||
|
@ -183,7 +184,7 @@ public final class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPooledConnections() {
|
public boolean hasPooledConnections() {
|
||||||
return !clientConfig.getPoolNodes().isEmpty();
|
return pool != null && !clientConfig.getPoolNodes().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundedChannelPool<HttpAddress> getPool() {
|
public BoundedChannelPool<HttpAddress> getPool() {
|
||||||
|
@ -222,13 +223,13 @@ public final class Client {
|
||||||
} else {
|
} else {
|
||||||
transport = new Http2Transport(this, null);
|
transport = new Http2Transport(this, null);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
if (transport != null) {
|
if (transportListener != null) {
|
||||||
if (transportListener != null) {
|
transportListener.onOpen(transport);
|
||||||
transportListener.onOpen(transport);
|
|
||||||
}
|
|
||||||
transports.add(transport);
|
|
||||||
}
|
}
|
||||||
|
transports.add(transport);
|
||||||
return transport;
|
return transport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,15 +271,15 @@ public final class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void releaseChannel(Channel channel) throws IOException{
|
public void releaseChannel(Channel channel) throws IOException{
|
||||||
if (hasPooledConnections()) {
|
if (channel != null) {
|
||||||
try {
|
if (hasPooledConnections()) {
|
||||||
pool.release(channel);
|
try {
|
||||||
} catch (Exception e) {
|
pool.release(channel);
|
||||||
throw new IOException(e);
|
} catch (Exception e) {
|
||||||
}
|
throw new IOException(e);
|
||||||
} else {
|
}
|
||||||
if (channel != null) {
|
} else {
|
||||||
channel.close();
|
channel.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,14 +379,16 @@ public final class Client {
|
||||||
|
|
||||||
private static SslHandler newSslHandler(ClientConfig clientConfig, ByteBufAllocator allocator, HttpAddress httpAddress) {
|
private static SslHandler newSslHandler(ClientConfig clientConfig, ByteBufAllocator allocator, HttpAddress httpAddress) {
|
||||||
try {
|
try {
|
||||||
SslContext sslContext = newSslContext(clientConfig);
|
SslContext sslContext = newSslContext(clientConfig, httpAddress.getVersion());
|
||||||
SslHandler sslHandler = sslContext.newHandler(allocator);
|
InetSocketAddress peer = httpAddress.getInetSocketAddress();
|
||||||
|
SslHandler sslHandler = sslContext.newHandler(allocator, peer.getHostName(), peer.getPort());
|
||||||
SSLEngine engine = sslHandler.engine();
|
SSLEngine engine = sslHandler.engine();
|
||||||
List<String> serverNames = clientConfig.getServerNamesForIdentification();
|
List<String> serverNames = clientConfig.getServerNamesForIdentification();
|
||||||
if (serverNames.isEmpty()) {
|
if (serverNames.isEmpty()) {
|
||||||
serverNames = Collections.singletonList(httpAddress.getInetSocketAddress().getHostName());
|
serverNames = Collections.singletonList(peer.getHostName());
|
||||||
}
|
}
|
||||||
SSLParameters params = engine.getSSLParameters();
|
SSLParameters params = engine.getSSLParameters();
|
||||||
|
// use sslContext.newHandler(allocator, peerHost, peerPort) when using params.setEndpointIdentificationAlgorithm
|
||||||
params.setEndpointIdentificationAlgorithm("HTTPS");
|
params.setEndpointIdentificationAlgorithm("HTTPS");
|
||||||
List<SNIServerName> sniServerNames = new ArrayList<>();
|
List<SNIServerName> sniServerNames = new ArrayList<>();
|
||||||
for (String serverName : serverNames) {
|
for (String serverName : serverNames) {
|
||||||
|
@ -409,11 +412,11 @@ public final class Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SslContext newSslContext(ClientConfig clientConfig) throws SSLException {
|
private static SslContext newSslContext(ClientConfig clientConfig, HttpVersion httpVersion) throws SSLException {
|
||||||
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient()
|
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient()
|
||||||
.sslProvider(clientConfig.getSslProvider())
|
.sslProvider(clientConfig.getSslProvider())
|
||||||
.ciphers(Http2SecurityUtil.CIPHERS, clientConfig.getCipherSuiteFilter())
|
.ciphers(Http2SecurityUtil.CIPHERS, clientConfig.getCipherSuiteFilter())
|
||||||
.applicationProtocolConfig(newApplicationProtocolConfig());
|
.applicationProtocolConfig(newApplicationProtocolConfig(httpVersion));
|
||||||
if (clientConfig.getSslContextProvider() != null) {
|
if (clientConfig.getSslContextProvider() != null) {
|
||||||
sslContextBuilder.sslContextProvider(clientConfig.getSslContextProvider());
|
sslContextBuilder.sslContextProvider(clientConfig.getSslContextProvider());
|
||||||
}
|
}
|
||||||
|
@ -423,11 +426,16 @@ public final class Client {
|
||||||
return sslContextBuilder.build();
|
return sslContextBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ApplicationProtocolConfig newApplicationProtocolConfig() {
|
private static ApplicationProtocolConfig newApplicationProtocolConfig(HttpVersion httpVersion) {
|
||||||
return new ApplicationProtocolConfig(ApplicationProtocolConfig.Protocol.ALPN,
|
return httpVersion.majorVersion() == 1 ?
|
||||||
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
new ApplicationProtocolConfig(ApplicationProtocolConfig.Protocol.ALPN,
|
||||||
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
||||||
ApplicationProtocolNames.HTTP_2);
|
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
||||||
|
ApplicationProtocolNames.HTTP_1_1) :
|
||||||
|
new ApplicationProtocolConfig(ApplicationProtocolConfig.Protocol.ALPN,
|
||||||
|
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
||||||
|
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
||||||
|
ApplicationProtocolNames.HTTP_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface TransportListener {
|
public interface TransportListener {
|
||||||
|
|
|
@ -192,6 +192,8 @@ public class ClientBuilder {
|
||||||
|
|
||||||
public ClientBuilder addPoolNode(HttpAddress httpAddress) {
|
public ClientBuilder addPoolNode(HttpAddress httpAddress) {
|
||||||
clientConfig.addPoolNode(httpAddress);
|
clientConfig.addPoolNode(httpAddress);
|
||||||
|
clientConfig.setPoolVersion(httpAddress.getVersion());
|
||||||
|
clientConfig.setPoolSecure(httpAddress.isSecure());
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,16 +207,6 @@ public class ClientBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientBuilder setPoolVersion(HttpVersion poolVersion) {
|
|
||||||
clientConfig.setPoolVersion(poolVersion);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientBuilder setPoolSecure(boolean poolSecure) {
|
|
||||||
clientConfig.setPoolSecure(poolSecure);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientBuilder addServerNameForIdentification(String serverName) {
|
public ClientBuilder addServerNameForIdentification(String serverName) {
|
||||||
clientConfig.addServerNameForIdentification(serverName);
|
clientConfig.addServerNameForIdentification(serverName);
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class HttpAddress implements PoolKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpAddress of(Request request) {
|
public static HttpAddress of(Request request) {
|
||||||
return new HttpAddress(request.base(), request.httpVersion());
|
return new HttpAddress(request.url(), request.httpVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpAddress of(URL url, HttpVersion httpVersion) {
|
public static HttpAddress of(URL url, HttpVersion httpVersion) {
|
||||||
|
@ -76,7 +76,7 @@ public class HttpAddress implements PoolKey {
|
||||||
|
|
||||||
public InetSocketAddress getInetSocketAddress() {
|
public InetSocketAddress getInetSocketAddress() {
|
||||||
if (inetSocketAddress == null) {
|
if (inetSocketAddress == null) {
|
||||||
// this may execute DNS
|
// this may execute DNS lookup
|
||||||
this.inetSocketAddress = new InetSocketAddress(host, port);
|
this.inetSocketAddress = new InetSocketAddress(host, port);
|
||||||
}
|
}
|
||||||
return inetSocketAddress;
|
return inetSocketAddress;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.xbib.netty.http.client;
|
package org.xbib.netty.http.client;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
import io.netty.buffer.PooledByteBufAllocator;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
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;
|
||||||
|
@ -12,8 +14,6 @@ import org.xbib.netty.http.client.listener.HttpHeadersListener;
|
||||||
import org.xbib.netty.http.client.listener.HttpResponseListener;
|
import org.xbib.netty.http.client.listener.HttpResponseListener;
|
||||||
import org.xbib.netty.http.client.retry.BackOff;
|
import org.xbib.netty.http.client.retry.BackOff;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
@ -21,9 +21,9 @@ import java.util.concurrent.CompletableFuture;
|
||||||
/**
|
/**
|
||||||
* HTTP client request.
|
* HTTP client request.
|
||||||
*/
|
*/
|
||||||
public class Request implements Closeable {
|
public class Request {
|
||||||
|
|
||||||
private final URL base;
|
private final URL url;
|
||||||
|
|
||||||
private final HttpVersion httpVersion;
|
private final HttpVersion httpVersion;
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ public class Request implements Closeable {
|
||||||
String uri, ByteBuf content,
|
String uri, ByteBuf content,
|
||||||
long timeoutInMillis, boolean followRedirect, int maxRedirect, int redirectCount,
|
long timeoutInMillis, boolean followRedirect, int maxRedirect, int redirectCount,
|
||||||
boolean isBackOff, BackOff backOff) {
|
boolean isBackOff, BackOff backOff) {
|
||||||
this.base = url;
|
this.url = url;
|
||||||
this.httpVersion = httpVersion;
|
this.httpVersion = httpVersion;
|
||||||
this.httpMethod = httpMethod;
|
this.httpMethod = httpMethod;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
|
@ -77,8 +77,8 @@ public class Request implements Closeable {
|
||||||
this.backOff = backOff;
|
this.backOff = backOff;
|
||||||
}
|
}
|
||||||
|
|
||||||
public URL base() {
|
public URL url() {
|
||||||
return base;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpVersion httpVersion() {
|
public HttpVersion httpVersion() {
|
||||||
|
@ -139,12 +139,14 @@ public class Request implements Closeable {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Request[base='").append(base)
|
sb.append("Request[url='").append(url)
|
||||||
.append("',version=").append(httpVersion)
|
.append("',version=").append(httpVersion)
|
||||||
.append(",method=").append(httpMethod)
|
.append(",method=").append(httpMethod)
|
||||||
.append(",uri=").append(uri)
|
.append(",uri=").append(uri)
|
||||||
.append(",headers=").append(headers.entries())
|
.append(",headers=").append(headers.entries())
|
||||||
.append(",content=").append(content != null ? content.copy(0,16).toString(StandardCharsets.UTF_8) : "")
|
.append(",content=").append(content != null && content.readableBytes() >= 16 ?
|
||||||
|
content.copy(0,16).toString(StandardCharsets.UTF_8) + "..." :
|
||||||
|
content != null ? content.toString(StandardCharsets.UTF_8) : "")
|
||||||
.append("]");
|
.append("]");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
@ -222,13 +224,10 @@ public class Request implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RequestBuilder builder(HttpMethod httpMethod) {
|
public static RequestBuilder builder(HttpMethod httpMethod) {
|
||||||
return new RequestBuilder().setMethod(httpMethod);
|
return new RequestBuilder(PooledByteBufAllocator.DEFAULT).setMethod(httpMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static RequestBuilder builder(ByteBufAllocator allocator, HttpMethod httpMethod) {
|
||||||
public void close() throws IOException {
|
return new RequestBuilder(allocator).setMethod(httpMethod);
|
||||||
if (content != null) {
|
|
||||||
content.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
package org.xbib.netty.http.client;
|
package org.xbib.netty.http.client;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.PooledByteBufAllocator;
|
import io.netty.buffer.PooledByteBufAllocator;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpHeaderValues;
|
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||||
|
@ -51,6 +55,8 @@ public class RequestBuilder {
|
||||||
|
|
||||||
private static final HttpVersion HTTP_2_0 = HttpVersion.valueOf("HTTP/2.0");
|
private static final HttpVersion HTTP_2_0 = HttpVersion.valueOf("HTTP/2.0");
|
||||||
|
|
||||||
|
private final ByteBufAllocator allocator;
|
||||||
|
|
||||||
private final List<String> removeHeaders;
|
private final List<String> removeHeaders;
|
||||||
|
|
||||||
private final Collection<Cookie> cookies;
|
private final Collection<Cookie> cookies;
|
||||||
|
@ -85,7 +91,8 @@ public class RequestBuilder {
|
||||||
|
|
||||||
private BackOff backOff;
|
private BackOff backOff;
|
||||||
|
|
||||||
RequestBuilder() {
|
RequestBuilder(ByteBufAllocator allocator) {
|
||||||
|
this.allocator = allocator;
|
||||||
httpMethod = DEFAULT_METHOD;
|
httpMethod = DEFAULT_METHOD;
|
||||||
httpVersion = DEFAULT_HTTP_VERSION;
|
httpVersion = DEFAULT_HTTP_VERSION;
|
||||||
userAgent = DEFAULT_USER_AGENT;
|
userAgent = DEFAULT_USER_AGENT;
|
||||||
|
@ -341,11 +348,11 @@ public class RequestBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void content(CharSequence charSequence, AsciiString contentType) {
|
private void content(CharSequence charSequence, AsciiString contentType) {
|
||||||
content(charSequence.toString().getBytes(StandardCharsets.UTF_8), contentType);
|
content(ByteBufUtil.writeUtf8(allocator, charSequence), contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void content(byte[] buf, AsciiString contentType) {
|
private void content(byte[] buf, AsciiString contentType) {
|
||||||
content(PooledByteBufAllocator.DEFAULT.buffer(buf.length).writeBytes(buf), contentType);
|
content(allocator.buffer().writeBytes(buf), contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void content(ByteBuf body, AsciiString contentType) {
|
private void content(ByteBuf body, AsciiString contentType) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
@ -85,10 +86,10 @@ public class BoundedChannelPool<K extends PoolKey> implements Pool<Channel> {
|
||||||
}
|
}
|
||||||
this.numberOfNodes = nodes.size();
|
this.numberOfNodes = nodes.size();
|
||||||
bootstraps = new HashMap<>(numberOfNodes);
|
bootstraps = new HashMap<>(numberOfNodes);
|
||||||
channels = new HashMap<>(numberOfNodes);
|
channels = new ConcurrentHashMap<>(numberOfNodes);
|
||||||
availableChannels = new HashMap<>(numberOfNodes);
|
availableChannels = new ConcurrentHashMap<>(numberOfNodes);
|
||||||
counts = new HashMap<>(numberOfNodes);
|
counts = new ConcurrentHashMap<>(numberOfNodes);
|
||||||
failedCounts = new HashMap<>(numberOfNodes);
|
failedCounts = new ConcurrentHashMap<>(numberOfNodes);
|
||||||
for (K node : nodes) {
|
for (K node : nodes) {
|
||||||
ChannelPoolInitializer initializer = new ChannelPoolInitializer(node, channelPoolHandler);
|
ChannelPoolInitializer initializer = new ChannelPoolInitializer(node, channelPoolHandler);
|
||||||
bootstraps.put(node, bootstrap.clone().remoteAddress(node.getInetSocketAddress())
|
bootstraps.put(node, bootstrap.clone().remoteAddress(node.getInetSocketAddress())
|
||||||
|
@ -247,6 +248,9 @@ public class BoundedChannelPool<K extends PoolKey> implements Pool<Channel> {
|
||||||
int i = ThreadLocalRandom.current().nextInt(numberOfNodes);
|
int i = ThreadLocalRandom.current().nextInt(numberOfNodes);
|
||||||
for (int j = i; j < numberOfNodes; j ++) {
|
for (int j = i; j < numberOfNodes; j ++) {
|
||||||
nextKey = nodes.get(j % numberOfNodes);
|
nextKey = nodes.get(j % numberOfNodes);
|
||||||
|
if (counts == null) {
|
||||||
|
throw new ConnectException("strange");
|
||||||
|
}
|
||||||
next = counts.get(nextKey);
|
next = counts.get(nextKey);
|
||||||
if (next == 0) {
|
if (next == 0) {
|
||||||
key = nextKey;
|
key = nextKey;
|
||||||
|
@ -293,9 +297,7 @@ public class BoundedChannelPool<K extends PoolKey> implements Pool<Channel> {
|
||||||
channel.closeFuture().addListener(new CloseChannelListener(key, channel));
|
channel.closeFuture().addListener(new CloseChannelListener(key, channel));
|
||||||
channel.attr(attributeKey).set(key);
|
channel.attr(attributeKey).set(key);
|
||||||
channels.computeIfAbsent(key, node -> new ArrayList<>()).add(channel);
|
channels.computeIfAbsent(key, node -> new ArrayList<>()).add(channel);
|
||||||
synchronized (counts) {
|
counts.put(key, counts.get(key) + 1);
|
||||||
counts.put(key, counts.get(key) + 1);
|
|
||||||
}
|
|
||||||
if (retriesPerNode > 0) {
|
if (retriesPerNode > 0) {
|
||||||
failedCounts.put(key, 0);
|
failedCounts.put(key, 0);
|
||||||
}
|
}
|
||||||
|
@ -343,16 +345,12 @@ public class BoundedChannelPool<K extends PoolKey> implements Pool<Channel> {
|
||||||
logger.log(Level.FINE,"connection to " + key + " closed");
|
logger.log(Level.FINE,"connection to " + key + " closed");
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
synchronized (counts) {
|
if (counts.containsKey(key)) {
|
||||||
if (counts.containsKey(key)) {
|
counts.put(key, counts.get(key) - 1);
|
||||||
counts.put(key, counts.get(key) - 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
synchronized (channels) {
|
List<Channel> channels = BoundedChannelPool.this.channels.get(key);
|
||||||
List<Channel> channels = BoundedChannelPool.this.channels.get(key);
|
if (channels != null) {
|
||||||
if (channels != null) {
|
channels.remove(channel);
|
||||||
channels.remove(channel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
semaphore.release();
|
semaphore.release();
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -74,38 +74,34 @@ abstract class BaseTransport implements Transport {
|
||||||
// Our algorithm is: use always "origin form" for HTTP 1, use absolute form for HTTP 2.
|
// Our algorithm is: use always "origin form" for HTTP 1, use absolute form for HTTP 2.
|
||||||
// The reason is that Netty derives the HTTP/2 scheme header from the absolute form.
|
// The reason is that Netty derives the HTTP/2 scheme header from the absolute form.
|
||||||
String uri = request.httpVersion().majorVersion() == 1 ?
|
String uri = request.httpVersion().majorVersion() == 1 ?
|
||||||
request.base().relativeReference() : request.base().toString();
|
request.url().relativeReference() : request.url().toString();
|
||||||
FullHttpRequest fullHttpRequest = request.content() == null ?
|
FullHttpRequest fullHttpRequest = request.content() == null ?
|
||||||
new DefaultFullHttpRequest(request.httpVersion(), request.httpMethod(), uri) :
|
new DefaultFullHttpRequest(request.httpVersion(), request.httpMethod(), uri) :
|
||||||
new DefaultFullHttpRequest(request.httpVersion(), request.httpMethod(), uri,
|
new DefaultFullHttpRequest(request.httpVersion(), request.httpMethod(), uri,
|
||||||
request.content());
|
request.content());
|
||||||
try {
|
Integer streamId = nextStream();
|
||||||
Integer streamId = nextStream();
|
if (streamId != null && streamId > 0) {
|
||||||
if (streamId != null && streamId > 0) {
|
request.headers().set(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), Integer.toString(streamId));
|
||||||
request.headers().set(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), Integer.toString(streamId));
|
} else {
|
||||||
} else {
|
if (request.httpVersion().majorVersion() == 2) {
|
||||||
if (request.httpVersion().majorVersion() == 2) {
|
logger.log(Level.WARNING, "no streamId but HTTP/2 request. Strange!!! " + getClass().getName());
|
||||||
logger.log(Level.WARNING, "no streamId but HTTP/2 request. Strange!!! " + getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// add matching cookies from box (previous requests) and new cookies from request builder
|
}
|
||||||
Collection<Cookie> cookies = new ArrayList<>();
|
// add matching cookies from box (previous requests) and new cookies from request builder
|
||||||
cookies.addAll(matchCookiesFromBox(request));
|
Collection<Cookie> cookies = new ArrayList<>();
|
||||||
cookies.addAll(matchCookies(request));
|
cookies.addAll(matchCookiesFromBox(request));
|
||||||
if (!cookies.isEmpty()) {
|
cookies.addAll(matchCookies(request));
|
||||||
request.headers().set(HttpHeaderNames.COOKIE, ClientCookieEncoder.STRICT.encode(cookies));
|
if (!cookies.isEmpty()) {
|
||||||
}
|
request.headers().set(HttpHeaderNames.COOKIE, ClientCookieEncoder.STRICT.encode(cookies));
|
||||||
// add stream-id and cookie headers
|
}
|
||||||
fullHttpRequest.headers().set(request.headers());
|
// add stream-id and cookie headers
|
||||||
if (streamId != null) {
|
fullHttpRequest.headers().set(request.headers());
|
||||||
requests.put(streamId, request);
|
if (streamId != null) {
|
||||||
}
|
requests.put(streamId, request);
|
||||||
if (channel.isWritable()) {
|
}
|
||||||
channel.writeAndFlush(fullHttpRequest);
|
if (channel.isWritable()) {
|
||||||
|
channel.writeAndFlush(fullHttpRequest);
|
||||||
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
request.close();
|
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -214,14 +210,14 @@ abstract class BaseTransport implements Transport {
|
||||||
location = new PercentDecoder(StandardCharsets.UTF_8.newDecoder()).decode(location);
|
location = new PercentDecoder(StandardCharsets.UTF_8.newDecoder()).decode(location);
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
logger.log(Level.FINE, "found redirect location: " + location);
|
logger.log(Level.FINE, "found redirect location: " + location);
|
||||||
URL redirUrl = URL.base(request.base()).resolve(location);
|
URL redirUrl = URL.base(request.url()).resolve(location);
|
||||||
HttpMethod method = httpResponse.status().code() == 303 ? HttpMethod.GET : request.httpMethod();
|
HttpMethod method = httpResponse.status().code() == 303 ? HttpMethod.GET : request.httpMethod();
|
||||||
RequestBuilder newHttpRequestBuilder = Request.builder(method)
|
RequestBuilder newHttpRequestBuilder = Request.builder(method)
|
||||||
.url(redirUrl)
|
.url(redirUrl)
|
||||||
.setVersion(request.httpVersion())
|
.setVersion(request.httpVersion())
|
||||||
.setHeaders(request.headers())
|
.setHeaders(request.headers())
|
||||||
.content(request.content());
|
.content(request.content());
|
||||||
request.base().getQueryParams().forEach(pair ->
|
request.url().getQueryParams().forEach(pair ->
|
||||||
newHttpRequestBuilder.addParameter(pair.getFirst(), pair.getSecond())
|
newHttpRequestBuilder.addParameter(pair.getFirst(), pair.getSecond())
|
||||||
);
|
);
|
||||||
request.cookies().forEach(newHttpRequestBuilder::addCookie);
|
request.cookies().forEach(newHttpRequestBuilder::addCookie);
|
||||||
|
@ -309,13 +305,13 @@ abstract class BaseTransport implements Transport {
|
||||||
|
|
||||||
private List<Cookie> matchCookiesFromBox(Request request) {
|
private List<Cookie> matchCookiesFromBox(Request request) {
|
||||||
return cookieBox == null ? Collections.emptyList() : cookieBox.keySet().stream().filter(cookie ->
|
return cookieBox == null ? Collections.emptyList() : cookieBox.keySet().stream().filter(cookie ->
|
||||||
matchCookie(request.base(), cookie)
|
matchCookie(request.url(), cookie)
|
||||||
).collect(Collectors.toList());
|
).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Cookie> matchCookies(Request request) {
|
private List<Cookie> matchCookies(Request request) {
|
||||||
return request.cookies().stream().filter(cookie ->
|
return request.cookies().stream().filter(cookie ->
|
||||||
matchCookie(request.base(), cookie)
|
matchCookie(request.url(), cookie)
|
||||||
).collect(Collectors.toList());
|
).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,13 +69,11 @@ public class HttpTransport extends BaseTransport {
|
||||||
if (retryRequest != null) {
|
if (retryRequest != null) {
|
||||||
// retry transport, wait for completion
|
// retry transport, wait for completion
|
||||||
client.retry(this, retryRequest);
|
client.retry(this, retryRequest);
|
||||||
retryRequest.close();
|
|
||||||
} else {
|
} else {
|
||||||
Request continueRequest = continuation(request, fullHttpResponse);
|
Request continueRequest = continuation(request, fullHttpResponse);
|
||||||
if (continueRequest != null) {
|
if (continueRequest != null) {
|
||||||
// continue with new transport, synchronous call here, wait for completion
|
// continue with new transport, synchronous call here, wait for completion
|
||||||
client.continuation(this, continueRequest);
|
client.continuation(this, continueRequest);
|
||||||
continueRequest.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (URLSyntaxException | IOException e) {
|
} catch (URLSyntaxException | IOException e) {
|
||||||
|
@ -94,7 +92,7 @@ public class HttpTransport extends BaseTransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void awaitResponse(Integer streamId) throws IOException {
|
public void awaitResponse(Integer streamId) throws IOException, TimeoutException {
|
||||||
if (streamId == null) {
|
if (streamId == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -103,9 +101,17 @@ public class HttpTransport extends BaseTransport {
|
||||||
}
|
}
|
||||||
CompletableFuture<Boolean> promise = sequentialPromiseMap.get(streamId);
|
CompletableFuture<Boolean> promise = sequentialPromiseMap.get(streamId);
|
||||||
if (promise != null) {
|
if (promise != null) {
|
||||||
|
long millis = client.getClientConfig().getReadTimeoutMillis();
|
||||||
|
Request request = fromStreamId(streamId);
|
||||||
|
if (request != null && request.getTimeoutInMillis() > 0) {
|
||||||
|
millis = request.getTimeoutInMillis();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
promise.get(client.getClientConfig().getReadTimeoutMillis(), TimeUnit.MILLISECONDS);
|
promise.get(millis, TimeUnit.MILLISECONDS);
|
||||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
} catch (TimeoutException e) {
|
||||||
|
this.throwable = e;
|
||||||
|
throw new TimeoutException("timeout of " + millis + " milliseconds exceeded");
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
this.throwable = e;
|
this.throwable = e;
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -121,7 +127,7 @@ public class HttpTransport extends BaseTransport {
|
||||||
awaitResponse(streamId);
|
awaitResponse(streamId);
|
||||||
client.releaseChannel(channel);
|
client.releaseChannel(channel);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException | TimeoutException e) {
|
||||||
logger.log(Level.WARNING, e.getMessage(), e);
|
logger.log(Level.WARNING, e.getMessage(), e);
|
||||||
} finally {
|
} finally {
|
||||||
sequentialPromiseMap.clear();
|
sequentialPromiseMap.clear();
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.xbib.netty.http.client.Request;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public interface Transport {
|
public interface Transport {
|
||||||
|
@ -38,7 +39,7 @@ public interface Transport {
|
||||||
|
|
||||||
void pushPromiseReceived(Integer streamId, Integer promisedStreamId, Http2Headers headers);
|
void pushPromiseReceived(Integer streamId, Integer promisedStreamId, Http2Headers headers);
|
||||||
|
|
||||||
void awaitResponse(Integer streamId) throws IOException;
|
void awaitResponse(Integer streamId) throws IOException, TimeoutException;
|
||||||
|
|
||||||
Transport get();
|
Transport get();
|
||||||
|
|
||||||
|
|
|
@ -14,19 +14,19 @@ import java.util.logging.Logger;
|
||||||
|
|
||||||
public class CompletableFutureTest {
|
public class CompletableFutureTest {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ElasticsearchTest.class.getName());
|
private static final Logger logger = Logger.getLogger(CompletableFutureTest.class.getName());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get some weird content from one URL and post it to another URL, by composing completable futures.
|
* Get some weird content from one URL and post it to another URL, by composing completable futures.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testComposeCompletableFutures() throws IOException {
|
public void testComposeCompletableFutures() throws IOException {
|
||||||
Client client = new Client();
|
Client client = Client.builder().build();
|
||||||
try {
|
try {
|
||||||
final Function<FullHttpResponse, String> httpResponseStringFunction = response ->
|
final Function<FullHttpResponse, String> httpResponseStringFunction = response ->
|
||||||
response.content().toString(StandardCharsets.UTF_8);
|
response.content().toString(StandardCharsets.UTF_8);
|
||||||
Request request = Request.get()
|
Request request = Request.get()
|
||||||
.url("https://repo.maven.apache.org/maven2/org/xbib/netty-http-client/maven-metadata.xml.sha1")
|
.url("http://repo.maven.apache.org/maven2/org/xbib/netty-http-client/maven-metadata.xml.sha1")
|
||||||
.build();
|
.build();
|
||||||
CompletableFuture<String> completableFuture = client.execute(request, httpResponseStringFunction)
|
CompletableFuture<String> completableFuture = client.execute(request, httpResponseStringFunction)
|
||||||
.exceptionally(Throwable::getMessage)
|
.exceptionally(Throwable::getMessage)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.xbib.netty.http.client.test;
|
package org.xbib.netty.http.client.test;
|
||||||
|
|
||||||
import org.conscrypt.Conscrypt;
|
import org.conscrypt.Conscrypt;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.netty.http.client.Client;
|
import org.xbib.netty.http.client.Client;
|
||||||
import org.xbib.netty.http.client.Request;
|
import org.xbib.netty.http.client.Request;
|
||||||
|
@ -17,13 +18,14 @@ public class ConscryptTest extends LoggingBase {
|
||||||
@Test
|
@Test
|
||||||
public void testConscrypt() throws IOException {
|
public void testConscrypt() throws IOException {
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
|
.enableDebug()
|
||||||
.setJdkSslProvider()
|
.setJdkSslProvider()
|
||||||
.setSslContextProvider(Conscrypt.newProvider())
|
.setSslContextProvider(Conscrypt.newProvider())
|
||||||
.build();
|
.build();
|
||||||
logger.log(Level.INFO, client.getClientConfig().toString());
|
logger.log(Level.INFO, client.getClientConfig().toString());
|
||||||
try {
|
try {
|
||||||
Request request = Request.get()
|
Request request = Request.get()
|
||||||
.url("https://xbib.org")
|
.url("https://google.com")
|
||||||
.setVersion("HTTP/1.1")
|
.setVersion("HTTP/1.1")
|
||||||
.build()
|
.build()
|
||||||
.setResponseListener(fullHttpResponse -> {
|
.setResponseListener(fullHttpResponse -> {
|
||||||
|
|
|
@ -3,13 +3,16 @@ package org.xbib.netty.http.client.test;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.netty.http.client.Client;
|
import org.xbib.netty.http.client.Client;
|
||||||
|
import org.xbib.netty.http.client.HttpAddress;
|
||||||
import org.xbib.netty.http.client.Request;
|
import org.xbib.netty.http.client.Request;
|
||||||
import org.xbib.netty.http.client.transport.Transport;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
@ -22,8 +25,27 @@ public class ElasticsearchTest extends LoggingBase {
|
||||||
private static final Logger logger = Logger.getLogger(ElasticsearchTest.class.getName());
|
private static final Logger logger = Logger.getLogger(ElasticsearchTest.class.getName());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testElasticsearch() throws IOException {
|
||||||
|
Client client = Client.builder().enableDebug().build();
|
||||||
|
try {
|
||||||
|
Request request = Request.get().url("http://localhost:9200")
|
||||||
|
.build()
|
||||||
|
.setResponseListener(fullHttpResponse -> {
|
||||||
|
String response = fullHttpResponse.content().toString(StandardCharsets.UTF_8);
|
||||||
|
logger.log(Level.INFO, "status = " + fullHttpResponse.status() + " response body = " + response);
|
||||||
|
});
|
||||||
|
logger.info("request = " + request.toString());
|
||||||
|
client.execute(request);
|
||||||
|
} finally {
|
||||||
|
client.shutdownGracefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
public void testElasticsearchCreateDocument() throws IOException {
|
public void testElasticsearchCreateDocument() throws IOException {
|
||||||
Client client = new Client();
|
Client client = Client.builder().enableDebug().build();
|
||||||
try {
|
try {
|
||||||
Request request = Request.put().url("http://localhost:9200/test/test/1")
|
Request request = Request.put().url("http://localhost:9200/test/test/1")
|
||||||
.json("{\"text\":\"Hello World\"}")
|
.json("{\"text\":\"Hello World\"}")
|
||||||
|
@ -32,6 +54,7 @@ public class ElasticsearchTest extends LoggingBase {
|
||||||
String response = fullHttpResponse.content().toString(StandardCharsets.UTF_8);
|
String response = fullHttpResponse.content().toString(StandardCharsets.UTF_8);
|
||||||
logger.log(Level.INFO, "status = " + fullHttpResponse.status() + " response body = " + response);
|
logger.log(Level.INFO, "status = " + fullHttpResponse.status() + " response body = " + response);
|
||||||
});
|
});
|
||||||
|
logger.info("request = " + request.toString());
|
||||||
client.execute(request);
|
client.execute(request);
|
||||||
} finally {
|
} finally {
|
||||||
client.shutdownGracefully();
|
client.shutdownGracefully();
|
||||||
|
@ -39,6 +62,7 @@ public class ElasticsearchTest extends LoggingBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore
|
||||||
public void testElasticsearchMatchQuery() throws IOException {
|
public void testElasticsearchMatchQuery() throws IOException {
|
||||||
Client client = new Client();
|
Client client = new Client();
|
||||||
try {
|
try {
|
||||||
|
@ -55,24 +79,46 @@ public class ElasticsearchTest extends LoggingBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This shows the usage of 4 concurrent pooled connections on 4 threads, querying Elasticsearch.
|
||||||
|
* @throws IOException if test fails
|
||||||
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testElasticsearchConcurrent() throws IOException {
|
public void testElasticsearchPooled() throws IOException {
|
||||||
Client client = Client.builder().setReadTimeoutMillis(20000).build();
|
HttpAddress httpAddress = HttpAddress.http1("localhost", 9200);
|
||||||
|
int limit = 4;
|
||||||
|
Client client = Client.builder()
|
||||||
|
.addPoolNode(httpAddress)
|
||||||
|
.setPoolNodeConnectionLimit(limit)
|
||||||
|
.build();
|
||||||
int max = 1000;
|
int max = 1000;
|
||||||
|
int threads = 4;
|
||||||
try {
|
try {
|
||||||
List<Request> queries = new ArrayList<>();
|
ExecutorService executorService = Executors.newFixedThreadPool(threads);
|
||||||
for (int i = 0; i < max; i++) {
|
for (int n = 0; n < threads; n++) {
|
||||||
queries.add(newRequest());
|
executorService.submit(() -> {
|
||||||
}
|
List<Request> queries = new ArrayList<>();
|
||||||
Transport transport = client.execute(queries.get(0)).get();
|
for (int i = 0; i < max; i++) {
|
||||||
for (int i = 1; i < max; i++) {
|
queries.add(newRequest());
|
||||||
transport.execute(queries.get(i)).get();
|
}
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < max; i++) {
|
||||||
|
client.pooledExecute(queries.get(i)).get();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.log(Level.WARNING, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
executorService.shutdown();
|
||||||
|
executorService.awaitTermination(60, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.log(Level.WARNING, e.getMessage(), e);
|
||||||
} finally {
|
} finally {
|
||||||
client.shutdownGracefully();
|
client.shutdownGracefully();
|
||||||
logger.log(Level.INFO, "count=" + count);
|
|
||||||
}
|
}
|
||||||
assertEquals(max, count.get());
|
logger.log(Level.INFO, "count=" + count);
|
||||||
|
assertEquals(max * threads, count.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Request newRequest() {
|
private Request newRequest() {
|
||||||
|
@ -81,10 +127,12 @@ public class ElasticsearchTest extends LoggingBase {
|
||||||
.json("{\"query\":{\"match\":{\"text\":\"Hello World\"}}}")
|
.json("{\"query\":{\"match\":{\"text\":\"Hello World\"}}}")
|
||||||
.addHeader("connection", "keep-alive")
|
.addHeader("connection", "keep-alive")
|
||||||
.build()
|
.build()
|
||||||
.setResponseListener(fullHttpResponse ->
|
.setResponseListener(fullHttpResponse -> {
|
||||||
logger.log(Level.FINE, "status = " + fullHttpResponse.status() +
|
count.getAndIncrement();
|
||||||
" counter = " + count.getAndIncrement() +
|
if (fullHttpResponse.status().code() != 200) {
|
||||||
" response body = " + fullHttpResponse.content().toString(StandardCharsets.UTF_8)));
|
logger.log(Level.WARNING,"error: " + fullHttpResponse.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private final AtomicInteger count = new AtomicInteger();
|
private final AtomicInteger count = new AtomicInteger();
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
package org.xbib.netty.http.client.test;
|
package org.xbib.netty.http.client.test;
|
||||||
|
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xbib.netty.http.client.Client;
|
import org.xbib.netty.http.client.Client;
|
||||||
import org.xbib.netty.http.client.Request;
|
import org.xbib.netty.http.client.Request;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -17,16 +14,9 @@ public class Http1Test extends LoggingBase {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Http1Test.class.getName());
|
private static final Logger logger = Logger.getLogger(Http1Test.class.getName());
|
||||||
|
|
||||||
@After
|
|
||||||
public void checkThreads() {
|
|
||||||
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
|
|
||||||
logger.log(Level.INFO, "threads = " + threadSet.size() );
|
|
||||||
threadSet.forEach( thread -> logger.log(Level.INFO, thread.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHttp1() throws Exception {
|
public void testHttp1() throws Exception {
|
||||||
Client client = new Client();
|
Client client = Client.builder().enableDebug().build();
|
||||||
try {
|
try {
|
||||||
Request request = Request.get().url("http://xbib.org").build()
|
Request request = Request.get().url("http://xbib.org").build()
|
||||||
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
|
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
|
||||||
|
@ -40,7 +30,6 @@ public class Http1Test extends LoggingBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
|
||||||
public void testParallelRequests() throws IOException {
|
public void testParallelRequests() throws IOException {
|
||||||
Client client = Client.builder().enableDebug().build();
|
Client client = Client.builder().enableDebug().build();
|
||||||
try {
|
try {
|
||||||
|
@ -70,7 +59,6 @@ public class Http1Test extends LoggingBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
|
||||||
public void testSequentialRequests() throws Exception {
|
public void testSequentialRequests() throws Exception {
|
||||||
Client client = Client.builder().enableDebug().build();
|
Client client = Client.builder().enableDebug().build();
|
||||||
try {
|
try {
|
||||||
|
@ -79,7 +67,7 @@ public class Http1Test extends LoggingBase {
|
||||||
msg.content().toString(StandardCharsets.UTF_8)));
|
msg.content().toString(StandardCharsets.UTF_8)));
|
||||||
client.execute(request1).get();
|
client.execute(request1).get();
|
||||||
|
|
||||||
Request request2 = Request.get().url("https://google.com").setVersion("HTTP/2.0").build()
|
Request request2 = Request.get().url("http://google.com").setVersion("HTTP/1.1").build()
|
||||||
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
|
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
|
||||||
msg.content().toString(StandardCharsets.UTF_8)));
|
msg.content().toString(StandardCharsets.UTF_8)));
|
||||||
client.execute(request2).get();
|
client.execute(request2).get();
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class Http2Test extends LoggingBase {
|
||||||
*
|
*
|
||||||
* demo/h2_demo_frame.html sends no content, only a push promise, and does not continue
|
* demo/h2_demo_frame.html sends no content, only a push promise, and does not continue
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException if test fails
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class LeakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testForLeaks() throws IOException, InterruptedException {
|
public void testForLeaks() throws IOException {
|
||||||
Client client = new Client();
|
Client client = new Client();
|
||||||
client.shutdownGracefully();
|
client.shutdownGracefully();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.xbib.netty.http.client.test;
|
package org.xbib.netty.http.client.test;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.xbib.net.URL;
|
||||||
import org.xbib.netty.http.client.Client;
|
import org.xbib.netty.http.client.Client;
|
||||||
import org.xbib.netty.http.client.HttpAddress;
|
import org.xbib.netty.http.client.HttpAddress;
|
||||||
import org.xbib.netty.http.client.Request;
|
import org.xbib.netty.http.client.Request;
|
||||||
|
@ -21,15 +23,15 @@ public class PooledClientTest extends LoggingBase {
|
||||||
@Test
|
@Test
|
||||||
public void testPooledClientWithSingleNode() throws IOException {
|
public void testPooledClientWithSingleNode() throws IOException {
|
||||||
int loop = 10;
|
int loop = 10;
|
||||||
HttpAddress httpAddress = HttpAddress.http1("xbib.org", 80);
|
int threads = Runtime.getRuntime().availableProcessors();
|
||||||
|
URL url = URL.from("http://xbib.org");
|
||||||
|
HttpAddress httpAddress = HttpAddress.of(url, HttpVersion.HTTP_1_1);
|
||||||
Client client = Client.builder()
|
Client client = Client.builder()
|
||||||
.addPoolNode(httpAddress)
|
.addPoolNode(httpAddress)
|
||||||
.setPoolSecure(httpAddress.isSecure())
|
.setPoolNodeConnectionLimit(threads)
|
||||||
.setPoolNodeConnectionLimit(16)
|
|
||||||
.build();
|
.build();
|
||||||
AtomicInteger count = new AtomicInteger();
|
AtomicInteger count = new AtomicInteger();
|
||||||
try {
|
try {
|
||||||
int threads = 16;
|
|
||||||
ExecutorService executorService = Executors.newFixedThreadPool(threads);
|
ExecutorService executorService = Executors.newFixedThreadPool(threads);
|
||||||
for (int n = 0; n < threads; n++) {
|
for (int n = 0; n < threads; n++) {
|
||||||
executorService.submit(() -> {
|
executorService.submit(() -> {
|
||||||
|
@ -37,25 +39,26 @@ public class PooledClientTest extends LoggingBase {
|
||||||
logger.log(Level.INFO, "starting " + Thread.currentThread());
|
logger.log(Level.INFO, "starting " + Thread.currentThread());
|
||||||
for (int i = 0; i < loop; i++) {
|
for (int i = 0; i < loop; i++) {
|
||||||
Request request = Request.get()
|
Request request = Request.get()
|
||||||
.url("http://xbib.org/repository/")
|
.url(url.toString())
|
||||||
.setVersion("HTTP/1.1")
|
.setVersion(httpAddress.getVersion())
|
||||||
|
//.setTimeoutInMillis(25000L)
|
||||||
.build()
|
.build()
|
||||||
.setResponseListener(fullHttpResponse -> {
|
.setResponseListener(fullHttpResponse -> {
|
||||||
String response = fullHttpResponse.content().toString(StandardCharsets.UTF_8);
|
String response = fullHttpResponse.content().toString(StandardCharsets.UTF_8);
|
||||||
//logger.log(Level.INFO, "status = " + fullHttpResponse.status() + " response body = " + response);
|
//logger.log(Level.INFO, "status = " + fullHttpResponse.status() + " response body = " + response);
|
||||||
|
count.getAndIncrement();
|
||||||
});
|
});
|
||||||
client.pooledExecute(request).get();
|
client.pooledExecute(request).get();
|
||||||
count.getAndIncrement();
|
|
||||||
}
|
}
|
||||||
logger.log(Level.INFO, "done " + Thread.currentThread());
|
logger.log(Level.INFO, "done " + Thread.currentThread());
|
||||||
} catch (IOException e) {
|
} catch (Throwable e) {
|
||||||
logger.log(Level.WARNING, e.getMessage(), e);
|
logger.log(Level.WARNING, e.getMessage(), e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
executorService.awaitTermination(60, TimeUnit.SECONDS);
|
executorService.awaitTermination(60, TimeUnit.SECONDS);
|
||||||
} catch (InterruptedException e) {
|
} catch (Throwable e) {
|
||||||
logger.log(Level.WARNING, e.getMessage(), e);
|
logger.log(Level.WARNING, e.getMessage(), e);
|
||||||
} finally {
|
} finally {
|
||||||
client.shutdownGracefully();
|
client.shutdownGracefully();
|
||||||
|
|
|
@ -13,7 +13,7 @@ public class RequestBuilderTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleRequest() {
|
public void testSimpleRequest() {
|
||||||
Request request = Request.builder(HttpMethod.GET).build();
|
Request request = Request.builder(HttpMethod.GET).content("Hello", "text/plain").build();
|
||||||
logger.log(Level.INFO, request.toString());
|
logger.log(Level.INFO, request.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package org.xbib.netty.http.client.test;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.xbib.netty.http.client.Client;
|
||||||
|
import org.xbib.netty.http.client.Request;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class SecureHttp1Test extends LoggingBase {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(SecureHttp1Test.class.getName());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHttp1() throws Exception {
|
||||||
|
Client client = Client.builder().enableDebug().build();
|
||||||
|
try {
|
||||||
|
Request request = Request.get().url("https://www.google.com/").build()
|
||||||
|
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
|
||||||
|
msg.headers().entries() +
|
||||||
|
msg.content().toString(StandardCharsets.UTF_8) +
|
||||||
|
" status=" + msg.status().code()));
|
||||||
|
client.execute(request).get();
|
||||||
|
} finally {
|
||||||
|
client.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testParallelRequests() throws IOException {
|
||||||
|
Client client = Client.builder().enableDebug().build();
|
||||||
|
try {
|
||||||
|
Request request1 = Request.builder(HttpMethod.GET)
|
||||||
|
.url("https://google.com").setVersion("HTTP/1.1")
|
||||||
|
.build()
|
||||||
|
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
|
||||||
|
msg.headers().entries() +
|
||||||
|
//msg.content().toString(StandardCharsets.UTF_8) +
|
||||||
|
" status=" + msg.status().code()));
|
||||||
|
Request request2 = Request.builder(HttpMethod.GET)
|
||||||
|
.url("https://google.com").setVersion("HTTP/1.1")
|
||||||
|
.build()
|
||||||
|
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
|
||||||
|
msg.headers().entries() +
|
||||||
|
//msg.content().toString(StandardCharsets.UTF_8) +
|
||||||
|
" status=" + msg.status().code()));
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
client.execute(request1);
|
||||||
|
client.execute(request2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
client.shutdownGracefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testSequentialRequests() throws Exception {
|
||||||
|
Client client = Client.builder().enableDebug().build();
|
||||||
|
try {
|
||||||
|
Request request1 = Request.get().url("https://google.com").build()
|
||||||
|
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
|
||||||
|
msg.content().toString(StandardCharsets.UTF_8)));
|
||||||
|
client.execute(request1).get();
|
||||||
|
|
||||||
|
Request request2 = Request.get().url("https://google.com").setVersion("HTTP/2.0").build()
|
||||||
|
.setResponseListener(msg -> logger.log(Level.INFO, "got response: " +
|
||||||
|
msg.content().toString(StandardCharsets.UTF_8)));
|
||||||
|
client.execute(request2).get();
|
||||||
|
} finally {
|
||||||
|
client.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,14 +40,14 @@ public class EpollTest {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(EpollTest.class.getName());
|
private static final Logger logger = Logger.getLogger(EpollTest.class.getName());
|
||||||
|
|
||||||
private static final int CONCURRENCY = 10;
|
private static final int CONCURRENCY = 4;
|
||||||
|
|
||||||
private static final List<HttpAddress> NODES =
|
private static final List<HttpAddress> NODES =
|
||||||
Collections.singletonList(HttpAddress.http1("localhost", 12345));
|
Collections.singletonList(HttpAddress.http1("localhost", 12345));
|
||||||
|
|
||||||
private static final long TEST_TIME_SECONDS = 100;
|
private static final long TEST_TIME_SECONDS = 100;
|
||||||
|
|
||||||
private static final int ATTEMPTS = 10_000;
|
private static final int ATTEMPTS = 1_000;
|
||||||
|
|
||||||
private static final int FAIL_EVERY_ATTEMPT = 10;
|
private static final int FAIL_EVERY_ATTEMPT = 10;
|
||||||
|
|
||||||
|
|
|
@ -37,14 +37,14 @@ public class NioTest {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(NioTest.class.getName());
|
private static final Logger logger = Logger.getLogger(NioTest.class.getName());
|
||||||
|
|
||||||
private static final int CONCURRENCY = 10;
|
private static final int CONCURRENCY = 4;
|
||||||
|
|
||||||
private static final List<HttpAddress> NODES =
|
private static final List<HttpAddress> NODES =
|
||||||
Collections.singletonList(HttpAddress.http1("localhost", 12345));
|
Collections.singletonList(HttpAddress.http1("localhost", 12345));
|
||||||
|
|
||||||
private static final long TEST_TIME_SECONDS = 100;
|
private static final long TEST_TIME_SECONDS = 100;
|
||||||
|
|
||||||
private static final int ATTEMPTS = 10_000;
|
private static final int ATTEMPTS = 1_000;
|
||||||
|
|
||||||
private static final int FAIL_EVERY_ATTEMPT = 10;
|
private static final int FAIL_EVERY_ATTEMPT = 10;
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,6 @@ public class Http2FramesTest {
|
||||||
private static final Logger logger = Logger.getLogger(Http2FramesTest.class.getName());
|
private static final Logger logger = Logger.getLogger(Http2FramesTest.class.getName());
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
|
||||||
public void testHttp2Frames() throws Exception {
|
public void testHttp2Frames() throws Exception {
|
||||||
final InetSocketAddress inetSocketAddress = new InetSocketAddress("webtide.com", 443);
|
final InetSocketAddress inetSocketAddress = new InetSocketAddress("webtide.com", 443);
|
||||||
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
|
CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
|
||||||
|
@ -52,59 +51,59 @@ public class Http2FramesTest {
|
||||||
Channel clientChannel = null;
|
Channel clientChannel = null;
|
||||||
try {
|
try {
|
||||||
Bootstrap bootstrap = new Bootstrap()
|
Bootstrap bootstrap = new Bootstrap()
|
||||||
.group(eventLoopGroup)
|
.group(eventLoopGroup)
|
||||||
.channel(NioSocketChannel.class)
|
.channel(NioSocketChannel.class)
|
||||||
.handler(new ChannelInitializer<Channel>() {
|
.handler(new ChannelInitializer<Channel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel ch) throws Exception {
|
||||||
|
SslContext sslContext = SslContextBuilder.forClient()
|
||||||
|
.sslProvider(SslProvider.JDK)
|
||||||
|
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
||||||
|
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
|
||||||
|
.applicationProtocolConfig(new ApplicationProtocolConfig(
|
||||||
|
ApplicationProtocolConfig.Protocol.ALPN,
|
||||||
|
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
||||||
|
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
||||||
|
ApplicationProtocolNames.HTTP_2))
|
||||||
|
.build();
|
||||||
|
SslHandler sslHandler = sslContext.newHandler(ch.alloc());
|
||||||
|
SSLEngine engine = sslHandler.engine();
|
||||||
|
String fullQualifiedHostname = inetSocketAddress.getHostName();
|
||||||
|
SSLParameters params = engine.getSSLParameters();
|
||||||
|
params.setServerNames(Arrays.asList(new SNIServerName[]{new SNIHostName(fullQualifiedHostname)}));
|
||||||
|
engine.setSSLParameters(params);
|
||||||
|
ch.pipeline().addLast(sslHandler);
|
||||||
|
Http2FrameAdapter frameAdapter = new Http2FrameAdapter() {
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(Channel ch) throws Exception {
|
public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) {
|
||||||
SslContext sslContext = SslContextBuilder.forClient()
|
logger.log(Level.FINE, "settings received, now writing request");
|
||||||
.sslProvider(SslProvider.JDK)
|
Http2ConnectionHandler handler = ctx.pipeline().get(Http2ConnectionHandler.class);
|
||||||
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
handler.encoder().writeHeaders(ctx, 3,
|
||||||
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
|
new DefaultHttp2Headers().method(HttpMethod.GET.asciiName())
|
||||||
.applicationProtocolConfig(new ApplicationProtocolConfig(
|
.path("/")
|
||||||
ApplicationProtocolConfig.Protocol.ALPN,
|
.scheme("https")
|
||||||
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
|
.authority(inetSocketAddress.getHostName()),
|
||||||
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
|
0, true, ctx.newPromise());
|
||||||
ApplicationProtocolNames.HTTP_2))
|
ctx.channel().flush();
|
||||||
.build();
|
|
||||||
SslHandler sslHandler = sslContext.newHandler(ch.alloc());
|
|
||||||
SSLEngine engine = sslHandler.engine();
|
|
||||||
String fullQualifiedHostname = inetSocketAddress.getHostName();
|
|
||||||
SSLParameters params = engine.getSSLParameters();
|
|
||||||
params.setServerNames(Arrays.asList(new SNIServerName[]{new SNIHostName(fullQualifiedHostname)}));
|
|
||||||
engine.setSSLParameters(params);
|
|
||||||
ch.pipeline().addLast(sslHandler);
|
|
||||||
Http2FrameAdapter frameAdapter = new Http2FrameAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) {
|
|
||||||
logger.log(Level.FINE, "settings received, now writing request");
|
|
||||||
Http2ConnectionHandler handler = ctx.pipeline().get(Http2ConnectionHandler.class);
|
|
||||||
handler.encoder().writeHeaders(ctx, 3,
|
|
||||||
new DefaultHttp2Headers().method(HttpMethod.GET.asciiName())
|
|
||||||
.path("/")
|
|
||||||
.scheme("https")
|
|
||||||
.authority(inetSocketAddress.getHostName()),
|
|
||||||
0, true, ctx.newPromise());
|
|
||||||
ctx.channel().flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
|
||||||
boolean endOfStream) throws Http2Exception {
|
|
||||||
int i = super.onDataRead(ctx, streamId, data, padding, endOfStream);
|
|
||||||
if (endOfStream) {
|
|
||||||
completableFuture.complete(true);
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Http2ConnectionHandlerBuilder builder = new Http2ConnectionHandlerBuilder()
|
|
||||||
.server(false)
|
|
||||||
.frameListener(frameAdapter)
|
|
||||||
.frameLogger(new Http2FrameLogger(LogLevel.INFO, "client"));
|
|
||||||
ch.pipeline().addLast(builder.build());
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
@Override
|
||||||
|
public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
||||||
|
boolean endOfStream) throws Http2Exception {
|
||||||
|
int i = super.onDataRead(ctx, streamId, data, padding, endOfStream);
|
||||||
|
if (endOfStream) {
|
||||||
|
completableFuture.complete(true);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Http2ConnectionHandlerBuilder builder = new Http2ConnectionHandlerBuilder()
|
||||||
|
.server(false)
|
||||||
|
.frameListener(frameAdapter)
|
||||||
|
.frameLogger(new Http2FrameLogger(LogLevel.INFO, "client"));
|
||||||
|
ch.pipeline().addLast(builder.build());
|
||||||
|
}
|
||||||
|
});
|
||||||
logger.log(Level.INFO, () -> "connecting");
|
logger.log(Level.INFO, () -> "connecting");
|
||||||
clientChannel = bootstrap.connect(inetSocketAddress).sync().channel();
|
clientChannel = bootstrap.connect(inetSocketAddress).sync().channel();
|
||||||
logger.log(Level.INFO, () -> "waiting for end of stream");
|
logger.log(Level.INFO, () -> "waiting for end of stream");
|
||||||
|
|
Loading…
Reference in a new issue