diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/NettyHttpClient.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/NettyHttpClient.java
index a48d2f88609c..90fbafd96b16 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/NettyHttpClient.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/NettyHttpClient.java
@@ -33,7 +33,6 @@
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
-import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.proxy.ProxyHandler;
import io.netty.handler.ssl.ApplicationProtocolConfig;
@@ -55,6 +54,8 @@
import static io.clientcore.core.utils.ServerSentEventUtils.attemptRetry;
import static io.clientcore.core.utils.ServerSentEventUtils.processTextEventStream;
+import static io.clientcore.http.netty4.implementation.Netty4HandlerNames.HTTP_RESPONSE;
+import static io.clientcore.http.netty4.implementation.Netty4HandlerNames.PROGRESS_AND_TIMEOUT;
import static io.clientcore.http.netty4.implementation.Netty4Utility.awaitLatch;
import static io.clientcore.http.netty4.implementation.Netty4Utility.createCodec;
import static io.clientcore.http.netty4.implementation.Netty4Utility.sendHttp11Request;
@@ -166,11 +167,10 @@ protected void initChannel(Channel channel) throws SSLException {
channel.pipeline().addLast(Netty4HandlerNames.SSL, ssl.newHandler(channel.alloc(), host, port));
channel.pipeline()
.addLast(Netty4HandlerNames.SSL_INITIALIZER, new Netty4SslInitializationHandler());
- }
- if (isHttps) {
channel.pipeline()
- .addLast(new Netty4AlpnHandler(request, addProgressAndTimeoutHandler, errorReference, latch));
+ .addLast(Netty4HandlerNames.ALPN,
+ new Netty4AlpnHandler(request, responseReference, errorReference, latch));
}
}
});
@@ -198,14 +198,10 @@ protected void initChannel(Channel channel) throws SSLException {
// effectively be a no-op.
if (addProgressAndTimeoutHandler) {
channel.pipeline()
- .addLast(Netty4HandlerNames.PROGRESS_AND_TIMEOUT, new Netty4ProgressAndTimeoutHandler(
- progressReporter, writeTimeoutMillis, responseTimeoutMillis, readTimeoutMillis));
+ .addLast(PROGRESS_AND_TIMEOUT, new Netty4ProgressAndTimeoutHandler(progressReporter,
+ writeTimeoutMillis, responseTimeoutMillis, readTimeoutMillis));
}
- Netty4ResponseHandler responseHandler
- = new Netty4ResponseHandler(request, responseReference, errorReference, latch);
- channel.pipeline().addLast(Netty4HandlerNames.RESPONSE, responseHandler);
-
Throwable earlyError = errorReference.get();
if (earlyError != null) {
// If an error occurred between the connect and the request being sent, don't proceed with sending
@@ -237,15 +233,14 @@ protected void initChannel(Channel channel) throws SSLException {
} else {
// If there isn't an SslHandler, we can send the request immediately.
// Add the HTTP/1.1 codec, as we only support HTTP/2 when using SSL ALPN.
- HttpClientCodec codec = createCodec();
- if (addProgressAndTimeoutHandler) {
- channel.pipeline()
- .addBefore(Netty4HandlerNames.PROGRESS_AND_TIMEOUT, Netty4HandlerNames.HTTP_1_1_CODEC, codec);
- } else {
- channel.pipeline().addBefore(Netty4HandlerNames.RESPONSE, Netty4HandlerNames.HTTP_1_1_CODEC, codec);
- }
+ Netty4ResponseHandler responseHandler
+ = new Netty4ResponseHandler(request, responseReference, errorReference, latch);
+ channel.pipeline().addLast(HTTP_RESPONSE, responseHandler);
+
+ String addBefore = addProgressAndTimeoutHandler ? PROGRESS_AND_TIMEOUT : HTTP_RESPONSE;
+ channel.pipeline().addBefore(addBefore, Netty4HandlerNames.HTTP_CODEC, createCodec());
- sendHttp11Request(request, channel, addProgressAndTimeoutHandler, errorReference)
+ sendHttp11Request(request, channel, errorReference)
.addListener((ChannelFutureListener) sendListener -> {
if (!sendListener.isSuccess()) {
setOrSuppressError(errorReference, sendListener.cause());
@@ -288,7 +283,7 @@ protected void initChannel(Channel channel) throws SSLException {
// We're ignoring the response content.
CountDownLatch drainLatch = new CountDownLatch(1);
channel.pipeline().addLast(new Netty4EagerConsumeChannelHandler(drainLatch, ignored -> {
- }));
+ }, info.isHttp2()));
channel.config().setAutoRead(true);
awaitLatch(drainLatch);
} else if (bodyHandling == ResponseBodyHandling.STREAM) {
@@ -306,7 +301,7 @@ protected void initChannel(Channel channel) throws SSLException {
}
}
- body = new Netty4ChannelBinaryData(info.getEagerContent(), channel, length);
+ body = new Netty4ChannelBinaryData(info.getEagerContent(), channel, length, info.isHttp2());
} else {
// All cases otherwise assume BUFFER.
CountDownLatch drainLatch = new CountDownLatch(1);
@@ -316,7 +311,7 @@ protected void initChannel(Channel channel) throws SSLException {
} catch (IOException ex) {
throw LOGGER.throwableAtError().log(ex, CoreException::from);
}
- }));
+ }, info.isHttp2()));
channel.config().setAutoRead(true);
awaitLatch(drainLatch);
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/NettyHttpClientBuilder.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/NettyHttpClientBuilder.java
index d2f02a37afca..b74853327e3a 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/NettyHttpClientBuilder.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/NettyHttpClientBuilder.java
@@ -133,7 +133,7 @@ private static Class extends SocketChannel> getChannelClass(String className)
private Duration readTimeout;
private Duration responseTimeout;
private Duration writeTimeout;
- // private HttpProtocolVersion maximumHttpVersion = HttpProtocolVersion.HTTP_2;
+ private HttpProtocolVersion maximumHttpVersion = HttpProtocolVersion.HTTP_2;
/**
* Creates a new instance of {@link NettyHttpClientBuilder}.
@@ -260,26 +260,26 @@ public NettyHttpClientBuilder proxy(ProxyOptions proxyOptions) {
return this;
}
- // /**
- // * Sets the maximum {@link HttpProtocolVersion HTTP protocol version} that the HTTP client will support.
- // *
- // * By default, the maximum HTTP protocol version is set to {@link HttpProtocolVersion#HTTP_2 HTTP_2}.
- // *
- // * If {@code httpVersion} is null, it will reset the maximum HTTP protocol version to
- // * {@link HttpProtocolVersion#HTTP_2 HTTP_2}.
- // *
- // * @param httpVersion The maximum HTTP protocol version that the HTTP client will support.
- // * @return The updated {@link JdkHttpClientBuilder} object.
- // */
- // public NettyHttpClientBuilder maximumHttpVersion(HttpProtocolVersion httpVersion) {
- // if (httpVersion != null) {
- // this.maximumHttpVersion = httpVersion;
- // } else {
- // this.maximumHttpVersion = HttpProtocolVersion.HTTP_2;
- // }
- //
- // return this;
- // }
+ /**
+ * Sets the maximum {@link HttpProtocolVersion HTTP protocol version} that the HTTP client will support.
+ *
+ * By default, the maximum HTTP protocol version is set to {@link HttpProtocolVersion#HTTP_2 HTTP_2}.
+ *
+ * If {@code httpVersion} is null, it will reset the maximum HTTP protocol version to
+ * {@link HttpProtocolVersion#HTTP_2 HTTP_2}.
+ *
+ * @param httpVersion The maximum HTTP protocol version that the HTTP client will support.
+ * @return The updated builder.
+ */
+ public NettyHttpClientBuilder maximumHttpVersion(HttpProtocolVersion httpVersion) {
+ if (httpVersion != null) {
+ this.maximumHttpVersion = httpVersion;
+ } else {
+ this.maximumHttpVersion = HttpProtocolVersion.HTTP_2;
+ }
+
+ return this;
+ }
/**
* Builds the NettyHttpClient.
@@ -312,7 +312,7 @@ public HttpClient build() {
ProxyOptions buildProxyOptions
= (proxyOptions == null) ? ProxyOptions.fromConfiguration(buildConfiguration, true) : proxyOptions;
- return new NettyHttpClient(bootstrap, sslContextModifier, HttpProtocolVersion.HTTP_1_1,
+ return new NettyHttpClient(bootstrap, sslContextModifier, maximumHttpVersion,
new ChannelInitializationProxyHandler(buildProxyOptions), getTimeoutMillis(readTimeout),
getTimeoutMillis(responseTimeout), getTimeoutMillis(writeTimeout));
}
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4AlpnHandler.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4AlpnHandler.java
index 2d048fcfc059..024dc9f7686a 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4AlpnHandler.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4AlpnHandler.java
@@ -4,13 +4,16 @@
import io.clientcore.core.http.models.HttpRequest;
import io.netty.channel.ChannelFutureListener;
-import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
-import io.netty.handler.codec.http2.Http2FrameCodec;
-import io.netty.handler.codec.http2.Http2FrameCodecBuilder;
-import io.netty.handler.codec.http2.Http2MultiplexHandler;
-import io.netty.handler.flush.FlushConsolidationHandler;
+import io.netty.handler.codec.http2.DefaultHttp2Connection;
+import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
+import io.netty.handler.codec.http2.Http2Connection;
+import io.netty.handler.codec.http2.Http2FrameListener;
+import io.netty.handler.codec.http2.Http2Settings;
+import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandler;
+import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
+import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
@@ -19,7 +22,6 @@
import static io.clientcore.http.netty4.implementation.Netty4Utility.createCodec;
import static io.clientcore.http.netty4.implementation.Netty4Utility.sendHttp11Request;
-import static io.clientcore.http.netty4.implementation.Netty4Utility.sendHttp2Request;
import static io.clientcore.http.netty4.implementation.Netty4Utility.setOrSuppressError;
/**
@@ -27,8 +29,9 @@
* either HTTP/1.1 or HTTP/2 based on the result of negotiation.
*/
public final class Netty4AlpnHandler extends ApplicationProtocolNegotiationHandler {
+ private static final int TWO_FIFTY_SIX_KB = 256 * 1024;
private final HttpRequest request;
- private final boolean addProgressAndTimeoutHandler;
+ private final AtomicReference responseReference;
private final AtomicReference errorReference;
private final CountDownLatch latch;
@@ -36,15 +39,14 @@ public final class Netty4AlpnHandler extends ApplicationProtocolNegotiationHandl
* Creates a new instance of {@link Netty4AlpnHandler} with a fallback to using HTTP/1.1.
*
* @param request The request to send once ALPN negotiation completes.
- * @param addProgressAndTimeoutHandler Whether the progress and timeout handler was added to the ChannelPipeline.
* @param errorReference An AtomicReference keeping track of errors during the request lifecycle.
* @param latch A CountDownLatch that will be released once the request completes.
*/
- public Netty4AlpnHandler(HttpRequest request, boolean addProgressAndTimeoutHandler,
+ public Netty4AlpnHandler(HttpRequest request, AtomicReference responseReference,
AtomicReference errorReference, CountDownLatch latch) {
super(ApplicationProtocolNames.HTTP_1_1);
this.request = request;
- this.addProgressAndTimeoutHandler = addProgressAndTimeoutHandler;
+ this.responseReference = responseReference;
this.errorReference = errorReference;
this.latch = latch;
}
@@ -57,16 +59,41 @@ public boolean isSharable() {
@Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
- FlushConsolidationHandler flushConsolidationHandler = new FlushConsolidationHandler(1024, true);
- Http2FrameCodec http2FrameCodec = Http2FrameCodecBuilder.forClient().validateHeaders(true).build();
- Http2MultiplexHandler http2MultiplexHandler = new Http2MultiplexHandler(NoOpHandler.INSTANCE);
- ChannelPipeline pipeline = ctx.pipeline();
- pipeline.addAfter(Netty4HandlerNames.SSL, Netty4HandlerNames.HTTP_2_FLUSH, flushConsolidationHandler);
- pipeline.addAfter(Netty4HandlerNames.HTTP_2_FLUSH, Netty4HandlerNames.HTTP_2_CODEC, http2FrameCodec);
- pipeline.addAfter(Netty4HandlerNames.HTTP_2_CODEC, Netty4HandlerNames.HTTP_2_MULTIPLEX,
- http2MultiplexHandler);
+ // TODO (alzimmer): InboundHttp2ToHttpAdapter buffers the entire response into a FullHttpResponse. Need to
+ // create a streaming version of this to support huge response payloads.
+ Http2Connection http2Connection = new DefaultHttp2Connection(false);
+ Http2Settings settings = new Http2Settings().headerTableSize(4096)
+ .maxHeaderListSize(TWO_FIFTY_SIX_KB)
+ .pushEnabled(false)
+ .initialWindowSize(TWO_FIFTY_SIX_KB);
+ Http2FrameListener frameListener = new DelegatingDecompressorFrameListener(http2Connection,
+ new InboundHttp2ToHttpAdapterBuilder(http2Connection).maxContentLength(Integer.MAX_VALUE)
+ .propagateSettings(true)
+ .validateHttpHeaders(true)
+ .build());
- sendHttp2Request(request, ctx.channel(), addProgressAndTimeoutHandler, errorReference)
+ HttpToHttp2ConnectionHandler connectionHandler
+ = new HttpToHttp2ConnectionHandlerBuilder().initialSettings(settings)
+ .frameListener(frameListener)
+ .connection(http2Connection)
+ .validateHeaders(true)
+ .build();
+
+ if (ctx.pipeline().get(Netty4HandlerNames.PROGRESS_AND_TIMEOUT) != null) {
+ ctx.pipeline()
+ .addAfter(Netty4HandlerNames.PROGRESS_AND_TIMEOUT, Netty4HandlerNames.HTTP_RESPONSE,
+ new Netty4ResponseHandler(request, responseReference, errorReference, latch));
+ ctx.pipeline()
+ .addBefore(Netty4HandlerNames.PROGRESS_AND_TIMEOUT, Netty4HandlerNames.HTTP_CODEC,
+ connectionHandler);
+ } else {
+ ctx.pipeline().addAfter(Netty4HandlerNames.SSL, Netty4HandlerNames.HTTP_CODEC, connectionHandler);
+ ctx.pipeline()
+ .addAfter(Netty4HandlerNames.HTTP_CODEC, Netty4HandlerNames.HTTP_RESPONSE,
+ new Netty4ResponseHandler(request, responseReference, errorReference, latch));
+ }
+
+ sendHttp11Request(request, ctx.channel(), errorReference)
.addListener((ChannelFutureListener) sendListener -> {
if (!sendListener.isSuccess()) {
setOrSuppressError(errorReference, sendListener.cause());
@@ -77,9 +104,20 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
}
});
} else if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
- ctx.pipeline().addAfter(Netty4HandlerNames.SSL, Netty4HandlerNames.HTTP_1_1_CODEC, createCodec());
+ if (ctx.pipeline().get(Netty4HandlerNames.PROGRESS_AND_TIMEOUT) != null) {
+ ctx.pipeline()
+ .addAfter(Netty4HandlerNames.PROGRESS_AND_TIMEOUT, Netty4HandlerNames.HTTP_RESPONSE,
+ new Netty4ResponseHandler(request, responseReference, errorReference, latch));
+ ctx.pipeline()
+ .addBefore(Netty4HandlerNames.PROGRESS_AND_TIMEOUT, Netty4HandlerNames.HTTP_CODEC, createCodec());
+ } else {
+ ctx.pipeline().addAfter(Netty4HandlerNames.SSL, Netty4HandlerNames.HTTP_CODEC, createCodec());
+ ctx.pipeline()
+ .addAfter(Netty4HandlerNames.HTTP_CODEC, Netty4HandlerNames.HTTP_RESPONSE,
+ new Netty4ResponseHandler(request, responseReference, errorReference, latch));
+ }
- sendHttp11Request(request, ctx.channel(), addProgressAndTimeoutHandler, errorReference)
+ sendHttp11Request(request, ctx.channel(), errorReference)
.addListener((ChannelFutureListener) sendListener -> {
if (!sendListener.isSuccess()) {
setOrSuppressError(errorReference, sendListener.cause());
@@ -93,13 +131,4 @@ protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
throw new IllegalStateException("unknown protocol: " + protocol);
}
}
-
- private static final class NoOpHandler extends ChannelHandlerAdapter {
- private static final NoOpHandler INSTANCE = new NoOpHandler();
-
- @Override
- public boolean isSharable() {
- return true;
- }
- }
}
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ChannelBinaryData.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ChannelBinaryData.java
index ba46556faf72..837f4d164b1b 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ChannelBinaryData.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ChannelBinaryData.java
@@ -35,6 +35,7 @@ public final class Netty4ChannelBinaryData extends BinaryData {
private final Channel channel;
private final Long length;
+ private final boolean isHttp2;
// Non-final to allow nulling out after use.
private ByteArrayOutputStream eagerContent;
@@ -47,11 +48,13 @@ public final class Netty4ChannelBinaryData extends BinaryData {
* @param eagerContent Response body content that was eagerly read by Netty while processing the HTTP headers.
* @param channel The Netty {@link Channel}.
* @param length Size of the response body (if known).
+ * @param isHttp2 Flag indicating whether the handler is used for HTTP/2 or not.
*/
- public Netty4ChannelBinaryData(ByteArrayOutputStream eagerContent, Channel channel, Long length) {
+ public Netty4ChannelBinaryData(ByteArrayOutputStream eagerContent, Channel channel, Long length, boolean isHttp2) {
this.eagerContent = eagerContent;
this.channel = channel;
this.length = length;
+ this.isHttp2 = isHttp2;
}
@Override
@@ -62,8 +65,8 @@ public byte[] toBytes() {
if (bytes == null) {
CountDownLatch latch = new CountDownLatch(1);
- Netty4EagerConsumeChannelHandler handler
- = new Netty4EagerConsumeChannelHandler(latch, buf -> buf.readBytes(eagerContent, buf.readableBytes()));
+ Netty4EagerConsumeChannelHandler handler = new Netty4EagerConsumeChannelHandler(latch,
+ buf -> buf.readBytes(eagerContent, buf.readableBytes()), isHttp2);
channel.pipeline().addLast(Netty4HandlerNames.EAGER_CONSUME, handler);
channel.config().setAutoRead(true);
@@ -102,7 +105,7 @@ public T toObject(Type type, ObjectSerializer serializer) {
@Override
public InputStream toStream() {
if (bytes == null) {
- return new Netty4ChannelInputStream(eagerContent, channel);
+ return new Netty4ChannelInputStream(eagerContent, channel, isHttp2);
} else {
return new ByteArrayInputStream(bytes);
}
@@ -130,7 +133,7 @@ public void writeTo(OutputStream outputStream) {
CountDownLatch latch = new CountDownLatch(1);
Netty4EagerConsumeChannelHandler handler = new Netty4EagerConsumeChannelHandler(latch,
- buf -> buf.readBytes(outputStream, buf.readableBytes()));
+ buf -> buf.readBytes(outputStream, buf.readableBytes()), isHttp2);
channel.pipeline().addLast(Netty4HandlerNames.EAGER_CONSUME, handler);
channel.config().setAutoRead(true);
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ChannelInputStream.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ChannelInputStream.java
index e97927ae3fc4..418df7945248 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ChannelInputStream.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ChannelInputStream.java
@@ -15,6 +15,7 @@
*/
public final class Netty4ChannelInputStream extends InputStream {
private final Channel channel;
+ private final boolean isHttp2;
// Indicator for the Channel being fully read.
// This will become true before 'streamDone' becomes true, but both may become true in the same operation.
@@ -44,8 +45,9 @@ public final class Netty4ChannelInputStream extends InputStream {
* @param eagerContent Any response body content eagerly read from the {@link Channel} when processing the initial
* status line and response headers.
* @param channel The {@link Channel} to read from.
+ * @param isHttp2 Flag indicating whether the Channel is used for HTTP/2 or not.
*/
- Netty4ChannelInputStream(ByteArrayOutputStream eagerContent, Channel channel) {
+ Netty4ChannelInputStream(ByteArrayOutputStream eagerContent, Channel channel, boolean isHttp2) {
if (eagerContent != null && eagerContent.size() > 0) {
this.currentBuffer = eagerContent.toByteArray();
} else {
@@ -57,6 +59,7 @@ public final class Netty4ChannelInputStream extends InputStream {
if (channel.pipeline().get(Netty4InitiateOneReadHandler.class) != null) {
channel.pipeline().remove(Netty4InitiateOneReadHandler.class);
}
+ this.isHttp2 = isHttp2;
}
byte[] getCurrentBuffer() {
@@ -168,8 +171,10 @@ public long skip(long n) throws IOException {
public void close() {
currentBuffer = null;
additionalBuffers.clear();
- channel.disconnect();
- channel.close();
+ if (channel.isOpen() || channel.isActive()) {
+ channel.disconnect();
+ channel.close();
+ }
}
private boolean setupNextBuffer() throws IOException {
@@ -210,7 +215,7 @@ private boolean readMore() throws IOException {
byteBuf.readBytes(buffer);
additionalBuffers.add(buffer);
- });
+ }, isHttp2);
channel.pipeline().addLast(Netty4HandlerNames.READ_ONE, handler);
}
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4EagerConsumeChannelHandler.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4EagerConsumeChannelHandler.java
index 2d7aef28a00d..74cf9911a145 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4EagerConsumeChannelHandler.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4EagerConsumeChannelHandler.java
@@ -9,6 +9,7 @@
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.LastHttpContent;
+import io.netty.handler.codec.http2.Http2DataFrame;
import io.netty.util.ReferenceCountUtil;
import java.io.IOException;
@@ -22,6 +23,7 @@
public final class Netty4EagerConsumeChannelHandler extends ChannelInboundHandlerAdapter {
private final CountDownLatch latch;
private final IOExceptionCheckedConsumer byteBufConsumer;
+ private final boolean isHttp2;
private boolean lastRead;
private Throwable exception;
@@ -31,10 +33,13 @@ public final class Netty4EagerConsumeChannelHandler extends ChannelInboundHandle
*
* @param latch The latch to count down when the response is fully read, or an exception occurs.
* @param byteBufConsumer The consumer to process the {@link ByteBuf ByteBufs} as they are read.
+ * @param isHttp2 Flag indicating whether the handler is used for HTTP/2 or not.
*/
- public Netty4EagerConsumeChannelHandler(CountDownLatch latch, IOExceptionCheckedConsumer byteBufConsumer) {
+ public Netty4EagerConsumeChannelHandler(CountDownLatch latch, IOExceptionCheckedConsumer byteBufConsumer,
+ boolean isHttp2) {
this.latch = latch;
this.byteBufConsumer = byteBufConsumer;
+ this.isHttp2 = isHttp2;
}
@Override
@@ -56,7 +61,11 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) {
}
}
- lastRead = msg instanceof LastHttpContent;
+ if (isHttp2) {
+ lastRead = msg instanceof Http2DataFrame && ((Http2DataFrame) msg).isEndStream();
+ } else {
+ lastRead = msg instanceof LastHttpContent;
+ }
ctx.fireChannelRead(msg);
}
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4HandlerNames.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4HandlerNames.java
index 9f90763e4716..eee40c7c4fe7 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4HandlerNames.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4HandlerNames.java
@@ -3,9 +3,6 @@
package io.clientcore.http.netty4.implementation;
import io.netty.handler.codec.http.HttpClientCodec;
-import io.netty.handler.codec.http2.Http2FrameCodec;
-import io.netty.handler.codec.http2.Http2MultiplexHandler;
-import io.netty.handler.flush.FlushConsolidationHandler;
import io.netty.handler.proxy.ProxyHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
@@ -38,29 +35,14 @@ public final class Netty4HandlerNames {
public static final String SSL_INITIALIZER = "clientcore.sslinitializer";
/**
- * Name for the {@link Netty4H2OrHttp11Handler}.
+ * Name for the {@link Netty4AlpnHandler}.
*/
- public static final String HTTP_VERSION_PICKER = "clientcore.httpversionpicker";
+ public static final String ALPN = "clientcore.alpn";
/**
* Name for the HTTP/1.1 {@link HttpClientCodec}
*/
- public static final String HTTP_1_1_CODEC = "clientcore.http11codec";
-
- /**
- * Name for the HTTP/2 {@link FlushConsolidationHandler}.
- */
- public static final String HTTP_2_FLUSH = "clientcore.http2flush";
-
- /**
- * Name for the HTTP/2 {@link Http2FrameCodec}.
- */
- public static final String HTTP_2_CODEC = "clientcore.http2codec";
-
- /**
- * Name for the HTTP/2 {@link Http2MultiplexHandler}.
- */
- public static final String HTTP_2_MULTIPLEX = "clientcore.http2multiplex";
+ public static final String HTTP_CODEC = "clientcore.httpcodec";
/**
* Name for the {@link Netty4ProgressAndTimeoutHandler}.
@@ -75,7 +57,7 @@ public final class Netty4HandlerNames {
/**
* Name for the {@link Netty4ResponseHandler}.
*/
- public static final String RESPONSE = "clientcore.response";
+ public static final String HTTP_RESPONSE = "clientcore.httpresponse";
/**
* Name for the {@link Netty4EagerConsumeChannelHandler}.
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4InitiateOneReadHandler.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4InitiateOneReadHandler.java
index 8ddc1c9a4fc6..6a242ed03246 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4InitiateOneReadHandler.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4InitiateOneReadHandler.java
@@ -9,6 +9,7 @@
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.LastHttpContent;
+import io.netty.handler.codec.http2.Http2DataFrame;
import io.netty.util.ReferenceCountUtil;
import java.io.IOException;
@@ -21,6 +22,8 @@
*/
public final class Netty4InitiateOneReadHandler extends ChannelInboundHandlerAdapter {
private final IOExceptionCheckedConsumer byteBufConsumer;
+ private final boolean isHttp2;
+
private CountDownLatch latch;
private boolean lastRead;
@@ -35,10 +38,13 @@ public final class Netty4InitiateOneReadHandler extends ChannelInboundHandlerAda
*
* @param latch The latch to count down when the channel read completes.
* @param byteBufConsumer The consumer to process the {@link ByteBuf ByteBufs} as they are read.
+ * @param isHttp2 Flag indicating whether the handler is used for HTTP/2 or not.
*/
- public Netty4InitiateOneReadHandler(CountDownLatch latch, IOExceptionCheckedConsumer byteBufConsumer) {
+ public Netty4InitiateOneReadHandler(CountDownLatch latch, IOExceptionCheckedConsumer byteBufConsumer,
+ boolean isHttp2) {
this.latch = latch;
this.byteBufConsumer = byteBufConsumer;
+ this.isHttp2 = isHttp2;
}
/**
@@ -77,7 +83,11 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) {
}
}
- lastRead = msg instanceof LastHttpContent;
+ if (isHttp2) {
+ lastRead = msg instanceof Http2DataFrame && ((Http2DataFrame) msg).isEndStream();
+ } else {
+ lastRead = msg instanceof LastHttpContent;
+ }
ctx.fireChannelRead(msg);
}
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ResponseHandler.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ResponseHandler.java
index a9478416f01a..5da09d7c8924 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ResponseHandler.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4ResponseHandler.java
@@ -25,8 +25,8 @@
import static io.clientcore.http.netty4.implementation.Netty4Utility.setOrSuppressError;
/**
- * A {@link ChannelInboundHandler} implementation that appropriately handles the response reading from the server based
- * on the information provided from the headers.
+ * A {@link ChannelInboundHandler} implementation that appropriately handles {@code HTTP/1.1} responses by using the
+ * response headers to determine how to read the response from the server.
*
* When used with {@code NettyHttpClient} this handler must be added to the pipeline so that the {@link HttpClientCodec}
* is able to decode the data of the response.
@@ -77,7 +77,7 @@ public boolean isSharable() {
}
@Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
setOrSuppressError(errorReference, cause);
latch.countDown();
}
@@ -126,8 +126,8 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
started = true;
HttpResponse response = (HttpResponse) msg;
this.statusCode = response.status().code();
- this.headers = (response.headers() instanceof WrappedHttpHeaders)
- ? ((WrappedHttpHeaders) response.headers()).getCoreHeaders()
+ this.headers = (response.headers() instanceof WrappedHttp11Headers)
+ ? ((WrappedHttp11Headers) response.headers()).getCoreHeaders()
: Netty4Utility.convertHeaders(response.headers());
if (msg instanceof FullHttpResponse) {
@@ -173,11 +173,7 @@ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
responseReference.set(new ResponseStateInfo(ctx.channel(), complete, statusCode, headers, eagerContent,
- ResponseBodyHandling.getBodyHandling(request, headers)));
+ ResponseBodyHandling.getBodyHandling(request, headers), false));
latch.countDown();
}
-
- private enum BodyHandling {
- IGNORE, STREAM, BUFFER
- }
}
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4StreamingHttp2Adapter.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4StreamingHttp2Adapter.java
new file mode 100644
index 000000000000..9c15c5b66098
--- /dev/null
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4StreamingHttp2Adapter.java
@@ -0,0 +1,139 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+package io.clientcore.http.netty4.implementation;
+
+import io.clientcore.core.instrumentation.logging.ClientLogger;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.DefaultHttpContent;
+import io.netty.handler.codec.http.DefaultHttpResponse;
+import io.netty.handler.codec.http.DefaultLastHttpContent;
+import io.netty.handler.codec.http.FullHttpMessage;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpResponse;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http2.Http2CodecUtil;
+import io.netty.handler.codec.http2.Http2Connection;
+import io.netty.handler.codec.http2.Http2Error;
+import io.netty.handler.codec.http2.Http2EventAdapter;
+import io.netty.handler.codec.http2.Http2Exception;
+import io.netty.handler.codec.http2.Http2Headers;
+import io.netty.handler.codec.http2.Http2Settings;
+import io.netty.handler.codec.http2.Http2Stream;
+import io.netty.handler.codec.http2.HttpConversionUtil;
+import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapter;
+
+import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
+import static io.netty.handler.codec.http2.Http2Exception.connectionError;
+import static io.netty.handler.codec.http2.HttpConversionUtil.addHttp2ToHttpHeaders;
+import static io.netty.handler.codec.http2.HttpConversionUtil.parseStatus;
+
+/**
+ * Implementation of {@link Http2EventAdapter} that converts HTTP/2 frames into HTTP/1.1 objects.
+ *
+ * This is similar to {@link InboundHttp2ToHttpAdapter} but it doesn't buffer the entire response into a
+ * {@link FullHttpResponse}. Rather it streams frames as they arrive, allowing for more efficient memory usage.
+ */
+final class Netty4StreamingHttp2Adapter extends Http2EventAdapter {
+ private static final ClientLogger LOGGER = new ClientLogger(Netty4StreamingHttp2Adapter.class);
+
+ private final Http2Connection connection;
+
+ Netty4StreamingHttp2Adapter(Http2Connection connection) {
+ this.connection = connection;
+ }
+
+ // TODO (alzimmer): This implementation is close but needs a way to control when WINDOWS_UPDATE frames are sent to
+ // prevent race conditions between switching from the initial response data handling in Netty4ResponseHandler and
+ // either eager or deferred content reading in the custom handlers.
+ // For now, while huge responses don't need to be supported yet, use InboundHttp2ToHttpAdapter to buffer the
+ // entire response into a FullHttpResponse.
+ @Override
+ public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream)
+ throws Http2Exception {
+ Http2Stream stream = connection.stream(streamId);
+ if (stream == null) {
+ throw LOGGER.throwableAtError()
+ .addKeyValue("streamId", streamId)
+ .log("Data Frame received for unknown stream", message -> connectionError(PROTOCOL_ERROR, message));
+ }
+
+ // data may be using pooled buffers (can't find a way to determine if it is pooled or not), and downstream may
+ // not eagerly consume the data. Create a copy to ensure that the data is not reclaimed / corrupted before use.
+ int dataReadableBytes = data.readableBytes();
+ data = Unpooled.copiedBuffer(data);
+ if (endOfStream) {
+ ctx.fireChannelRead(new DefaultLastHttpContent(data));
+ } else {
+ ctx.fireChannelRead(new DefaultHttpContent(data));
+ }
+
+ // All bytes have been processed.
+ return dataReadableBytes + padding;
+ }
+
+ @Override
+ public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
+ boolean endOfStream) throws Http2Exception {
+ onHeadersRead(ctx, streamId, headers, -1, (short) -1, false, padding, endOfStream);
+ }
+
+ @Override
+ public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
+ short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
+ Http2Stream stream = connection.stream(streamId);
+ if (stream == null) {
+ throw LOGGER.throwableAtError()
+ .addKeyValue("streamId", streamId)
+ .log("Header Frame received for unknown stream", message -> connectionError(PROTOCOL_ERROR, message));
+ }
+
+ HttpHeaders httpHeaders = new WrappedHttp11Headers(new io.clientcore.core.http.models.HttpHeaders());
+ addHttp2ToHttpHeaders(streamId, headers, httpHeaders, HttpVersion.HTTP_1_1, false, false);
+
+ HttpResponse response;
+ if (endOfStream) {
+ response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, parseStatus(headers.status()),
+ Unpooled.EMPTY_BUFFER, new WrappedHttp11Headers(new io.clientcore.core.http.models.HttpHeaders()),
+ new WrappedHttp11Headers(new io.clientcore.core.http.models.HttpHeaders()));
+ addHttp2ToHttpHeaders(streamId, headers, (FullHttpMessage) response, false);
+ } else {
+ HttpHeaders wrappedHeaders = new WrappedHttp11Headers(new io.clientcore.core.http.models.HttpHeaders());
+ response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, parseStatus(headers.status()), wrappedHeaders);
+ addHttp2ToHttpHeaders(streamId, headers, wrappedHeaders, HttpVersion.HTTP_1_1, false, false);
+ }
+
+ // Add special headers for stream dependency and weight.
+ if (streamDependency > Http2CodecUtil.CONNECTION_STREAM_ID) {
+ response.headers()
+ .setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), streamDependency);
+ }
+ if (weight > 0) {
+ response.headers().setShort(HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), weight);
+ }
+
+ ctx.fireChannelRead(response);
+ }
+
+ @Override
+ public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
+ ctx.fireExceptionCaught(LOGGER.throwableAtError()
+ .log("HTTP/2 to HTTP layer caught stream reset",
+ message -> connectionError(Http2Error.valueOf(errorCode), message)));
+ }
+
+ @Override
+ public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId, Http2Headers headers,
+ int padding) throws Http2Exception {
+ ctx.fireExceptionCaught(new UnsupportedOperationException("Push promises are not supported."));
+ }
+
+ @Override
+ public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
+ // Propagate settings for downstream handlers to process.
+ ctx.fireChannelRead(settings);
+ }
+}
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4Utility.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4Utility.java
index 07cae2037b9a..bc0daf8efe8c 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4Utility.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/Netty4Utility.java
@@ -19,20 +19,15 @@
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
-import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpRequest;
+import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpDecoderConfig;
+import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeadersFactory;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
-import io.netty.handler.codec.http2.DefaultHttp2DataFrame;
-import io.netty.handler.codec.http2.DefaultHttp2Headers;
-import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame;
-import io.netty.handler.codec.http2.Http2DataChunkedInput;
-import io.netty.handler.codec.http2.Http2Headers;
-import io.netty.handler.codec.http2.Http2HeadersFrame;
import io.netty.handler.stream.ChunkedInput;
import io.netty.handler.stream.ChunkedNioFile;
import io.netty.handler.stream.ChunkedStream;
@@ -74,16 +69,10 @@ public final class Netty4Utility {
private static final List OPTIONAL_NETTY_VERSION_ARTIFACTS = Arrays
.asList("netty-transport-native-unix-common", "netty-transport-native-epoll", "netty-transport-native-kqueue");
- /**
- * Name given to the {@link Netty4ProgressAndTimeoutHandler} used in the {@link ChannelPipeline} created by
- * {@code NettyHttpClient}.
- */
- public static final String PROGRESS_AND_TIMEOUT_HANDLER_NAME = "Netty4-Progress-And-Timeout-Handler";
-
/**
* Converts Netty HttpHeaders to ClientCore HttpHeaders.
*
- * Most Netty requests should store headers in {@link WrappedHttpHeaders}, but if that doesn't happen this method
+ * Most Netty requests should store headers in {@link WrappedHttp11Headers}, but if that doesn't happen this method
* can be used to convert the Netty headers to ClientCore headers.
*
* @param nettyHeaders Netty HttpHeaders.
@@ -144,7 +133,7 @@ static void readByteBufIntoOutputStream(ByteBuf byteBuf, OutputStream stream) th
/**
* Creates an {@link HttpClientCodec} that uses a custom {@link HttpDecoderConfig} that injects
- * {@link WrappedHttpHeaders} functionality.
+ * {@link WrappedHttp11Headers} functionality.
*
* @return A new {@link HttpClientCodec} instance.
*/
@@ -156,20 +145,20 @@ public static HttpClientCodec createCodec() {
}
/**
- * Custom implementation of {@link HttpHeadersFactory} that creates {@link WrappedHttpHeaders}.
+ * Custom implementation of {@link HttpHeadersFactory} that creates {@link WrappedHttp11Headers}.
*
- * Using {@link WrappedHttpHeaders} is a performance optimization to remove converting Netty's HttpHeaders to
+ * Using {@link WrappedHttp11Headers} is a performance optimization to remove converting Netty's HttpHeaders to
* ClientCore's HttpHeaders and vice versa.
*/
private static final class WrappedHttpHeadersFactory implements HttpHeadersFactory {
@Override
public io.netty.handler.codec.http.HttpHeaders newHeaders() {
- return new WrappedHttpHeaders(new io.clientcore.core.http.models.HttpHeaders());
+ return new WrappedHttp11Headers(new io.clientcore.core.http.models.HttpHeaders());
}
@Override
public io.netty.handler.codec.http.HttpHeaders newEmptyHeaders() {
- return new WrappedHttpHeaders(new io.clientcore.core.http.models.HttpHeaders());
+ return new WrappedHttp11Headers(new io.clientcore.core.http.models.HttpHeaders());
}
}
@@ -178,15 +167,15 @@ public io.netty.handler.codec.http.HttpHeaders newEmptyHeaders() {
* {@link io.netty.handler.codec.http.HttpHeaders}.
*
* This method inspects the Netty {@link io.netty.handler.codec.http.HttpHeaders} for being an instance of
- * {@link WrappedHttpHeaders}. If it is not an instanceof it will use the {@code nettyHeaderName} to retrieve all
+ * {@link WrappedHttp11Headers}. If it is not an instanceof it will use the {@code nettyHeaderName} to retrieve all
* values. If it is an instanceof it will use the {@code clientCoreHeaderName}.
*
* This method is an attempt to optimize retrieval as Netty and ClientCore use different structures for managing
* headers, where in many cases lookup is faster for ClientCore headers.
*
* @param headers The Netty {@link io.netty.handler.codec.http.HttpHeaders} to retrieve all header values from.
- * @param nettyHeaderName The header name to use when retrieving from a non-{@link WrappedHttpHeaders}.
- * @param clientCoreHeaderName The header name to use when retrieving from a {@link WrappedHttpHeaders}.
+ * @param nettyHeaderName The header name to use when retrieving from a non-{@link WrappedHttp11Headers}.
+ * @param clientCoreHeaderName The header name to use when retrieving from a {@link WrappedHttp11Headers}.
* @return The value for the header name, or null if the header didn't exist in the headers.
*/
public static String get(io.netty.handler.codec.http.HttpHeaders headers, CharSequence nettyHeaderName,
@@ -200,21 +189,21 @@ public static String get(io.netty.handler.codec.http.HttpHeaders headers, CharSe
* {@link io.netty.handler.codec.http.HttpHeaders}.
*
* This method inspects the Netty {@link io.netty.handler.codec.http.HttpHeaders} for being an instance of
- * {@link WrappedHttpHeaders}. If it is not an instanceof it will use the {@code nettyHeaderName} to retrieve all
+ * {@link WrappedHttp11Headers}. If it is not an instanceof it will use the {@code nettyHeaderName} to retrieve all
* values. If it is an instanceof it will use the {@code clientCoreHeaderName}.
*
* This method is an attempt to optimize retrieval as Netty and ClientCore use different structures for managing
* headers, where in many cases lookup is faster for ClientCore headers.
*
* @param headers The Netty {@link io.netty.handler.codec.http.HttpHeaders} to retrieve all header values from.
- * @param nettyHeaderName The header name to use when retrieving from a non-{@link WrappedHttpHeaders}.
- * @param clientCoreHeaderName The header name to use when retrieving from a {@link WrappedHttpHeaders}.
+ * @param nettyHeaderName The header name to use when retrieving from a non-{@link WrappedHttp11Headers}.
+ * @param clientCoreHeaderName The header name to use when retrieving from a {@link WrappedHttp11Headers}.
* @return The list of values for the header name, or an empty list if the header didn't exist in the headers.
*/
public static List getAll(io.netty.handler.codec.http.HttpHeaders headers, CharSequence nettyHeaderName,
HttpHeaderName clientCoreHeaderName) {
- if (headers instanceof WrappedHttpHeaders) {
- HttpHeader header = ((WrappedHttpHeaders) headers).getCoreHeaders().get(clientCoreHeaderName);
+ if (headers instanceof WrappedHttp11Headers) {
+ HttpHeader header = ((WrappedHttp11Headers) headers).getCoreHeaders().get(clientCoreHeaderName);
return (header == null) ? Collections.emptyList() : header.getValues();
} else {
return headers.getAll(nettyHeaderName);
@@ -250,20 +239,18 @@ public static void setOrSuppressError(AtomicReference errorReference,
*
* @param request The HTTP request to send.
* @param channel The Channel to send the request.
- * @param progressAndTimeoutHandlerAdded Whether the ChannelPipeline associated with the Channel had the progress
- * and timeout handler added.
* @param errorReference An AtomicReference tracking exceptions seen during the request lifecycle.
* @return A ChannelFuture that will complete once the request has been sent.
*/
public static ChannelFuture sendHttp11Request(HttpRequest request, Channel channel,
- boolean progressAndTimeoutHandlerAdded, AtomicReference errorReference) {
+ AtomicReference errorReference) {
HttpMethod nettyMethod = HttpMethod.valueOf(request.getHttpMethod().toString());
String uri = request.getUri().toString();
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(request.getHeaders());
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(request.getHeaders());
// TODO (alzimmer): This will mutate the underlying ClientCore HttpHeaders. Will need to think about this design
// more once it's closer to completion.
- wrappedHttpHeaders.getCoreHeaders().set(HttpHeaderName.HOST, request.getUri().getHost());
+ wrappedHttp11Headers.getCoreHeaders().set(HttpHeaderName.HOST, request.getUri().getHost());
BinaryData requestBody = request.getBody();
if (requestBody instanceof FileBinaryData) {
@@ -272,15 +259,14 @@ public static ChannelFuture sendHttp11Request(HttpRequest request, Channel chann
return sendChunkedHttp11(channel,
new ChunkedNioFile(FileChannel.open(fileBinaryData.getFile(), StandardOpenOption.READ),
fileBinaryData.getPosition(), fileBinaryData.getLength(), 8192),
- new DefaultHttpRequest(HttpVersion.HTTP_1_1, nettyMethod, uri, wrappedHttpHeaders),
- progressAndTimeoutHandlerAdded, errorReference);
+ new DefaultHttpRequest(HttpVersion.HTTP_1_1, nettyMethod, uri, wrappedHttp11Headers),
+ errorReference);
} catch (IOException ex) {
return channel.newFailedFuture(ex);
}
} else if (requestBody instanceof InputStreamBinaryData) {
return sendChunkedHttp11(channel, new ChunkedStream(requestBody.toStream()),
- new DefaultHttpRequest(HttpVersion.HTTP_1_1, nettyMethod, uri, wrappedHttpHeaders),
- progressAndTimeoutHandlerAdded, errorReference);
+ new DefaultHttpRequest(HttpVersion.HTTP_1_1, nettyMethod, uri, wrappedHttp11Headers), errorReference);
} else {
ByteBuf body = Unpooled.EMPTY_BUFFER;
if (requestBody != null && requestBody != BinaryData.empty()) {
@@ -291,7 +277,7 @@ public static ChannelFuture sendHttp11Request(HttpRequest request, Channel chann
if (body.readableBytes() > 0) {
// TODO (alzimmer): Should we be setting Content-Length here again? Shouldn't this be handled externally
// by the creator of the HttpRequest?
- wrappedHttpHeaders.getCoreHeaders()
+ wrappedHttp11Headers.getCoreHeaders()
.set(HttpHeaderName.CONTENT_LENGTH, String.valueOf(body.readableBytes()));
}
@@ -301,23 +287,16 @@ public static ChannelFuture sendHttp11Request(HttpRequest request, Channel chann
}
return channel.writeAndFlush(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, nettyMethod, uri, body,
- wrappedHttpHeaders, trailersFactory().newHeaders()));
+ wrappedHttp11Headers, trailersFactory().newHeaders()));
}
}
private static ChannelFuture sendChunkedHttp11(Channel channel, ChunkedInput chunkedInput,
- io.netty.handler.codec.http.HttpRequest initialLineAndHeaders, boolean progressAndTimeoutHandlerAdded,
- AtomicReference errorReference) {
+ io.netty.handler.codec.http.HttpRequest initialLineAndHeaders, AtomicReference errorReference) {
if (channel.pipeline().get(Netty4HandlerNames.CHUNKED_WRITER) == null) {
// Add the ChunkedWriteHandler which will handle sending the chunkedInput.
- ChunkedWriteHandler chunkedWriteHandler = new ChunkedWriteHandler();
- if (progressAndTimeoutHandlerAdded) {
- channel.pipeline()
- .addBefore(Netty4HandlerNames.PROGRESS_AND_TIMEOUT, Netty4HandlerNames.CHUNKED_WRITER,
- chunkedWriteHandler);
- } else {
- channel.pipeline().addLast(Netty4HandlerNames.CHUNKED_WRITER, chunkedWriteHandler);
- }
+ channel.pipeline()
+ .addAfter(Netty4HandlerNames.HTTP_CODEC, Netty4HandlerNames.CHUNKED_WRITER, new ChunkedWriteHandler());
}
Throwable error = errorReference.get();
@@ -326,99 +305,7 @@ private static ChannelFuture sendChunkedHttp11(Channel channel, ChunkedInput errorReference) {
- // HTTP/2 requests are more complicated than HTTP/1.1 as they are a stream of frames with specific purposes.
- // Additionally, since we're using multiplexing, we need to associate a stream ID with each frame.
-
- // Send the headers frame(s).
- // Unlike in HTTP/1.1, there isn't a status line on requests. Rather pseudo headers are used.
- // TODO (alzimmer): Create an Http2Headers implementations similar to WrappedHttpHeaders.
- Http2Headers headers = new DefaultHttp2Headers();
- headers.method(request.getHttpMethod().toString());
- headers.scheme(request.getUri().getScheme());
- headers.authority(request.getUri().getAuthority());
- if (request.getUri().getPath() != null) {
- headers.path(request.getUri().getPath());
- }
-
- // If the request doesn't have a body or is a HEAD request, only a headers frame should be sent before the
- // client indicates closure of its half of the stream.
- BinaryData requestBody = request.getBody();
- Long bodyLength = requestBody == null ? null : requestBody.getLength();
- boolean headersOnly = (bodyLength == null || bodyLength == 0)
- || request.getHttpMethod() == io.clientcore.core.http.models.HttpMethod.HEAD;
-
- request.getHeaders()
- .stream()
- .forEach(httpHeader -> headers.add(httpHeader.getName().getCaseInsensitiveName(), httpHeader.getValues()));
- Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(headers, headersOnly);
-
- if (headersOnly) {
- return channel.write(headersFrame);
- }
-
- channel.write(headersFrame);
-
- // Now it's time to write the data frames.
- if (requestBody instanceof FileBinaryData) {
- FileBinaryData fileBinaryData = (FileBinaryData) requestBody;
- try {
- return sendChunkedHttp2(channel,
- new ChunkedNioFile(FileChannel.open(fileBinaryData.getFile(), StandardOpenOption.READ),
- fileBinaryData.getPosition(), fileBinaryData.getLength(), 8192),
- progressAndTimeoutHandlerAdded, errorReference);
- } catch (IOException ex) {
- return channel.newFailedFuture(ex);
- }
- } else if (requestBody instanceof InputStreamBinaryData) {
- return sendChunkedHttp2(channel, new ChunkedStream(requestBody.toStream()), progressAndTimeoutHandlerAdded,
- errorReference);
- } else {
- ByteBuf body = Unpooled.wrappedBuffer(requestBody.toBytes());
-
- Throwable error = errorReference.get();
- if (error != null) {
- return channel.newFailedFuture(error);
- }
-
- return channel.writeAndFlush(new DefaultHttp2DataFrame(body, true));
- }
- }
-
- private static ChannelFuture sendChunkedHttp2(Channel channel, ChunkedInput chunkedInput,
- boolean progressAndTimeoutHandlerAdded, AtomicReference errorReference) {
- if (channel.pipeline().get(Netty4HandlerNames.CHUNKED_WRITER) == null) {
- // Add the ChunkedWriteHandler which will handle sending the chunkedInput.
- ChunkedWriteHandler chunkedWriteHandler = new ChunkedWriteHandler();
- if (progressAndTimeoutHandlerAdded) {
- channel.pipeline()
- .addBefore(Netty4HandlerNames.PROGRESS_AND_TIMEOUT, Netty4HandlerNames.CHUNKED_WRITER,
- chunkedWriteHandler);
- } else {
- channel.pipeline().addLast(Netty4HandlerNames.CHUNKED_WRITER, chunkedWriteHandler);
- }
- }
-
- Throwable error = errorReference.get();
- if (error != null) {
- return channel.newFailedFuture(error);
- }
-
- return channel.writeAndFlush(new Http2DataChunkedInput(chunkedInput, null));
+ return channel.writeAndFlush(new HttpChunkedInput(chunkedInput));
}
/**
@@ -510,6 +397,47 @@ private void log() {
}
}
+ /**
+ * Helper method that hot paths some well-known AsciiString HttpHeaderNames that are known to be used by Netty
+ * internally.
+ *
+ * @param asciiString The CharSequence to check for a known HttpHeaderName.
+ * @return The corresponding HttpHeaderName if it matches a known one, otherwise a new HttpHeaderName created from
+ * the given CharSequence.
+ */
+ @SuppressWarnings("deprecation")
+ public static HttpHeaderName fromPossibleAsciiString(CharSequence asciiString) {
+ if (HttpHeaderNames.ACCEPT_ENCODING == asciiString) {
+ return HttpHeaderName.ACCEPT_ENCODING;
+ } else if (HttpHeaderNames.CONNECTION == asciiString) {
+ return HttpHeaderName.CONNECTION;
+ } else if (HttpHeaderNames.CONTENT_ENCODING == asciiString) {
+ return HttpHeaderName.CONTENT_ENCODING;
+ } else if (HttpHeaderNames.CONTENT_LENGTH == asciiString) {
+ return HttpHeaderName.CONTENT_LENGTH;
+ } else if (HttpHeaderNames.CONTENT_TYPE == asciiString) {
+ return HttpHeaderName.CONTENT_TYPE;
+ } else if (HttpHeaderNames.COOKIE == asciiString) {
+ return HttpHeaderName.COOKIE;
+ } else if (HttpHeaderNames.EXPECT == asciiString) {
+ return HttpHeaderName.EXPECT;
+ } else if (HttpHeaderNames.HOST == asciiString) {
+ return HttpHeaderName.HOST;
+ } else if (HttpHeaderNames.KEEP_ALIVE == asciiString) {
+ return HttpHeaderName.KEEP_ALIVE;
+ } else if (HttpHeaderNames.PROXY_AUTHORIZATION == asciiString) {
+ return HttpHeaderName.PROXY_AUTHORIZATION;
+ } else if (HttpHeaderNames.TE == asciiString) {
+ return HttpHeaderName.TE;
+ } else if (HttpHeaderNames.TRAILER == asciiString) {
+ return HttpHeaderName.TRAILER;
+ } else if (HttpHeaderNames.TRANSFER_ENCODING == asciiString) {
+ return HttpHeaderName.TRANSFER_ENCODING;
+ } else {
+ return HttpHeaderName.fromString(asciiString.toString());
+ }
+ }
+
private Netty4Utility() {
}
}
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/ResponseStateInfo.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/ResponseStateInfo.java
index feb01a1d42af..c31e381c7b60 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/ResponseStateInfo.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/ResponseStateInfo.java
@@ -19,38 +19,80 @@ public final class ResponseStateInfo {
private final HttpHeaders headers;
private final ByteArrayOutputStream eagerContent;
private final ResponseBodyHandling responseBodyHandling;
+ private final boolean isHttp2;
ResponseStateInfo(Channel responseChannel, boolean channelConsumptionComplete, int statusCode, HttpHeaders headers,
- ByteArrayOutputStream eagerContent, ResponseBodyHandling responseBodyHandling) {
+ ByteArrayOutputStream eagerContent, ResponseBodyHandling responseBodyHandling, boolean isHttp2) {
this.responseChannel = responseChannel;
this.channelConsumptionComplete = channelConsumptionComplete;
this.statusCode = statusCode;
this.headers = headers;
this.eagerContent = eagerContent;
this.responseBodyHandling = responseBodyHandling;
+ this.isHttp2 = isHttp2;
}
+ /**
+ * Gets the Netty {@link Channel} that holds the connection to the response.
+ *
+ * @return The Netty {@link Channel} that holds the connection to the response.
+ */
public Channel getResponseChannel() {
return responseChannel;
}
+ /**
+ * Flag indicating whether the channel consumption is complete.
+ *
+ * @return Whether the channel consumption is complete.
+ */
public boolean isChannelConsumptionComplete() {
return channelConsumptionComplete;
}
+ /**
+ * Gets the HTTP status code of the response.
+ *
+ * @return The HTTP status code of the response.
+ */
public int getStatusCode() {
return statusCode;
}
+ /**
+ * Gets the HTTP headers of the response.
+ *
+ * @return The HTTP headers of the response.
+ */
public HttpHeaders getHeaders() {
return headers;
}
+ /**
+ * Gets the content that was eagerly read from the Netty pipeline when processing the initial status line and
+ * headers.
+ *
+ * @return The content that was eagerly read from the Netty pipeline.
+ */
public ByteArrayOutputStream getEagerContent() {
return eagerContent;
}
+ /**
+ * Gets the response body handling strategy.
+ *
+ * @return The response body handling strategy.
+ */
public ResponseBodyHandling getResponseBodyHandling() {
return responseBodyHandling;
}
+
+ /**
+ * Flag indicating whether the connection is using HTTP/2 or not.
+ *
+ * @return Whether the connection is using HTTP/2.
+ */
+ public boolean isHttp2() {
+ return isHttp2;
+ }
}
diff --git a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/WrappedHttpHeaders.java b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/WrappedHttp11Headers.java
similarity index 83%
rename from sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/WrappedHttpHeaders.java
rename to sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/WrappedHttp11Headers.java
index bb79c85fdd66..ae1a3a1c2c62 100644
--- a/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/WrappedHttpHeaders.java
+++ b/sdk/clientcore/http-netty4/src/main/java/io/clientcore/http/netty4/implementation/WrappedHttp11Headers.java
@@ -6,7 +6,6 @@
import io.clientcore.core.http.models.HttpHeaderName;
import io.clientcore.core.instrumentation.logging.ClientLogger;
import io.netty.handler.codec.DateFormatter;
-import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import java.util.AbstractMap;
@@ -22,6 +21,8 @@
import java.util.function.BiFunction;
import java.util.stream.Collectors;
+import static io.clientcore.http.netty4.implementation.Netty4Utility.fromPossibleAsciiString;
+
/**
* Implementation of Netty's {@link HttpHeaders} wrapping an instance of ClientCore's
* {@link io.clientcore.core.http.models.HttpHeader}, eliminating the need to convert between the two HTTP header
@@ -31,18 +32,18 @@
* converted to Netty's {@link io.netty.handler.codec.http2.Http2Headers}, a future optimization if we begin seeing
* more usage of {@code HTTP/2} would be having a similar wrapper for the {@code HTTP/2} headers.
*/
-public final class WrappedHttpHeaders extends HttpHeaders {
- private static final ClientLogger LOGGER = new ClientLogger(WrappedHttpHeaders.class);
+public final class WrappedHttp11Headers extends HttpHeaders {
+ private static final ClientLogger LOGGER = new ClientLogger(WrappedHttp11Headers.class);
private io.clientcore.core.http.models.HttpHeaders coreHeaders;
/**
- * Creates a new instance of {@link WrappedHttpHeaders} wrapping the provided {@code coreHeaders}.
+ * Creates a new instance of {@link WrappedHttp11Headers} wrapping the provided {@code coreHeaders}.
*
* @param coreHeaders The ClientCore {@link io.clientcore.core.http.models.HttpHeaders} to wrap providing
* integration with Netty {@link HttpHeaders}.
* @throws NullPointerException If {@code coreHeaders} is null.
*/
- public WrappedHttpHeaders(io.clientcore.core.http.models.HttpHeaders coreHeaders) {
+ public WrappedHttp11Headers(io.clientcore.core.http.models.HttpHeaders coreHeaders) {
this.coreHeaders = Objects.requireNonNull(coreHeaders, "'coreHeaders' cannot be null.");
}
@@ -301,8 +302,8 @@ public HttpHeaders add(CharSequence name, Iterable> values) {
@Override
public HttpHeaders add(HttpHeaders headers) {
- if (headers instanceof WrappedHttpHeaders) {
- coreHeaders.addAll(((WrappedHttpHeaders) headers).coreHeaders);
+ if (headers instanceof WrappedHttp11Headers) {
+ coreHeaders.addAll(((WrappedHttp11Headers) headers).coreHeaders);
return this;
} else {
return super.add(headers);
@@ -355,8 +356,8 @@ public HttpHeaders set(CharSequence name, Iterable> values) {
@Override
public HttpHeaders set(HttpHeaders headers) {
- if (headers instanceof WrappedHttpHeaders) {
- coreHeaders = new io.clientcore.core.http.models.HttpHeaders(((WrappedHttpHeaders) headers).coreHeaders);
+ if (headers instanceof WrappedHttp11Headers) {
+ coreHeaders = new io.clientcore.core.http.models.HttpHeaders(((WrappedHttp11Headers) headers).coreHeaders);
} else {
super.set(headers);
}
@@ -366,8 +367,8 @@ public HttpHeaders set(HttpHeaders headers) {
@Override
public HttpHeaders setAll(HttpHeaders headers) {
- if (headers instanceof WrappedHttpHeaders) {
- coreHeaders.setAll(((WrappedHttpHeaders) headers).coreHeaders);
+ if (headers instanceof WrappedHttp11Headers) {
+ coreHeaders.setAll(((WrappedHttp11Headers) headers).coreHeaders);
} else {
super.setAll(headers);
}
@@ -377,7 +378,7 @@ public HttpHeaders setAll(HttpHeaders headers) {
@Override
public HttpHeaders copy() {
- return new WrappedHttpHeaders(new io.clientcore.core.http.models.HttpHeaders(coreHeaders));
+ return new WrappedHttp11Headers(new io.clientcore.core.http.models.HttpHeaders(coreHeaders));
}
@Override
@@ -397,39 +398,4 @@ public HttpHeaders clear() {
coreHeaders = new io.clientcore.core.http.models.HttpHeaders();
return this;
}
-
- // Helper method that hot paths some well-known AsciiString HttpHeaderNames that are known to be used by Netty
- // internally.
- @SuppressWarnings("deprecation")
- private static HttpHeaderName fromPossibleAsciiString(CharSequence asciiString) {
- if (HttpHeaderNames.ACCEPT_ENCODING == asciiString) {
- return HttpHeaderName.ACCEPT_ENCODING;
- } else if (HttpHeaderNames.CONNECTION == asciiString) {
- return HttpHeaderName.CONNECTION;
- } else if (HttpHeaderNames.CONTENT_ENCODING == asciiString) {
- return HttpHeaderName.CONTENT_ENCODING;
- } else if (HttpHeaderNames.CONTENT_LENGTH == asciiString) {
- return HttpHeaderName.CONTENT_LENGTH;
- } else if (HttpHeaderNames.CONTENT_TYPE == asciiString) {
- return HttpHeaderName.CONTENT_TYPE;
- } else if (HttpHeaderNames.COOKIE == asciiString) {
- return HttpHeaderName.COOKIE;
- } else if (HttpHeaderNames.EXPECT == asciiString) {
- return HttpHeaderName.EXPECT;
- } else if (HttpHeaderNames.HOST == asciiString) {
- return HttpHeaderName.HOST;
- } else if (HttpHeaderNames.KEEP_ALIVE == asciiString) {
- return HttpHeaderName.KEEP_ALIVE;
- } else if (HttpHeaderNames.PROXY_AUTHORIZATION == asciiString) {
- return HttpHeaderName.PROXY_AUTHORIZATION;
- } else if (HttpHeaderNames.TE == asciiString) {
- return HttpHeaderName.TE;
- } else if (HttpHeaderNames.TRAILER == asciiString) {
- return HttpHeaderName.TRAILER;
- } else if (HttpHeaderNames.TRANSFER_ENCODING == asciiString) {
- return HttpHeaderName.TRANSFER_ENCODING;
- } else {
- return HttpHeaderName.fromString(asciiString.toString());
- }
- }
}
diff --git a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/NettyHttp2HttpClientTests.java b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/NettyHttp2HttpClientTests.java
index f0484462d652..9fb189ccd741 100644
--- a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/NettyHttp2HttpClientTests.java
+++ b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/NettyHttp2HttpClientTests.java
@@ -5,19 +5,25 @@
import io.clientcore.core.http.client.HttpClient;
import io.clientcore.core.http.client.HttpProtocolVersion;
+import io.clientcore.core.http.models.HttpMethod;
+import io.clientcore.core.http.models.HttpRequest;
+import io.clientcore.core.http.models.Response;
+import io.clientcore.core.models.binarydata.BinaryData;
import io.clientcore.core.shared.HttpClientTests;
import io.clientcore.core.shared.HttpClientTestsServer;
import io.clientcore.core.shared.InsecureTrustManager;
import io.clientcore.core.shared.LocalTestServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import java.security.SecureRandom;
+import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
-@Disabled("Support will be added in the future, in another PR.")
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
@Timeout(value = 3, unit = TimeUnit.MINUTES)
public class NettyHttp2HttpClientTests extends HttpClientTests {
private static LocalTestServer server;
@@ -28,7 +34,7 @@ public class NettyHttp2HttpClientTests extends HttpClientTests {
HTTP_CLIENT_INSTANCE = new NettyHttpClientBuilder()
.sslContextModifier(
builder -> builder.trustManager(new InsecureTrustManager()).secureRandom(new SecureRandom()))
- //.maximumHttpVersion(HttpProtocolVersion.HTTP_2)
+ .maximumHttpVersion(HttpProtocolVersion.HTTP_2)
.build();
}
@@ -71,4 +77,17 @@ protected String getServerUri(boolean secure) {
protected HttpClient getHttpClient() {
return HTTP_CLIENT_INSTANCE;
}
+
+ @Test
+ public void canSendBinaryDataDebug() {
+ byte[] expectedBytes = new byte[1024 * 1024];
+ ThreadLocalRandom.current().nextBytes(expectedBytes);
+ HttpRequest request = new HttpRequest().setMethod(HttpMethod.PUT)
+ .setUri(getRequestUri("echo"))
+ .setBody(BinaryData.fromBytes(expectedBytes));
+
+ try (Response response = getHttpClient().send(request)) {
+ assertArrayEquals(expectedBytes, response.getValue().toBytes());
+ }
+ }
}
diff --git a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4ChannelBinaryDataTests.java b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4Http11ChannelBinaryDataTests.java
similarity index 92%
rename from sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4ChannelBinaryDataTests.java
rename to sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4Http11ChannelBinaryDataTests.java
index 6e82642d4754..a51af8e6be19 100644
--- a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4ChannelBinaryDataTests.java
+++ b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4Http11ChannelBinaryDataTests.java
@@ -30,11 +30,11 @@
* Tests {@link Netty4ChannelBinaryData}.
*/
@Timeout(value = 3, unit = TimeUnit.MINUTES)
-public class Netty4ChannelBinaryDataTests {
+public class Netty4Http11ChannelBinaryDataTests {
@Test
public void toBytesWillThrowIsLengthIsTooLarge() {
assertThrows(IllegalStateException.class,
- () -> new Netty4ChannelBinaryData(null, null, Long.MAX_VALUE).toBytes());
+ () -> new Netty4ChannelBinaryData(null, null, Long.MAX_VALUE, false).toBytes());
}
@Test
@@ -45,7 +45,7 @@ public void toBytesCaches() throws IOException {
eagerContent.write(expected);
Netty4ChannelBinaryData binaryData
- = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) expected.length);
+ = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) expected.length, false);
assertArraysEqual(expected, binaryData.toBytes());
assertArraysEqual(expected, binaryData.toBytes());
@@ -60,7 +60,7 @@ public void toStringTest() throws IOException {
eagerContent.write(bytes);
Netty4ChannelBinaryData binaryData
- = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) bytes.length);
+ = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) bytes.length, false);
assertEquals(expected, binaryData.toString());
}
@@ -74,7 +74,7 @@ public void toByteBuffer() throws IOException {
eagerContent.write(bytes);
Netty4ChannelBinaryData binaryData
- = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) bytes.length);
+ = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) bytes.length, false);
assertEquals(expected, binaryData.toByteBuffer());
}
@@ -87,7 +87,7 @@ public void toStreamUsesBytesIfToBytesWasAlreadyCalled() throws IOException {
eagerContent.write(expected);
Netty4ChannelBinaryData binaryData
- = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) expected.length);
+ = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) expected.length, false);
assertArraysEqual(expected, binaryData.toBytes());
@@ -112,7 +112,7 @@ public void writeToOutputStreamUsesBytesIfToBytesWasAlreadyCalled() throws IOExc
eagerContent.write(expected);
Netty4ChannelBinaryData binaryData
- = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) expected.length);
+ = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) expected.length, false);
assertArraysEqual(expected, binaryData.toBytes());
@@ -124,17 +124,17 @@ public void writeToOutputStreamUsesBytesIfToBytesWasAlreadyCalled() throws IOExc
@Test
public void channelBinaryDataLengthIsKnown() {
- assertEquals(1, new Netty4ChannelBinaryData(null, null, 1L).getLength());
+ assertEquals(1, new Netty4ChannelBinaryData(null, null, 1L, false).getLength());
}
@Test
public void channelBinaryDataLengthIsUnknown() {
- assertNull(new Netty4ChannelBinaryData(null, null, null).getLength());
+ assertNull(new Netty4ChannelBinaryData(null, null, null, false).getLength());
}
@Test
public void channelBinaryDataIsNeverReplayable() {
- assertFalse(new Netty4ChannelBinaryData(null, null, null).isReplayable());
+ assertFalse(new Netty4ChannelBinaryData(null, null, null, false).isReplayable());
}
@Test
@@ -145,7 +145,7 @@ public void channelBinaryDataToReplayableReturnsAByteArrayBinaryData() throws IO
eagerContent.write(expected);
Netty4ChannelBinaryData binaryData
- = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) expected.length);
+ = new Netty4ChannelBinaryData(eagerContent, channelWithNoData(), (long) expected.length, false);
BinaryData replayable = binaryData.toReplayableBinaryData();
assertInstanceOf(ByteArrayBinaryData.class, replayable);
diff --git a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4ChannelInputStreamTests.java b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4Http11ChannelInputStreamTests.java
similarity index 95%
rename from sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4ChannelInputStreamTests.java
rename to sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4Http11ChannelInputStreamTests.java
index d070769fe761..67df3b7ac43e 100644
--- a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4ChannelInputStreamTests.java
+++ b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4Http11ChannelInputStreamTests.java
@@ -37,11 +37,11 @@
* Tests {@link Netty4ChannelInputStream}.
*/
@Timeout(value = 3, unit = TimeUnit.MINUTES)
-public class Netty4ChannelInputStreamTests {
+public class Netty4Http11ChannelInputStreamTests {
@Test
public void nullEagerContentResultsInEmptyInitialCurrentBuffer() {
try (Netty4ChannelInputStream channelInputStream
- = new Netty4ChannelInputStream(null, createCloseableChannel())) {
+ = new Netty4ChannelInputStream(null, createCloseableChannel(), false)) {
assertEquals(0, channelInputStream.getCurrentBuffer().length);
}
}
@@ -49,7 +49,7 @@ public void nullEagerContentResultsInEmptyInitialCurrentBuffer() {
@Test
public void emptyEagerContentResultsInEmptyInitialCurrentBuffer() {
try (Netty4ChannelInputStream channelInputStream
- = new Netty4ChannelInputStream(new ByteArrayOutputStream(), createCloseableChannel())) {
+ = new Netty4ChannelInputStream(new ByteArrayOutputStream(), createCloseableChannel(), false)) {
assertEquals(0, channelInputStream.getCurrentBuffer().length);
}
}
@@ -63,7 +63,8 @@ public void readConsumesCurrentBufferAndHasNoMoreData() throws IOException {
eagerContent.write(expected);
// MockChannels aren't active by default, so once the eagerContent is consumed the stream will be done.
- Netty4ChannelInputStream channelInputStream = new Netty4ChannelInputStream(eagerContent, new MockChannel());
+ Netty4ChannelInputStream channelInputStream
+ = new Netty4ChannelInputStream(eagerContent, new MockChannel(), false);
// Make sure the Netty4ChannelInputStream copied the eager content correctly.
assertArraysEqual(expected, channelInputStream.getCurrentBuffer());
@@ -95,7 +96,7 @@ public void readConsumesCurrentBufferAndRequestsMoreData() throws IOException {
handler.channelRead(ctx, wrappedBuffer(expected, 16, 16));
handler.channelRead(ctx, LastHttpContent.EMPTY_LAST_CONTENT);
handler.channelReadComplete(ctx);
- }));
+ }), false);
int index = 0;
byte[] actual = new byte[32];
@@ -117,7 +118,7 @@ public void multipleSmallerSkips() throws IOException {
// MockChannels aren't active by default, so once the eagerContent is consumed the stream will be done.
try (Netty4ChannelInputStream channelInputStream
- = new Netty4ChannelInputStream(eagerContent, createCloseableChannel())) {
+ = new Netty4ChannelInputStream(eagerContent, createCloseableChannel(), false)) {
long skipped = channelInputStream.skip(16);
assertEquals(16, skipped);
@@ -140,7 +141,7 @@ public void largeReadTriggersMultipleChannelReads() throws IOException {
ThreadLocalRandom.current().nextBytes(expected);
try (Netty4ChannelInputStream channelInputStream
- = new Netty4ChannelInputStream(null, createChannelThatReads8Kb(expected))) {
+ = new Netty4ChannelInputStream(null, createChannelThatReads8Kb(expected), false)) {
byte[] actual = new byte[8192];
int read = channelInputStream.read(actual);
@@ -161,7 +162,7 @@ public void largeSkipTriggersMultipleChannelReads() throws IOException {
ThreadLocalRandom.current().nextBytes(expected);
try (Netty4ChannelInputStream channelInputStream
- = new Netty4ChannelInputStream(null, createChannelThatReads8Kb(expected))) {
+ = new Netty4ChannelInputStream(null, createChannelThatReads8Kb(expected), false)) {
long skipped = channelInputStream.skip(8192);
assertEquals(8192, skipped);
@@ -176,7 +177,7 @@ public void closingStreamClosesChannel() {
AtomicInteger disconnectCount = new AtomicInteger();
new Netty4ChannelInputStream(null,
- createCloseableChannel(closeCount::incrementAndGet, disconnectCount::incrementAndGet)).close();
+ createCloseableChannel(closeCount::incrementAndGet, disconnectCount::incrementAndGet), false).close();
assertEquals(1, closeCount.get());
}
@@ -184,7 +185,8 @@ public void closingStreamClosesChannel() {
@ParameterizedTest
@MethodSource("errorSupplier")
public void streamPropagatesErrorFiredInChannel(Throwable expected) {
- InputStream inputStream = new Netty4ChannelInputStream(null, createPartialReadThenErrorChannel(expected));
+ InputStream inputStream
+ = new Netty4ChannelInputStream(null, createPartialReadThenErrorChannel(expected), false);
Throwable actual = assertThrows(Throwable.class, () -> inputStream.read(new byte[8192]));
diff --git a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4HttpProxyHandlerTests.java b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4HttpProxyHandlerTests.java
index 47960fa7021c..76014c56b9e5 100644
--- a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4HttpProxyHandlerTests.java
+++ b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/Netty4HttpProxyHandlerTests.java
@@ -42,10 +42,13 @@ public void validateProxyAuthenticationInfoThrowsOnMismatch(String infoHeader, S
}
private static Stream mismatchData() {
- return Stream.of(Arguments.of("cnonce=1", "cnonce=2",
- "Property received in the 'Proxy-Authentication-Info' header doesn't match the value sent in the 'Proxy-Authorization' header; {\"propertyName\":\"cnonce\",\"received\":\"1\",\"sent\":\"2\"}"),
+ return Stream.of(
+ Arguments.of("cnonce=1", "cnonce=2",
+ "Property received in the 'Proxy-Authentication-Info' header doesn't match the value sent in the "
+ + "'Proxy-Authorization' header; {\"propertyName\":\"cnonce\",\"received\":\"1\",\"sent\":\"2\"}"),
Arguments.of("nc=1", "nc=2",
- "Property received in the 'Proxy-Authentication-Info' header doesn't match the value sent in the 'Proxy-Authorization' header; {\"propertyName\":\"nc\",\"received\":\"1\",\"sent\":\"2\"}"));
+ "Property received in the 'Proxy-Authentication-Info' header doesn't match the value sent in the "
+ + "'Proxy-Authorization' header; {\"propertyName\":\"nc\",\"received\":\"1\",\"sent\":\"2\"}"));
}
@Test
diff --git a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/WrappedHttp11HeadersTests.java b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/WrappedHttp11HeadersTests.java
new file mode 100644
index 000000000000..e396a5b508f5
--- /dev/null
+++ b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/WrappedHttp11HeadersTests.java
@@ -0,0 +1,656 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+package io.clientcore.http.netty4.implementation;
+
+import io.clientcore.core.http.models.HttpHeader;
+import io.clientcore.core.http.models.HttpHeaderName;
+import io.clientcore.core.http.models.HttpHeaders;
+import io.clientcore.core.utils.DateTimeRfc1123;
+import io.netty.handler.codec.http.DefaultHttpHeaders;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import org.junit.jupiter.api.Test;
+
+import java.time.OffsetDateTime;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertLinesMatch;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Unit tests for {@link WrappedHttp11Headers}.
+ */
+public class WrappedHttp11HeadersTests {
+ @Test
+ public void throwsOnNullClientCoreHttpHeaders() {
+ assertThrows(NullPointerException.class, () -> new WrappedHttp11Headers(null));
+ }
+
+ @Test
+ public void addCharSequenceIterable() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ wrappedHttp11Headers.add(HttpHeaderNames.ACCEPT_ENCODING, Arrays.asList("gzip", "deflate"));
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.ACCEPT_ENCODING);
+ assertNotNull(header);
+ assertLinesMatch(Arrays.asList("gzip", "deflate"), header.getValues());
+ }
+
+ @Test
+ public void addCharSequenceIterableThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.add(HttpHeaderNames.ACCEPT_ENCODING, (Iterable>) null));
+ }
+
+ @Test
+ public void addCharSequenceIterableThrowsIfAnyNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.add(HttpHeaderNames.ACCEPT_ENCODING, Arrays.asList("gzip", null)));
+ }
+
+ @Test
+ public void addCharSequenceObject() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ wrappedHttp11Headers.add(HttpHeaderNames.CONTENT_LENGTH, 42);
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
+ assertNotNull(header);
+ assertEquals("42", header.getValue());
+ }
+
+ @Test
+ public void addCharSequenceObjectThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.add(HttpHeaderNames.CONTENT_LENGTH, (Object) null));
+ }
+
+ @Test
+ public void addStringIterable() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ wrappedHttp11Headers.add("Accept-Encoding", Arrays.asList("gzip", "deflate"));
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.ACCEPT_ENCODING);
+ assertNotNull(header);
+ assertLinesMatch(Arrays.asList("gzip", "deflate"), header.getValues());
+ }
+
+ @Test
+ public void addStringIterableThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class, () -> wrappedHttp11Headers.add("Accept-Encoding", (Iterable>) null));
+ }
+
+ @Test
+ public void addStringIterableThrowsIfAnyNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.add("Accept-Encoding", Arrays.asList("gzip", null)));
+ }
+
+ @Test
+ public void addStringObject() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ wrappedHttp11Headers.add("Content-Length", 42);
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
+ assertNotNull(header);
+ assertEquals("42", header.getValue());
+ }
+
+ @Test
+ public void addStringObjectThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class, () -> wrappedHttp11Headers.add("Content-Length", (Object) null));
+ }
+
+ @Test
+ public void addCharSequenceInt() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ wrappedHttp11Headers.addInt("Content-Length", 42);
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
+ assertNotNull(header);
+ assertEquals("42", header.getValue());
+ }
+
+ @Test
+ public void addCharSequenceShort() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ wrappedHttp11Headers.addShort("Content-Length", (short) 42);
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
+ assertNotNull(header);
+ assertEquals("42", header.getValue());
+ }
+
+ @Test
+ public void addHttpHeadersHotPathsWrappedHttpHeaders() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ WrappedHttp11Headers toAdd = new WrappedHttp11Headers(
+ new HttpHeaders().set(HttpHeaderName.KEEP_ALIVE, "true").set(HttpHeaderName.CONTENT_LENGTH, "42"));
+
+ wrappedHttp11Headers.add(toAdd);
+
+ HttpHeaders coreHeaders = wrappedHttp11Headers.getCoreHeaders();
+ assertEquals(2, coreHeaders.getSize());
+ assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
+ assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void addHttpHeadersFallsBackToSuperImplementation() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ io.netty.handler.codec.http.HttpHeaders toAdd = new DefaultHttpHeaders().add(HttpHeaderNames.KEEP_ALIVE, "true")
+ .add(HttpHeaderNames.CONTENT_LENGTH, "42");
+
+ wrappedHttp11Headers.add(toAdd);
+
+ HttpHeaders coreHeaders = wrappedHttp11Headers.getCoreHeaders();
+ assertEquals(2, coreHeaders.getSize());
+ assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
+ assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
+ }
+
+ @Test
+ public void addHttpHeadersThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.add((io.netty.handler.codec.http.HttpHeaders) null));
+ }
+
+ @Test
+ public void clearUsesNewClientCoreHttpHeaders() {
+ HttpHeaders initial = new HttpHeaders();
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(initial);
+ wrappedHttp11Headers.clear();
+
+ assertNotSame(initial, wrappedHttp11Headers.getCoreHeaders());
+ }
+
+ @Test
+ public void containsCharSequenceReturnsFalseWhenHeaderDoesNotExist() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertFalse(wrappedHttp11Headers.contains(HttpHeaderNames.CONTENT_LENGTH));
+ }
+
+ @Test
+ public void containsCharSequence() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.CONNECTION, "connection"));
+
+ assertTrue(wrappedHttp11Headers.contains(HttpHeaderNames.CONNECTION));
+ }
+
+ @Test
+ public void containsStringReturnsFalseWhenHeaderDoesNotExist() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertFalse(wrappedHttp11Headers.contains("Content-Length"));
+ }
+
+ @Test
+ public void containsString() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.CONNECTION, "connection"));
+
+ assertTrue(wrappedHttp11Headers.contains("Connection"));
+ }
+
+ @Test
+ public void copyUsesNewClientCoreHeaders() {
+ HttpHeaders initial = new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42");
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(initial);
+
+ WrappedHttp11Headers copy = (WrappedHttp11Headers) wrappedHttp11Headers.copy();
+
+ assertNotSame(initial, copy.getCoreHeaders());
+ assertEquals("42", wrappedHttp11Headers.get(HttpHeaderNames.CONTENT_LENGTH));
+ }
+
+ @Test
+ public void getCharSequenceReturnsNullWhenHeaderDoesNotExist() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertNull(wrappedHttp11Headers.get(HttpHeaderNames.CONTENT_ENCODING));
+ }
+
+ @Test
+ public void getCharSequenceReturnsFirstValue() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders()
+ .set(HttpHeaderName.CONTENT_ENCODING, Arrays.asList("application/json", "application/xml")));
+
+ assertEquals("application/json", wrappedHttp11Headers.get(HttpHeaderNames.CONTENT_ENCODING));
+ }
+
+ @Test
+ public void getStringReturnsNullWhenHeaderDoesNotExist() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertNull(wrappedHttp11Headers.get("Content-Encoding"));
+ }
+
+ @Test
+ public void getStringReturnsFirstValue() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders()
+ .set(HttpHeaderName.CONTENT_ENCODING, Arrays.asList("application/json", "application/xml")));
+
+ assertEquals("application/json", wrappedHttp11Headers.get("Content-Encoding"));
+ }
+
+ @Test
+ public void getAllCharSequenceReturnsEmptyListWhenHeaderDoesNotExist() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertSame(Collections.emptyList(), wrappedHttp11Headers.getAll(HttpHeaderNames.CONTENT_TYPE));
+ }
+
+ @Test
+ public void getAllCharSequence() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(
+ new HttpHeaders().set(HttpHeaderName.CONTENT_TYPE, Arrays.asList("application/json", "application/xml")));
+
+ assertLinesMatch(Arrays.asList("application/json", "application/xml"),
+ wrappedHttp11Headers.getAll(HttpHeaderNames.CONTENT_TYPE));
+ }
+
+ @Test
+ public void getAllStringReturnsEmptyListWhenHeaderDoesNotExist() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertSame(Collections.emptyList(), wrappedHttp11Headers.getAll("Content-Type"));
+ }
+
+ @Test
+ public void getAllString() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(
+ new HttpHeaders().set(HttpHeaderName.CONTENT_TYPE, Arrays.asList("application/json", "application/xml")));
+
+ assertLinesMatch(Arrays.asList("application/json", "application/xml"),
+ wrappedHttp11Headers.getAll("Content-Type"));
+ }
+
+ @Test
+ public void getIntReturnsNullWhenHeaderDoesNotExist() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertNull(wrappedHttp11Headers.getInt(HttpHeaderNames.CONTENT_LENGTH));
+ }
+
+ @Test
+ public void getIntReturnsFirstValue() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.COOKIE, Arrays.asList("1", "2")));
+
+ assertEquals(1, wrappedHttp11Headers.getInt(HttpHeaderNames.COOKIE));
+ }
+
+ @Test
+ public void getIntReturnsNullOnInvalidParse() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.EXPECT, "expect"));
+
+ assertNull(wrappedHttp11Headers.getInt(HttpHeaderNames.EXPECT));
+ }
+
+ @Test
+ public void getIntWithDefaultReturnsDefaultOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertEquals(1, wrappedHttp11Headers.getInt(HttpHeaderNames.CONTENT_LENGTH, 1));
+ }
+
+ @Test
+ public void getIntWithDefaultReturnsDefaultOnInvalidParse() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.HOST, "host"));
+
+ assertEquals(1, wrappedHttp11Headers.getInt(HttpHeaderNames.HOST, 1));
+ }
+
+ @Test
+ public void getIntReturnsActualValue() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42"));
+
+ assertEquals(42, wrappedHttp11Headers.getInt(HttpHeaderNames.CONTENT_LENGTH, 24));
+ }
+
+ @Test
+ public void getShortReturnsNullWhenHeaderDoesNotExist() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertNull(wrappedHttp11Headers.getShort(HttpHeaderNames.CONTENT_LENGTH));
+ }
+
+ @Test
+ public void getShortReturnsFirstValue() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.COOKIE, Arrays.asList("1", "2")));
+
+ assertEquals((short) 1, wrappedHttp11Headers.getShort(HttpHeaderNames.COOKIE));
+ }
+
+ @Test
+ public void getShortReturnsNullOnInvalidParse() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.PROXY_AUTHORIZATION, "authorization"));
+
+ assertNull(wrappedHttp11Headers.getShort(HttpHeaderNames.PROXY_AUTHORIZATION));
+ }
+
+ @Test
+ public void getShortWithDefaultReturnsDefaultOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertEquals((short) 1, wrappedHttp11Headers.getShort(HttpHeaderNames.CONTENT_LENGTH, (short) 1));
+ }
+
+ @Test
+ public void getShortWithDefaultReturnsDefaultOnInvalidParse() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.TE, "TE"));
+
+ assertEquals((short) 1, wrappedHttp11Headers.getShort(HttpHeaderNames.TE, (short) 1));
+ }
+
+ @Test
+ public void getShortReturnsActualValue() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42"));
+
+ assertEquals((short) 42, wrappedHttp11Headers.getShort(HttpHeaderNames.CONTENT_LENGTH, (short) 24));
+ }
+
+ @Test
+ public void isEmptyIsBasedOnClientCoreHeaders() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertTrue(wrappedHttp11Headers.isEmpty());
+
+ wrappedHttp11Headers.getCoreHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42");
+ assertFalse(wrappedHttp11Headers.isEmpty());
+ }
+
+ @Test
+ public void removeCharSequence() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.TRAILER, "trailer"));
+ wrappedHttp11Headers.remove(HttpHeaderNames.TRAILER);
+
+ assertTrue(wrappedHttp11Headers.isEmpty());
+ assertNull(wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.TRAILER));
+ }
+
+ @Test
+ public void removeString() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.TRAILER, "trailer"));
+ wrappedHttp11Headers.remove("Trailer");
+
+ assertTrue(wrappedHttp11Headers.isEmpty());
+ assertNull(wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.TRAILER));
+ }
+
+ @Test
+ public void setCharSequenceIterable() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.ACCEPT_ENCODING, "*"));
+ wrappedHttp11Headers.set(HttpHeaderNames.ACCEPT_ENCODING, Arrays.asList("gzip", "deflate"));
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.ACCEPT_ENCODING);
+ assertNotNull(header);
+ assertLinesMatch(Arrays.asList("gzip", "deflate"), header.getValues());
+ }
+
+ @Test
+ public void setCharSequenceIterableThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.set(HttpHeaderNames.ACCEPT_ENCODING, (Iterable>) null));
+ }
+
+ @Test
+ public void setCharSequenceIterableThrowsIfAnyNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.set(HttpHeaderNames.ACCEPT_ENCODING, Arrays.asList("gzip", null)));
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void setCharSequenceObject() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.KEEP_ALIVE, "false"));
+ wrappedHttp11Headers.set(HttpHeaderNames.KEEP_ALIVE, "true");
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.KEEP_ALIVE);
+ assertNotNull(header);
+ assertEquals("true", header.getValue());
+ }
+
+ @Test
+ public void setCharSequenceObjectThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.set(HttpHeaderNames.CONTENT_LENGTH, (Object) null));
+ }
+
+ @Test
+ public void setStringIterable() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.ACCEPT_ENCODING, "*"));
+ wrappedHttp11Headers.set("Accept-Encoding", Arrays.asList("gzip", "deflate"));
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.ACCEPT_ENCODING);
+ assertNotNull(header);
+ assertLinesMatch(Arrays.asList("gzip", "deflate"), header.getValues());
+ }
+
+ @Test
+ public void setStringIterableThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class, () -> wrappedHttp11Headers.set("Accept-Encoding", (Iterable>) null));
+ }
+
+ @Test
+ public void setStringIterableThrowsIfAnyNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.set("Accept-Encoding", Arrays.asList("gzip", null)));
+ }
+
+ @Test
+ public void setStringObject() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "24"));
+ wrappedHttp11Headers.set("Content-Length", 42);
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
+ assertNotNull(header);
+ assertEquals("42", header.getValue());
+ }
+
+ @Test
+ public void setStringObjectThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class, () -> wrappedHttp11Headers.set("Content-Length", (Object) null));
+ }
+
+ @Test
+ public void setCharSequenceInt() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "24"));
+ wrappedHttp11Headers.setInt("Content-Length", 42);
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
+ assertNotNull(header);
+ assertEquals("42", header.getValue());
+ }
+
+ @Test
+ public void setCharSequenceShort() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "24"));
+ wrappedHttp11Headers.setShort("Content-Length", (short) 42);
+
+ HttpHeader header = wrappedHttp11Headers.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
+ assertNotNull(header);
+ assertEquals("42", header.getValue());
+ }
+
+ @Test
+ public void setHttpHeadersHotPathsWrappedHttpHeaders() {
+ HttpHeaders initial = new HttpHeaders(0);
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(initial);
+ WrappedHttp11Headers toAdd = new WrappedHttp11Headers(
+ new HttpHeaders().set(HttpHeaderName.KEEP_ALIVE, "true").set(HttpHeaderName.CONTENT_LENGTH, "42"));
+
+ wrappedHttp11Headers.set(toAdd);
+
+ HttpHeaders coreHeaders = wrappedHttp11Headers.getCoreHeaders();
+ assertNotSame(initial, coreHeaders);
+ assertNotSame(toAdd.getCoreHeaders(), coreHeaders);
+ assertEquals(2, coreHeaders.getSize());
+ assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
+ assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
+ }
+
+ @Test
+ public void setHttpHeadersFallsBackToSuperImplementation() {
+ HttpHeaders initial = new HttpHeaders();
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(initial);
+ io.netty.handler.codec.http.HttpHeaders toAdd
+ = new DefaultHttpHeaders().add("Keep-Alive", "true").add(HttpHeaderNames.CONTENT_LENGTH, "42");
+
+ wrappedHttp11Headers.set(toAdd);
+
+ HttpHeaders coreHeaders = wrappedHttp11Headers.getCoreHeaders();
+ assertNotSame(initial, coreHeaders);
+ assertEquals(2, coreHeaders.getSize());
+ assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
+ assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
+ }
+
+ @Test
+ public void setHttpHeadersThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class,
+ () -> wrappedHttp11Headers.set((io.netty.handler.codec.http.HttpHeaders) null));
+ }
+
+ @Test
+ public void setAllHttpHeadersHotPathsWrappedHttpHeaders() {
+ HttpHeaders initial = new HttpHeaders(0);
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(initial);
+ WrappedHttp11Headers toAdd = new WrappedHttp11Headers(
+ new HttpHeaders().set(HttpHeaderName.KEEP_ALIVE, "true").set(HttpHeaderName.CONTENT_LENGTH, "42"));
+
+ wrappedHttp11Headers.setAll(toAdd);
+
+ HttpHeaders coreHeaders = wrappedHttp11Headers.getCoreHeaders();
+ assertSame(initial, coreHeaders);
+ assertEquals(2, coreHeaders.getSize());
+ assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
+ assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
+ }
+
+ @Test
+ public void setAllHttpHeadersFallsBackToSuperImplementation() {
+ HttpHeaders initial = new HttpHeaders();
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(initial);
+ io.netty.handler.codec.http.HttpHeaders toAdd
+ = new DefaultHttpHeaders().add("Keep-Alive", "true").add(HttpHeaderNames.CONTENT_LENGTH, "42");
+
+ wrappedHttp11Headers.setAll(toAdd);
+
+ HttpHeaders coreHeaders = wrappedHttp11Headers.getCoreHeaders();
+ assertSame(initial, coreHeaders);
+ assertEquals(2, coreHeaders.getSize());
+ assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
+ assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
+ }
+
+ @Test
+ public void setAllHttpHeadersThrowsOnNull() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+ assertThrows(NullPointerException.class, () -> wrappedHttp11Headers.setAll(null));
+ }
+
+ @Test
+ public void names() {
+ HttpHeaders clientCoreHeaders = new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42")
+ .set(HttpHeaderName.CONTENT_TYPE, "application/json")
+ .set(HttpHeaderName.ACCEPT, Arrays.asList("application/json", "text/json"));
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(clientCoreHeaders);
+
+ Set expectedNames = new HashSet<>(Arrays.asList(HttpHeaderName.CONTENT_LENGTH.getCaseSensitiveName(),
+ HttpHeaderName.CONTENT_TYPE.getCaseSensitiveName(), HttpHeaderName.ACCEPT.getCaseSensitiveName()));
+ Set names = wrappedHttp11Headers.names();
+
+ assertEquals(expectedNames, names);
+ }
+
+ @Test
+ public void getTimeMillis() {
+ OffsetDateTime now = OffsetDateTime.now();
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(
+ new HttpHeaders().set(HttpHeaderName.DATE, DateTimeRfc1123.toRfc1123String(now)));
+
+ Long timeMillis = wrappedHttp11Headers.getTimeMillis(HttpHeaderNames.DATE);
+ assertNotNull(timeMillis);
+
+ // Use OffsetDateTime.toEpochSecond() * 1000L to get the expected millis as DateTimeRfc1123 only has the ability
+ // to represent to seconds. If OffsetDateTime.toInstant().toEpochMilli() is used, it will return the current
+ // time in milliseconds, which cannot be represented.
+ assertEquals(now.toEpochSecond() * 1000L, timeMillis);
+ }
+
+ @Test
+ public void getTimeMillisReturnsNullWhenHeaderDoesNotExist() {
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(new HttpHeaders());
+
+ assertNull(wrappedHttp11Headers.getTimeMillis(HttpHeaderNames.DATE));
+ }
+
+ @Test
+ public void getTimeMillisReturnsNullWhenHeaderIsNotADate() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.DATE, "notADate"));
+
+ assertNull(wrappedHttp11Headers.getTimeMillis(HttpHeaderNames.DATE));
+ }
+
+ @Test
+ public void getTimeMillisReturnsDefaultWhenHeaderIsNotADate() {
+ WrappedHttp11Headers wrappedHttp11Headers
+ = new WrappedHttp11Headers(new HttpHeaders().set(HttpHeaderName.DATE, "notADate"));
+
+ assertEquals(42L, wrappedHttp11Headers.getTimeMillis(HttpHeaderNames.DATE, 42L));
+ }
+
+ @Test
+ public void getTimeMillisReturnsActualValue() {
+ OffsetDateTime now = OffsetDateTime.now();
+ WrappedHttp11Headers wrappedHttp11Headers = new WrappedHttp11Headers(
+ new HttpHeaders().set(HttpHeaderName.DATE, DateTimeRfc1123.toRfc1123String(now)));
+
+ // Use OffsetDateTime.toEpochSecond() * 1000L to get the expected millis as DateTimeRfc1123 only has the ability
+ // to represent to seconds. If OffsetDateTime.toInstant().toEpochMilli() is used, it will return the current
+ // time in milliseconds, which cannot be represented.
+ assertEquals(now.toEpochSecond() * 1000L,
+ wrappedHttp11Headers.getTimeMillis(HttpHeaderNames.DATE, now.toInstant().toEpochMilli()));
+ }
+}
diff --git a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/WrappedHttpHeadersTests.java b/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/WrappedHttpHeadersTests.java
deleted file mode 100644
index 260bed58a757..000000000000
--- a/sdk/clientcore/http-netty4/src/test/java/io/clientcore/http/netty4/implementation/WrappedHttpHeadersTests.java
+++ /dev/null
@@ -1,655 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-package io.clientcore.http.netty4.implementation;
-
-import io.clientcore.core.http.models.HttpHeader;
-import io.clientcore.core.http.models.HttpHeaderName;
-import io.clientcore.core.http.models.HttpHeaders;
-import io.clientcore.core.utils.DateTimeRfc1123;
-import io.netty.handler.codec.http.DefaultHttpHeaders;
-import io.netty.handler.codec.http.HttpHeaderNames;
-import org.junit.jupiter.api.Test;
-
-import java.time.OffsetDateTime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertLinesMatch;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNotSame;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * Unit tests for {@link WrappedHttpHeaders}.
- */
-public class WrappedHttpHeadersTests {
- @Test
- public void throwsOnNullClientCoreHttpHeaders() {
- assertThrows(NullPointerException.class, () -> new WrappedHttpHeaders(null));
- }
-
- @Test
- public void addCharSequenceIterable() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- wrappedHttpHeaders.add(HttpHeaderNames.ACCEPT_ENCODING, Arrays.asList("gzip", "deflate"));
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.ACCEPT_ENCODING);
- assertNotNull(header);
- assertLinesMatch(Arrays.asList("gzip", "deflate"), header.getValues());
- }
-
- @Test
- public void addCharSequenceIterableThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.add(HttpHeaderNames.ACCEPT_ENCODING, (Iterable>) null));
- }
-
- @Test
- public void addCharSequenceIterableThrowsIfAnyNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.add(HttpHeaderNames.ACCEPT_ENCODING, Arrays.asList("gzip", null)));
- }
-
- @Test
- public void addCharSequenceObject() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- wrappedHttpHeaders.add(HttpHeaderNames.CONTENT_LENGTH, 42);
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
- assertNotNull(header);
- assertEquals("42", header.getValue());
- }
-
- @Test
- public void addCharSequenceObjectThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.add(HttpHeaderNames.CONTENT_LENGTH, (Object) null));
- }
-
- @Test
- public void addStringIterable() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- wrappedHttpHeaders.add("Accept-Encoding", Arrays.asList("gzip", "deflate"));
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.ACCEPT_ENCODING);
- assertNotNull(header);
- assertLinesMatch(Arrays.asList("gzip", "deflate"), header.getValues());
- }
-
- @Test
- public void addStringIterableThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class, () -> wrappedHttpHeaders.add("Accept-Encoding", (Iterable>) null));
- }
-
- @Test
- public void addStringIterableThrowsIfAnyNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.add("Accept-Encoding", Arrays.asList("gzip", null)));
- }
-
- @Test
- public void addStringObject() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- wrappedHttpHeaders.add("Content-Length", 42);
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
- assertNotNull(header);
- assertEquals("42", header.getValue());
- }
-
- @Test
- public void addStringObjectThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class, () -> wrappedHttpHeaders.add("Content-Length", (Object) null));
- }
-
- @Test
- public void addCharSequenceInt() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- wrappedHttpHeaders.addInt("Content-Length", 42);
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
- assertNotNull(header);
- assertEquals("42", header.getValue());
- }
-
- @Test
- public void addCharSequenceShort() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- wrappedHttpHeaders.addShort("Content-Length", (short) 42);
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
- assertNotNull(header);
- assertEquals("42", header.getValue());
- }
-
- @Test
- public void addHttpHeadersHotPathsWrappedHttpHeaders() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- WrappedHttpHeaders toAdd = new WrappedHttpHeaders(
- new HttpHeaders().set(HttpHeaderName.KEEP_ALIVE, "true").set(HttpHeaderName.CONTENT_LENGTH, "42"));
-
- wrappedHttpHeaders.add(toAdd);
-
- HttpHeaders coreHeaders = wrappedHttpHeaders.getCoreHeaders();
- assertEquals(2, coreHeaders.getSize());
- assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
- assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
- }
-
- @SuppressWarnings("deprecation")
- @Test
- public void addHttpHeadersFallsBackToSuperImplementation() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- io.netty.handler.codec.http.HttpHeaders toAdd = new DefaultHttpHeaders().add(HttpHeaderNames.KEEP_ALIVE, "true")
- .add(HttpHeaderNames.CONTENT_LENGTH, "42");
-
- wrappedHttpHeaders.add(toAdd);
-
- HttpHeaders coreHeaders = wrappedHttpHeaders.getCoreHeaders();
- assertEquals(2, coreHeaders.getSize());
- assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
- assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
- }
-
- @Test
- public void addHttpHeadersThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.add((io.netty.handler.codec.http.HttpHeaders) null));
- }
-
- @Test
- public void clearUsesNewClientCoreHttpHeaders() {
- HttpHeaders initial = new HttpHeaders();
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(initial);
- wrappedHttpHeaders.clear();
-
- assertNotSame(initial, wrappedHttpHeaders.getCoreHeaders());
- }
-
- @Test
- public void containsCharSequenceReturnsFalseWhenHeaderDoesNotExist() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertFalse(wrappedHttpHeaders.contains(HttpHeaderNames.CONTENT_LENGTH));
- }
-
- @Test
- public void containsCharSequence() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.CONNECTION, "connection"));
-
- assertTrue(wrappedHttpHeaders.contains(HttpHeaderNames.CONNECTION));
- }
-
- @Test
- public void containsStringReturnsFalseWhenHeaderDoesNotExist() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertFalse(wrappedHttpHeaders.contains("Content-Length"));
- }
-
- @Test
- public void containsString() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.CONNECTION, "connection"));
-
- assertTrue(wrappedHttpHeaders.contains("Connection"));
- }
-
- @Test
- public void copyUsesNewClientCoreHeaders() {
- HttpHeaders initial = new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42");
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(initial);
-
- WrappedHttpHeaders copy = (WrappedHttpHeaders) wrappedHttpHeaders.copy();
-
- assertNotSame(initial, copy.getCoreHeaders());
- assertEquals("42", wrappedHttpHeaders.get(HttpHeaderNames.CONTENT_LENGTH));
- }
-
- @Test
- public void getCharSequenceReturnsNullWhenHeaderDoesNotExist() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertNull(wrappedHttpHeaders.get(HttpHeaderNames.CONTENT_ENCODING));
- }
-
- @Test
- public void getCharSequenceReturnsFirstValue() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders()
- .set(HttpHeaderName.CONTENT_ENCODING, Arrays.asList("application/json", "application/xml")));
-
- assertEquals("application/json", wrappedHttpHeaders.get(HttpHeaderNames.CONTENT_ENCODING));
- }
-
- @Test
- public void getStringReturnsNullWhenHeaderDoesNotExist() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertNull(wrappedHttpHeaders.get("Content-Encoding"));
- }
-
- @Test
- public void getStringReturnsFirstValue() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders()
- .set(HttpHeaderName.CONTENT_ENCODING, Arrays.asList("application/json", "application/xml")));
-
- assertEquals("application/json", wrappedHttpHeaders.get("Content-Encoding"));
- }
-
- @Test
- public void getAllCharSequenceReturnsEmptyListWhenHeaderDoesNotExist() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertSame(Collections.emptyList(), wrappedHttpHeaders.getAll(HttpHeaderNames.CONTENT_TYPE));
- }
-
- @Test
- public void getAllCharSequence() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(
- new HttpHeaders().set(HttpHeaderName.CONTENT_TYPE, Arrays.asList("application/json", "application/xml")));
-
- assertLinesMatch(Arrays.asList("application/json", "application/xml"),
- wrappedHttpHeaders.getAll(HttpHeaderNames.CONTENT_TYPE));
- }
-
- @Test
- public void getAllStringReturnsEmptyListWhenHeaderDoesNotExist() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertSame(Collections.emptyList(), wrappedHttpHeaders.getAll("Content-Type"));
- }
-
- @Test
- public void getAllString() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(
- new HttpHeaders().set(HttpHeaderName.CONTENT_TYPE, Arrays.asList("application/json", "application/xml")));
-
- assertLinesMatch(Arrays.asList("application/json", "application/xml"),
- wrappedHttpHeaders.getAll("Content-Type"));
- }
-
- @Test
- public void getIntReturnsNullWhenHeaderDoesNotExist() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertNull(wrappedHttpHeaders.getInt(HttpHeaderNames.CONTENT_LENGTH));
- }
-
- @Test
- public void getIntReturnsFirstValue() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.COOKIE, Arrays.asList("1", "2")));
-
- assertEquals(1, wrappedHttpHeaders.getInt(HttpHeaderNames.COOKIE));
- }
-
- @Test
- public void getIntReturnsNullOnInvalidParse() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.EXPECT, "expect"));
-
- assertNull(wrappedHttpHeaders.getInt(HttpHeaderNames.EXPECT));
- }
-
- @Test
- public void getIntWithDefaultReturnsDefaultOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertEquals(1, wrappedHttpHeaders.getInt(HttpHeaderNames.CONTENT_LENGTH, 1));
- }
-
- @Test
- public void getIntWithDefaultReturnsDefaultOnInvalidParse() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.HOST, "host"));
-
- assertEquals(1, wrappedHttpHeaders.getInt(HttpHeaderNames.HOST, 1));
- }
-
- @Test
- public void getIntReturnsActualValue() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42"));
-
- assertEquals(42, wrappedHttpHeaders.getInt(HttpHeaderNames.CONTENT_LENGTH, 24));
- }
-
- @Test
- public void getShortReturnsNullWhenHeaderDoesNotExist() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertNull(wrappedHttpHeaders.getShort(HttpHeaderNames.CONTENT_LENGTH));
- }
-
- @Test
- public void getShortReturnsFirstValue() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.COOKIE, Arrays.asList("1", "2")));
-
- assertEquals((short) 1, wrappedHttpHeaders.getShort(HttpHeaderNames.COOKIE));
- }
-
- @Test
- public void getShortReturnsNullOnInvalidParse() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.PROXY_AUTHORIZATION, "authorization"));
-
- assertNull(wrappedHttpHeaders.getShort(HttpHeaderNames.PROXY_AUTHORIZATION));
- }
-
- @Test
- public void getShortWithDefaultReturnsDefaultOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertEquals((short) 1, wrappedHttpHeaders.getShort(HttpHeaderNames.CONTENT_LENGTH, (short) 1));
- }
-
- @Test
- public void getShortWithDefaultReturnsDefaultOnInvalidParse() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.TE, "TE"));
-
- assertEquals((short) 1, wrappedHttpHeaders.getShort(HttpHeaderNames.TE, (short) 1));
- }
-
- @Test
- public void getShortReturnsActualValue() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42"));
-
- assertEquals((short) 42, wrappedHttpHeaders.getShort(HttpHeaderNames.CONTENT_LENGTH, (short) 24));
- }
-
- @Test
- public void isEmptyIsBasedOnClientCoreHeaders() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertTrue(wrappedHttpHeaders.isEmpty());
-
- wrappedHttpHeaders.getCoreHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42");
- assertFalse(wrappedHttpHeaders.isEmpty());
- }
-
- @Test
- public void removeCharSequence() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.TRAILER, "trailer"));
- wrappedHttpHeaders.remove(HttpHeaderNames.TRAILER);
-
- assertTrue(wrappedHttpHeaders.isEmpty());
- assertNull(wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.TRAILER));
- }
-
- @Test
- public void removeString() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.TRAILER, "trailer"));
- wrappedHttpHeaders.remove("Trailer");
-
- assertTrue(wrappedHttpHeaders.isEmpty());
- assertNull(wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.TRAILER));
- }
-
- @Test
- public void setCharSequenceIterable() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.ACCEPT_ENCODING, "*"));
- wrappedHttpHeaders.set(HttpHeaderNames.ACCEPT_ENCODING, Arrays.asList("gzip", "deflate"));
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.ACCEPT_ENCODING);
- assertNotNull(header);
- assertLinesMatch(Arrays.asList("gzip", "deflate"), header.getValues());
- }
-
- @Test
- public void setCharSequenceIterableThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.set(HttpHeaderNames.ACCEPT_ENCODING, (Iterable>) null));
- }
-
- @Test
- public void setCharSequenceIterableThrowsIfAnyNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.set(HttpHeaderNames.ACCEPT_ENCODING, Arrays.asList("gzip", null)));
- }
-
- @SuppressWarnings("deprecation")
- @Test
- public void setCharSequenceObject() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.KEEP_ALIVE, "false"));
- wrappedHttpHeaders.set(HttpHeaderNames.KEEP_ALIVE, "true");
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.KEEP_ALIVE);
- assertNotNull(header);
- assertEquals("true", header.getValue());
- }
-
- @Test
- public void setCharSequenceObjectThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.set(HttpHeaderNames.CONTENT_LENGTH, (Object) null));
- }
-
- @Test
- public void setStringIterable() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.ACCEPT_ENCODING, "*"));
- wrappedHttpHeaders.set("Accept-Encoding", Arrays.asList("gzip", "deflate"));
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.ACCEPT_ENCODING);
- assertNotNull(header);
- assertLinesMatch(Arrays.asList("gzip", "deflate"), header.getValues());
- }
-
- @Test
- public void setStringIterableThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class, () -> wrappedHttpHeaders.set("Accept-Encoding", (Iterable>) null));
- }
-
- @Test
- public void setStringIterableThrowsIfAnyNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.set("Accept-Encoding", Arrays.asList("gzip", null)));
- }
-
- @Test
- public void setStringObject() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "24"));
- wrappedHttpHeaders.set("Content-Length", 42);
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
- assertNotNull(header);
- assertEquals("42", header.getValue());
- }
-
- @Test
- public void setStringObjectThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class, () -> wrappedHttpHeaders.set("Content-Length", (Object) null));
- }
-
- @Test
- public void setCharSequenceInt() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "24"));
- wrappedHttpHeaders.setInt("Content-Length", 42);
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
- assertNotNull(header);
- assertEquals("42", header.getValue());
- }
-
- @Test
- public void setCharSequenceShort() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "24"));
- wrappedHttpHeaders.setShort("Content-Length", (short) 42);
-
- HttpHeader header = wrappedHttpHeaders.getCoreHeaders().get(HttpHeaderName.CONTENT_LENGTH);
- assertNotNull(header);
- assertEquals("42", header.getValue());
- }
-
- @Test
- public void setHttpHeadersHotPathsWrappedHttpHeaders() {
- HttpHeaders initial = new HttpHeaders(0);
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(initial);
- WrappedHttpHeaders toAdd = new WrappedHttpHeaders(
- new HttpHeaders().set(HttpHeaderName.KEEP_ALIVE, "true").set(HttpHeaderName.CONTENT_LENGTH, "42"));
-
- wrappedHttpHeaders.set(toAdd);
-
- HttpHeaders coreHeaders = wrappedHttpHeaders.getCoreHeaders();
- assertNotSame(initial, coreHeaders);
- assertNotSame(toAdd.getCoreHeaders(), coreHeaders);
- assertEquals(2, coreHeaders.getSize());
- assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
- assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
- }
-
- @Test
- public void setHttpHeadersFallsBackToSuperImplementation() {
- HttpHeaders initial = new HttpHeaders();
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(initial);
- io.netty.handler.codec.http.HttpHeaders toAdd
- = new DefaultHttpHeaders().add("Keep-Alive", "true").add(HttpHeaderNames.CONTENT_LENGTH, "42");
-
- wrappedHttpHeaders.set(toAdd);
-
- HttpHeaders coreHeaders = wrappedHttpHeaders.getCoreHeaders();
- assertNotSame(initial, coreHeaders);
- assertEquals(2, coreHeaders.getSize());
- assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
- assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
- }
-
- @Test
- public void setHttpHeadersThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class,
- () -> wrappedHttpHeaders.set((io.netty.handler.codec.http.HttpHeaders) null));
- }
-
- @Test
- public void setAllHttpHeadersHotPathsWrappedHttpHeaders() {
- HttpHeaders initial = new HttpHeaders(0);
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(initial);
- WrappedHttpHeaders toAdd = new WrappedHttpHeaders(
- new HttpHeaders().set(HttpHeaderName.KEEP_ALIVE, "true").set(HttpHeaderName.CONTENT_LENGTH, "42"));
-
- wrappedHttpHeaders.setAll(toAdd);
-
- HttpHeaders coreHeaders = wrappedHttpHeaders.getCoreHeaders();
- assertSame(initial, coreHeaders);
- assertEquals(2, coreHeaders.getSize());
- assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
- assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
- }
-
- @Test
- public void setAllHttpHeadersFallsBackToSuperImplementation() {
- HttpHeaders initial = new HttpHeaders();
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(initial);
- io.netty.handler.codec.http.HttpHeaders toAdd
- = new DefaultHttpHeaders().add("Keep-Alive", "true").add(HttpHeaderNames.CONTENT_LENGTH, "42");
-
- wrappedHttpHeaders.setAll(toAdd);
-
- HttpHeaders coreHeaders = wrappedHttpHeaders.getCoreHeaders();
- assertSame(initial, coreHeaders);
- assertEquals(2, coreHeaders.getSize());
- assertEquals("true", coreHeaders.getValue(HttpHeaderName.KEEP_ALIVE));
- assertEquals("42", coreHeaders.getValue(HttpHeaderName.CONTENT_LENGTH));
- }
-
- @Test
- public void setAllHttpHeadersThrowsOnNull() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
- assertThrows(NullPointerException.class, () -> wrappedHttpHeaders.setAll(null));
- }
-
- @Test
- public void names() {
- HttpHeaders clientCoreHeaders = new HttpHeaders().set(HttpHeaderName.CONTENT_LENGTH, "42")
- .set(HttpHeaderName.CONTENT_TYPE, "application/json")
- .set(HttpHeaderName.ACCEPT, Arrays.asList("application/json", "text/json"));
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(clientCoreHeaders);
-
- Set expectedNames = new HashSet<>(Arrays.asList(HttpHeaderName.CONTENT_LENGTH.getCaseSensitiveName(),
- HttpHeaderName.CONTENT_TYPE.getCaseSensitiveName(), HttpHeaderName.ACCEPT.getCaseSensitiveName()));
- Set names = wrappedHttpHeaders.names();
-
- assertEquals(expectedNames, names);
- }
-
- @Test
- public void getTimeMillis() {
- OffsetDateTime now = OffsetDateTime.now();
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.DATE, DateTimeRfc1123.toRfc1123String(now)));
-
- Long timeMillis = wrappedHttpHeaders.getTimeMillis(HttpHeaderNames.DATE);
- assertNotNull(timeMillis);
-
- // Use OffsetDateTime.toEpochSecond() * 1000L to get the expected millis as DateTimeRfc1123 only has the ability
- // to represent to seconds. If OffsetDateTime.toInstant().toEpochMilli() is used, it will return the current
- // time in milliseconds, which cannot be represented.
- assertEquals(now.toEpochSecond() * 1000L, timeMillis);
- }
-
- @Test
- public void getTimeMillisReturnsNullWhenHeaderDoesNotExist() {
- WrappedHttpHeaders wrappedHttpHeaders = new WrappedHttpHeaders(new HttpHeaders());
-
- assertNull(wrappedHttpHeaders.getTimeMillis(HttpHeaderNames.DATE));
- }
-
- @Test
- public void getTimeMillisReturnsNullWhenHeaderIsNotADate() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.DATE, "notADate"));
-
- assertNull(wrappedHttpHeaders.getTimeMillis(HttpHeaderNames.DATE));
- }
-
- @Test
- public void getTimeMillisReturnsDefaultWhenHeaderIsNotADate() {
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.DATE, "notADate"));
-
- assertEquals(42L, wrappedHttpHeaders.getTimeMillis(HttpHeaderNames.DATE, 42L));
- }
-
- @Test
- public void getTimeMillisReturnsActualValue() {
- OffsetDateTime now = OffsetDateTime.now();
- WrappedHttpHeaders wrappedHttpHeaders
- = new WrappedHttpHeaders(new HttpHeaders().set(HttpHeaderName.DATE, DateTimeRfc1123.toRfc1123String(now)));
-
- // Use OffsetDateTime.toEpochSecond() * 1000L to get the expected millis as DateTimeRfc1123 only has the ability
- // to represent to seconds. If OffsetDateTime.toInstant().toEpochMilli() is used, it will return the current
- // time in milliseconds, which cannot be represented.
- assertEquals(now.toEpochSecond() * 1000L,
- wrappedHttpHeaders.getTimeMillis(HttpHeaderNames.DATE, now.toInstant().toEpochMilli()));
- }
-}