Remove public API's that take Promise (#11625)
Motivation: We did recently change the Channel / ChannelHandler API to always act on the Future only. We should do the same for our handlers. Modifications: - Adjust http2 API - Adjust other handlers API Result: Easier to use API and more consistent
This commit is contained in:
parent
73377f0fd5
commit
d7580b526a
@ -236,31 +236,19 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
*/
|
*/
|
||||||
public Future<Void> handshake(Channel channel) {
|
public Future<Void> handshake(Channel channel) {
|
||||||
requireNonNull(channel, "channel");
|
requireNonNull(channel, "channel");
|
||||||
return handshake(channel, channel.newPromise());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Begins the opening handshake
|
|
||||||
*
|
|
||||||
* @param channel
|
|
||||||
* Channel
|
|
||||||
* @param promise
|
|
||||||
* the {@link Promise} to be notified when the opening handshake is sent
|
|
||||||
*/
|
|
||||||
public final Future<Void> handshake(Channel channel, final Promise<Void> promise) {
|
|
||||||
ChannelPipeline pipeline = channel.pipeline();
|
ChannelPipeline pipeline = channel.pipeline();
|
||||||
HttpResponseDecoder decoder = pipeline.get(HttpResponseDecoder.class);
|
HttpResponseDecoder decoder = pipeline.get(HttpResponseDecoder.class);
|
||||||
if (decoder == null) {
|
if (decoder == null) {
|
||||||
HttpClientCodec codec = pipeline.get(HttpClientCodec.class);
|
HttpClientCodec codec = pipeline.get(HttpClientCodec.class);
|
||||||
if (codec == null) {
|
if (codec == null) {
|
||||||
promise.setFailure(new IllegalStateException("ChannelPipeline does not contain " +
|
return channel.newFailedFuture(new IllegalStateException("ChannelPipeline does not contain " +
|
||||||
"an HttpResponseDecoder or HttpClientCodec"));
|
"an HttpResponseDecoder or HttpClientCodec"));
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FullHttpRequest request = newHandshakeRequest();
|
FullHttpRequest request = newHandshakeRequest();
|
||||||
|
|
||||||
|
Promise<Void> promise = channel.newPromise();
|
||||||
channel.writeAndFlush(request).addListener(channel, (ch, future) -> {
|
channel.writeAndFlush(request).addListener(channel, (ch, future) -> {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
ChannelPipeline p = ch.pipeline();
|
ChannelPipeline p = ch.pipeline();
|
||||||
@ -385,29 +373,12 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
* the {@link Future} which is notified once the handshake completes.
|
* the {@link Future} which is notified once the handshake completes.
|
||||||
*/
|
*/
|
||||||
public final Future<Void> processHandshake(final Channel channel, HttpResponse response) {
|
public final Future<Void> processHandshake(final Channel channel, HttpResponse response) {
|
||||||
return processHandshake(channel, response, channel.newPromise());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the opening handshake initiated by {@link #handshake}}.
|
|
||||||
*
|
|
||||||
* @param channel
|
|
||||||
* Channel
|
|
||||||
* @param response
|
|
||||||
* HTTP response containing the closing handshake details
|
|
||||||
* @param promise
|
|
||||||
* the {@link Promise} to notify once the handshake completes.
|
|
||||||
* @return future
|
|
||||||
* the {@link Future} which is notified once the handshake completes.
|
|
||||||
*/
|
|
||||||
public final Future<Void> processHandshake(final Channel channel, HttpResponse response,
|
|
||||||
final Promise<Void> promise) {
|
|
||||||
if (response instanceof FullHttpResponse) {
|
if (response instanceof FullHttpResponse) {
|
||||||
try {
|
try {
|
||||||
finishHandshake(channel, (FullHttpResponse) response);
|
finishHandshake(channel, (FullHttpResponse) response);
|
||||||
promise.setSuccess(null);
|
return channel.newSucceededFuture();
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
promise.setFailure(cause);
|
return channel.newFailedFuture(cause);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ChannelPipeline p = channel.pipeline();
|
ChannelPipeline p = channel.pipeline();
|
||||||
@ -415,10 +386,12 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
if (ctx == null) {
|
if (ctx == null) {
|
||||||
ctx = p.context(HttpClientCodec.class);
|
ctx = p.context(HttpClientCodec.class);
|
||||||
if (ctx == null) {
|
if (ctx == null) {
|
||||||
return promise.setFailure(new IllegalStateException("ChannelPipeline does not contain " +
|
return channel.newFailedFuture(new IllegalStateException("ChannelPipeline does not contain " +
|
||||||
"an HttpResponseDecoder or HttpClientCodec"));
|
"an HttpResponseDecoder or HttpClientCodec"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Promise<Void> promise = channel.newPromise();
|
||||||
// Add aggregator and ensure we feed the HttpResponse so it is aggregated. A limit of 8192 should be more
|
// Add aggregator and ensure we feed the HttpResponse so it is aggregated. A limit of 8192 should be more
|
||||||
// then enough for the websockets handshake payload.
|
// then enough for the websockets handshake payload.
|
||||||
//
|
//
|
||||||
@ -459,9 +432,9 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
promise.setFailure(cause);
|
promise.setFailure(cause);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify the {@link FullHttpResponse} and throws a {@link WebSocketHandshakeException} if something is wrong.
|
* Verify the {@link FullHttpResponse} and throws a {@link WebSocketHandshakeException} if something is wrong.
|
||||||
|
@ -169,7 +169,7 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* The {@link Future} which is notified once the opening handshake completes
|
* The {@link Future} which is notified once the opening handshake completes
|
||||||
*/
|
*/
|
||||||
public Future<Void> handshake(Channel channel, FullHttpRequest req) {
|
public Future<Void> handshake(Channel channel, FullHttpRequest req) {
|
||||||
return handshake(channel, req, null, channel.newPromise());
|
return handshake(channel, req, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,13 +183,10 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* HTTP Request
|
* HTTP Request
|
||||||
* @param responseHeaders
|
* @param responseHeaders
|
||||||
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added
|
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added
|
||||||
* @param promise
|
|
||||||
* the {@link Promise} to be notified when the opening handshake is done
|
|
||||||
* @return future
|
* @return future
|
||||||
* the {@link Future} which is notified when the opening handshake is done
|
* the {@link Future} which is notified when the opening handshake is done
|
||||||
*/
|
*/
|
||||||
public final Future<Void> handshake(Channel channel, FullHttpRequest req,
|
public final Future<Void> handshake(Channel channel, FullHttpRequest req, HttpHeaders responseHeaders) {
|
||||||
HttpHeaders responseHeaders, final Promise<Void> promise) {
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("{} WebSocket version {} server handshake", channel, version());
|
logger.debug("{} WebSocket version {} server handshake", channel, version());
|
||||||
@ -208,9 +205,8 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
// this means the user use an HttpServerCodec
|
// this means the user use an HttpServerCodec
|
||||||
ctx = p.context(HttpServerCodec.class);
|
ctx = p.context(HttpServerCodec.class);
|
||||||
if (ctx == null) {
|
if (ctx == null) {
|
||||||
promise.setFailure(
|
return channel.newFailedFuture(
|
||||||
new IllegalStateException("No HttpDecoder and no HttpServerCodec in the pipeline"));
|
new IllegalStateException("No HttpDecoder and no HttpServerCodec in the pipeline"));
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
p.addBefore(ctx.name(), "wsencoder", newWebSocketEncoder());
|
p.addBefore(ctx.name(), "wsencoder", newWebSocketEncoder());
|
||||||
p.addBefore(ctx.name(), "wsdecoder", newWebsocketDecoder());
|
p.addBefore(ctx.name(), "wsdecoder", newWebsocketDecoder());
|
||||||
@ -221,16 +217,12 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
encoderName = p.context(HttpResponseEncoder.class).name();
|
encoderName = p.context(HttpResponseEncoder.class).name();
|
||||||
p.addBefore(encoderName, "wsencoder", newWebSocketEncoder());
|
p.addBefore(encoderName, "wsencoder", newWebSocketEncoder());
|
||||||
}
|
}
|
||||||
channel.writeAndFlush(response).addListener(channel, (ch, future) -> {
|
return channel.writeAndFlush(response).addListener(channel, (ch, future) -> {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
ChannelPipeline p1 = ch.pipeline();
|
ChannelPipeline p1 = ch.pipeline();
|
||||||
p1.remove(encoderName);
|
p1.remove(encoderName);
|
||||||
promise.setSuccess(null);
|
|
||||||
} else {
|
|
||||||
promise.setFailure(future.cause());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,7 +237,7 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* The {@link Future} which is notified once the opening handshake completes
|
* The {@link Future} which is notified once the opening handshake completes
|
||||||
*/
|
*/
|
||||||
public Future<Void> handshake(Channel channel, HttpRequest req) {
|
public Future<Void> handshake(Channel channel, HttpRequest req) {
|
||||||
return handshake(channel, req, null, channel.newPromise());
|
return handshake(channel, req, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -259,16 +251,14 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* HTTP Request
|
* HTTP Request
|
||||||
* @param responseHeaders
|
* @param responseHeaders
|
||||||
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added
|
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added
|
||||||
* @param promise
|
|
||||||
* the {@link Promise} to be notified when the opening handshake is done
|
|
||||||
* @return future
|
* @return future
|
||||||
* the {@link Future} which is notified when the opening handshake is done
|
* the {@link Future} which is notified when the opening handshake is done
|
||||||
*/
|
*/
|
||||||
public final Future<Void> handshake(final Channel channel, HttpRequest req,
|
public final Future<Void> handshake(final Channel channel, HttpRequest req,
|
||||||
final HttpHeaders responseHeaders, final Promise<Void> promise) {
|
final HttpHeaders responseHeaders) {
|
||||||
|
|
||||||
if (req instanceof FullHttpRequest) {
|
if (req instanceof FullHttpRequest) {
|
||||||
return handshake(channel, (FullHttpRequest) req, responseHeaders, promise);
|
return handshake(channel, (FullHttpRequest) req, responseHeaders);
|
||||||
}
|
}
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("{} WebSocket version {} server handshake", channel, version());
|
logger.debug("{} WebSocket version {} server handshake", channel, version());
|
||||||
@ -279,11 +269,12 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
// this means the user use an HttpServerCodec
|
// this means the user use an HttpServerCodec
|
||||||
ctx = p.context(HttpServerCodec.class);
|
ctx = p.context(HttpServerCodec.class);
|
||||||
if (ctx == null) {
|
if (ctx == null) {
|
||||||
promise.setFailure(
|
return channel.newFailedFuture(
|
||||||
new IllegalStateException("No HttpDecoder and no HttpServerCodec in the pipeline"));
|
new IllegalStateException("No HttpDecoder and no HttpServerCodec in the pipeline"));
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Promise<Void> promise = channel.newPromise();
|
||||||
// Add aggregator and ensure we feed the HttpRequest so it is aggregated. A limit o 8192 should be more then
|
// Add aggregator and ensure we feed the HttpRequest so it is aggregated. A limit o 8192 should be more then
|
||||||
// enough for the websockets handshake payload.
|
// enough for the websockets handshake payload.
|
||||||
//
|
//
|
||||||
@ -295,7 +286,7 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
|
protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
|
||||||
// Remove ourself and do the actual handshake
|
// Remove ourself and do the actual handshake
|
||||||
ctx.pipeline().remove(this);
|
ctx.pipeline().remove(this);
|
||||||
handshake(channel, msg, responseHeaders, promise);
|
handshake(channel, msg, responseHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -100,7 +100,7 @@ public abstract class WebSocketServerHandshakerTest {
|
|||||||
"ws://example.com/chat");
|
"ws://example.com/chat");
|
||||||
request.headers().set("x-client-header", "value");
|
request.headers().set("x-client-header", "value");
|
||||||
try {
|
try {
|
||||||
serverHandshaker.handshake(null, request, null, null);
|
serverHandshaker.handshake(null, request, null);
|
||||||
} catch (WebSocketServerHandshakeException exception) {
|
} catch (WebSocketServerHandshakeException exception) {
|
||||||
assertNotNull(exception.getMessage());
|
assertNotNull(exception.getMessage());
|
||||||
assertEquals(request.headers(), exception.request().headers());
|
assertEquals(request.headers(), exception.request().headers());
|
||||||
|
@ -143,12 +143,12 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
|
public Future<Void> writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
|
||||||
final boolean endOfStream, Promise<Void> promise) {
|
final boolean endOfStream) {
|
||||||
final Http2Stream stream = connection().stream(streamId);
|
final Http2Stream stream = connection().stream(streamId);
|
||||||
final EmbeddedChannel channel = stream == null ? null : (EmbeddedChannel) stream.getProperty(propertyKey);
|
final EmbeddedChannel channel = stream == null ? null : (EmbeddedChannel) stream.getProperty(propertyKey);
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
// The compressor may be null if no compatible encoding type was found in this stream's headers
|
// The compressor may be null if no compatible encoding type was found in this stream's headers
|
||||||
return super.writeData(ctx, streamId, data, padding, endOfStream, promise);
|
return super.writeData(ctx, streamId, data, padding, endOfStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -161,13 +161,13 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
|||||||
buf = nextReadableBuf(channel);
|
buf = nextReadableBuf(channel);
|
||||||
}
|
}
|
||||||
return super.writeData(ctx, streamId, buf == null ? Unpooled.EMPTY_BUFFER : buf, padding,
|
return super.writeData(ctx, streamId, buf == null ? Unpooled.EMPTY_BUFFER : buf, padding,
|
||||||
true, promise);
|
true);
|
||||||
}
|
}
|
||||||
// END_STREAM is not set and the assumption is data is still forthcoming.
|
// END_STREAM is not set and the assumption is data is still forthcoming.
|
||||||
promise.setSuccess(null);
|
return ctx.newSucceededFuture();
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Promise<Void> promise = ctx.newPromise();
|
||||||
PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
|
PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ByteBuf nextBuf = nextReadableBuf(channel);
|
ByteBuf nextBuf = nextReadableBuf(channel);
|
||||||
@ -177,9 +177,8 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
|||||||
compressedEndOfStream = nextBuf == null;
|
compressedEndOfStream = nextBuf == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise<Void> bufPromise = ctx.newPromise();
|
Future<Void> future = super.writeData(ctx, streamId, buf, padding, compressedEndOfStream);
|
||||||
combiner.add(bufPromise);
|
combiner.add(future);
|
||||||
super.writeData(ctx, streamId, buf, padding, compressedEndOfStream, bufPromise);
|
|
||||||
if (nextBuf == null) {
|
if (nextBuf == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -188,56 +187,54 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
|||||||
buf = nextBuf;
|
buf = nextBuf;
|
||||||
}
|
}
|
||||||
combiner.finish(promise);
|
combiner.finish(promise);
|
||||||
|
return promise;
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
promise.tryFailure(cause);
|
return ctx.newFailedFuture(cause);
|
||||||
} finally {
|
} finally {
|
||||||
if (endOfStream) {
|
if (endOfStream) {
|
||||||
cleanup(stream, channel);
|
cleanup(stream, channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||||
boolean endStream, Promise<Void> promise) {
|
boolean endStream) {
|
||||||
try {
|
try {
|
||||||
// Determine if compression is required and sanitize the headers.
|
// Determine if compression is required and sanitize the headers.
|
||||||
EmbeddedChannel compressor = newCompressor(ctx, headers, endStream);
|
EmbeddedChannel compressor = newCompressor(ctx, headers, endStream);
|
||||||
|
|
||||||
// Write the headers and create the stream object.
|
// Write the headers and create the stream object.
|
||||||
Future<Void> future = super.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
Future<Void> future = super.writeHeaders(ctx, streamId, headers, padding, endStream);
|
||||||
|
|
||||||
// After the stream object has been created, then attach the compressor as a property for data compression.
|
// After the stream object has been created, then attach the compressor as a property for data compression.
|
||||||
bindCompressorToStream(compressor, streamId);
|
bindCompressorToStream(compressor, streamId);
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
promise.tryFailure(e);
|
return ctx.newFailedFuture(e);
|
||||||
}
|
}
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(final ChannelHandlerContext ctx, final int streamId, final Http2Headers headers,
|
public Future<Void> writeHeaders(final ChannelHandlerContext ctx, final int streamId, final Http2Headers headers,
|
||||||
final int streamDependency, final short weight, final boolean exclusive, final int padding,
|
final int streamDependency, final short weight, final boolean exclusive, final int padding,
|
||||||
final boolean endOfStream, final Promise<Void> promise) {
|
final boolean endOfStream) {
|
||||||
try {
|
try {
|
||||||
// Determine if compression is required and sanitize the headers.
|
// Determine if compression is required and sanitize the headers.
|
||||||
EmbeddedChannel compressor = newCompressor(ctx, headers, endOfStream);
|
EmbeddedChannel compressor = newCompressor(ctx, headers, endOfStream);
|
||||||
|
|
||||||
// Write the headers and create the stream object.
|
// Write the headers and create the stream object.
|
||||||
Future<Void> future = super.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive,
|
Future<Void> future = super.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive,
|
||||||
padding, endOfStream, promise);
|
padding, endOfStream);
|
||||||
|
|
||||||
// After the stream object has been created, then attach the compressor as a property for data compression.
|
// After the stream object has been created, then attach the compressor as a property for data compression.
|
||||||
bindCompressorToStream(compressor, streamId);
|
bindCompressorToStream(compressor, streamId);
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
promise.tryFailure(e);
|
return ctx.newFailedFuture(e);
|
||||||
}
|
}
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,7 +17,6 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.Promise;
|
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
@ -35,73 +34,70 @@ public class DecoratingHttp2FrameWriter implements Http2FrameWriter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
||||||
boolean endStream, Promise<Void> promise) {
|
boolean endStream) {
|
||||||
return delegate.writeData(ctx, streamId, data, padding, endStream, promise);
|
return delegate.writeData(ctx, streamId, data, padding, endStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||||
boolean endStream, Promise<Void> promise) {
|
boolean endStream) {
|
||||||
return delegate.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
return delegate.writeHeaders(ctx, streamId, headers, padding, endStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int streamDependency, short weight, boolean exclusive, int padding,
|
int streamDependency, short weight, boolean exclusive, int padding,
|
||||||
boolean endStream, Promise<Void> promise) {
|
boolean endStream) {
|
||||||
return delegate
|
return delegate
|
||||||
.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream, promise);
|
.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
||||||
boolean exclusive, Promise<Void> promise) {
|
boolean exclusive) {
|
||||||
return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||||
Promise<Void> promise) {
|
return delegate.writeRstStream(ctx, streamId, errorCode);
|
||||||
return delegate.writeRstStream(ctx, streamId, errorCode, promise);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings, Promise<Void> promise) {
|
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings) {
|
||||||
return delegate.writeSettings(ctx, settings, promise);
|
return delegate.writeSettings(ctx, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||||
return delegate.writeSettingsAck(ctx, promise);
|
return delegate.writeSettingsAck(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data) {
|
||||||
return delegate.writePing(ctx, ack, data, promise);
|
return delegate.writePing(ctx, ack, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||||
Http2Headers headers, int padding, Promise<Void> promise) {
|
Http2Headers headers, int padding) {
|
||||||
return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise);
|
return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
|
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) {
|
||||||
Promise<Void> promise) {
|
return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData);
|
||||||
return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
|
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) {
|
||||||
Promise<Void> promise) {
|
return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement);
|
||||||
return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
||||||
ByteBuf payload, Promise<Void> promise) {
|
ByteBuf payload) {
|
||||||
return delegate.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
return delegate.writeFrame(ctx, frameType, streamId, flags, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,7 +20,6 @@ import io.netty.handler.codec.http2.Http2Stream.State;
|
|||||||
import io.netty.util.collection.IntObjectHashMap;
|
import io.netty.util.collection.IntObjectHashMap;
|
||||||
import io.netty.util.collection.IntObjectMap;
|
import io.netty.util.collection.IntObjectMap;
|
||||||
import io.netty.util.collection.IntObjectMap.PrimitiveEntry;
|
import io.netty.util.collection.IntObjectMap.PrimitiveEntry;
|
||||||
import io.netty.util.concurrent.Future;
|
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.UnaryPromiseNotifier;
|
import io.netty.util.concurrent.UnaryPromiseNotifier;
|
||||||
import io.netty.util.internal.EmptyArrays;
|
import io.netty.util.internal.EmptyArrays;
|
||||||
@ -116,7 +115,7 @@ public class DefaultHttp2Connection implements Http2Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> close(final Promise<Void> promise) {
|
public void close(final Promise<Void> promise) {
|
||||||
requireNonNull(promise, "promise");
|
requireNonNull(promise, "promise");
|
||||||
// Since we allow this method to be called multiple times, we must make sure that all the promises are notified
|
// Since we allow this method to be called multiple times, we must make sure that all the promises are notified
|
||||||
// when all streams are removed and the close operation completes.
|
// when all streams are removed and the close operation completes.
|
||||||
@ -131,7 +130,7 @@ public class DefaultHttp2Connection implements Http2Connection {
|
|||||||
}
|
}
|
||||||
if (isStreamMapEmpty()) {
|
if (isStreamMapEmpty()) {
|
||||||
promise.trySuccess(null);
|
promise.trySuccess(null);
|
||||||
return promise;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator<PrimitiveEntry<Http2Stream>> itr = streamMap.entries().iterator();
|
Iterator<PrimitiveEntry<Http2Stream>> itr = streamMap.entries().iterator();
|
||||||
@ -162,7 +161,6 @@ public class DefaultHttp2Connection implements Http2Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return closePromise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -498,7 +498,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
|
|||||||
// Acknowledge receipt of the settings. We should do this before we process the settings to ensure our
|
// Acknowledge receipt of the settings. We should do this before we process the settings to ensure our
|
||||||
// remote peer applies these settings before any subsequent frames that we may send which depend upon
|
// remote peer applies these settings before any subsequent frames that we may send which depend upon
|
||||||
// these new settings. See https://github.com/netty/netty/issues/6520.
|
// these new settings. See https://github.com/netty/netty/issues/6520.
|
||||||
encoder.writeSettingsAck(ctx, ctx.newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
|
|
||||||
encoder.remoteSettings(settings);
|
encoder.remoteSettings(settings);
|
||||||
} else {
|
} else {
|
||||||
@ -512,7 +512,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
|
|||||||
public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
|
public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
|
||||||
if (autoAckPing) {
|
if (autoAckPing) {
|
||||||
// Send an ack back to the remote client.
|
// Send an ack back to the remote client.
|
||||||
encoder.writePing(ctx, true, data, ctx.newPromise());
|
encoder.writePing(ctx, true, data);
|
||||||
}
|
}
|
||||||
listener.onPingRead(ctx, data);
|
listener.onPingRead(ctx, data);
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
|
public Future<Void> writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
|
||||||
final boolean endOfStream, Promise<Void> promise) {
|
final boolean endOfStream) {
|
||||||
final Http2Stream stream;
|
final Http2Stream stream;
|
||||||
try {
|
try {
|
||||||
stream = requireStream(streamId);
|
stream = requireStream(streamId);
|
||||||
@ -136,9 +136,10 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
data.release();
|
data.release();
|
||||||
return promise.setFailure(e);
|
return ctx.newFailedFuture(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Promise<Void> promise = ctx.newPromise();
|
||||||
// Hand control of the frame to the flow controller.
|
// Hand control of the frame to the flow controller.
|
||||||
flowController().addFlowControlled(stream,
|
flowController().addFlowControlled(stream,
|
||||||
new FlowControlledData(stream, data, padding, endOfStream, promise, ctx.channel()));
|
new FlowControlledData(stream, data, padding, endOfStream, promise, ctx.channel()));
|
||||||
@ -147,8 +148,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||||
boolean endStream, Promise<Void> promise) {
|
boolean endStream) {
|
||||||
return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream, promise);
|
return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean validateHeadersSentState(Http2Stream stream, Http2Headers headers, boolean isServer,
|
private static boolean validateHeadersSentState(Http2Stream stream, Http2Headers headers, boolean isServer,
|
||||||
@ -163,10 +164,9 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(final ChannelHandlerContext ctx, final int streamId,
|
public Future<Void> writeHeaders(final ChannelHandlerContext ctx, final int streamId,
|
||||||
final Http2Headers headers, final int streamDependency, final short weight,
|
final Http2Headers headers, final int streamDependency, final short weight,
|
||||||
final boolean exclusive, final int padding, final boolean endOfStream,
|
final boolean exclusive, final int padding, final boolean endOfStream) {
|
||||||
Promise<Void> promise) {
|
|
||||||
return writeHeaders0(ctx, streamId, headers, true, streamDependency,
|
return writeHeaders0(ctx, streamId, headers, true, streamDependency,
|
||||||
weight, exclusive, padding, endOfStream, promise);
|
weight, exclusive, padding, endOfStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,19 +177,19 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
Http2Headers headers, final boolean hasPriority,
|
Http2Headers headers, final boolean hasPriority,
|
||||||
int streamDependency, final short weight,
|
int streamDependency, final short weight,
|
||||||
boolean exclusive, final int padding,
|
boolean exclusive, final int padding,
|
||||||
boolean endOfStream, Promise<Void> promise) {
|
boolean endOfStream) {
|
||||||
if (hasPriority) {
|
if (hasPriority) {
|
||||||
return frameWriter.writeHeaders(ctx, streamId, headers, streamDependency,
|
return frameWriter.writeHeaders(ctx, streamId, headers, streamDependency,
|
||||||
weight, exclusive, padding, endOfStream, promise);
|
weight, exclusive, padding, endOfStream);
|
||||||
}
|
}
|
||||||
return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream, promise);
|
return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Future<Void> writeHeaders0(final ChannelHandlerContext ctx, final int streamId,
|
private Future<Void> writeHeaders0(final ChannelHandlerContext ctx, final int streamId,
|
||||||
final Http2Headers headers, final boolean hasPriority,
|
final Http2Headers headers, final boolean hasPriority,
|
||||||
final int streamDependency, final short weight,
|
final int streamDependency, final short weight,
|
||||||
final boolean exclusive, final int padding,
|
final boolean exclusive, final int padding,
|
||||||
final boolean endOfStream, Promise<Void> promise) {
|
final boolean endOfStream) {
|
||||||
try {
|
try {
|
||||||
Http2Stream stream = connection.stream(streamId);
|
Http2Stream stream = connection.stream(streamId);
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
@ -202,8 +202,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
stream = connection.local().createStream(streamId, /*endOfStream*/ false);
|
stream = connection.local().createStream(streamId, /*endOfStream*/ false);
|
||||||
} catch (Http2Exception cause) {
|
} catch (Http2Exception cause) {
|
||||||
if (connection.remote().mayHaveCreatedStream(streamId)) {
|
if (connection.remote().mayHaveCreatedStream(streamId)) {
|
||||||
promise.tryFailure(new IllegalStateException("Stream no longer exists: " + streamId, cause));
|
return ctx.newFailedFuture(
|
||||||
return promise;
|
new IllegalStateException("Stream no longer exists: " + streamId, cause));
|
||||||
}
|
}
|
||||||
throw cause;
|
throw cause;
|
||||||
}
|
}
|
||||||
@ -231,7 +231,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream);
|
boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream);
|
||||||
|
|
||||||
Future<Void> future = sendHeaders(frameWriter, ctx, streamId, headers, hasPriority, streamDependency,
|
Future<Void> future = sendHeaders(frameWriter, ctx, streamId, headers, hasPriority, streamDependency,
|
||||||
weight, exclusive, padding, endOfStream, promise);
|
weight, exclusive, padding, endOfStream);
|
||||||
|
|
||||||
// Writing headers may fail during the encode state if they violate HPACK limits.
|
// Writing headers may fail during the encode state if they violate HPACK limits.
|
||||||
|
|
||||||
@ -261,6 +261,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
|
|
||||||
return future;
|
return future;
|
||||||
} else {
|
} else {
|
||||||
|
Promise<Void> promise = ctx.newPromise();
|
||||||
// Pass headers to the flow-controller so it can maintain their sequence relative to DATA frames.
|
// Pass headers to the flow-controller so it can maintain their sequence relative to DATA frames.
|
||||||
flowController.addFlowControlled(stream,
|
flowController.addFlowControlled(stream,
|
||||||
new FlowControlledHeaders(stream, headers, hasPriority, streamDependency,
|
new FlowControlledHeaders(stream, headers, hasPriority, streamDependency,
|
||||||
@ -269,27 +270,24 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
lifecycleManager.onError(ctx, true, t);
|
lifecycleManager.onError(ctx, true, t);
|
||||||
promise.tryFailure(t);
|
return ctx.newFailedFuture(t);
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
||||||
boolean exclusive, Promise<Void> promise) {
|
boolean exclusive) {
|
||||||
return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||||
Promise<Void> promise) {
|
|
||||||
// Delegate to the lifecycle manager for proper updating of connection state.
|
// Delegate to the lifecycle manager for proper updating of connection state.
|
||||||
return lifecycleManager.resetStream(ctx, streamId, errorCode, promise);
|
return lifecycleManager.resetStream(ctx, streamId, errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings) {
|
||||||
Promise<Void> promise) {
|
|
||||||
outstandingLocalSettingsQueue.add(settings);
|
outstandingLocalSettingsQueue.add(settings);
|
||||||
try {
|
try {
|
||||||
Boolean pushEnabled = settings.pushEnabled();
|
Boolean pushEnabled = settings.pushEnabled();
|
||||||
@ -297,27 +295,28 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
throw connectionError(PROTOCOL_ERROR, "Server sending SETTINGS frame with ENABLE_PUSH specified");
|
throw connectionError(PROTOCOL_ERROR, "Server sending SETTINGS frame with ENABLE_PUSH specified");
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
return promise.setFailure(e);
|
return ctx.newFailedFuture(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return frameWriter.writeSettings(ctx, settings, promise);
|
return frameWriter.writeSettings(ctx, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||||
if (outstandingRemoteSettingsQueue == null) {
|
if (outstandingRemoteSettingsQueue == null) {
|
||||||
return frameWriter.writeSettingsAck(ctx, promise);
|
return frameWriter.writeSettingsAck(ctx);
|
||||||
}
|
}
|
||||||
Http2Settings settings = outstandingRemoteSettingsQueue.poll();
|
Http2Settings settings = outstandingRemoteSettingsQueue.poll();
|
||||||
if (settings == null) {
|
if (settings == null) {
|
||||||
return promise.setFailure(new Http2Exception(INTERNAL_ERROR, "attempted to write a SETTINGS ACK with no " +
|
return ctx.newFailedFuture(new Http2Exception(INTERNAL_ERROR, "attempted to write a SETTINGS ACK with no " +
|
||||||
" pending SETTINGS"));
|
" pending SETTINGS"));
|
||||||
}
|
}
|
||||||
SimpleChannelPromiseAggregator aggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
SimpleChannelPromiseAggregator aggregator =
|
||||||
|
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||||
// Acknowledge receipt of the settings. We should do this before we process the settings to ensure our
|
// Acknowledge receipt of the settings. We should do this before we process the settings to ensure our
|
||||||
// remote peer applies these settings before any subsequent frames that we may send which depend upon
|
// remote peer applies these settings before any subsequent frames that we may send which depend upon
|
||||||
// these new settings. See https://github.com/netty/netty/issues/6520.
|
// these new settings. See https://github.com/netty/netty/issues/6520.
|
||||||
frameWriter.writeSettingsAck(ctx, aggregator.newPromise());
|
frameWriter.writeSettingsAck(ctx).cascadeTo(aggregator.newPromise());
|
||||||
|
|
||||||
// We create a "new promise" to make sure that status from both the write and the application are taken into
|
// We create a "new promise" to make sure that status from both the write and the application are taken into
|
||||||
// account independently.
|
// account independently.
|
||||||
@ -333,13 +332,13 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data) {
|
||||||
return frameWriter.writePing(ctx, ack, data, promise);
|
return frameWriter.writePing(ctx, ack, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||||
Http2Headers headers, int padding, Promise<Void> promise) {
|
Http2Headers headers, int padding) {
|
||||||
try {
|
try {
|
||||||
if (connection.goAwayReceived()) {
|
if (connection.goAwayReceived()) {
|
||||||
throw connectionError(PROTOCOL_ERROR, "Sending PUSH_PROMISE after GO_AWAY received.");
|
throw connectionError(PROTOCOL_ERROR, "Sending PUSH_PROMISE after GO_AWAY received.");
|
||||||
@ -349,8 +348,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
// Reserve the promised stream.
|
// Reserve the promised stream.
|
||||||
connection.local().reservePushStream(promisedStreamId, stream);
|
connection.local().reservePushStream(promisedStreamId, stream);
|
||||||
|
|
||||||
Future<Void> future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding,
|
Future<Void> future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding);
|
||||||
promise);
|
|
||||||
// Writing headers may fail during the encode state if they violate HPACK limits.
|
// Writing headers may fail during the encode state if they violate HPACK limits.
|
||||||
if (future.isSuccess() || !future.isDone()) {
|
if (future.isSuccess() || !future.isDone()) {
|
||||||
// This just sets internal stream state which is used elsewhere in the codec and doesn't
|
// This just sets internal stream state which is used elsewhere in the codec and doesn't
|
||||||
@ -368,28 +366,25 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
return future;
|
return future;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
lifecycleManager.onError(ctx, true, t);
|
lifecycleManager.onError(ctx, true, t);
|
||||||
promise.tryFailure(t);
|
return ctx.newFailedFuture(t);
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
|
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) {
|
||||||
Promise<Void> promise) {
|
return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData);
|
||||||
return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData, promise);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
|
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) {
|
||||||
Promise<Void> promise) {
|
return ctx.newFailedFuture(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" +
|
||||||
return promise.setFailure(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" +
|
|
||||||
" objects to control window sizes"));
|
" objects to control window sizes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
||||||
ByteBuf payload, Promise<Void> promise) {
|
ByteBuf payload) {
|
||||||
return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -424,7 +419,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
@Override
|
@Override
|
||||||
public void consumeReceivedSettings(Http2Settings settings) {
|
public void consumeReceivedSettings(Http2Settings settings) {
|
||||||
if (outstandingRemoteSettingsQueue == null) {
|
if (outstandingRemoteSettingsQueue == null) {
|
||||||
outstandingRemoteSettingsQueue = new ArrayDeque<Http2Settings>(2);
|
outstandingRemoteSettingsQueue = new ArrayDeque<>(2);
|
||||||
}
|
}
|
||||||
outstandingRemoteSettingsQueue.add(settings);
|
outstandingRemoteSettingsQueue.add(settings);
|
||||||
}
|
}
|
||||||
@ -508,7 +503,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
|
|
||||||
// Write the frame(s).
|
// Write the frame(s).
|
||||||
frameWriter().writeData(ctx, stream.id(), toWrite, writablePadding,
|
frameWriter().writeData(ctx, stream.id(), toWrite, writablePadding,
|
||||||
endOfStream && size() == 0, writePromise);
|
endOfStream && size() == 0).cascadeTo(writePromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -580,7 +575,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
promise.addListener(this);
|
promise.addListener(this);
|
||||||
|
|
||||||
Future<Void> f = sendHeaders(frameWriter, ctx, stream.id(), headers, hasPriority, streamDependency,
|
Future<Void> f = sendHeaders(frameWriter, ctx, stream.id(), headers, hasPriority, streamDependency,
|
||||||
weight, exclusive, padding, endOfStream, promise);
|
weight, exclusive, padding, endOfStream);
|
||||||
|
f.cascadeTo(promise);
|
||||||
// Writing headers may fail during the encode state if they violate HPACK limits.
|
// Writing headers may fail during the encode state if they violate HPACK limits.
|
||||||
if (!f.isFailed()) { // "not failed" means either not done, or completed successfully.
|
if (!f.isFailed()) { // "not failed" means either not done, or completed successfully.
|
||||||
// This just sets internal stream state which is used elsewhere in the codec and doesn't
|
// This just sets internal stream state which is used elsewhere in the codec and doesn't
|
||||||
@ -621,7 +617,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(Future<? extends Void> future) throws Exception {
|
public void operationComplete(Future<? extends Void> future) {
|
||||||
if (future.isFailed()) {
|
if (future.isFailed()) {
|
||||||
error(flowController().channelHandlerContext(), future.cause());
|
error(flowController().channelHandlerContext(), future.cause());
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregato
|
|||||||
import io.netty.handler.codec.http2.Http2FrameWriter.Configuration;
|
import io.netty.handler.codec.http2.Http2FrameWriter.Configuration;
|
||||||
import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
|
import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.Promise;
|
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import static io.netty.buffer.Unpooled.directBuffer;
|
import static io.netty.buffer.Unpooled.directBuffer;
|
||||||
@ -133,9 +132,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
||||||
int padding, boolean endStream, Promise<Void> promise) {
|
int padding, boolean endStream) {
|
||||||
final SimpleChannelPromiseAggregator promiseAggregator =
|
final SimpleChannelPromiseAggregator promiseAggregator =
|
||||||
new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||||
ByteBuf frameHeader = null;
|
ByteBuf frameHeader = null;
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
@ -242,7 +241,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
// Use a try/finally here in case the data has been released before calling this method. This is not
|
// Use a try/finally here in case the data has been released before calling this method. This is not
|
||||||
// necessary above because we internally allocate frameHeader.
|
// necessary above because we internally allocate frameHeader.
|
||||||
try {
|
try {
|
||||||
if (data != null) {
|
if (data != null &&
|
||||||
|
// Check if the data was released already.
|
||||||
|
data.refCnt() > 0) {
|
||||||
data.release();
|
data.release();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -256,22 +257,22 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||||
Http2Headers headers, int padding, boolean endStream, Promise<Void> promise) {
|
Http2Headers headers, int padding, boolean endStream) {
|
||||||
return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
|
return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
|
||||||
false, 0, (short) 0, false, promise);
|
false, 0, (short) 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||||
Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
||||||
int padding, boolean endStream, Promise<Void> promise) {
|
int padding, boolean endStream) {
|
||||||
return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
|
return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
|
||||||
true, streamDependency, weight, exclusive, promise);
|
true, streamDependency, weight, exclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId,
|
||||||
int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
|
int streamDependency, short weight, boolean exclusive) {
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY);
|
verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY);
|
||||||
@ -282,15 +283,14 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency);
|
buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency);
|
||||||
// Adjust the weight so that it fits into a single byte on the wire.
|
// Adjust the weight so that it fits into a single byte on the wire.
|
||||||
buf.writeByte(weight - 1);
|
buf.writeByte(weight - 1);
|
||||||
return ctx.write(buf).cascadeTo(promise);
|
return ctx.write(buf);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
return promise.setFailure(t);
|
return ctx.newFailedFuture(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||||
Promise<Void> promise) {
|
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
verifyErrorCode(errorCode);
|
verifyErrorCode(errorCode);
|
||||||
@ -298,15 +298,14 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
ByteBuf buf = ctx.alloc().buffer(RST_STREAM_FRAME_LENGTH);
|
ByteBuf buf = ctx.alloc().buffer(RST_STREAM_FRAME_LENGTH);
|
||||||
writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, RST_STREAM, new Http2Flags(), streamId);
|
writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, RST_STREAM, new Http2Flags(), streamId);
|
||||||
buf.writeInt((int) errorCode);
|
buf.writeInt((int) errorCode);
|
||||||
return ctx.write(buf).cascadeTo(promise);
|
return ctx.write(buf);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
return promise.setFailure(t);
|
return ctx.newFailedFuture(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings) {
|
||||||
Promise<Void> promise) {
|
|
||||||
try {
|
try {
|
||||||
requireNonNull(settings, "settings");
|
requireNonNull(settings, "settings");
|
||||||
int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
|
int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
|
||||||
@ -316,39 +315,40 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
buf.writeChar(entry.key());
|
buf.writeChar(entry.key());
|
||||||
buf.writeInt(entry.value().intValue());
|
buf.writeInt(entry.value().intValue());
|
||||||
}
|
}
|
||||||
return ctx.write(buf).cascadeTo(promise);
|
return ctx.write(buf);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
return promise.setFailure(t);
|
return ctx.newFailedFuture(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||||
try {
|
try {
|
||||||
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
||||||
writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0);
|
writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0);
|
||||||
return ctx.write(buf).cascadeTo(promise);
|
return ctx.write(buf);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
return promise.setFailure(t);
|
return ctx.newFailedFuture(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data) {
|
||||||
Http2Flags flags = ack ? new Http2Flags().ack(true) : new Http2Flags();
|
Http2Flags flags = ack ? new Http2Flags().ack(true) : new Http2Flags();
|
||||||
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + PING_FRAME_PAYLOAD_LENGTH);
|
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + PING_FRAME_PAYLOAD_LENGTH);
|
||||||
// Assume nothing below will throw until buf is written. That way we don't have to take care of ownership
|
// Assume nothing below will throw until buf is written. That way we don't have to take care of ownership
|
||||||
// in the catch block.
|
// in the catch block.
|
||||||
writeFrameHeaderInternal(buf, PING_FRAME_PAYLOAD_LENGTH, PING, flags, 0);
|
writeFrameHeaderInternal(buf, PING_FRAME_PAYLOAD_LENGTH, PING, flags, 0);
|
||||||
buf.writeLong(data);
|
buf.writeLong(data);
|
||||||
return ctx.write(buf).cascadeTo(promise);
|
return ctx.write(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||||
Http2Headers headers, int padding, Promise<Void> promise) {
|
Http2Headers headers, int padding) {
|
||||||
ByteBuf headerBlock = null;
|
ByteBuf headerBlock = null;
|
||||||
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
SimpleChannelPromiseAggregator promiseAggregator =
|
||||||
|
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
verifyStreamId(promisedStreamId, "Promised Stream ID");
|
verifyStreamId(promisedStreamId, "Promised Stream ID");
|
||||||
@ -403,8 +403,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||||
ByteBuf debugData, Promise<Void> promise) {
|
ByteBuf debugData) {
|
||||||
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
SimpleChannelPromiseAggregator promiseAggregator =
|
||||||
|
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||||
try {
|
try {
|
||||||
verifyStreamOrConnectionId(lastStreamId, "Last Stream ID");
|
verifyStreamOrConnectionId(lastStreamId, "Last Stream ID");
|
||||||
verifyErrorCode(errorCode);
|
verifyErrorCode(errorCode);
|
||||||
@ -437,7 +438,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
|
||||||
int windowSizeIncrement, Promise<Void> promise) {
|
int windowSizeIncrement) {
|
||||||
try {
|
try {
|
||||||
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
||||||
verifyWindowSizeIncrement(windowSizeIncrement);
|
verifyWindowSizeIncrement(windowSizeIncrement);
|
||||||
@ -445,16 +446,17 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
ByteBuf buf = ctx.alloc().buffer(WINDOW_UPDATE_FRAME_LENGTH);
|
ByteBuf buf = ctx.alloc().buffer(WINDOW_UPDATE_FRAME_LENGTH);
|
||||||
writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, WINDOW_UPDATE, new Http2Flags(), streamId);
|
writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, WINDOW_UPDATE, new Http2Flags(), streamId);
|
||||||
buf.writeInt(windowSizeIncrement);
|
buf.writeInt(windowSizeIncrement);
|
||||||
return ctx.write(buf).cascadeTo(promise);
|
return ctx.write(buf);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
return promise.setFailure(t);
|
return ctx.newFailedFuture(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||||
Http2Flags flags, ByteBuf payload, Promise<Void> promise) {
|
Http2Flags flags, ByteBuf payload) {
|
||||||
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
SimpleChannelPromiseAggregator promiseAggregator =
|
||||||
|
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||||
try {
|
try {
|
||||||
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
||||||
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
||||||
@ -481,9 +483,10 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
|
|
||||||
private Future<Void> writeHeadersInternal(ChannelHandlerContext ctx,
|
private Future<Void> writeHeadersInternal(ChannelHandlerContext ctx,
|
||||||
int streamId, Http2Headers headers, int padding, boolean endStream,
|
int streamId, Http2Headers headers, int padding, boolean endStream,
|
||||||
boolean hasPriority, int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
|
boolean hasPriority, int streamDependency, short weight, boolean exclusive) {
|
||||||
ByteBuf headerBlock = null;
|
ByteBuf headerBlock = null;
|
||||||
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
SimpleChannelPromiseAggregator promiseAggregator =
|
||||||
|
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
if (hasPriority) {
|
if (hasPriority) {
|
||||||
|
@ -483,7 +483,7 @@ public class DefaultHttp2LocalFlowController implements Http2LocalFlowController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send a window update for the stream/connection.
|
// Send a window update for the stream/connection.
|
||||||
frameWriter.writeWindowUpdate(ctx, stream.id(), deltaWindowSize, ctx.newPromise());
|
frameWriter.writeWindowUpdate(ctx, stream.id(), deltaWindowSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,10 +253,10 @@ public interface Http2Connection {
|
|||||||
* all streams that exists (active or otherwise) will be closed and removed.
|
* all streams that exists (active or otherwise) will be closed and removed.
|
||||||
* <p>Note if iterating active streams via {@link #forEachActiveStream(Http2StreamVisitor)} and an exception is
|
* <p>Note if iterating active streams via {@link #forEachActiveStream(Http2StreamVisitor)} and an exception is
|
||||||
* thrown it is necessary to call this method again to ensure the close completes.
|
* thrown it is necessary to call this method again to ensure the close completes.
|
||||||
|
*
|
||||||
* @param promise Will be completed when all streams have been removed, and listeners have been notified.
|
* @param promise Will be completed when all streams have been removed, and listeners have been notified.
|
||||||
* @return A future that will be completed when all streams have been removed, and listeners have been notified.
|
|
||||||
*/
|
*/
|
||||||
Future<Void> close(Promise<Void> promise);
|
void close(Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new key that is unique within this {@link Http2Connection}.
|
* Creates a new key that is unique within this {@link Http2Connection}.
|
||||||
|
@ -61,9 +61,8 @@ public interface Http2ConnectionEncoder extends Http2FrameWriter {
|
|||||||
/**
|
/**
|
||||||
* Writes the given data to the internal {@link Http2FrameWriter} without performing any
|
* Writes the given data to the internal {@link Http2FrameWriter} without performing any
|
||||||
* state checks on the connection/stream.
|
* state checks on the connection/stream.
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||||
Http2Flags flags, ByteBuf payload, Promise<Void> promise);
|
Http2Flags flags, ByteBuf payload);
|
||||||
}
|
}
|
||||||
|
@ -356,7 +356,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Both client and server must send their initial settings.
|
// Both client and server must send their initial settings.
|
||||||
encoder.writeSettings(ctx, initialSettings, ctx.newPromise())
|
encoder.writeSettings(ctx, initialSettings)
|
||||||
.addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
|
.addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
|
||||||
|
|
||||||
if (isClient) {
|
if (isClient) {
|
||||||
@ -450,7 +450,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
// a GO_AWAY has been sent we send a empty buffer just so we can wait to close until all other data has been
|
// a GO_AWAY has been sent we send a empty buffer just so we can wait to close until all other data has been
|
||||||
// flushed to the OS.
|
// flushed to the OS.
|
||||||
// https://github.com/netty/netty/issues/5307
|
// https://github.com/netty/netty/issues/5307
|
||||||
Future<Void> f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise());
|
Future<Void> f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null);
|
||||||
ctx.flush();
|
ctx.flush();
|
||||||
Promise<Void> promise = ctx.newPromise();
|
Promise<Void> promise = ctx.newPromise();
|
||||||
doGracefulShutdown(ctx, f, promise);
|
doGracefulShutdown(ctx, f, promise);
|
||||||
@ -626,7 +626,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
Promise<Void> promise = ctx.newPromise();
|
Promise<Void> promise = ctx.newPromise();
|
||||||
Future<Void> future = goAway(ctx, http2Ex, ctx.newPromise());
|
Future<Void> future = goAway(ctx, http2Ex);
|
||||||
if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) {
|
if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) {
|
||||||
doGracefulShutdown(ctx, future, promise);
|
doGracefulShutdown(ctx, future, promise);
|
||||||
} else {
|
} else {
|
||||||
@ -663,7 +663,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
try {
|
try {
|
||||||
stream = encoder.connection().remote().createStream(streamId, true);
|
stream = encoder.connection().remote().createStream(streamId, true);
|
||||||
} catch (Http2Exception e) {
|
} catch (Http2Exception e) {
|
||||||
resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
|
resetUnknownStream(ctx, streamId, http2Ex.error().code());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -680,10 +680,10 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
|
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
if (!outbound || connection().local().mayHaveCreatedStream(streamId)) {
|
if (!outbound || connection().local().mayHaveCreatedStream(streamId)) {
|
||||||
resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
|
resetUnknownStream(ctx, streamId, http2Ex.error().code());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resetStream(ctx, stream, http2Ex.error().code(), ctx.newPromise());
|
resetStream(ctx, stream, http2Ex.error().code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -695,7 +695,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
* @param stream the Http2Stream on which the header was received
|
* @param stream the Http2Stream on which the header was received
|
||||||
*/
|
*/
|
||||||
protected void handleServerHeaderDecodeSizeError(ChannelHandlerContext ctx, Http2Stream stream) {
|
protected void handleServerHeaderDecodeSizeError(ChannelHandlerContext ctx, Http2Stream stream) {
|
||||||
encoder().writeHeaders(ctx, stream.id(), HEADERS_TOO_LARGE_HEADERS, 0, true, ctx.newPromise());
|
encoder().writeHeaders(ctx, stream.id(), HEADERS_TOO_LARGE_HEADERS, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Http2FrameWriter frameWriter() {
|
protected Http2FrameWriter frameWriter() {
|
||||||
@ -707,9 +707,8 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
* triggered by the first frame of a stream being invalid. That is, there was an error reading the frame before
|
* triggered by the first frame of a stream being invalid. That is, there was an error reading the frame before
|
||||||
* we could create a new stream.
|
* we could create a new stream.
|
||||||
*/
|
*/
|
||||||
private Future<Void> resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
|
private Future<Void> resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||||
Promise<Void> promise) {
|
Future<Void> future = frameWriter().writeRstStream(ctx, streamId, errorCode);
|
||||||
Future<Void> future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise);
|
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
closeConnectionOnError(ctx, future);
|
closeConnectionOnError(ctx, future);
|
||||||
} else {
|
} else {
|
||||||
@ -719,21 +718,20 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||||
Promise<Void> promise) {
|
|
||||||
final Http2Stream stream = connection().stream(streamId);
|
final Http2Stream stream = connection().stream(streamId);
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
return resetUnknownStream(ctx, streamId, errorCode, promise);
|
return resetUnknownStream(ctx, streamId, errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resetStream(ctx, stream, errorCode, promise);
|
return resetStream(ctx, stream, errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Future<Void> resetStream(final ChannelHandlerContext ctx, final Http2Stream stream,
|
private Future<Void> resetStream(final ChannelHandlerContext ctx, final Http2Stream stream,
|
||||||
long errorCode, Promise<Void> promise) {
|
long errorCode) {
|
||||||
if (stream.isResetSent()) {
|
if (stream.isResetSent()) {
|
||||||
// Don't write a RST_STREAM frame if we have already written one.
|
// Don't write a RST_STREAM frame if we have already written one.
|
||||||
return promise.setSuccess(null);
|
return ctx.newSucceededFuture();
|
||||||
}
|
}
|
||||||
// Synchronously set the resetSent flag to prevent any subsequent calls
|
// Synchronously set the resetSent flag to prevent any subsequent calls
|
||||||
// from resulting in multiple reset frames being sent.
|
// from resulting in multiple reset frames being sent.
|
||||||
@ -747,9 +745,9 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
// https://tools.ietf.org/html/rfc7540#section-6.4.
|
// https://tools.ietf.org/html/rfc7540#section-6.4.
|
||||||
if (stream.state() == IDLE ||
|
if (stream.state() == IDLE ||
|
||||||
connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) {
|
connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) {
|
||||||
future = promise.setSuccess(null);
|
future = ctx.newSucceededFuture();
|
||||||
} else {
|
} else {
|
||||||
future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise);
|
future = frameWriter().writeRstStream(ctx, stream.id(), errorCode);
|
||||||
}
|
}
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
processRstStreamWriteResult(ctx, stream, future);
|
processRstStreamWriteResult(ctx, stream, future);
|
||||||
@ -762,24 +760,22 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode,
|
public Future<Void> goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode,
|
||||||
final ByteBuf debugData, Promise<Void> promise) {
|
final ByteBuf debugData) {
|
||||||
final Http2Connection connection = connection();
|
final Http2Connection connection = connection();
|
||||||
try {
|
try {
|
||||||
if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) {
|
if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) {
|
||||||
debugData.release();
|
debugData.release();
|
||||||
promise.trySuccess(null);
|
return ctx.newSucceededFuture();
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
debugData.release();
|
debugData.release();
|
||||||
promise.tryFailure(cause);
|
return ctx.newFailedFuture(cause);
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and
|
// Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and
|
||||||
// result in an IllegalRefCountException.
|
// result in an IllegalRefCountException.
|
||||||
debugData.retain();
|
debugData.retain();
|
||||||
Future<Void> future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
Future<Void> future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData);
|
||||||
|
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future);
|
processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future);
|
||||||
@ -815,7 +811,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
* Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush
|
* Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush
|
||||||
* immediately, this is the responsibility of the caller.
|
* immediately, this is the responsibility of the caller.
|
||||||
*/
|
*/
|
||||||
private Future<Void> goAway(ChannelHandlerContext ctx, Http2Exception cause, Promise<Void> promise) {
|
private Future<Void> goAway(ChannelHandlerContext ctx, Http2Exception cause) {
|
||||||
long errorCode = cause != null ? cause.error().code() : NO_ERROR.code();
|
long errorCode = cause != null ? cause.error().code() : NO_ERROR.code();
|
||||||
int lastKnownStream;
|
int lastKnownStream;
|
||||||
if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) {
|
if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) {
|
||||||
@ -827,7 +823,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
} else {
|
} else {
|
||||||
lastKnownStream = connection().remote().lastStreamCreated();
|
lastKnownStream = connection().remote().lastStreamCreated();
|
||||||
}
|
}
|
||||||
return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), promise);
|
return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -17,7 +17,6 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.FutureListener;
|
import io.netty.util.concurrent.FutureListener;
|
||||||
import io.netty.util.concurrent.Promise;
|
|
||||||
import io.netty.util.internal.ObjectUtil;
|
import io.netty.util.internal.ObjectUtil;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
@ -49,38 +48,41 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||||
Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
|
FutureListener<Void> listener = handleOutstandingControlFrames(ctx);
|
||||||
if (newPromise == null) {
|
Future<Void> f = super.writeSettingsAck(ctx);
|
||||||
return promise;
|
if (listener != null) {
|
||||||
|
f.addListener(listener);
|
||||||
}
|
}
|
||||||
return super.writeSettingsAck(ctx, newPromise);
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data) {
|
||||||
// Only apply the limit to ping acks.
|
// Only apply the limit to ping acks.
|
||||||
if (ack) {
|
if (ack) {
|
||||||
Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
|
FutureListener<Void> listener = handleOutstandingControlFrames(ctx);
|
||||||
if (newPromise == null) {
|
Future<Void> f = super.writePing(ctx, ack, data);
|
||||||
return promise;
|
if (listener != null) {
|
||||||
|
f.addListener(listener);
|
||||||
}
|
}
|
||||||
return super.writePing(ctx, ack, data, newPromise);
|
return f;
|
||||||
}
|
}
|
||||||
return super.writePing(ctx, ack, data, promise);
|
return super.writePing(ctx, ack, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeRstStream(
|
public Future<Void> writeRstStream(
|
||||||
ChannelHandlerContext ctx, int streamId, long errorCode, Promise<Void> promise) {
|
ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||||
Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
|
FutureListener<Void> listener = handleOutstandingControlFrames(ctx);
|
||||||
if (newPromise == null) {
|
Future<Void> f = super.writeRstStream(ctx, streamId, errorCode);
|
||||||
return promise;
|
if (listener != null) {
|
||||||
|
f.addListener(listener);
|
||||||
}
|
}
|
||||||
return super.writeRstStream(ctx, streamId, errorCode, newPromise);
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Promise<Void> handleOutstandingControlFrames(ChannelHandlerContext ctx, Promise<Void> promise) {
|
private FutureListener<Void> handleOutstandingControlFrames(ChannelHandlerContext ctx) {
|
||||||
if (!limitReached) {
|
if (!limitReached) {
|
||||||
if (outstandingControlFrames == maxOutstandingControlFrames) {
|
if (outstandingControlFrames == maxOutstandingControlFrames) {
|
||||||
// Let's try to flush once as we may be able to flush some of the control frames.
|
// Let's try to flush once as we may be able to flush some of the control frames.
|
||||||
@ -96,13 +98,14 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
|
|||||||
// First notify the Http2LifecycleManager and then close the connection.
|
// First notify the Http2LifecycleManager and then close the connection.
|
||||||
lifecycleManager.onError(ctx, true, exception);
|
lifecycleManager.onError(ctx, true, exception);
|
||||||
ctx.close();
|
ctx.close();
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
outstandingControlFrames++;
|
outstandingControlFrames++;
|
||||||
|
|
||||||
// We did not reach the limit yet, add the listener to decrement the number of outstanding control frames
|
// We did not reach the limit yet, add the listener to decrement the number of outstanding control frames
|
||||||
// once the promise was completed
|
// once the promise was completed
|
||||||
promise.addListener(outstandingControlFramesListener);
|
return outstandingControlFramesListener;
|
||||||
}
|
}
|
||||||
return promise;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,8 @@ public interface Http2DataWriter {
|
|||||||
* A 256 byte padding is encoded as the pad length field with value 255 and 255 padding bytes
|
* A 256 byte padding is encoded as the pad length field with value 255 and 255 padding bytes
|
||||||
* appended to the end of the frame.
|
* appended to the end of the frame.
|
||||||
* @param endStream indicates if this is the last frame to be sent for the stream.
|
* @param endStream indicates if this is the last frame to be sent for the stream.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
Future<Void> writeData(ChannelHandlerContext ctx, int streamId,
|
Future<Void> writeData(ChannelHandlerContext ctx, int streamId,
|
||||||
ByteBuf data, int padding, boolean endStream, Promise<Void> promise);
|
ByteBuf data, int padding, boolean endStream);
|
||||||
}
|
}
|
||||||
|
@ -282,13 +282,12 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
|
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
|
||||||
Promise<Void> promise = ctx.newPromise();
|
|
||||||
if (msg instanceof Http2DataFrame) {
|
if (msg instanceof Http2DataFrame) {
|
||||||
Http2DataFrame dataFrame = (Http2DataFrame) msg;
|
Http2DataFrame dataFrame = (Http2DataFrame) msg;
|
||||||
encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(),
|
return encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(),
|
||||||
dataFrame.padding(), dataFrame.isEndStream(), promise);
|
dataFrame.padding(), dataFrame.isEndStream());
|
||||||
} else if (msg instanceof Http2HeadersFrame) {
|
} else if (msg instanceof Http2HeadersFrame) {
|
||||||
writeHeadersFrame(ctx, (Http2HeadersFrame) msg, promise);
|
return writeHeadersFrame(ctx, (Http2HeadersFrame) msg);
|
||||||
} else if (msg instanceof Http2WindowUpdateFrame) {
|
} else if (msg instanceof Http2WindowUpdateFrame) {
|
||||||
Http2WindowUpdateFrame frame = (Http2WindowUpdateFrame) msg;
|
Http2WindowUpdateFrame frame = (Http2WindowUpdateFrame) msg;
|
||||||
Http2FrameStream frameStream = frame.stream();
|
Http2FrameStream frameStream = frame.stream();
|
||||||
@ -300,9 +299,9 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
} else {
|
} else {
|
||||||
consumeBytes(frameStream.id(), frame.windowSizeIncrement());
|
consumeBytes(frameStream.id(), frame.windowSizeIncrement());
|
||||||
}
|
}
|
||||||
promise.setSuccess(null);
|
return ctx.newSucceededFuture();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
promise.setFailure(t);
|
return ctx.newFailedFuture(t);
|
||||||
}
|
}
|
||||||
} else if (msg instanceof Http2ResetFrame) {
|
} else if (msg instanceof Http2ResetFrame) {
|
||||||
Http2ResetFrame rstFrame = (Http2ResetFrame) msg;
|
Http2ResetFrame rstFrame = (Http2ResetFrame) msg;
|
||||||
@ -310,41 +309,40 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
// Only ever send a reset frame if stream may have existed before as otherwise we may send a RST on a
|
// Only ever send a reset frame if stream may have existed before as otherwise we may send a RST on a
|
||||||
// stream in an invalid state and cause a connection error.
|
// stream in an invalid state and cause a connection error.
|
||||||
if (connection().streamMayHaveExisted(id)) {
|
if (connection().streamMayHaveExisted(id)) {
|
||||||
encoder().writeRstStream(ctx, rstFrame.stream().id(), rstFrame.errorCode(), promise);
|
return encoder().writeRstStream(ctx, rstFrame.stream().id(), rstFrame.errorCode());
|
||||||
} else {
|
} else {
|
||||||
ReferenceCountUtil.release(rstFrame);
|
ReferenceCountUtil.release(rstFrame);
|
||||||
promise.setFailure(Http2Exception.streamError(
|
return ctx.newFailedFuture(Http2Exception.streamError(
|
||||||
rstFrame.stream().id(), Http2Error.PROTOCOL_ERROR, "Stream never existed"));
|
rstFrame.stream().id(), Http2Error.PROTOCOL_ERROR, "Stream never existed"));
|
||||||
}
|
}
|
||||||
} else if (msg instanceof Http2PingFrame) {
|
} else if (msg instanceof Http2PingFrame) {
|
||||||
Http2PingFrame frame = (Http2PingFrame) msg;
|
Http2PingFrame frame = (Http2PingFrame) msg;
|
||||||
encoder().writePing(ctx, frame.ack(), frame.content(), promise);
|
return encoder().writePing(ctx, frame.ack(), frame.content());
|
||||||
} else if (msg instanceof Http2SettingsFrame) {
|
} else if (msg instanceof Http2SettingsFrame) {
|
||||||
encoder().writeSettings(ctx, ((Http2SettingsFrame) msg).settings(), promise);
|
return encoder().writeSettings(ctx, ((Http2SettingsFrame) msg).settings());
|
||||||
} else if (msg instanceof Http2SettingsAckFrame) {
|
} else if (msg instanceof Http2SettingsAckFrame) {
|
||||||
// In the event of manual SETTINGS ACK, it is assumed the encoder will apply the earliest received but not
|
// In the event of manual SETTINGS ACK, it is assumed the encoder will apply the earliest received but not
|
||||||
// yet ACKed settings.
|
// yet ACKed settings.
|
||||||
encoder().writeSettingsAck(ctx, promise);
|
return encoder().writeSettingsAck(ctx);
|
||||||
} else if (msg instanceof Http2GoAwayFrame) {
|
} else if (msg instanceof Http2GoAwayFrame) {
|
||||||
writeGoAwayFrame(ctx, (Http2GoAwayFrame) msg, promise);
|
return writeGoAwayFrame(ctx, (Http2GoAwayFrame) msg);
|
||||||
} else if (msg instanceof Http2PushPromiseFrame) {
|
} else if (msg instanceof Http2PushPromiseFrame) {
|
||||||
Http2PushPromiseFrame pushPromiseFrame = (Http2PushPromiseFrame) msg;
|
Http2PushPromiseFrame pushPromiseFrame = (Http2PushPromiseFrame) msg;
|
||||||
writePushPromise(ctx, pushPromiseFrame, promise);
|
return writePushPromise(ctx, pushPromiseFrame);
|
||||||
} else if (msg instanceof Http2PriorityFrame) {
|
} else if (msg instanceof Http2PriorityFrame) {
|
||||||
Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg;
|
Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg;
|
||||||
encoder().writePriority(ctx, priorityFrame.stream().id(), priorityFrame.streamDependency(),
|
return encoder().writePriority(ctx, priorityFrame.stream().id(), priorityFrame.streamDependency(),
|
||||||
priorityFrame.weight(), priorityFrame.exclusive(), promise);
|
priorityFrame.weight(), priorityFrame.exclusive());
|
||||||
} else if (msg instanceof Http2UnknownFrame) {
|
} else if (msg instanceof Http2UnknownFrame) {
|
||||||
Http2UnknownFrame unknownFrame = (Http2UnknownFrame) msg;
|
Http2UnknownFrame unknownFrame = (Http2UnknownFrame) msg;
|
||||||
encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(),
|
return encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(),
|
||||||
unknownFrame.flags(), unknownFrame.content(), promise);
|
unknownFrame.flags(), unknownFrame.content());
|
||||||
} else if (!(msg instanceof Http2Frame)) {
|
} else if (!(msg instanceof Http2Frame)) {
|
||||||
ctx.write(msg).cascadeTo(promise);
|
return ctx.write(msg);
|
||||||
} else {
|
} else {
|
||||||
ReferenceCountUtil.release(msg);
|
ReferenceCountUtil.release(msg);
|
||||||
promise.setFailure(new UnsupportedMessageTypeException(msg));
|
return ctx.newFailedFuture(new UnsupportedMessageTypeException(msg));
|
||||||
}
|
}
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void increaseInitialConnectionWindow(int deltaBytes) throws Http2Exception {
|
private void increaseInitialConnectionWindow(int deltaBytes) throws Http2Exception {
|
||||||
@ -366,10 +364,10 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
return connection().local().flowController().consumeBytes(stream, bytes);
|
return connection().local().flowController().consumeBytes(stream, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame, Promise<Void> promise) {
|
private Future<Void> writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame) {
|
||||||
if (frame.lastStreamId() > -1) {
|
if (frame.lastStreamId() > -1) {
|
||||||
frame.release();
|
frame.release();
|
||||||
throw new IllegalArgumentException("Last stream id must not be set on GOAWAY frame");
|
return ctx.newFailedFuture(new IllegalArgumentException("Last stream id must not be set on GOAWAY frame"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int lastStreamCreated = connection().remote().lastStreamCreated();
|
int lastStreamCreated = connection().remote().lastStreamCreated();
|
||||||
@ -378,66 +376,72 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
if (lastStreamId > Integer.MAX_VALUE) {
|
if (lastStreamId > Integer.MAX_VALUE) {
|
||||||
lastStreamId = Integer.MAX_VALUE;
|
lastStreamId = Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
goAway(ctx, (int) lastStreamId, frame.errorCode(), frame.content(), promise);
|
return goAway(ctx, (int) lastStreamId, frame.errorCode(), frame.content());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame,
|
private Future<Void> writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame) {
|
||||||
final Promise<Void> promise) {
|
|
||||||
|
|
||||||
if (isStreamIdValid(headersFrame.stream().id())) {
|
if (isStreamIdValid(headersFrame.stream().id())) {
|
||||||
encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(),
|
return encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(),
|
||||||
headersFrame.isEndStream(), promise);
|
headersFrame.padding(), headersFrame.isEndStream());
|
||||||
} else if (initializeNewStream(ctx, (DefaultHttp2FrameStream) headersFrame.stream(), promise)) {
|
} else {
|
||||||
|
Future<Void> future = initializeNewStream(ctx, (DefaultHttp2FrameStream) headersFrame.stream());
|
||||||
|
if (future == null) {
|
||||||
final int streamId = headersFrame.stream().id();
|
final int streamId = headersFrame.stream().id();
|
||||||
|
|
||||||
encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(),
|
future = encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(),
|
||||||
headersFrame.isEndStream(), promise);
|
headersFrame.isEndStream());
|
||||||
|
|
||||||
if (!promise.isDone()) {
|
if (!future.isDone()) {
|
||||||
numBufferedStreams++;
|
numBufferedStreams++;
|
||||||
// Clean up the stream being initialized if writing the headers fails and also
|
// Clean up the stream being initialized if writing the headers fails and also
|
||||||
// decrement the number of buffered streams.
|
// decrement the number of buffered streams.
|
||||||
promise.addListener(channelFuture -> {
|
future.addListener(channelFuture -> {
|
||||||
numBufferedStreams--;
|
numBufferedStreams--;
|
||||||
|
|
||||||
handleHeaderFuture(channelFuture, streamId);
|
handleHeaderFuture(channelFuture, streamId);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
handleHeaderFuture(promise, streamId);
|
handleHeaderFuture(future, streamId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return future;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame,
|
private Future<Void> writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame) {
|
||||||
final Promise<Void> promise) {
|
|
||||||
if (isStreamIdValid(pushPromiseFrame.pushStream().id())) {
|
if (isStreamIdValid(pushPromiseFrame.pushStream().id())) {
|
||||||
encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(),
|
return encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(),
|
||||||
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise);
|
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding());
|
||||||
} else if (initializeNewStream(ctx, (DefaultHttp2FrameStream) pushPromiseFrame.pushStream(), promise)) {
|
} else {
|
||||||
|
Future<Void> future = initializeNewStream(ctx, (DefaultHttp2FrameStream) pushPromiseFrame.pushStream());
|
||||||
|
if (future == null) {
|
||||||
final int streamId = pushPromiseFrame.stream().id();
|
final int streamId = pushPromiseFrame.stream().id();
|
||||||
encoder().writePushPromise(ctx, streamId, pushPromiseFrame.pushStream().id(),
|
future = encoder().writePushPromise(ctx, streamId, pushPromiseFrame.pushStream().id(),
|
||||||
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise);
|
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding());
|
||||||
|
|
||||||
if (promise.isDone()) {
|
if (future.isDone()) {
|
||||||
handleHeaderFuture(promise, streamId);
|
handleHeaderFuture(future, streamId);
|
||||||
} else {
|
} else {
|
||||||
numBufferedStreams++;
|
numBufferedStreams++;
|
||||||
// Clean up the stream being initialized if writing the headers fails and also
|
// Clean up the stream being initialized if writing the headers fails and also
|
||||||
// decrement the number of buffered streams.
|
// decrement the number of buffered streams.
|
||||||
promise.addListener(f -> {
|
future.addListener(f -> {
|
||||||
numBufferedStreams--;
|
numBufferedStreams--;
|
||||||
handleHeaderFuture(f, streamId);
|
handleHeaderFuture(f, streamId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream,
|
private Future<Void> initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream) {
|
||||||
Promise<Void> promise) {
|
|
||||||
final Http2Connection connection = connection();
|
final Http2Connection connection = connection();
|
||||||
final int streamId = connection.local().incrementAndGetNextStreamId();
|
final int streamId = connection.local().incrementAndGetNextStreamId();
|
||||||
if (streamId < 0) {
|
if (streamId < 0) {
|
||||||
promise.setFailure(new Http2NoMoreStreamIdsException());
|
Future<Void> f = ctx.newFailedFuture(new Http2NoMoreStreamIdsException());
|
||||||
|
|
||||||
// Simulate a GOAWAY being received due to stream exhaustion on this connection. We use the maximum
|
// Simulate a GOAWAY being received due to stream exhaustion on this connection. We use the maximum
|
||||||
// valid stream ID for the current peer.
|
// valid stream ID for the current peer.
|
||||||
@ -445,7 +449,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
Integer.MAX_VALUE - 1, NO_ERROR.code(),
|
Integer.MAX_VALUE - 1, NO_ERROR.code(),
|
||||||
writeAscii(ctx.alloc(), "Stream IDs exhausted on local stream creation")));
|
writeAscii(ctx.alloc(), "Stream IDs exhausted on local stream creation")));
|
||||||
|
|
||||||
return false;
|
return f;
|
||||||
}
|
}
|
||||||
http2FrameStream.id = streamId;
|
http2FrameStream.id = streamId;
|
||||||
|
|
||||||
@ -458,7 +462,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
|
|
||||||
// We should not re-use ids.
|
// We should not re-use ids.
|
||||||
assert old == null;
|
assert old == null;
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleHeaderFuture(Future<?> channelFuture, int streamId) {
|
private void handleHeaderFuture(Future<?> channelFuture, int streamId) {
|
||||||
|
@ -18,7 +18,6 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.Promise;
|
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
@ -54,7 +53,6 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and
|
* @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and
|
||||||
* 256 (inclusive).
|
* 256 (inclusive).
|
||||||
* @param endStream indicates if this is the last frame to be sent for the stream.
|
* @param endStream indicates if this is the last frame to be sent for the stream.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
* <a href="https://tools.ietf.org/html/rfc7540#section-10.5.1">Section 10.5.1</a> states the following:
|
* <a href="https://tools.ietf.org/html/rfc7540#section-10.5.1">Section 10.5.1</a> states the following:
|
||||||
* <pre>
|
* <pre>
|
||||||
@ -65,7 +63,7 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
||||||
*/
|
*/
|
||||||
Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int padding, boolean endStream, Promise<Void> promise);
|
int padding, boolean endStream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a HEADERS frame with priority specified to the remote endpoint.
|
* Writes a HEADERS frame with priority specified to the remote endpoint.
|
||||||
@ -80,7 +78,6 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and
|
* @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and
|
||||||
* 256 (inclusive).
|
* 256 (inclusive).
|
||||||
* @param endStream indicates if this is the last frame to be sent for the stream.
|
* @param endStream indicates if this is the last frame to be sent for the stream.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
* <a href="https://tools.ietf.org/html/rfc7540#section-10.5.1">Section 10.5.1</a> states the following:
|
* <a href="https://tools.ietf.org/html/rfc7540#section-10.5.1">Section 10.5.1</a> states the following:
|
||||||
* <pre>
|
* <pre>
|
||||||
@ -91,8 +88,7 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
||||||
*/
|
*/
|
||||||
Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream,
|
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream);
|
||||||
Promise<Void> promise);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a PRIORITY frame to the remote endpoint.
|
* Writes a PRIORITY frame to the remote endpoint.
|
||||||
@ -103,11 +99,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* depend on the connection.
|
* depend on the connection.
|
||||||
* @param weight the weight for this stream.
|
* @param weight the weight for this stream.
|
||||||
* @param exclusive whether this stream should be the exclusive dependant of its parent.
|
* @param exclusive whether this stream should be the exclusive dependant of its parent.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency,
|
Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency,
|
||||||
short weight, boolean exclusive, Promise<Void> promise);
|
short weight, boolean exclusive);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a RST_STREAM frame to the remote endpoint.
|
* Writes a RST_STREAM frame to the remote endpoint.
|
||||||
@ -115,31 +110,26 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param ctx the context to use for writing.
|
* @param ctx the context to use for writing.
|
||||||
* @param streamId the stream for which to send the frame.
|
* @param streamId the stream for which to send the frame.
|
||||||
* @param errorCode the error code indicating the nature of the failure.
|
* @param errorCode the error code indicating the nature of the failure.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode);
|
||||||
Promise<Void> promise);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a SETTINGS frame to the remote endpoint.
|
* Writes a SETTINGS frame to the remote endpoint.
|
||||||
*
|
*
|
||||||
* @param ctx the context to use for writing.
|
* @param ctx the context to use for writing.
|
||||||
* @param settings the settings to be sent.
|
* @param settings the settings to be sent.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings);
|
||||||
Promise<Void> promise);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a SETTINGS acknowledgment to the remote endpoint.
|
* Writes a SETTINGS acknowledgment to the remote endpoint.
|
||||||
*
|
*
|
||||||
* @param ctx the context to use for writing.
|
* @param ctx the context to use for writing.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise);
|
Future<Void> writeSettingsAck(ChannelHandlerContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a PING frame to the remote endpoint.
|
* Writes a PING frame to the remote endpoint.
|
||||||
@ -148,11 +138,9 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param ack indicates whether this is an ack of a PING frame previously received from the
|
* @param ack indicates whether this is an ack of a PING frame previously received from the
|
||||||
* remote endpoint.
|
* remote endpoint.
|
||||||
* @param data the payload of the frame.
|
* @param data the payload of the frame.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data,
|
Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data);
|
||||||
Promise<Void> promise);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a PUSH_PROMISE frame to the remote endpoint.
|
* Writes a PUSH_PROMISE frame to the remote endpoint.
|
||||||
@ -163,7 +151,6 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param headers the headers to be sent.
|
* @param headers the headers to be sent.
|
||||||
* @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and
|
* @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and
|
||||||
* 256 (inclusive).
|
* 256 (inclusive).
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
* <a href="https://tools.ietf.org/html/rfc7540#section-10.5.1">Section 10.5.1</a> states the following:
|
* <a href="https://tools.ietf.org/html/rfc7540#section-10.5.1">Section 10.5.1</a> states the following:
|
||||||
* <pre>
|
* <pre>
|
||||||
@ -174,7 +161,7 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
||||||
*/
|
*/
|
||||||
Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||||
Http2Headers headers, int padding, Promise<Void> promise);
|
Http2Headers headers, int padding);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a GO_AWAY frame to the remote endpoint.
|
* Writes a GO_AWAY frame to the remote endpoint.
|
||||||
@ -183,11 +170,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param lastStreamId the last known stream of this endpoint.
|
* @param lastStreamId the last known stream of this endpoint.
|
||||||
* @param errorCode the error code, if the connection was abnormally terminated.
|
* @param errorCode the error code, if the connection was abnormally terminated.
|
||||||
* @param debugData application-defined debug data. This will be released by this method.
|
* @param debugData application-defined debug data. This will be released by this method.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||||
ByteBuf debugData, Promise<Void> promise);
|
ByteBuf debugData);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a WINDOW_UPDATE frame to the remote endpoint.
|
* Writes a WINDOW_UPDATE frame to the remote endpoint.
|
||||||
@ -196,11 +182,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param streamId the stream for which to send the frame.
|
* @param streamId the stream for which to send the frame.
|
||||||
* @param windowSizeIncrement the number of bytes by which the local inbound flow control window
|
* @param windowSizeIncrement the number of bytes by which the local inbound flow control window
|
||||||
* is increasing.
|
* is increasing.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
|
Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
|
||||||
int windowSizeIncrement, Promise<Void> promise);
|
int windowSizeIncrement);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic write method for any HTTP/2 frame. This allows writing of non-standard frames.
|
* Generic write method for any HTTP/2 frame. This allows writing of non-standard frames.
|
||||||
@ -210,11 +195,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param streamId the stream for which to send the frame.
|
* @param streamId the stream for which to send the frame.
|
||||||
* @param flags the flags to write for this frame.
|
* @param flags the flags to write for this frame.
|
||||||
* @param payload the payload to write for this frame. This will be released by this method.
|
* @param payload the payload to write for this frame. This will be released by this method.
|
||||||
* @param promise the promise for the write.
|
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||||
Http2Flags flags, ByteBuf payload, Promise<Void> promise);
|
Http2Flags flags, ByteBuf payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the configuration related elements for this {@link Http2FrameWriter}
|
* Get the configuration related elements for this {@link Http2FrameWriter}
|
||||||
|
@ -59,13 +59,11 @@ public interface Http2LifecycleManager {
|
|||||||
* @param ctx The context used for communication and buffer allocation if necessary.
|
* @param ctx The context used for communication and buffer allocation if necessary.
|
||||||
* @param streamId The identifier of the stream to reset.
|
* @param streamId The identifier of the stream to reset.
|
||||||
* @param errorCode Justification as to why this stream is being reset. See {@link Http2Error}.
|
* @param errorCode Justification as to why this stream is being reset. See {@link Http2Error}.
|
||||||
* @param promise Used to indicate the return status of this operation.
|
|
||||||
* @return Will be considered successful when the connection and stream state has been updated, and a
|
* @return Will be considered successful when the connection and stream state has been updated, and a
|
||||||
* {@code RST_STREAM} frame has been sent to the peer. If the stream state has already been updated and a
|
* {@code RST_STREAM} frame has been sent to the peer. If the stream state has already been updated and a
|
||||||
* {@code RST_STREAM} frame has been sent then the return status may indicate success immediately.
|
* {@code RST_STREAM} frame has been sent then the return status may indicate success immediately.
|
||||||
*/
|
*/
|
||||||
Future<Void> resetStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
Future<Void> resetStream(ChannelHandlerContext ctx, int streamId, long errorCode);
|
||||||
Promise<Void> promise);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevents the peer from creating streams and close the connection if {@code errorCode} is not
|
* Prevents the peer from creating streams and close the connection if {@code errorCode} is not
|
||||||
@ -78,13 +76,12 @@ public interface Http2LifecycleManager {
|
|||||||
* @param lastStreamId The last stream that the local endpoint is claiming it will accept.
|
* @param lastStreamId The last stream that the local endpoint is claiming it will accept.
|
||||||
* @param errorCode The rational as to why the connection is being closed. See {@link Http2Error}.
|
* @param errorCode The rational as to why the connection is being closed. See {@link Http2Error}.
|
||||||
* @param debugData For diagnostic purposes (carries no semantic value).
|
* @param debugData For diagnostic purposes (carries no semantic value).
|
||||||
* @param promise Used to indicate the return status of this operation.
|
|
||||||
* @return Will be considered successful when the connection and stream state has been updated, and a
|
* @return Will be considered successful when the connection and stream state has been updated, and a
|
||||||
* {@code GO_AWAY} frame has been sent to the peer. If the stream state has already been updated and a
|
* {@code GO_AWAY} frame has been sent to the peer. If the stream state has already been updated and a
|
||||||
* {@code GO_AWAY} frame has been sent then the return status may indicate success immediately.
|
* {@code GO_AWAY} frame has been sent then the return status may indicate success immediately.
|
||||||
*/
|
*/
|
||||||
Future<Void> goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
Future<Void> goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||||
ByteBuf debugData, Promise<Void> promise);
|
ByteBuf debugData);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the given error.
|
* Processes the given error.
|
||||||
|
@ -18,7 +18,6 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.Promise;
|
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http2.Http2FrameLogger.Direction.OUTBOUND;
|
import static io.netty.handler.codec.http2.Http2FrameLogger.Direction.OUTBOUND;
|
||||||
@ -40,93 +39,92 @@ public class Http2OutboundFrameLogger implements Http2FrameWriter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
||||||
int padding, boolean endStream, Promise<Void> promise) {
|
int padding, boolean endStream) {
|
||||||
logger.logData(OUTBOUND, ctx, streamId, data, padding, endStream);
|
logger.logData(OUTBOUND, ctx, streamId, data, padding, endStream);
|
||||||
return writer.writeData(ctx, streamId, data, padding, endStream, promise);
|
return writer.writeData(ctx, streamId, data, padding, endStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||||
Http2Headers headers, int padding, boolean endStream, Promise<Void> promise) {
|
Http2Headers headers, int padding, boolean endStream) {
|
||||||
logger.logHeaders(OUTBOUND, ctx, streamId, headers, padding, endStream);
|
logger.logHeaders(OUTBOUND, ctx, streamId, headers, padding, endStream);
|
||||||
return writer.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
return writer.writeHeaders(ctx, streamId, headers, padding, endStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||||
Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
||||||
int padding, boolean endStream, Promise<Void> promise) {
|
int padding, boolean endStream) {
|
||||||
logger.logHeaders(OUTBOUND, ctx, streamId, headers, streamDependency, weight, exclusive,
|
logger.logHeaders(OUTBOUND, ctx, streamId, headers, streamDependency, weight, exclusive,
|
||||||
padding, endStream);
|
padding, endStream);
|
||||||
return writer.writeHeaders(ctx, streamId, headers, streamDependency, weight,
|
return writer.writeHeaders(ctx, streamId, headers, streamDependency, weight,
|
||||||
exclusive, padding, endStream, promise);
|
exclusive, padding, endStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId,
|
||||||
int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
|
int streamDependency, short weight, boolean exclusive) {
|
||||||
logger.logPriority(OUTBOUND, ctx, streamId, streamDependency, weight, exclusive);
|
logger.logPriority(OUTBOUND, ctx, streamId, streamDependency, weight, exclusive);
|
||||||
return writer.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
return writer.writePriority(ctx, streamId, streamDependency, weight, exclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx,
|
||||||
int streamId, long errorCode, Promise<Void> promise) {
|
int streamId, long errorCode) {
|
||||||
logger.logRstStream(OUTBOUND, ctx, streamId, errorCode);
|
logger.logRstStream(OUTBOUND, ctx, streamId, errorCode);
|
||||||
return writer.writeRstStream(ctx, streamId, errorCode, promise);
|
return writer.writeRstStream(ctx, streamId, errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeSettings(ChannelHandlerContext ctx,
|
public Future<Void> writeSettings(ChannelHandlerContext ctx,
|
||||||
Http2Settings settings, Promise<Void> promise) {
|
Http2Settings settings) {
|
||||||
logger.logSettings(OUTBOUND, ctx, settings);
|
logger.logSettings(OUTBOUND, ctx, settings);
|
||||||
return writer.writeSettings(ctx, settings, promise);
|
return writer.writeSettings(ctx, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||||
logger.logSettingsAck(OUTBOUND, ctx);
|
logger.logSettingsAck(OUTBOUND, ctx);
|
||||||
return writer.writeSettingsAck(ctx, promise);
|
return writer.writeSettingsAck(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack,
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack,
|
||||||
long data, Promise<Void> promise) {
|
long data) {
|
||||||
if (ack) {
|
if (ack) {
|
||||||
logger.logPingAck(OUTBOUND, ctx, data);
|
logger.logPingAck(OUTBOUND, ctx, data);
|
||||||
} else {
|
} else {
|
||||||
logger.logPing(OUTBOUND, ctx, data);
|
logger.logPing(OUTBOUND, ctx, data);
|
||||||
}
|
}
|
||||||
return writer.writePing(ctx, ack, data, promise);
|
return writer.writePing(ctx, ack, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId,
|
||||||
int promisedStreamId, Http2Headers headers, int padding,
|
int promisedStreamId, Http2Headers headers, int padding) {
|
||||||
Promise<Void> promise) {
|
|
||||||
logger.logPushPromise(OUTBOUND, ctx, streamId, promisedStreamId, headers, padding);
|
logger.logPushPromise(OUTBOUND, ctx, streamId, promisedStreamId, headers, padding);
|
||||||
return writer.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise);
|
return writer.writePushPromise(ctx, streamId, promisedStreamId, headers, padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||||
ByteBuf debugData, Promise<Void> promise) {
|
ByteBuf debugData) {
|
||||||
logger.logGoAway(OUTBOUND, ctx, lastStreamId, errorCode, debugData);
|
logger.logGoAway(OUTBOUND, ctx, lastStreamId, errorCode, debugData);
|
||||||
return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx,
|
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx,
|
||||||
int streamId, int windowSizeIncrement, Promise<Void> promise) {
|
int streamId, int windowSizeIncrement) {
|
||||||
logger.logWindowsUpdate(OUTBOUND, ctx, streamId, windowSizeIncrement);
|
logger.logWindowsUpdate(OUTBOUND, ctx, streamId, windowSizeIncrement);
|
||||||
return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
|
return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||||
Http2Flags flags, ByteBuf payload, Promise<Void> promise) {
|
Http2Flags flags, ByteBuf payload) {
|
||||||
logger.logUnknownFrame(OUTBOUND, ctx, frameType, streamId, flags, payload);
|
logger.logUnknownFrame(OUTBOUND, ctx, frameType, streamId, flags, payload);
|
||||||
return writer.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
return writer.writeFrame(ctx, frameType, streamId, flags, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -106,7 +106,7 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
|
|||||||
Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders);
|
Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders);
|
||||||
endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable();
|
endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable();
|
||||||
writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers,
|
writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers,
|
||||||
endStream, promiseAggregator);
|
endStream).cascadeTo(promiseAggregator.newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!endStream && msg instanceof HttpContent) {
|
if (!endStream && msg instanceof HttpContent) {
|
||||||
@ -125,12 +125,14 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
|
|||||||
// Write the data
|
// Write the data
|
||||||
final ByteBuf content = ((HttpContent) msg).content();
|
final ByteBuf content = ((HttpContent) msg).content();
|
||||||
endStream = isLastContent && trailers.isEmpty();
|
endStream = isLastContent && trailers.isEmpty();
|
||||||
encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise());
|
encoder.writeData(ctx, currentStreamId, content, 0, endStream)
|
||||||
|
.cascadeTo(promiseAggregator.newPromise());
|
||||||
release = false;
|
release = false;
|
||||||
|
|
||||||
if (!trailers.isEmpty()) {
|
if (!trailers.isEmpty()) {
|
||||||
// Write trailing headers.
|
// Write trailing headers.
|
||||||
writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator);
|
writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true)
|
||||||
|
.cascadeTo(promiseAggregator.newPromise());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
@ -145,14 +147,13 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
|
|||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeHeaders(ChannelHandlerContext ctx, Http2ConnectionEncoder encoder, int streamId,
|
private static Future<Void> writeHeaders(ChannelHandlerContext ctx, Http2ConnectionEncoder encoder, int streamId,
|
||||||
HttpHeaders headers, Http2Headers http2Headers, boolean endStream,
|
HttpHeaders headers, Http2Headers http2Headers, boolean endStream) {
|
||||||
SimpleChannelPromiseAggregator promiseAggregator) {
|
|
||||||
int dependencyId = headers.getInt(
|
int dependencyId = headers.getInt(
|
||||||
HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 0);
|
HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 0);
|
||||||
short weight = headers.getShort(
|
short weight = headers.getShort(
|
||||||
HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT);
|
HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT);
|
||||||
encoder.writeHeaders(ctx, streamId, http2Headers, dependencyId, weight, false,
|
return encoder.writeHeaders(ctx, streamId, http2Headers, dependencyId, weight, false,
|
||||||
0, endStream, promiseAggregator.newPromise());
|
0, endStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,40 +154,41 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int padding, boolean endStream, Promise<Void> promise) {
|
int padding, boolean endStream) {
|
||||||
return writeHeaders(ctx, streamId, headers, 0, Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT,
|
return writeHeaders(ctx, streamId, headers, 0, Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT,
|
||||||
false, padding, endStream, promise);
|
false, padding, endStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int streamDependency, short weight, boolean exclusive,
|
int streamDependency, short weight, boolean exclusive,
|
||||||
int padding, boolean endOfStream, Promise<Void> promise) {
|
int padding, boolean endOfStream) {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
return promise.setFailure(new Http2ChannelClosedException());
|
return ctx.newFailedFuture(new Http2ChannelClosedException());
|
||||||
}
|
}
|
||||||
if (isExistingStream(streamId) || canCreateStream()) {
|
if (isExistingStream(streamId) || canCreateStream()) {
|
||||||
return super.writeHeaders(ctx, streamId, headers, streamDependency, weight,
|
return super.writeHeaders(ctx, streamId, headers, streamDependency, weight,
|
||||||
exclusive, padding, endOfStream, promise);
|
exclusive, padding, endOfStream);
|
||||||
}
|
}
|
||||||
if (goAwayDetail != null) {
|
if (goAwayDetail != null) {
|
||||||
return promise.setFailure(new Http2GoAwayException(goAwayDetail));
|
return ctx.newFailedFuture(new Http2GoAwayException(goAwayDetail));
|
||||||
}
|
}
|
||||||
PendingStream pendingStream = pendingStreams.get(streamId);
|
PendingStream pendingStream = pendingStreams.get(streamId);
|
||||||
if (pendingStream == null) {
|
if (pendingStream == null) {
|
||||||
pendingStream = new PendingStream(ctx, streamId);
|
pendingStream = new PendingStream(ctx, streamId);
|
||||||
pendingStreams.put(streamId, pendingStream);
|
pendingStreams.put(streamId, pendingStream);
|
||||||
}
|
}
|
||||||
|
Promise<Void> promise = ctx.newPromise();
|
||||||
|
|
||||||
pendingStream.frames.add(new HeadersFrame(headers, streamDependency, weight, exclusive,
|
pendingStream.frames.add(new HeadersFrame(headers, streamDependency, weight, exclusive,
|
||||||
padding, endOfStream, promise));
|
padding, endOfStream, promise));
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||||
Promise<Void> promise) {
|
|
||||||
if (isExistingStream(streamId)) {
|
if (isExistingStream(streamId)) {
|
||||||
return super.writeRstStream(ctx, streamId, errorCode, promise);
|
return super.writeRstStream(ctx, streamId, errorCode);
|
||||||
}
|
}
|
||||||
// Since the delegate doesn't know about any buffered streams we have to handle cancellation
|
// Since the delegate doesn't know about any buffered streams we have to handle cancellation
|
||||||
// of the promises and releasing of the ByteBufs here.
|
// of the promises and releasing of the ByteBufs here.
|
||||||
@ -198,27 +199,27 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
// about the stream anymore and thus there is not point in failing the promises and invoking
|
// about the stream anymore and thus there is not point in failing the promises and invoking
|
||||||
// error handling routines.
|
// error handling routines.
|
||||||
stream.close(null);
|
stream.close(null);
|
||||||
promise.setSuccess(null);
|
return ctx.newSucceededFuture();
|
||||||
} else {
|
} else {
|
||||||
promise.setFailure(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
|
return ctx.newFailedFuture(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
|
||||||
}
|
}
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
||||||
int padding, boolean endOfStream, Promise<Void> promise) {
|
int padding, boolean endOfStream) {
|
||||||
if (isExistingStream(streamId)) {
|
if (isExistingStream(streamId)) {
|
||||||
return super.writeData(ctx, streamId, data, padding, endOfStream, promise);
|
return super.writeData(ctx, streamId, data, padding, endOfStream);
|
||||||
}
|
}
|
||||||
PendingStream pendingStream = pendingStreams.get(streamId);
|
PendingStream pendingStream = pendingStreams.get(streamId);
|
||||||
if (pendingStream != null) {
|
if (pendingStream != null) {
|
||||||
|
Promise<Void> promise = ctx.newPromise();
|
||||||
pendingStream.frames.add(new DataFrame(data, padding, endOfStream, promise));
|
pendingStream.frames.add(new DataFrame(data, padding, endOfStream, promise));
|
||||||
|
return promise;
|
||||||
} else {
|
} else {
|
||||||
ReferenceCountUtil.safeRelease(data);
|
ReferenceCountUtil.safeRelease(data);
|
||||||
promise.setFailure(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
|
return ctx.newFailedFuture(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
|
||||||
}
|
}
|
||||||
return promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -352,7 +353,8 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void send(ChannelHandlerContext ctx, int streamId) {
|
void send(ChannelHandlerContext ctx, int streamId) {
|
||||||
writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endOfStream, promise);
|
writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endOfStream)
|
||||||
|
.cascadeTo(promise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +378,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void send(ChannelHandlerContext ctx, int streamId) {
|
void send(ChannelHandlerContext ctx, int streamId) {
|
||||||
writeData(ctx, streamId, data, padding, endOfStream, promise);
|
writeData(ctx, streamId, data, padding, endOfStream).cascadeTo(promise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ public class DataCompressionHttp2Test {
|
|||||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, true, newPromiseClient());
|
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, true);
|
||||||
clientHandler.flush(ctxClient());
|
clientHandler.flush(ctxClient());
|
||||||
});
|
});
|
||||||
awaitServer();
|
awaitServer();
|
||||||
@ -157,8 +157,8 @@ public class DataCompressionHttp2Test {
|
|||||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||||
clientHandler.flush(ctxClient());
|
clientHandler.flush(ctxClient());
|
||||||
});
|
});
|
||||||
awaitServer();
|
awaitServer();
|
||||||
@ -178,8 +178,8 @@ public class DataCompressionHttp2Test {
|
|||||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||||
clientHandler.flush(ctxClient());
|
clientHandler.flush(ctxClient());
|
||||||
});
|
});
|
||||||
awaitServer();
|
awaitServer();
|
||||||
@ -201,9 +201,9 @@ public class DataCompressionHttp2Test {
|
|||||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||||
clientEncoder.writeData(ctxClient(), 3, data1.retain(), 0, false, newPromiseClient());
|
clientEncoder.writeData(ctxClient(), 3, data1.retain(), 0, false);
|
||||||
clientEncoder.writeData(ctxClient(), 3, data2.retain(), 0, true, newPromiseClient());
|
clientEncoder.writeData(ctxClient(), 3, data2.retain(), 0, true);
|
||||||
clientHandler.flush(ctxClient());
|
clientHandler.flush(ctxClient());
|
||||||
});
|
});
|
||||||
awaitServer();
|
awaitServer();
|
||||||
@ -224,8 +224,8 @@ public class DataCompressionHttp2Test {
|
|||||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR);
|
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR);
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||||
clientHandler.flush(ctxClient());
|
clientHandler.flush(ctxClient());
|
||||||
});
|
});
|
||||||
awaitServer();
|
awaitServer();
|
||||||
@ -245,8 +245,8 @@ public class DataCompressionHttp2Test {
|
|||||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR);
|
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR);
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||||
clientHandler.flush(ctxClient());
|
clientHandler.flush(ctxClient());
|
||||||
});
|
});
|
||||||
awaitServer();
|
awaitServer();
|
||||||
@ -266,8 +266,8 @@ public class DataCompressionHttp2Test {
|
|||||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD);
|
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD);
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||||
clientHandler.flush(ctxClient());
|
clientHandler.flush(ctxClient());
|
||||||
});
|
});
|
||||||
awaitServer();
|
awaitServer();
|
||||||
@ -287,8 +287,8 @@ public class DataCompressionHttp2Test {
|
|||||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD);
|
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD);
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||||
clientHandler.flush(ctxClient());
|
clientHandler.flush(ctxClient());
|
||||||
});
|
});
|
||||||
awaitServer();
|
awaitServer();
|
||||||
@ -310,8 +310,8 @@ public class DataCompressionHttp2Test {
|
|||||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.DEFLATE);
|
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.DEFLATE);
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||||
clientHandler.flush(ctxClient());
|
clientHandler.flush(ctxClient());
|
||||||
});
|
});
|
||||||
awaitServer();
|
awaitServer();
|
||||||
|
@ -79,7 +79,6 @@ public class DefaultHttp2ConnectionDecoderTest {
|
|||||||
private static final int STATE_RECV_TRAILERS = 1 << 1;
|
private static final int STATE_RECV_TRAILERS = 1 << 1;
|
||||||
|
|
||||||
private Http2ConnectionDecoder decoder;
|
private Http2ConnectionDecoder decoder;
|
||||||
private Promise<Void> promise;
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Http2Connection connection;
|
private Http2Connection connection;
|
||||||
@ -130,7 +129,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
|||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
Promise<Void> promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
|
|
||||||
final AtomicInteger headersReceivedState = new AtomicInteger();
|
final AtomicInteger headersReceivedState = new AtomicInteger();
|
||||||
when(channel.isActive()).thenReturn(true);
|
when(channel.isActive()).thenReturn(true);
|
||||||
@ -209,7 +208,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
|||||||
decode().onSettingsRead(ctx, new Http2Settings());
|
decode().onSettingsRead(ctx, new Http2Settings());
|
||||||
verify(listener).onSettingsRead(eq(ctx), eq(new Http2Settings()));
|
verify(listener).onSettingsRead(eq(ctx), eq(new Http2Settings()));
|
||||||
assertTrue(decoder.prefaceReceived());
|
assertTrue(decoder.prefaceReceived());
|
||||||
verify(encoder).writeSettingsAck(eq(ctx), eq(promise));
|
verify(encoder).writeSettingsAck(eq(ctx));
|
||||||
|
|
||||||
// Simulate receiving the SETTINGS ACK for the initial settings.
|
// Simulate receiving the SETTINGS ACK for the initial settings.
|
||||||
decode().onSettingsAckRead(ctx);
|
decode().onSettingsAckRead(ctx);
|
||||||
@ -793,7 +792,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void pingReadShouldReplyWithAck() throws Exception {
|
public void pingReadShouldReplyWithAck() throws Exception {
|
||||||
decode().onPingRead(ctx, 0L);
|
decode().onPingRead(ctx, 0L);
|
||||||
verify(encoder).writePing(eq(ctx), eq(true), eq(0L), eq(promise));
|
verify(encoder).writePing(eq(ctx), eq(true), eq(0L));
|
||||||
verify(listener, never()).onPingAckRead(eq(ctx), any(long.class));
|
verify(listener, never()).onPingAckRead(eq(ctx), any(long.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ import io.netty.channel.ChannelPipeline;
|
|||||||
import io.netty.channel.DefaultChannelConfig;
|
import io.netty.channel.DefaultChannelConfig;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http2.Http2RemoteFlowController.FlowControlled;
|
import io.netty.handler.codec.http2.Http2RemoteFlowController.FlowControlled;
|
||||||
import io.netty.util.concurrent.DefaultPromise;
|
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
@ -126,25 +125,23 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
when(channel.unsafe()).thenReturn(unsafe);
|
when(channel.unsafe()).thenReturn(unsafe);
|
||||||
ChannelConfig config = new DefaultChannelConfig(channel);
|
ChannelConfig config = new DefaultChannelConfig(channel);
|
||||||
when(channel.config()).thenReturn(config);
|
when(channel.config()).thenReturn(config);
|
||||||
doAnswer(in -> newPromise()
|
|
||||||
.setFailure((Throwable) in.getArgument(0))).when(channel).newFailedFuture(any(Throwable.class));
|
|
||||||
|
|
||||||
when(writer.configuration()).thenReturn(writerConfig);
|
when(writer.configuration()).thenReturn(writerConfig);
|
||||||
when(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy);
|
when(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy);
|
||||||
when(frameSizePolicy.maxFrameSize()).thenReturn(64);
|
when(frameSizePolicy.maxFrameSize()).thenReturn(64);
|
||||||
doAnswer((Answer<Future<Void>>) in -> ((Promise<Void>) in.getArguments()[2])
|
doAnswer((Answer<Future<Void>>) in -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null))
|
||||||
.setSuccess(null)).when(writer).writeSettings(eq(ctx), any(Http2Settings.class), any(Promise.class));
|
.when(writer).writeSettings(eq(ctx), any(Http2Settings.class));
|
||||||
doAnswer((Answer<Future<Void>>) in -> {
|
doAnswer((Answer<Future<Void>>) in -> {
|
||||||
((ByteBuf) in.getArguments()[3]).release();
|
((ByteBuf) in.getArguments()[3]).release();
|
||||||
return ((Promise<Void>) in.getArguments()[4]).setSuccess(null);
|
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||||
}).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class), any(Promise.class));
|
}).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class));
|
||||||
|
|
||||||
writtenData = new ArrayList<>();
|
writtenData = new ArrayList<>();
|
||||||
writtenPadding = new ArrayList<>();
|
writtenPadding = new ArrayList<>();
|
||||||
when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
|
when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()))
|
||||||
any(Promise.class))).then((Answer<Future<Void>>) in -> {
|
.then((Answer<Future<Void>>) in -> {
|
||||||
// Make sure we only receive stream closure on the last frame and that void promises
|
// Make sure we only receive stream closure on the last frame and that void promises
|
||||||
// are used for all writes except the last one.
|
// are used for all writes except the last one.
|
||||||
Promise<Void> promise = (Promise<Void>) in.getArguments()[5];
|
|
||||||
if (streamClosed) {
|
if (streamClosed) {
|
||||||
fail("Stream already closed");
|
fail("Stream already closed");
|
||||||
} else {
|
} else {
|
||||||
@ -156,39 +153,41 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
// Release the buffer just as DefaultHttp2FrameWriter does
|
// Release the buffer just as DefaultHttp2FrameWriter does
|
||||||
data.release();
|
data.release();
|
||||||
// Let the promise succeed to trigger listeners.
|
// Let the promise succeed to trigger listeners.
|
||||||
return promise.setSuccess(null);
|
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||||
});
|
});
|
||||||
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(),
|
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(),
|
||||||
anyInt(), anyBoolean(), any(Promise.class)))
|
anyInt(), anyBoolean()))
|
||||||
.then((Answer<Future<Void>>) invocationOnMock -> {
|
.then((Answer<Future<Void>>) invocationOnMock -> {
|
||||||
Promise<Void> promise = invocationOnMock.getArgument(8);
|
|
||||||
if (streamClosed) {
|
if (streamClosed) {
|
||||||
fail("Stream already closed");
|
fail("Stream already closed");
|
||||||
} else {
|
} else {
|
||||||
streamClosed = (Boolean) invocationOnMock.getArguments()[5];
|
streamClosed = (Boolean) invocationOnMock.getArguments()[5];
|
||||||
}
|
}
|
||||||
return promise.setSuccess(null);
|
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||||
});
|
});
|
||||||
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class),
|
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class),
|
||||||
anyInt(), anyBoolean(), any(Promise.class)))
|
anyInt(), anyBoolean()))
|
||||||
.then((Answer<Future<Void>>) invocationOnMock -> {
|
.then((Answer<Future<Void>>) invocationOnMock -> {
|
||||||
Promise<Void> promise = invocationOnMock.getArgument(5);
|
|
||||||
if (streamClosed) {
|
if (streamClosed) {
|
||||||
fail("Stream already closed");
|
fail("Stream already closed");
|
||||||
} else {
|
} else {
|
||||||
streamClosed = invocationOnMock.getArgument(4);
|
streamClosed = invocationOnMock.getArgument(4);
|
||||||
}
|
}
|
||||||
return promise.setSuccess(null);
|
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||||
});
|
});
|
||||||
payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class);
|
payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class);
|
||||||
doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture());
|
doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture());
|
||||||
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(ctx.channel()).thenReturn(channel);
|
when(ctx.channel()).thenReturn(channel);
|
||||||
doAnswer((Answer<Promise<Void>>) in -> newPromise()).when(ctx).newPromise();
|
doAnswer((Answer<Promise<Void>>) in -> ImmediateEventExecutor.INSTANCE.newPromise()).when(ctx).newPromise();
|
||||||
doAnswer((Answer<Future<Void>>) in -> newSucceededFuture()).when(ctx).newSucceededFuture();
|
doAnswer((Answer<Future<Void>>) in -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null))
|
||||||
|
.when(ctx).newSucceededFuture();
|
||||||
|
doAnswer((Answer<Future<Void>>) in -> ImmediateEventExecutor.INSTANCE.newFailedFuture(in.getArgument(0)))
|
||||||
|
.when(ctx).newFailedFuture(any(Throwable.class));
|
||||||
when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden"));
|
when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden"));
|
||||||
when(channel.alloc()).thenReturn(PooledByteBufAllocator.DEFAULT);
|
when(channel.alloc()).thenReturn(PooledByteBufAllocator.DEFAULT);
|
||||||
|
doAnswer((Answer<Future<Void>>) in -> ImmediateEventExecutor.INSTANCE.newFailedFuture(in.getArgument(0)))
|
||||||
|
.when(channel).newFailedFuture(any(Throwable.class));
|
||||||
// Use a server-side connection so we can test server push.
|
// Use a server-side connection so we can test server push.
|
||||||
connection = new DefaultHttp2Connection(true);
|
connection = new DefaultHttp2Connection(true);
|
||||||
connection.remote().flowController(remoteFlow);
|
connection.remote().flowController(remoteFlow);
|
||||||
@ -210,8 +209,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
private void dataWriteShouldSignalThatFrameWasConsumedOnError0(boolean endOfStream) throws Exception {
|
private void dataWriteShouldSignalThatFrameWasConsumedOnError0(boolean endOfStream) throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
final ByteBuf data = dummyData();
|
final ByteBuf data = dummyData();
|
||||||
Promise<Void> p = newPromise();
|
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream);
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream, p);
|
|
||||||
|
|
||||||
FlowControlled controlled = payloadCaptor.getValue();
|
FlowControlled controlled = payloadCaptor.getValue();
|
||||||
assertEquals(8, controlled.size());
|
assertEquals(8, controlled.size());
|
||||||
@ -224,21 +222,20 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
assertEquals(0, controlled.size());
|
assertEquals(0, controlled.size());
|
||||||
assertEquals("abcd", writtenData.get(0));
|
assertEquals("abcd", writtenData.get(0));
|
||||||
assertEquals(0, data.refCnt());
|
assertEquals(0, data.refCnt());
|
||||||
assertSame(error, p.cause());
|
assertSame(error, f.cause());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dataWriteShouldSucceed() throws Exception {
|
public void dataWriteShouldSucceed() throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
final ByteBuf data = dummyData();
|
final ByteBuf data = dummyData();
|
||||||
Promise<Void> p = newPromise();
|
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 0, true);
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, true, p);
|
|
||||||
assertEquals(8, payloadCaptor.getValue().size());
|
assertEquals(8, payloadCaptor.getValue().size());
|
||||||
payloadCaptor.getValue().write(ctx, 8);
|
payloadCaptor.getValue().write(ctx, 8);
|
||||||
assertEquals(0, payloadCaptor.getValue().size());
|
assertEquals(0, payloadCaptor.getValue().size());
|
||||||
assertEquals("abcdefgh", writtenData.get(0));
|
assertEquals("abcdefgh", writtenData.get(0));
|
||||||
assertEquals(0, data.refCnt());
|
assertEquals(0, data.refCnt());
|
||||||
assertTrue(p.isSuccess());
|
assertTrue(f.isSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -246,35 +243,33 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
final ByteBuf data = dummyData().retain();
|
final ByteBuf data = dummyData().retain();
|
||||||
|
|
||||||
Promise<Void> promise1 = newPromise();
|
Future<Void> future1 = encoder.writeData(ctx, STREAM_ID, data, 0, true);
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise1);
|
Future<Void> future2 = encoder.writeData(ctx, STREAM_ID, data, 0, true);
|
||||||
Promise<Void> promise2 = newPromise();
|
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise2);
|
|
||||||
|
|
||||||
// Now merge the two payloads.
|
// Now merge the two payloads.
|
||||||
List<FlowControlled> capturedWrites = payloadCaptor.getAllValues();
|
List<FlowControlled> capturedWrites = payloadCaptor.getAllValues();
|
||||||
FlowControlled mergedPayload = capturedWrites.get(0);
|
FlowControlled mergedPayload = capturedWrites.get(0);
|
||||||
mergedPayload.merge(ctx, capturedWrites.get(1));
|
mergedPayload.merge(ctx, capturedWrites.get(1));
|
||||||
assertEquals(16, mergedPayload.size());
|
assertEquals(16, mergedPayload.size());
|
||||||
assertFalse(promise1.isDone());
|
assertFalse(future1.isDone());
|
||||||
assertFalse(promise2.isDone());
|
assertFalse(future2.isDone());
|
||||||
|
|
||||||
// Write the merged payloads and verify it was written correctly.
|
// Write the merged payloads and verify it was written correctly.
|
||||||
mergedPayload.write(ctx, 16);
|
mergedPayload.write(ctx, 16);
|
||||||
assertEquals(0, mergedPayload.size());
|
assertEquals(0, mergedPayload.size());
|
||||||
assertEquals("abcdefghabcdefgh", writtenData.get(0));
|
assertEquals("abcdefghabcdefgh", writtenData.get(0));
|
||||||
assertEquals(0, data.refCnt());
|
assertEquals(0, data.refCnt());
|
||||||
assertTrue(promise1.isSuccess());
|
assertTrue(future1.isSuccess());
|
||||||
assertTrue(promise2.isSuccess());
|
assertTrue(future2.isSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dataFramesDontMergeWithHeaders() throws Exception {
|
public void dataFramesDontMergeWithHeaders() throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
final ByteBuf data = dummyData().retain();
|
final ByteBuf data = dummyData().retain();
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, false, newPromise());
|
encoder.writeData(ctx, STREAM_ID, data, 0, false);
|
||||||
when(remoteFlow.hasFlowControlled(any(Http2Stream.class))).thenReturn(true);
|
when(remoteFlow.hasFlowControlled(any(Http2Stream.class))).thenReturn(true);
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, newPromise());
|
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||||
List<FlowControlled> capturedWrites = payloadCaptor.getAllValues();
|
List<FlowControlled> capturedWrites = payloadCaptor.getAllValues();
|
||||||
assertFalse(capturedWrites.get(0).merge(ctx, capturedWrites.get(1)));
|
assertFalse(capturedWrites.get(0).merge(ctx, capturedWrites.get(1)));
|
||||||
}
|
}
|
||||||
@ -289,8 +284,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
private void assertSplitPaddingOnEmptyBuffer(ByteBuf data) throws Exception {
|
private void assertSplitPaddingOnEmptyBuffer(ByteBuf data) throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
when(frameSizePolicy.maxFrameSize()).thenReturn(5);
|
when(frameSizePolicy.maxFrameSize()).thenReturn(5);
|
||||||
Promise<Void> p = newPromise();
|
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 10, true);
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 10, true, p);
|
|
||||||
assertEquals(10, payloadCaptor.getValue().size());
|
assertEquals(10, payloadCaptor.getValue().size());
|
||||||
payloadCaptor.getValue().write(ctx, 10);
|
payloadCaptor.getValue().write(ctx, 10);
|
||||||
// writer was called 2 times
|
// writer was called 2 times
|
||||||
@ -298,18 +292,17 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
assertEquals("", writtenData.get(0));
|
assertEquals("", writtenData.get(0));
|
||||||
assertEquals(10, (int) writtenPadding.get(0));
|
assertEquals(10, (int) writtenPadding.get(0));
|
||||||
assertEquals(0, data.refCnt());
|
assertEquals(0, data.refCnt());
|
||||||
assertTrue(p.isSuccess());
|
assertTrue(f.isSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWriteForUnknownStreamShouldCreateStream() throws Exception {
|
public void headersWriteForUnknownStreamShouldCreateStream() throws Exception {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
Promise<Void> promise = newPromise();
|
Future<Void> f = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
|
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
|
||||||
eq(false), eq(promise));
|
eq(false));
|
||||||
assertTrue(promise.isSuccess());
|
assertTrue(f.isSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -318,46 +311,41 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
Http2Stream parent = createStream(STREAM_ID, false);
|
Http2Stream parent = createStream(STREAM_ID, false);
|
||||||
reservePushStream(PUSH_STREAM_ID, parent);
|
reservePushStream(PUSH_STREAM_ID, parent);
|
||||||
|
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
|
||||||
assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state());
|
assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state());
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void trailersDoNotEndStreamThrows() {
|
public void trailersDoNotEndStreamThrows() {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
|
||||||
|
|
||||||
Promise<Void> promise2 = newPromise();
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
|
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void trailersDoNotEndStreamWithDataThrows() {
|
public void trailersDoNotEndStreamWithDataThrows() {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
|
||||||
|
|
||||||
Http2Stream stream = connection.stream(streamId);
|
Http2Stream stream = connection.stream(streamId);
|
||||||
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
||||||
|
|
||||||
Promise<Void> promise2 = newPromise();
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
|
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -373,20 +361,17 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
private void tooManyHeadersThrows(boolean eos) {
|
private void tooManyHeadersThrows(boolean eos) {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||||
Promise<Void> promise2 = newPromise();
|
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
|
|
||||||
|
|
||||||
Promise<Void> promise3 = newPromise();
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
|
||||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
|
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false));
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(true), eq(promise2));
|
eq(0), eq(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -414,23 +399,21 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
Http2Headers infoHeaders = informationalHeaders();
|
Http2Headers infoHeaders = informationalHeaders();
|
||||||
for (int i = 0; i < infoHeaderCount; ++i) {
|
for (int i = 0; i < infoHeaderCount; ++i) {
|
||||||
encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false, newPromise());
|
encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false);
|
||||||
}
|
}
|
||||||
Promise<Void> promise2 = newPromise();
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
|
||||||
|
|
||||||
Promise<Void> promise3 = newPromise();
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
|
||||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertEquals(eos, future.isSuccess());
|
assertEquals(eos, future.isSuccess());
|
||||||
|
|
||||||
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
|
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
|
||||||
eq(0), eq(false), any(Promise.class));
|
eq(0), eq(false));
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise2));
|
eq(0), eq(false));
|
||||||
if (eos) {
|
if (eos) {
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(true), eq(promise3));
|
eq(0), eq(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,24 +436,21 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
private void tooManyHeadersWithDataThrows(boolean eos) {
|
private void tooManyHeadersWithDataThrows(boolean eos) {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
|
||||||
|
|
||||||
Http2Stream stream = connection.stream(streamId);
|
Http2Stream stream = connection.stream(streamId);
|
||||||
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
||||||
|
|
||||||
Promise<Void> promise2 = newPromise();
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
|
|
||||||
|
|
||||||
Promise<Void> promise3 = newPromise();
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
|
||||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
|
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false));
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(true), eq(promise2));
|
eq(0), eq(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -498,27 +478,25 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
Http2Headers infoHeaders = informationalHeaders();
|
Http2Headers infoHeaders = informationalHeaders();
|
||||||
for (int i = 0; i < infoHeaderCount; ++i) {
|
for (int i = 0; i < infoHeaderCount; ++i) {
|
||||||
encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false, newPromise());
|
encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Http2Stream stream = connection.stream(streamId);
|
Http2Stream stream = connection.stream(streamId);
|
||||||
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
||||||
|
|
||||||
Promise<Void> promise2 = newPromise();
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
|
||||||
|
|
||||||
Promise<Void> promise3 = newPromise();
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
|
||||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertEquals(eos, future.isSuccess());
|
assertEquals(eos, future.isSuccess());
|
||||||
|
|
||||||
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
|
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
|
||||||
eq(0), eq(false), any(Promise.class));
|
eq(0), eq(false));
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise2));
|
eq(0), eq(false));
|
||||||
if (eos) {
|
if (eos) {
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(true), eq(promise3));
|
eq(0), eq(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,8 +504,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void pushPromiseWriteAfterGoAwayReceivedShouldFail() throws Exception {
|
public void pushPromiseWriteAfterGoAwayReceivedShouldFail() throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
goAwayReceived(0);
|
goAwayReceived(0);
|
||||||
Future<Void> future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0,
|
Future<Void> future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0);
|
||||||
newPromise());
|
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
}
|
}
|
||||||
@ -535,42 +512,38 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void pushPromiseWriteShouldReserveStream() throws Exception {
|
public void pushPromiseWriteShouldReserveStream() throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0);
|
||||||
encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, promise);
|
|
||||||
assertEquals(RESERVED_LOCAL, stream(PUSH_STREAM_ID).state());
|
assertEquals(RESERVED_LOCAL, stream(PUSH_STREAM_ID).state());
|
||||||
verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID),
|
verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID),
|
||||||
eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(promise));
|
eq(EmptyHttp2Headers.INSTANCE), eq(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void priorityWriteAfterGoAwayShouldSucceed() throws Exception {
|
public void priorityWriteAfterGoAwayShouldSucceed() throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
goAwayReceived(Integer.MAX_VALUE);
|
goAwayReceived(Integer.MAX_VALUE);
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writePriority(ctx, STREAM_ID, 0, (short) 255, true);
|
||||||
encoder.writePriority(ctx, STREAM_ID, 0, (short) 255, true, promise);
|
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true));
|
||||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true), eq(promise));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void priorityWriteShouldSetPriorityForStream() throws Exception {
|
public void priorityWriteShouldSetPriorityForStream() throws Exception {
|
||||||
Promise<Void> promise = newPromise();
|
|
||||||
short weight = 255;
|
short weight = 255;
|
||||||
encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
|
encoder.writePriority(ctx, STREAM_ID, 0, weight, true);
|
||||||
|
|
||||||
// Verify that this did NOT create a stream object.
|
// Verify that this did NOT create a stream object.
|
||||||
Http2Stream stream = stream(STREAM_ID);
|
Http2Stream stream = stream(STREAM_ID);
|
||||||
assertNull(stream);
|
assertNull(stream);
|
||||||
|
|
||||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true), eq(promise));
|
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void priorityWriteOnPreviouslyExistingStreamShouldSucceed() throws Exception {
|
public void priorityWriteOnPreviouslyExistingStreamShouldSucceed() throws Exception {
|
||||||
createStream(STREAM_ID, false).close();
|
createStream(STREAM_ID, false).close();
|
||||||
Promise<Void> promise = newPromise();
|
|
||||||
short weight = 255;
|
short weight = 255;
|
||||||
encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
|
encoder.writePriority(ctx, STREAM_ID, 0, weight, true);
|
||||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true), eq(promise));
|
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -579,53 +552,47 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
createStream(parentStreamId, false).close();
|
createStream(parentStreamId, false).close();
|
||||||
|
|
||||||
Promise<Void> promise = newPromise();
|
|
||||||
short weight = 255;
|
short weight = 255;
|
||||||
encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true, promise);
|
encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true);
|
||||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true), eq(promise));
|
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void rstStreamWriteForUnknownStreamShouldIgnore() throws Exception {
|
public void rstStreamWriteForUnknownStreamShouldIgnore() throws Exception {
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code());
|
||||||
encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code(), promise);
|
verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong());
|
||||||
verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong(), eq(promise));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void rstStreamShouldCloseStream() throws Exception {
|
public void rstStreamShouldCloseStream() throws Exception {
|
||||||
// Create the stream and send headers.
|
// Create the stream and send headers.
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, newPromise());
|
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||||
|
|
||||||
// Now verify that a stream reset is performed.
|
// Now verify that a stream reset is performed.
|
||||||
stream(STREAM_ID);
|
stream(STREAM_ID);
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
|
||||||
encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong());
|
||||||
verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong(), eq(promise));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pingWriteAfterGoAwayShouldSucceed() throws Exception {
|
public void pingWriteAfterGoAwayShouldSucceed() throws Exception {
|
||||||
Promise<Void> promise = newPromise();
|
|
||||||
goAwayReceived(0);
|
goAwayReceived(0);
|
||||||
encoder.writePing(ctx, false, 0L, promise);
|
encoder.writePing(ctx, false, 0L);
|
||||||
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
|
verify(writer).writePing(eq(ctx), eq(false), eq(0L));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pingWriteShouldSucceed() throws Exception {
|
public void pingWriteShouldSucceed() throws Exception {
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writePing(ctx, false, 0L);
|
||||||
encoder.writePing(ctx, false, 0L, promise);
|
verify(writer).writePing(eq(ctx), eq(false), eq(0L));
|
||||||
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void settingsWriteAfterGoAwayShouldSucceed() throws Exception {
|
public void settingsWriteAfterGoAwayShouldSucceed() throws Exception {
|
||||||
goAwayReceived(0);
|
goAwayReceived(0);
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeSettings(ctx, new Http2Settings());
|
||||||
encoder.writeSettings(ctx, new Http2Settings(), promise);
|
verify(writer).writeSettings(eq(ctx), any(Http2Settings.class));
|
||||||
verify(writer).writeSettings(eq(ctx), any(Http2Settings.class), eq(promise));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -635,9 +602,8 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
settings.maxConcurrentStreams(1000);
|
settings.maxConcurrentStreams(1000);
|
||||||
settings.headerTableSize(2000);
|
settings.headerTableSize(2000);
|
||||||
|
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeSettings(ctx, settings);
|
||||||
encoder.writeSettings(ctx, settings, promise);
|
verify(writer).writeSettings(eq(ctx), eq(settings));
|
||||||
verify(writer).writeSettings(eq(ctx), eq(settings), eq(promise));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -646,11 +612,10 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
|
|
||||||
Http2Stream stream = createStream(STREAM_ID, false);
|
Http2Stream stream = createStream(STREAM_ID, false);
|
||||||
ByteBuf data = dummyData();
|
ByteBuf data = dummyData();
|
||||||
Promise<Void> promise = newPromise();
|
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true);
|
||||||
encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true, promise);
|
assertTrue(f.isSuccess());
|
||||||
assertTrue(promise.isSuccess());
|
|
||||||
verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
|
verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
|
||||||
verify(lifecycleManager).closeStreamLocal(stream, promise);
|
verify(lifecycleManager).closeStreamLocal(eq(stream), eq(f));
|
||||||
assertEquals(data.toString(UTF_8), writtenData.get(0));
|
assertEquals(data.toString(UTF_8), writtenData.get(0));
|
||||||
data.release();
|
data.release();
|
||||||
}
|
}
|
||||||
@ -659,11 +624,10 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void headersWriteShouldHalfCloseStream() throws Exception {
|
public void headersWriteShouldHalfCloseStream() throws Exception {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
Promise<Void> promise = newPromise();
|
Future<Void> f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
|
||||||
|
|
||||||
assertTrue(promise.isSuccess());
|
assertTrue(f.isSuccess());
|
||||||
verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise));
|
verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -671,64 +635,54 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
Http2Stream parent = createStream(STREAM_ID, false);
|
Http2Stream parent = createStream(STREAM_ID, false);
|
||||||
Http2Stream stream = reservePushStream(PUSH_STREAM_ID, parent);
|
Http2Stream stream = reservePushStream(PUSH_STREAM_ID, parent);
|
||||||
Promise<Void> promise = newPromise();
|
Future<Void> f = encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||||
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
|
||||||
assertEquals(HALF_CLOSED_REMOTE, stream.state());
|
assertEquals(HALF_CLOSED_REMOTE, stream.state());
|
||||||
assertTrue(promise.isSuccess());
|
assertTrue(f.isSuccess());
|
||||||
verify(lifecycleManager).closeStreamLocal(eq(stream), eq(promise));
|
verify(lifecycleManager).closeStreamLocal(eq(stream), eq(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception {
|
public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception {
|
||||||
final Promise<Void> promise = newPromise();
|
|
||||||
final Throwable ex = new RuntimeException();
|
final Throwable ex = new RuntimeException();
|
||||||
// Fake an encoding error, like HPACK's HeaderListSizeException
|
// Fake an encoding error, like HPACK's HeaderListSizeException
|
||||||
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
|
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true)))
|
||||||
.thenAnswer((Answer<Future<Void>>) invocation -> {
|
.thenReturn(ImmediateEventExecutor.INSTANCE.newFailedFuture(ex));
|
||||||
promise.setFailure(ex);
|
|
||||||
return promise;
|
|
||||||
});
|
|
||||||
|
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
Http2Stream stream = createStream(STREAM_ID, false);
|
Http2Stream stream = createStream(STREAM_ID, false);
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
Future<Void> f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||||
|
|
||||||
assertTrue(promise.isDone());
|
assertTrue(f.isDone());
|
||||||
assertFalse(promise.isSuccess());
|
assertFalse(f.isSuccess());
|
||||||
assertFalse(stream.isHeadersSent());
|
assertFalse(stream.isHeadersSent());
|
||||||
InOrder inOrder = inOrder(lifecycleManager);
|
InOrder inOrder = inOrder(lifecycleManager);
|
||||||
inOrder.verify(lifecycleManager).onError(eq(ctx), eq(true), eq(ex));
|
inOrder.verify(lifecycleManager).onError(eq(ctx), eq(true), eq(ex));
|
||||||
inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise));
|
inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception {
|
public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception {
|
||||||
final Promise<Void> promise = newPromise();
|
|
||||||
final Throwable ex = new RuntimeException();
|
final Throwable ex = new RuntimeException();
|
||||||
// Fake an encoding error, like HPACK's HeaderListSizeException
|
// Fake an encoding error, like HPACK's HeaderListSizeException
|
||||||
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
|
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true)))
|
||||||
.thenAnswer((Answer<Future<Void>>) invocation -> {
|
.thenReturn(ImmediateEventExecutor.INSTANCE.newFailedFuture(ex));
|
||||||
promise.setFailure(ex);
|
|
||||||
return promise;
|
|
||||||
});
|
|
||||||
|
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
Future<Void> f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||||
|
|
||||||
assertTrue(promise.isDone());
|
assertTrue(f.isDone());
|
||||||
assertFalse(promise.isSuccess());
|
assertFalse(f.isSuccess());
|
||||||
assertFalse(stream(STREAM_ID).isHeadersSent());
|
assertFalse(stream(STREAM_ID).isHeadersSent());
|
||||||
InOrder inOrder = inOrder(lifecycleManager);
|
InOrder inOrder = inOrder(lifecycleManager);
|
||||||
inOrder.verify(lifecycleManager).onError(eq(ctx), eq(true), eq(ex));
|
inOrder.verify(lifecycleManager).onError(eq(ctx), eq(true), eq(ex));
|
||||||
inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise));
|
inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void encoderDelegatesGoAwayToLifeCycleManager() {
|
public void encoderDelegatesGoAwayToLifeCycleManager() {
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null);
|
||||||
encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null, promise);
|
|
||||||
verify(lifecycleManager).goAway(eq(ctx), eq(STREAM_ID), eq(Http2Error.INTERNAL_ERROR.code()),
|
verify(lifecycleManager).goAway(eq(ctx), eq(STREAM_ID), eq(Http2Error.INTERNAL_ERROR.code()),
|
||||||
eq((ByteBuf) null), eq(promise));
|
eq((ByteBuf) null));
|
||||||
verifyNoMoreInteractions(writer);
|
verifyNoMoreInteractions(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,11 +690,10 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void dataWriteToClosedStreamShouldFail() throws Exception {
|
public void dataWriteToClosedStreamShouldFail() throws Exception {
|
||||||
createStream(STREAM_ID, false).close();
|
createStream(STREAM_ID, false).close();
|
||||||
ByteBuf data = mock(ByteBuf.class);
|
ByteBuf data = mock(ByteBuf.class);
|
||||||
Promise<Void> promise = newPromise();
|
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 0, false);
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
assertTrue(f.isDone());
|
||||||
assertTrue(promise.isDone());
|
assertFalse(f.isSuccess());
|
||||||
assertFalse(promise.isSuccess());
|
assertThat(f.cause(), instanceOf(IllegalArgumentException.class));
|
||||||
assertThat(promise.cause(), instanceOf(IllegalArgumentException.class));
|
|
||||||
verify(data).release();
|
verify(data).release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -748,11 +701,10 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void dataWriteToHalfClosedLocalStreamShouldFail() throws Exception {
|
public void dataWriteToHalfClosedLocalStreamShouldFail() throws Exception {
|
||||||
createStream(STREAM_ID, true);
|
createStream(STREAM_ID, true);
|
||||||
ByteBuf data = mock(ByteBuf.class);
|
ByteBuf data = mock(ByteBuf.class);
|
||||||
Promise<Void> promise = newPromise();
|
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 0, false);
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
assertTrue(f.isDone());
|
||||||
assertTrue(promise.isDone());
|
assertFalse(f.isSuccess());
|
||||||
assertFalse(promise.isSuccess());
|
assertThat(f.cause(), instanceOf(IllegalStateException.class));
|
||||||
assertThat(promise.cause(), instanceOf(IllegalStateException.class));
|
|
||||||
verify(data).release();
|
verify(data).release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -761,7 +713,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
Http2Stream stream = createStream(STREAM_ID, false);
|
Http2Stream stream = createStream(STREAM_ID, false);
|
||||||
connection.goAwaySent(0, 0, EMPTY_BUFFER);
|
connection.goAwaySent(0, 0, EMPTY_BUFFER);
|
||||||
ByteBuf data = mock(ByteBuf.class);
|
ByteBuf data = mock(ByteBuf.class);
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, false, newPromise());
|
encoder.writeData(ctx, STREAM_ID, data, 0, false);
|
||||||
verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
|
verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,10 +722,9 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
goAwaySent(0);
|
goAwaySent(0);
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -781,7 +732,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
Http2Stream stream = createStream(STREAM_ID, false);
|
Http2Stream stream = createStream(STREAM_ID, false);
|
||||||
goAwayReceived(STREAM_ID);
|
goAwayReceived(STREAM_ID);
|
||||||
ByteBuf data = mock(ByteBuf.class);
|
ByteBuf data = mock(ByteBuf.class);
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, false, newPromise());
|
encoder.writeData(ctx, STREAM_ID, data, 0, false);
|
||||||
verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
|
verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,31 +740,28 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception {
|
public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
goAwayReceived(STREAM_ID);
|
goAwayReceived(STREAM_ID);
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWithNoPriority() {
|
public void headersWithNoPriority() {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
Promise<Void> promise = newPromise();
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWithPriority() {
|
public void headersWithPriority() {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
Promise<Void> promise = newPromise();
|
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 10, DEFAULT_PRIORITY_WEIGHT,
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 10, DEFAULT_PRIORITY_WEIGHT,
|
||||||
true, 1, false, promise);
|
true, 1, false);
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(10),
|
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(10),
|
||||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(true), eq(1), eq(false), eq(promise));
|
eq(DEFAULT_PRIORITY_WEIGHT), eq(true), eq(1), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeAllFlowControlledFrames() {
|
private void writeAllFlowControlledFrames() {
|
||||||
@ -845,14 +793,6 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
connection.goAwaySent(lastStreamId, 0, EMPTY_BUFFER);
|
connection.goAwaySent(lastStreamId, 0, EMPTY_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Promise<Void> newPromise() {
|
|
||||||
return new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Future<Void> newSucceededFuture() {
|
|
||||||
return newPromise().setSuccess(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ByteBuf dummyData() {
|
private static ByteBuf dummyData() {
|
||||||
// The buffer is purposely 8 bytes so it will even work for a ping frame.
|
// The buffer is purposely 8 bytes so it will even work for a ping frame.
|
||||||
return wrappedBuffer("abcdefgh".getBytes(UTF_8));
|
return wrappedBuffer("abcdefgh".getBytes(UTF_8));
|
||||||
|
@ -22,8 +22,6 @@ import io.netty.channel.MultithreadEventLoopGroup;
|
|||||||
import io.netty.channel.local.LocalHandler;
|
import io.netty.channel.local.LocalHandler;
|
||||||
import io.netty.handler.codec.http2.Http2Connection.Endpoint;
|
import io.netty.handler.codec.http2.Http2Connection.Endpoint;
|
||||||
import io.netty.handler.codec.http2.Http2Stream.State;
|
import io.netty.handler.codec.http2.Http2Stream.State;
|
||||||
import io.netty.util.concurrent.Future;
|
|
||||||
import io.netty.util.concurrent.FutureListener;
|
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
@ -157,10 +155,9 @@ public class DefaultHttp2ConnectionTest {
|
|||||||
final Promise<Void> promise = group.next().newPromise();
|
final Promise<Void> promise = group.next().newPromise();
|
||||||
final CountDownLatch latch = new CountDownLatch(client.numActiveStreams());
|
final CountDownLatch latch = new CountDownLatch(client.numActiveStreams());
|
||||||
client.forEachActiveStream(stream -> {
|
client.forEachActiveStream(stream -> {
|
||||||
client.close(promise).addListener((FutureListener<Void>) future -> {
|
client.close(promise.addListener(future -> {
|
||||||
assertTrue(promise.isDone());
|
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
});
|
}));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
@ -186,10 +183,9 @@ public class DefaultHttp2ConnectionTest {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} catch (Http2Exception ignored) {
|
} catch (Http2Exception ignored) {
|
||||||
client.close(promise).addListener((FutureListener<Void>) future -> {
|
client.close(promise.addListener(future -> {
|
||||||
assertTrue(promise.isDone());
|
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
@ -636,10 +632,9 @@ public class DefaultHttp2ConnectionTest {
|
|||||||
private void testRemoveAllStreams() throws InterruptedException {
|
private void testRemoveAllStreams() throws InterruptedException {
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
final Promise<Void> promise = group.next().newPromise();
|
final Promise<Void> promise = group.next().newPromise();
|
||||||
client.close(promise).addListener((FutureListener<Void>) future -> {
|
client.close(promise.addListener(future -> {
|
||||||
assertTrue(promise.isDone());
|
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
});
|
}));
|
||||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,10 +20,8 @@ import io.netty.buffer.UnpooledByteBufAllocator;
|
|||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.concurrent.DefaultPromise;
|
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
import io.netty.util.concurrent.Promise;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -50,8 +48,6 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
|
|
||||||
private ByteBuf expectedOutbound;
|
private ByteBuf expectedOutbound;
|
||||||
|
|
||||||
private Promise<Void> promise;
|
|
||||||
|
|
||||||
private Http2HeadersEncoder http2HeadersEncoder;
|
private Http2HeadersEncoder http2HeadersEncoder;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
@ -76,8 +72,6 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
|
|
||||||
expectedOutbound = Unpooled.EMPTY_BUFFER;
|
expectedOutbound = Unpooled.EMPTY_BUFFER;
|
||||||
|
|
||||||
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
|
||||||
|
|
||||||
Answer<Object> answer = var1 -> {
|
Answer<Object> answer = var1 -> {
|
||||||
Object msg = var1.getArgument(0);
|
Object msg = var1.getArgument(0);
|
||||||
if (msg instanceof ByteBuf) {
|
if (msg instanceof ByteBuf) {
|
||||||
@ -90,6 +84,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(ctx.channel()).thenReturn(channel);
|
when(ctx.channel()).thenReturn(channel);
|
||||||
when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE);
|
when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE);
|
||||||
|
when(ctx.newPromise()).thenReturn(ImmediateEventExecutor.INSTANCE.newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@ -105,7 +100,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
Http2Headers headers = new DefaultHttp2Headers()
|
Http2Headers headers = new DefaultHttp2Headers()
|
||||||
.method("GET").path("/").authority("foo.com").scheme("https");
|
.method("GET").path("/").authority("foo.com").scheme("https");
|
||||||
|
|
||||||
frameWriter.writeHeaders(ctx, streamId, headers, 0, true, promise);
|
frameWriter.writeHeaders(ctx, streamId, headers, 0, true);
|
||||||
|
|
||||||
byte[] expectedPayload = headerPayload(streamId, headers);
|
byte[] expectedPayload = headerPayload(streamId, headers);
|
||||||
byte[] expectedFrameBytes = {
|
byte[] expectedFrameBytes = {
|
||||||
@ -124,7 +119,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
Http2Headers headers = new DefaultHttp2Headers()
|
Http2Headers headers = new DefaultHttp2Headers()
|
||||||
.method("GET").path("/").authority("foo.com").scheme("https");
|
.method("GET").path("/").authority("foo.com").scheme("https");
|
||||||
|
|
||||||
frameWriter.writeHeaders(ctx, streamId, headers, 5, true, promise);
|
frameWriter.writeHeaders(ctx, streamId, headers, 5, true);
|
||||||
|
|
||||||
byte[] expectedPayload = headerPayload(streamId, headers, (byte) 4);
|
byte[] expectedPayload = headerPayload(streamId, headers, (byte) 4);
|
||||||
byte[] expectedFrameBytes = {
|
byte[] expectedFrameBytes = {
|
||||||
@ -143,7 +138,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
Http2Headers headers = new DefaultHttp2Headers()
|
Http2Headers headers = new DefaultHttp2Headers()
|
||||||
.method("GET").path("/").authority("foo.com").scheme("https");
|
.method("GET").path("/").authority("foo.com").scheme("https");
|
||||||
|
|
||||||
frameWriter.writeHeaders(ctx, streamId, headers, 0, false, promise);
|
frameWriter.writeHeaders(ctx, streamId, headers, 0, false);
|
||||||
|
|
||||||
byte[] expectedPayload = headerPayload(streamId, headers);
|
byte[] expectedPayload = headerPayload(streamId, headers);
|
||||||
byte[] expectedFrameBytes = {
|
byte[] expectedFrameBytes = {
|
||||||
@ -170,7 +165,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE);
|
http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE);
|
||||||
frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE);
|
frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE);
|
||||||
frameWriter.maxFrameSize(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
|
frameWriter.maxFrameSize(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
|
||||||
frameWriter.writeHeaders(ctx, streamId, headers, 0, true, promise);
|
frameWriter.writeHeaders(ctx, streamId, headers, 0, true);
|
||||||
|
|
||||||
byte[] expectedPayload = headerPayload(streamId, headers);
|
byte[] expectedPayload = headerPayload(streamId, headers);
|
||||||
|
|
||||||
@ -211,7 +206,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE);
|
http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE);
|
||||||
frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE);
|
frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE);
|
||||||
frameWriter.maxFrameSize(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
|
frameWriter.maxFrameSize(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
|
||||||
frameWriter.writeHeaders(ctx, streamId, headers, 5, true, promise);
|
frameWriter.writeHeaders(ctx, streamId, headers, 5, true);
|
||||||
|
|
||||||
byte[] expectedPayload = buildLargeHeaderPayload(streamId, headers, (byte) 4,
|
byte[] expectedPayload = buildLargeHeaderPayload(streamId, headers, (byte) 4,
|
||||||
Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
|
Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
|
||||||
@ -245,7 +240,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeFrameZeroPayload() throws Exception {
|
public void writeFrameZeroPayload() throws Exception {
|
||||||
frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), Unpooled.EMPTY_BUFFER, promise);
|
frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), Unpooled.EMPTY_BUFFER);
|
||||||
|
|
||||||
byte[] expectedFrameBytes = {
|
byte[] expectedFrameBytes = {
|
||||||
(byte) 0x00, (byte) 0x00, (byte) 0x00, // payload length
|
(byte) 0x00, (byte) 0x00, (byte) 0x00, // payload length
|
||||||
@ -264,7 +259,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
|
|
||||||
// will auto release after frameWriter.writeFrame succeed
|
// will auto release after frameWriter.writeFrame succeed
|
||||||
ByteBuf payloadByteBuf = Unpooled.wrappedBuffer(payload);
|
ByteBuf payloadByteBuf = Unpooled.wrappedBuffer(payload);
|
||||||
frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), payloadByteBuf, promise);
|
frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), payloadByteBuf);
|
||||||
|
|
||||||
byte[] expectedFrameHeaderBytes = {
|
byte[] expectedFrameHeaderBytes = {
|
||||||
(byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length
|
(byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length
|
||||||
@ -279,7 +274,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void writePriority() {
|
public void writePriority() {
|
||||||
frameWriter.writePriority(
|
frameWriter.writePriority(
|
||||||
ctx, /* streamId= */ 1, /* dependencyId= */ 2, /* weight= */ (short) 256, /* exclusive= */ true, promise);
|
ctx, /* streamId= */ 1, /* dependencyId= */ 2, /* weight= */ (short) 256, /* exclusive= */ true);
|
||||||
|
|
||||||
expectedOutbound = Unpooled.copiedBuffer(new byte[] {
|
expectedOutbound = Unpooled.copiedBuffer(new byte[] {
|
||||||
(byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5
|
(byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5
|
||||||
@ -295,7 +290,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
@Test
|
@Test
|
||||||
public void writePriorityDefaults() {
|
public void writePriorityDefaults() {
|
||||||
frameWriter.writePriority(
|
frameWriter.writePriority(
|
||||||
ctx, /* streamId= */ 1, /* dependencyId= */ 0, /* weight= */ (short) 16, /* exclusive= */ false, promise);
|
ctx, /* streamId= */ 1, /* dependencyId= */ 0, /* weight= */ (short) 16, /* exclusive= */ false);
|
||||||
|
|
||||||
expectedOutbound = Unpooled.copiedBuffer(new byte[] {
|
expectedOutbound = Unpooled.copiedBuffer(new byte[] {
|
||||||
(byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5
|
(byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5
|
||||||
|
@ -417,17 +417,16 @@ public class DefaultHttp2LocalFlowControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void verifyWindowUpdateSent(int streamId, int windowSizeIncrement) {
|
private void verifyWindowUpdateSent(int streamId, int windowSizeIncrement) {
|
||||||
verify(frameWriter).writeWindowUpdate(eq(ctx), eq(streamId), eq(windowSizeIncrement), eq(promise));
|
verify(frameWriter).writeWindowUpdate(eq(ctx), eq(streamId), eq(windowSizeIncrement));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyWindowUpdateNotSent(int streamId) {
|
private void verifyWindowUpdateNotSent(int streamId) {
|
||||||
verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt(), eq(promise));
|
verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void verifyWindowUpdateNotSent() {
|
private void verifyWindowUpdateNotSent() {
|
||||||
verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
|
verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt());
|
||||||
any(Promise.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int window(int streamId) {
|
private int window(int streamId) {
|
||||||
|
@ -55,8 +55,10 @@ import static io.netty.handler.codec.http2.Http2Stream.State.IDLE;
|
|||||||
import static io.netty.util.CharsetUtil.US_ASCII;
|
import static io.netty.util.CharsetUtil.US_ASCII;
|
||||||
import static io.netty.util.CharsetUtil.UTF_8;
|
import static io.netty.util.CharsetUtil.UTF_8;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
@ -157,7 +159,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
buf.release();
|
buf.release();
|
||||||
return future;
|
return future;
|
||||||
}).when(frameWriter).writeGoAway(
|
}).when(frameWriter).writeGoAway(
|
||||||
any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class), any(Promise.class));
|
any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class));
|
||||||
doAnswer((Answer<Future<Void>>) invocation -> {
|
doAnswer((Answer<Future<Void>>) invocation -> {
|
||||||
Object o = invocation.getArguments()[0];
|
Object o = invocation.getArguments()[0];
|
||||||
if (o instanceof FutureListener) {
|
if (o instanceof FutureListener) {
|
||||||
@ -167,6 +169,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
}).when(future).addListener(any(FutureListener.class));
|
}).when(future).addListener(any(FutureListener.class));
|
||||||
when(future.cause()).thenReturn(fakeException);
|
when(future.cause()).thenReturn(fakeException);
|
||||||
when(channel.isActive()).thenReturn(true);
|
when(channel.isActive()).thenReturn(true);
|
||||||
|
when(future.isFailed()).thenReturn(true);
|
||||||
when(channel.pipeline()).thenReturn(pipeline);
|
when(channel.pipeline()).thenReturn(pipeline);
|
||||||
when(connection.remote()).thenReturn(remote);
|
when(connection.remote()).thenReturn(remote);
|
||||||
when(remote.flowController()).thenReturn(remoteFlowController);
|
when(remote.flowController()).thenReturn(remoteFlowController);
|
||||||
@ -185,10 +188,13 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(connection.goAwaySent(anyInt(), anyLong(), any(ByteBuf.class))).thenReturn(true);
|
when(connection.goAwaySent(anyInt(), anyLong(), any(ByteBuf.class))).thenReturn(true);
|
||||||
when(stream.open(anyBoolean())).thenReturn(stream);
|
when(stream.open(anyBoolean())).thenReturn(stream);
|
||||||
when(encoder.writeSettings(any(ChannelHandlerContext.class),
|
when(encoder.writeSettings(any(ChannelHandlerContext.class),
|
||||||
any(Http2Settings.class), eq(promise))).thenReturn(future);
|
any(Http2Settings.class))).thenReturn(future);
|
||||||
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(ctx.channel()).thenReturn(channel);
|
when(ctx.channel()).thenReturn(channel);
|
||||||
when(ctx.newSucceededFuture()).thenReturn(future);
|
when(ctx.newFailedFuture(any(Throwable.class)))
|
||||||
|
.thenAnswer(invocationOnMock ->
|
||||||
|
DefaultPromise.newFailedPromise(executor, invocationOnMock.getArgument(0)));
|
||||||
|
when(ctx.newSucceededFuture()).thenReturn(DefaultPromise.newSuccessfulPromise(executor, null));
|
||||||
when(ctx.newPromise()).thenReturn(promise);
|
when(ctx.newPromise()).thenReturn(promise);
|
||||||
when(ctx.write(any())).thenReturn(future);
|
when(ctx.write(any())).thenReturn(future);
|
||||||
when(ctx.executor()).thenReturn(executor);
|
when(ctx.executor()).thenReturn(executor);
|
||||||
@ -254,7 +260,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
final Answer<Object> verifier = in -> {
|
final Answer<Object> verifier = in -> {
|
||||||
assertEquals(in.getArgument(0), evt); // sanity check...
|
assertEquals(in.getArgument(0), evt); // sanity check...
|
||||||
verify(ctx).write(eq(connectionPrefaceBuf()));
|
verify(ctx).write(eq(connectionPrefaceBuf()));
|
||||||
verify(encoder).writeSettings(eq(ctx), any(Http2Settings.class), any(Promise.class));
|
verify(encoder).writeSettings(eq(ctx), any(Http2Settings.class));
|
||||||
verified.set(true);
|
verified.set(true);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@ -292,7 +298,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
handler.channelRead(ctx, copiedBuffer("BAD_PREFACE", UTF_8));
|
handler.channelRead(ctx, copiedBuffer("BAD_PREFACE", UTF_8));
|
||||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||||
verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class),
|
verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class),
|
||||||
eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture(), eq(promise));
|
eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture());
|
||||||
assertEquals(0, captor.getValue().refCnt());
|
assertEquals(0, captor.getValue().refCnt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +309,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
handler.channelRead(ctx, copiedBuffer("GET /path HTTP/1.1", US_ASCII));
|
handler.channelRead(ctx, copiedBuffer("GET /path HTTP/1.1", US_ASCII));
|
||||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||||
verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(Integer.MAX_VALUE),
|
verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(Integer.MAX_VALUE),
|
||||||
eq(PROTOCOL_ERROR.code()), captor.capture(), eq(promise));
|
eq(PROTOCOL_ERROR.code()), captor.capture());
|
||||||
assertEquals(0, captor.getValue().refCnt());
|
assertEquals(0, captor.getValue().refCnt());
|
||||||
assertTrue(goAwayDebugCap.contains("/path"));
|
assertTrue(goAwayDebugCap.contains("/path"));
|
||||||
}
|
}
|
||||||
@ -319,7 +325,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
handler.channelRead(ctx, buf);
|
handler.channelRead(ctx, buf);
|
||||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||||
verify(frameWriter, atLeastOnce()).writeGoAway(any(ChannelHandlerContext.class),
|
verify(frameWriter, atLeastOnce()).writeGoAway(any(ChannelHandlerContext.class),
|
||||||
eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture(), eq(promise));
|
eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture());
|
||||||
assertEquals(0, captor.getValue().refCnt());
|
assertEquals(0, captor.getValue().refCnt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,7 +379,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
handler.exceptionCaught(ctx, e);
|
handler.exceptionCaught(ctx, e);
|
||||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||||
verify(frameWriter).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()),
|
verify(frameWriter).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()),
|
||||||
captor.capture(), eq(promise));
|
captor.capture());
|
||||||
captor.getValue().release();
|
captor.getValue().release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,16 +395,16 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(stream.isHeadersSent()).thenReturn(false);
|
when(stream.isHeadersSent()).thenReturn(false);
|
||||||
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
||||||
eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
|
eq(PROTOCOL_ERROR.code()))).thenReturn(future);
|
||||||
|
|
||||||
handler.exceptionCaught(ctx, e);
|
handler.exceptionCaught(ctx, e);
|
||||||
|
|
||||||
ArgumentCaptor<Http2Headers> captor = ArgumentCaptor.forClass(Http2Headers.class);
|
ArgumentCaptor<Http2Headers> captor = ArgumentCaptor.forClass(Http2Headers.class);
|
||||||
verify(encoder).writeHeaders(eq(ctx), eq(STREAM_ID),
|
verify(encoder).writeHeaders(eq(ctx), eq(STREAM_ID),
|
||||||
captor.capture(), eq(padding), eq(true), eq(promise));
|
captor.capture(), eq(padding), eq(true));
|
||||||
Http2Headers headers = captor.getValue();
|
Http2Headers headers = captor.getValue();
|
||||||
assertEquals(HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.codeAsText(), headers.status());
|
assertEquals(HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.codeAsText(), headers.status());
|
||||||
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -413,13 +419,13 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(stream.isHeadersSent()).thenReturn(false);
|
when(stream.isHeadersSent()).thenReturn(false);
|
||||||
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
||||||
eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
|
eq(PROTOCOL_ERROR.code()))).thenReturn(future);
|
||||||
|
|
||||||
handler.exceptionCaught(ctx, e);
|
handler.exceptionCaught(ctx, e);
|
||||||
|
|
||||||
verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
|
verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
|
||||||
any(Http2Headers.class), eq(padding), eq(true), eq(promise));
|
any(Http2Headers.class), eq(padding), eq(true));
|
||||||
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -434,13 +440,13 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(stream.isHeadersSent()).thenReturn(false);
|
when(stream.isHeadersSent()).thenReturn(false);
|
||||||
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
||||||
eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
|
eq(PROTOCOL_ERROR.code()))).thenReturn(future);
|
||||||
|
|
||||||
handler.exceptionCaught(ctx, e);
|
handler.exceptionCaught(ctx, e);
|
||||||
|
|
||||||
verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
|
verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
|
||||||
any(Http2Headers.class), eq(padding), eq(true), eq(promise));
|
any(Http2Headers.class), eq(padding), eq(true));
|
||||||
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -448,7 +454,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
handler = new Http2ConnectionHandler(decoder, encoder, new Http2Settings()) {
|
handler = new Http2ConnectionHandler(decoder, encoder, new Http2Settings()) {
|
||||||
@Override
|
@Override
|
||||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||||
if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) {
|
if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
@ -470,13 +476,13 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(stream.isHeadersSent()).thenReturn(true);
|
when(stream.isHeadersSent()).thenReturn(true);
|
||||||
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
||||||
eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
|
eq(PROTOCOL_ERROR.code()))).thenReturn(future);
|
||||||
handler.exceptionCaught(ctx, e);
|
handler.exceptionCaught(ctx, e);
|
||||||
|
|
||||||
verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
|
verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
|
||||||
any(Http2Headers.class), eq(padding), eq(true), eq(promise));
|
any(Http2Headers.class), eq(padding), eq(true));
|
||||||
|
|
||||||
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -494,14 +500,14 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(stream.isHeadersSent()).thenReturn(false);
|
when(stream.isHeadersSent()).thenReturn(false);
|
||||||
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
||||||
eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
|
eq(PROTOCOL_ERROR.code()))).thenReturn(future);
|
||||||
handler.exceptionCaught(ctx, e);
|
handler.exceptionCaught(ctx, e);
|
||||||
|
|
||||||
verify(remote).createStream(STREAM_ID, true);
|
verify(remote).createStream(STREAM_ID, true);
|
||||||
verify(encoder).writeHeaders(eq(ctx), eq(STREAM_ID),
|
verify(encoder).writeHeaders(eq(ctx), eq(STREAM_ID),
|
||||||
any(Http2Headers.class), eq(padding), eq(true), eq(promise));
|
any(Http2Headers.class), eq(padding), eq(true));
|
||||||
|
|
||||||
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -518,9 +524,9 @@ public class Http2ConnectionHandlerTest {
|
|||||||
public void writeRstOnNonExistantStreamShouldSucceed() throws Exception {
|
public void writeRstOnNonExistantStreamShouldSucceed() throws Exception {
|
||||||
handler = newHandler();
|
handler = newHandler();
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID),
|
when(frameWriter.writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID),
|
||||||
eq(STREAM_CLOSED.code()), eq(promise))).thenReturn(future);
|
eq(STREAM_CLOSED.code()))).thenReturn(future);
|
||||||
handler.resetStream(ctx, NON_EXISTANT_STREAM_ID, STREAM_CLOSED.code(), promise);
|
handler.resetStream(ctx, NON_EXISTANT_STREAM_ID, STREAM_CLOSED.code());
|
||||||
verify(frameWriter).writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID), eq(STREAM_CLOSED.code()), eq(promise));
|
verify(frameWriter).writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID), eq(STREAM_CLOSED.code()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -528,21 +534,21 @@ public class Http2ConnectionHandlerTest {
|
|||||||
handler = newHandler();
|
handler = newHandler();
|
||||||
when(stream.id()).thenReturn(STREAM_ID);
|
when(stream.id()).thenReturn(STREAM_ID);
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
||||||
anyLong(), any(Promise.class))).thenReturn(future);
|
anyLong())).thenReturn(future);
|
||||||
when(stream.state()).thenReturn(CLOSED);
|
when(stream.state()).thenReturn(CLOSED);
|
||||||
when(stream.isHeadersSent()).thenReturn(true);
|
when(stream.isHeadersSent()).thenReturn(true);
|
||||||
// The stream is "closed" but is still known about by the connection (connection().stream(..)
|
// The stream is "closed" but is still known about by the connection (connection().stream(..)
|
||||||
// will return the stream). We should still write a RST_STREAM frame in this scenario.
|
// will return the stream). We should still write a RST_STREAM frame in this scenario.
|
||||||
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
|
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code());
|
||||||
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
|
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void writeRstOnIdleStreamShouldNotWriteButStillSucceed() throws Exception {
|
public void writeRstOnIdleStreamShouldNotWriteButStillSucceed() throws Exception {
|
||||||
handler = newHandler();
|
handler = newHandler();
|
||||||
when(stream.state()).thenReturn(IDLE);
|
when(stream.state()).thenReturn(IDLE);
|
||||||
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
|
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code());
|
||||||
verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
|
verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong());
|
||||||
verify(stream).close();
|
verify(stream).close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,11 +591,10 @@ public class Http2ConnectionHandlerTest {
|
|||||||
return null;
|
return null;
|
||||||
}).when(future).addListener(any(FutureListener.class));
|
}).when(future).addListener(any(FutureListener.class));
|
||||||
handler = newHandler();
|
handler = newHandler();
|
||||||
handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
|
handler.goAway(ctx, STREAM_ID, errorCode, data);
|
||||||
|
|
||||||
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
|
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
|
||||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data),
|
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data));
|
||||||
eq(promise));
|
|
||||||
verify(ctx).close();
|
verify(ctx).close();
|
||||||
assertEquals(0, data.refCnt());
|
assertEquals(0, data.refCnt());
|
||||||
}
|
}
|
||||||
@ -600,13 +605,12 @@ public class Http2ConnectionHandlerTest {
|
|||||||
ByteBuf data = dummyData();
|
ByteBuf data = dummyData();
|
||||||
long errorCode = Http2Error.INTERNAL_ERROR.code();
|
long errorCode = Http2Error.INTERNAL_ERROR.code();
|
||||||
|
|
||||||
handler.goAway(ctx, STREAM_ID + 2, errorCode, data.retain(), promise);
|
handler.goAway(ctx, STREAM_ID + 2, errorCode, data.retain());
|
||||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data),
|
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data));
|
||||||
eq(promise));
|
|
||||||
verify(connection).goAwaySent(eq(STREAM_ID + 2), eq(errorCode), eq(data));
|
verify(connection).goAwaySent(eq(STREAM_ID + 2), eq(errorCode), eq(data));
|
||||||
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
|
handler.goAway(ctx, STREAM_ID, errorCode, data);
|
||||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data), eq(promise));
|
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data));
|
||||||
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
|
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
|
||||||
assertEquals(0, data.refCnt());
|
assertEquals(0, data.refCnt());
|
||||||
}
|
}
|
||||||
@ -617,20 +621,24 @@ public class Http2ConnectionHandlerTest {
|
|||||||
ByteBuf data = dummyData();
|
ByteBuf data = dummyData();
|
||||||
long errorCode = Http2Error.INTERNAL_ERROR.code();
|
long errorCode = Http2Error.INTERNAL_ERROR.code();
|
||||||
|
|
||||||
handler.goAway(ctx, STREAM_ID, errorCode, data.retain(), promise);
|
Future<Void> future = handler.goAway(ctx, STREAM_ID, errorCode, data.retain());
|
||||||
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
|
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
|
||||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data), eq(promise));
|
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data));
|
||||||
// The frameWriter is only mocked, so it should not have interacted with the promise.
|
// The frameWriter is only mocked, so it should not have interacted with the promise.
|
||||||
assertFalse(promise.isDone());
|
assertFalse(future.isDone());
|
||||||
|
|
||||||
when(connection.goAwaySent()).thenReturn(true);
|
when(connection.goAwaySent()).thenReturn(true);
|
||||||
when(remote.lastStreamKnownByPeer()).thenReturn(STREAM_ID);
|
when(remote.lastStreamKnownByPeer()).thenReturn(STREAM_ID);
|
||||||
|
|
||||||
|
Exception ex = new IllegalStateException();
|
||||||
doAnswer((Answer<Boolean>) invocationOnMock -> {
|
doAnswer((Answer<Boolean>) invocationOnMock -> {
|
||||||
throw new IllegalStateException();
|
throw ex;
|
||||||
}).when(connection).goAwaySent(anyInt(), anyLong(), any(ByteBuf.class));
|
}).when(connection).goAwaySent(anyInt(), anyLong(), any(ByteBuf.class));
|
||||||
handler.goAway(ctx, STREAM_ID + 2, errorCode, data, promise);
|
Future<Void> future2 = handler.goAway(ctx, STREAM_ID + 2, errorCode, data);
|
||||||
assertTrue(promise.isDone());
|
assertTrue(future2.isDone());
|
||||||
assertFalse(promise.isSuccess());
|
assertFalse(future2.isSuccess());
|
||||||
|
assertSame(ex, future2.cause());
|
||||||
|
|
||||||
assertEquals(0, data.refCnt());
|
assertEquals(0, data.refCnt());
|
||||||
verifyNoMoreInteractions(frameWriter);
|
verifyNoMoreInteractions(frameWriter);
|
||||||
}
|
}
|
||||||
@ -657,9 +665,8 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(channel.isActive()).thenReturn(false);
|
when(channel.isActive()).thenReturn(false);
|
||||||
handler.channelInactive(ctx);
|
handler.channelInactive(ctx);
|
||||||
verify(frameWriter, never()).writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(),
|
verify(frameWriter, never()).writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(),
|
||||||
any(ByteBuf.class), any(Promise.class));
|
any(ByteBuf.class));
|
||||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong(),
|
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong());
|
||||||
any(Promise.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -729,22 +736,14 @@ public class Http2ConnectionHandlerTest {
|
|||||||
return stream;
|
return stream;
|
||||||
});
|
});
|
||||||
when(stream.isResetSent()).then((Answer<Boolean>) invocationOnMock -> resetSent.get());
|
when(stream.isResetSent()).then((Answer<Boolean>) invocationOnMock -> resetSent.get());
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class)))
|
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong()))
|
||||||
.then((Answer<Future<Void>>) invocationOnMock -> {
|
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
Promise<Void> promise = invocationOnMock.getArgument(3);
|
|
||||||
return promise.setSuccess(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
Promise<Void> promise =
|
Future<Void> f1 = handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code());
|
||||||
new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
Future<Void> f2 = handler.resetStream(ctx, STREAM_ID, CANCEL.code());
|
||||||
final Promise<Void> promise2 =
|
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong());
|
||||||
new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
assertTrue(f1.isSuccess());
|
||||||
promise.addListener(future -> handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise2));
|
assertTrue(f2.isSuccess());
|
||||||
|
|
||||||
handler.resetStream(ctx, STREAM_ID, CANCEL.code(), promise);
|
|
||||||
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
|
|
||||||
assertTrue(promise.isSuccess());
|
|
||||||
assertTrue(promise2.isSuccess());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf dummyData() {
|
private static ByteBuf dummyData() {
|
||||||
|
@ -35,6 +35,7 @@ import io.netty.util.AsciiString;
|
|||||||
import io.netty.util.IllegalReferenceCountException;
|
import io.netty.util.IllegalReferenceCountException;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@ -152,8 +153,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
(Integer) invocationOnMock.getArgument(1),
|
(Integer) invocationOnMock.getArgument(1),
|
||||||
(Http2Headers) invocationOnMock.getArgument(2),
|
(Http2Headers) invocationOnMock.getArgument(2),
|
||||||
0,
|
0,
|
||||||
false,
|
false);
|
||||||
ctx.newPromise());
|
|
||||||
http2Server.flush(ctx);
|
http2Server.flush(ctx);
|
||||||
return null;
|
return null;
|
||||||
}).when(serverListener).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
}).when(serverListener).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
||||||
@ -169,14 +169,14 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
final short weight = 16;
|
final short weight = 16;
|
||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, false, newPromise());
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, false);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
http2Client.encoder().writeRstStream(ctx(), 3, Http2Error.INTERNAL_ERROR.code(), newPromise());
|
http2Client.encoder().writeRstStream(ctx(), 3, Http2Error.INTERNAL_ERROR.code());
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, weight, false, 0, false, newPromise());
|
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, weight, false, 0, false);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -191,8 +191,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
final short weight = 16;
|
final short weight = 16;
|
||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, true,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, true);
|
||||||
newPromise());
|
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -250,18 +249,17 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
runInChannel(serverConnectedChannel, () -> {
|
runInChannel(serverConnectedChannel, () -> {
|
||||||
http2Server.encoder().writeSettings(serverCtx(),
|
http2Server.encoder().writeSettings(serverCtx(),
|
||||||
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
||||||
.maxHeaderListSize(100),
|
.maxHeaderListSize(100));
|
||||||
serverNewPromise());
|
|
||||||
http2Server.flush(serverCtx());
|
http2Server.flush(serverCtx());
|
||||||
});
|
});
|
||||||
|
|
||||||
assertTrue(serverSettingsAckLatch1.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
assertTrue(serverSettingsAckLatch1.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, false, newPromise())
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, false)
|
||||||
.addListener(future -> clientHeadersWriteException.set(future.cause()));
|
.addListener(future -> clientHeadersWriteException.set(future.cause()));
|
||||||
// It is expected that this write should fail locally and the remote peer will never see this.
|
// It is expected that this write should fail locally and the remote peer will never see this.
|
||||||
http2Client.encoder().writeData(ctx(), 3, Unpooled.buffer(), 0, true, newPromise())
|
http2Client.encoder().writeData(ctx(), 3, Unpooled.buffer(), 0, true)
|
||||||
.addListener(future -> {
|
.addListener(future -> {
|
||||||
clientDataWriteException.set(future.cause());
|
clientDataWriteException.set(future.cause());
|
||||||
clientDataWrite.countDown();
|
clientDataWrite.countDown();
|
||||||
@ -277,8 +275,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
runInChannel(serverConnectedChannel, () -> {
|
runInChannel(serverConnectedChannel, () -> {
|
||||||
http2Server.encoder().writeSettings(serverCtx(),
|
http2Server.encoder().writeSettings(serverCtx(),
|
||||||
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
||||||
.maxHeaderListSize(Http2CodecUtil.MAX_HEADER_LIST_SIZE),
|
.maxHeaderListSize(Http2CodecUtil.MAX_HEADER_LIST_SIZE));
|
||||||
serverNewPromise());
|
|
||||||
http2Server.flush(serverCtx());
|
http2Server.flush(serverCtx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -286,8 +283,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
assertTrue(serverSettingsAckLatch2.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
assertTrue(serverSettingsAckLatch2.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true,
|
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true).addListener(future -> {
|
||||||
newPromise()).addListener(future -> {
|
|
||||||
clientHeadersWriteException2.set(future.cause());
|
clientHeadersWriteException2.set(future.cause());
|
||||||
clientHeadersLatch.countDown();
|
clientHeadersLatch.countDown();
|
||||||
});
|
});
|
||||||
@ -344,8 +340,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
runInChannel(serverConnectedChannel, () -> {
|
runInChannel(serverConnectedChannel, () -> {
|
||||||
http2Server.encoder().writeSettings(serverCtx(),
|
http2Server.encoder().writeSettings(serverCtx(),
|
||||||
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
||||||
.initialWindowSize(0),
|
.initialWindowSize(0));
|
||||||
serverNewPromise());
|
|
||||||
http2Server.flush(serverCtx());
|
http2Server.flush(serverCtx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -354,9 +349,8 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
// The client should now attempt to send data, but the window size is 0 so it will be queued in the flow
|
// The client should now attempt to send data, but the window size is 0 so it will be queued in the flow
|
||||||
// controller.
|
// controller.
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||||
newPromise());
|
http2Client.encoder().writeData(ctx(), 3, Unpooled.wrappedBuffer(data), 0, true);
|
||||||
http2Client.encoder().writeData(ctx(), 3, Unpooled.wrappedBuffer(data), 0, true, newPromise());
|
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
clientWriteDataLatch.countDown();
|
clientWriteDataLatch.countDown();
|
||||||
});
|
});
|
||||||
@ -367,8 +361,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
runInChannel(serverConnectedChannel, () -> {
|
runInChannel(serverConnectedChannel, () -> {
|
||||||
http2Server.encoder().writeSettings(serverCtx(),
|
http2Server.encoder().writeSettings(serverCtx(),
|
||||||
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
||||||
.initialWindowSize(data.length),
|
.initialWindowSize(data.length));
|
||||||
serverNewPromise());
|
|
||||||
http2Server.flush(serverCtx());
|
http2Server.flush(serverCtx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -391,9 +384,8 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writePriority(ctx(), 5, 3, (short) 14, false, newPromise());
|
http2Client.encoder().writePriority(ctx(), 5, 3, (short) 14, false);
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||||
newPromise());
|
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -420,10 +412,8 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, false);
|
||||||
newPromise());
|
http2Client.encoder().frameWriter().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||||
http2Client.encoder().frameWriter().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
|
||||||
newPromise());
|
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -465,8 +455,8 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), streamId, headers, CONNECTION_STREAM_ID,
|
http2Client.encoder().writeHeaders(ctx(), streamId, headers, CONNECTION_STREAM_ID,
|
||||||
DEFAULT_PRIORITY_WEIGHT, false, 0, false, newPromise());
|
DEFAULT_PRIORITY_WEIGHT, false, 0, false);
|
||||||
http2Client.encoder().writeRstStream(ctx(), streamId, Http2Error.CANCEL.code(), newPromise());
|
http2Client.encoder().writeRstStream(ctx(), streamId, Http2Error.CANCEL.code());
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -478,7 +468,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
// Now have the server attempt to send a headers frame simulating some asynchronous work.
|
// Now have the server attempt to send a headers frame simulating some asynchronous work.
|
||||||
runInChannel(serverConnectedChannel, () -> {
|
runInChannel(serverConnectedChannel, () -> {
|
||||||
http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true, serverNewPromise())
|
http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true)
|
||||||
.addListener(future -> {
|
.addListener(future -> {
|
||||||
serverWriteHeadersCauseRef.set(future.cause());
|
serverWriteHeadersCauseRef.set(future.cause());
|
||||||
serverWriteHeadersLatch.countDown();
|
serverWriteHeadersLatch.countDown();
|
||||||
@ -510,8 +500,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
// Create a single stream by sending a HEADERS frame to the server.
|
// Create a single stream by sending a HEADERS frame to the server.
|
||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||||
newPromise());
|
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -547,8 +536,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
// Create a single stream by sending a HEADERS frame to the server.
|
// Create a single stream by sending a HEADERS frame to the server.
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||||
newPromise());
|
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -592,41 +580,42 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
bootstrapEnv(1, 1, 2, 1);
|
bootstrapEnv(1, 1, 2, 1);
|
||||||
|
|
||||||
final Promise<Void> emptyDataPromise = newPromise();
|
Promise<Void> promise = ImmediateEventExecutor.INSTANCE.newPromise();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false);
|
||||||
newPromise());
|
|
||||||
ByteBuf emptyBuf = Unpooled.buffer();
|
ByteBuf emptyBuf = Unpooled.buffer();
|
||||||
emptyBuf.release();
|
emptyBuf.release();
|
||||||
|
final Future<Void> future;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SINGLE_END_OF_STREAM:
|
case SINGLE_END_OF_STREAM:
|
||||||
http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, true, emptyDataPromise);
|
future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, true);
|
||||||
break;
|
break;
|
||||||
case SECOND_END_OF_STREAM:
|
case SECOND_END_OF_STREAM:
|
||||||
http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false, emptyDataPromise);
|
future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false);
|
||||||
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, true, newPromise());
|
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, true);
|
||||||
break;
|
break;
|
||||||
case SINGLE_WITH_TRAILERS:
|
case SINGLE_WITH_TRAILERS:
|
||||||
http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false, emptyDataPromise);
|
future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false);
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0,
|
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0,
|
||||||
(short) 16, false, 0, true, newPromise());
|
(short) 16, false, 0, true);
|
||||||
break;
|
break;
|
||||||
case SECOND_WITH_TRAILERS:
|
case SECOND_WITH_TRAILERS:
|
||||||
http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false, emptyDataPromise);
|
future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false);
|
||||||
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false, newPromise());
|
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false);
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0,
|
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0,
|
||||||
(short) 16, false, 0, true, newPromise());
|
(short) 16, false, 0, true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
|
future.cascadeTo(promise);
|
||||||
});
|
});
|
||||||
|
|
||||||
ExecutionException e = assertThrows(ExecutionException.class, new Executable() {
|
ExecutionException e = assertThrows(ExecutionException.class, new Executable() {
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Throwable {
|
public void execute() throws Throwable {
|
||||||
emptyDataPromise.get();
|
promise.get();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
assertThat(e.getCause(), is(instanceOf(IllegalReferenceCountException.class)));
|
assertThat(e.getCause(), is(instanceOf(IllegalReferenceCountException.class)));
|
||||||
@ -641,8 +630,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
final Promise<Void> assertPromise = newPromise();
|
final Promise<Void> assertPromise = newPromise();
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false);
|
||||||
newPromise());
|
|
||||||
clientChannel.pipeline().addFirst(new ChannelHandler() {
|
clientChannel.pipeline().addFirst(new ChannelHandler() {
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
|
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
|
||||||
@ -660,7 +648,8 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
});
|
});
|
||||||
|
|
||||||
http2Client.encoder().flowController().initialWindowSize(4);
|
http2Client.encoder().flowController().initialWindowSize(4);
|
||||||
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false, dataPromise);
|
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false)
|
||||||
|
.cascadeTo(dataPromise);
|
||||||
assertTrue(http2Client.encoder().flowController()
|
assertTrue(http2Client.encoder().flowController()
|
||||||
.hasFlowControlled(http2Client.connection().stream(3)));
|
.hasFlowControlled(http2Client.connection().stream(3)));
|
||||||
|
|
||||||
@ -697,8 +686,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
// Create a single stream by sending a HEADERS frame to the server.
|
// Create a single stream by sending a HEADERS frame to the server.
|
||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||||
newPromise());
|
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -733,7 +721,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
||||||
true, newPromise());
|
true);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -741,7 +729,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), MAX_VALUE + 1, headers, 0, (short) 16, false, 0,
|
http2Client.encoder().writeHeaders(ctx(), MAX_VALUE + 1, headers, 0, (short) 16, false, 0,
|
||||||
true, newPromise());
|
true);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -768,7 +756,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
||||||
false, newPromise());
|
false);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -778,7 +766,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
||||||
|
|
||||||
runInChannel(serverChannel, () -> {
|
runInChannel(serverChannel, () -> {
|
||||||
http2Server.encoder().writeGoAway(serverCtx(), 3, NO_ERROR.code(), EMPTY_BUFFER, serverNewPromise());
|
http2Server.encoder().writeGoAway(serverCtx(), 3, NO_ERROR.code(), EMPTY_BUFFER);
|
||||||
http2Server.flush(serverCtx());
|
http2Server.flush(serverCtx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -791,7 +779,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1);
|
final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1);
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
Future<Void> f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0,
|
Future<Void> f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0,
|
||||||
true, newPromise());
|
true);
|
||||||
clientWriteAfterGoAwayFutureRef.set(f);
|
clientWriteAfterGoAwayFutureRef.set(f);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
f.addListener(future -> clientWriteAfterGoAwayLatch.countDown());
|
f.addListener(future -> clientWriteAfterGoAwayLatch.countDown());
|
||||||
@ -839,9 +827,9 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 1, headers, 0, (short) 16, false, 0,
|
http2Client.encoder().writeHeaders(ctx(), 1, headers, 0, (short) 16, false, 0,
|
||||||
false, newPromise());
|
false);
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
||||||
false, newPromise());
|
false);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -851,7 +839,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
||||||
|
|
||||||
runInChannel(serverChannel, () -> {
|
runInChannel(serverChannel, () -> {
|
||||||
http2Server.encoder().writeGoAway(serverCtx(), 1, NO_ERROR.code(), EMPTY_BUFFER, serverNewPromise());
|
http2Server.encoder().writeGoAway(serverCtx(), 1, NO_ERROR.code(), EMPTY_BUFFER);
|
||||||
http2Server.flush(serverCtx());
|
http2Server.flush(serverCtx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -911,12 +899,12 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
// Create the stream and send all of the data at once.
|
// Create the stream and send all of the data at once.
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
||||||
false, newPromise());
|
false);
|
||||||
http2Client.encoder().writeData(ctx(), 3, data.retainedDuplicate(), 0, false, newPromise());
|
http2Client.encoder().writeData(ctx(), 3, data.retainedDuplicate(), 0, false);
|
||||||
|
|
||||||
// Write trailers.
|
// Write trailers.
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
||||||
true, newPromise());
|
true);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -989,14 +977,13 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
for (int streamId = 3; streamId < upperLimit; streamId += 2) {
|
for (int streamId = 3; streamId < upperLimit; streamId += 2) {
|
||||||
// Send a bunch of data on each stream.
|
// Send a bunch of data on each stream.
|
||||||
http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16,
|
http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16,
|
||||||
false, 0, false, newPromise());
|
false, 0, false);
|
||||||
http2Client.encoder().writePing(ctx(), false, pingData,
|
http2Client.encoder().writePing(ctx(), false, pingData);
|
||||||
newPromise());
|
|
||||||
http2Client.encoder().writeData(ctx(), streamId, data.retainedSlice(), 0,
|
http2Client.encoder().writeData(ctx(), streamId, data.retainedSlice(), 0,
|
||||||
false, newPromise());
|
false);
|
||||||
// Write trailers.
|
// Write trailers.
|
||||||
http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16,
|
http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16,
|
||||||
false, 0, true, newPromise());
|
false, 0, true);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -33,7 +33,6 @@ import org.junit.jupiter.api.BeforeEach;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
@ -59,7 +58,6 @@ import static org.mockito.Mockito.when;
|
|||||||
/**
|
/**
|
||||||
* Tests for {@link Http2ControlFrameLimitEncoder}.
|
* Tests for {@link Http2ControlFrameLimitEncoder}.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public class Http2ControlFrameLimitEncoderTest {
|
public class Http2ControlFrameLimitEncoderTest {
|
||||||
|
|
||||||
private Http2ControlFrameLimitEncoder encoder;
|
private Http2ControlFrameLimitEncoder encoder;
|
||||||
@ -101,22 +99,22 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
||||||
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
||||||
|
|
||||||
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class)))
|
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong()))
|
||||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise(invocationOnMock, 3));
|
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise());
|
||||||
when(writer.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
|
when(writer.writeSettingsAck(any(ChannelHandlerContext.class)))
|
||||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise(invocationOnMock, 1));
|
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise());
|
||||||
when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong(), any(Promise.class)))
|
when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong()))
|
||||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
|
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
|
||||||
Promise<Void> promise = handlePromise(invocationOnMock, 3);
|
Promise<Void> promise = handlePromise();
|
||||||
if (invocationOnMock.getArgument(1) == Boolean.FALSE) {
|
if (invocationOnMock.getArgument(1) == Boolean.FALSE) {
|
||||||
promise.trySuccess(null);
|
promise.trySuccess(null);
|
||||||
}
|
}
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
|
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)))
|
||||||
any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
|
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
|
||||||
ReferenceCountUtil.release(invocationOnMock.getArgument(3));
|
ReferenceCountUtil.release(invocationOnMock.getArgument(3));
|
||||||
Promise<Void> promise = invocationOnMock.getArgument(4);
|
Promise<Void> promise = ImmediateEventExecutor.INSTANCE.newPromise();
|
||||||
goAwayPromises.offer(promise);
|
goAwayPromises.offer(promise);
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
@ -139,7 +137,12 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(executor.inEventLoop()).thenReturn(true);
|
when(executor.inEventLoop()).thenReturn(true);
|
||||||
doAnswer((Answer<Promise>) invocation -> newPromise()).when(ctx).newPromise();
|
doAnswer((Answer<Promise>) invocation -> newPromise()).when(ctx).newPromise();
|
||||||
|
doAnswer((Answer<Future>) invocation ->
|
||||||
|
ImmediateEventExecutor.INSTANCE.newFailedFuture(invocation.getArgument(0)))
|
||||||
|
.when(ctx).newFailedFuture(any(Throwable.class));
|
||||||
|
|
||||||
when(ctx.executor()).thenReturn(executor);
|
when(ctx.executor()).thenReturn(executor);
|
||||||
|
when(ctx.close()).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
when(channel.isActive()).thenReturn(false);
|
when(channel.isActive()).thenReturn(false);
|
||||||
when(channel.config()).thenReturn(config);
|
when(channel.config()).thenReturn(config);
|
||||||
when(channel.isWritable()).thenReturn(true);
|
when(channel.isWritable()).thenReturn(true);
|
||||||
@ -152,12 +155,12 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
handler.handlerAdded(ctx);
|
handler.handlerAdded(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Promise<Void> handlePromise(InvocationOnMock invocationOnMock, int promiseIdx) {
|
private Promise<Void> handlePromise() {
|
||||||
Promise<Void> promise = invocationOnMock.getArgument(promiseIdx);
|
Promise<Void> p = ImmediateEventExecutor.INSTANCE.newPromise();
|
||||||
if (++numWrites == 2) {
|
if (++numWrites == 2) {
|
||||||
promise.setSuccess(null);
|
p.setSuccess(null);
|
||||||
}
|
}
|
||||||
return promise;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@ -178,75 +181,75 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLimitSettingsAck() {
|
public void testLimitSettingsAck() {
|
||||||
assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
|
assertFalse(encoder.writeSettingsAck(ctx).isDone());
|
||||||
// The second write is always marked as success by our mock, which means it will also not be queued and so
|
// The second write is always marked as success by our mock, which means it will also not be queued and so
|
||||||
// not count to the number of queued frames.
|
// not count to the number of queued frames.
|
||||||
assertTrue(encoder.writeSettingsAck(ctx, newPromise()).isSuccess());
|
assertTrue(encoder.writeSettingsAck(ctx).isSuccess());
|
||||||
assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
|
assertFalse(encoder.writeSettingsAck(ctx).isDone());
|
||||||
|
|
||||||
verifyFlushAndClose(0, false);
|
verifyFlushAndClose(0, false);
|
||||||
|
|
||||||
assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
|
assertFalse(encoder.writeSettingsAck(ctx).isDone());
|
||||||
assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
|
assertFalse(encoder.writeSettingsAck(ctx).isDone());
|
||||||
|
|
||||||
verifyFlushAndClose(1, true);
|
verifyFlushAndClose(1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLimitPingAck() {
|
public void testLimitPingAck() {
|
||||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
|
assertFalse(encoder.writePing(ctx, true, 8).isDone());
|
||||||
// The second write is always marked as success by our mock, which means it will also not be queued and so
|
// The second write is always marked as success by our mock, which means it will also not be queued and so
|
||||||
// not count to the number of queued frames.
|
// not count to the number of queued frames.
|
||||||
assertTrue(encoder.writePing(ctx, true, 8, newPromise()).isSuccess());
|
assertTrue(encoder.writePing(ctx, true, 8).isSuccess());
|
||||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
|
assertFalse(encoder.writePing(ctx, true, 8).isDone());
|
||||||
|
|
||||||
verifyFlushAndClose(0, false);
|
verifyFlushAndClose(0, false);
|
||||||
|
|
||||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
|
assertFalse(encoder.writePing(ctx, true, 8).isDone());
|
||||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
|
assertFalse(encoder.writePing(ctx, true, 8).isDone());
|
||||||
|
|
||||||
verifyFlushAndClose(1, true);
|
verifyFlushAndClose(1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotLimitPing() {
|
public void testNotLimitPing() {
|
||||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||||
|
|
||||||
verifyFlushAndClose(0, false);
|
verifyFlushAndClose(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLimitRst() {
|
public void testLimitRst() {
|
||||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||||
// The second write is always marked as success by our mock, which means it will also not be queued and so
|
// The second write is always marked as success by our mock, which means it will also not be queued and so
|
||||||
// not count to the number of queued frames.
|
// not count to the number of queued frames.
|
||||||
assertTrue(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isSuccess());
|
assertTrue(encoder.writeRstStream(ctx, 1, CANCEL.code()).isSuccess());
|
||||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||||
|
|
||||||
verifyFlushAndClose(0, false);
|
verifyFlushAndClose(0, false);
|
||||||
|
|
||||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||||
|
|
||||||
verifyFlushAndClose(1, true);
|
verifyFlushAndClose(1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLimit() {
|
public void testLimit() {
|
||||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||||
// The second write is always marked as success by our mock, which means it will also not be queued and so
|
// The second write is always marked as success by our mock, which means it will also not be queued and so
|
||||||
// not count to the number of queued frames.
|
// not count to the number of queued frames.
|
||||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isSuccess());
|
assertFalse(encoder.writePing(ctx, true, 8).isSuccess());
|
||||||
|
|
||||||
verifyFlushAndClose(0, false);
|
verifyFlushAndClose(0, false);
|
||||||
|
|
||||||
assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
|
assertFalse(encoder.writeSettingsAck(ctx).isDone());
|
||||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isSuccess());
|
assertFalse(encoder.writePing(ctx, true, 8).isSuccess());
|
||||||
|
|
||||||
verifyFlushAndClose(1, true);
|
verifyFlushAndClose(1, true);
|
||||||
}
|
}
|
||||||
@ -256,7 +259,7 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
verify(ctx, times(invocations)).close();
|
verify(ctx, times(invocations)).close();
|
||||||
if (failed) {
|
if (failed) {
|
||||||
verify(writer, times(1)).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(ENHANCE_YOUR_CALM.code()),
|
verify(writer, times(1)).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(ENHANCE_YOUR_CALM.code()),
|
||||||
any(ByteBuf.class), any(Promise.class));
|
any(ByteBuf.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
|
|
||||||
import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid;
|
import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid;
|
||||||
import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
|
import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
|
||||||
import static io.netty.handler.codec.http2.Http2TestUtil.anyChannelPromise;
|
|
||||||
import static io.netty.handler.codec.http2.Http2TestUtil.anyHttp2Settings;
|
import static io.netty.handler.codec.http2.Http2TestUtil.anyHttp2Settings;
|
||||||
import static io.netty.handler.codec.http2.Http2TestUtil.assertEqualsAndRelease;
|
import static io.netty.handler.codec.http2.Http2TestUtil.assertEqualsAndRelease;
|
||||||
import static io.netty.handler.codec.http2.Http2TestUtil.bb;
|
import static io.netty.handler.codec.http2.Http2TestUtil.bb;
|
||||||
@ -82,7 +81,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|||||||
/**
|
/**
|
||||||
* Unit tests for {@link Http2FrameCodec}.
|
* Unit tests for {@link Http2FrameCodec}.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public class Http2FrameCodecTest {
|
public class Http2FrameCodecTest {
|
||||||
|
|
||||||
// For verifying outbound frames
|
// For verifying outbound frames
|
||||||
@ -141,13 +139,13 @@ public class Http2FrameCodecTest {
|
|||||||
channel.pipeline().fireChannelActive();
|
channel.pipeline().fireChannelActive();
|
||||||
|
|
||||||
// Handshake
|
// Handshake
|
||||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), anyHttp2Settings(), anyChannelPromise());
|
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), anyHttp2Settings());
|
||||||
verifyNoMoreInteractions(frameWriter);
|
verifyNoMoreInteractions(frameWriter);
|
||||||
channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf());
|
channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf());
|
||||||
|
|
||||||
frameInboundWriter.writeInboundSettings(initialRemoteSettings);
|
frameInboundWriter.writeInboundSettings(initialRemoteSettings);
|
||||||
|
|
||||||
verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class), anyChannelPromise());
|
verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class));
|
||||||
|
|
||||||
frameInboundWriter.writeInboundSettingsAck();
|
frameInboundWriter.writeInboundSettingsAck();
|
||||||
|
|
||||||
@ -178,9 +176,9 @@ public class Http2FrameCodecTest {
|
|||||||
channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
|
channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
|
||||||
verify(frameWriter).writeHeaders(
|
verify(frameWriter).writeHeaders(
|
||||||
any(ChannelHandlerContext.class), eq(1), eq(response),
|
any(ChannelHandlerContext.class), eq(1), eq(response),
|
||||||
eq(27), eq(true), anyChannelPromise());
|
eq(27), eq(true));
|
||||||
verify(frameWriter, never()).writeRstStream(
|
verify(frameWriter, never()).writeRstStream(
|
||||||
any(ChannelHandlerContext.class), anyInt(), anyLong(), anyChannelPromise());
|
any(ChannelHandlerContext.class), anyInt(), anyLong());
|
||||||
|
|
||||||
assertEquals(State.CLOSED, stream.state());
|
assertEquals(State.CLOSED, stream.state());
|
||||||
event = inboundHandler.readInboundMessageOrUserEvent();
|
event = inboundHandler.readInboundMessageOrUserEvent();
|
||||||
@ -207,9 +205,9 @@ public class Http2FrameCodecTest {
|
|||||||
channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
|
channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
|
||||||
verify(frameWriter).writeHeaders(
|
verify(frameWriter).writeHeaders(
|
||||||
any(ChannelHandlerContext.class), eq(1), eq(response),
|
any(ChannelHandlerContext.class), eq(1), eq(response),
|
||||||
eq(27), eq(true), anyChannelPromise());
|
eq(27), eq(true));
|
||||||
verify(frameWriter, never()).writeRstStream(
|
verify(frameWriter, never()).writeRstStream(
|
||||||
any(ChannelHandlerContext.class), anyInt(), anyLong(), anyChannelPromise());
|
any(ChannelHandlerContext.class), anyInt(), anyLong());
|
||||||
|
|
||||||
assertEquals(State.CLOSED, stream.state());
|
assertEquals(State.CLOSED, stream.state());
|
||||||
assertTrue(channel.isActive());
|
assertTrue(channel.isActive());
|
||||||
@ -266,12 +264,12 @@ public class Http2FrameCodecTest {
|
|||||||
|
|
||||||
inboundHandler.writeOutbound(new DefaultHttp2HeadersFrame(response, false).stream(stream2));
|
inboundHandler.writeOutbound(new DefaultHttp2HeadersFrame(response, false).stream(stream2));
|
||||||
verify(frameWriter).writeHeaders(any(ChannelHandlerContext.class), eq(1), eq(response), eq(0),
|
verify(frameWriter).writeHeaders(any(ChannelHandlerContext.class), eq(1), eq(response), eq(0),
|
||||||
eq(false), anyChannelPromise());
|
eq(false));
|
||||||
|
|
||||||
channel.writeOutbound(new DefaultHttp2DataFrame(bb("world"), true, 27).stream(stream2));
|
channel.writeOutbound(new DefaultHttp2DataFrame(bb("world"), true, 27).stream(stream2));
|
||||||
ArgumentCaptor<ByteBuf> outboundData = ArgumentCaptor.forClass(ByteBuf.class);
|
ArgumentCaptor<ByteBuf> outboundData = ArgumentCaptor.forClass(ByteBuf.class);
|
||||||
verify(frameWriter).writeData(any(ChannelHandlerContext.class), eq(1), outboundData.capture(), eq(27),
|
verify(frameWriter).writeData(any(ChannelHandlerContext.class), eq(1), outboundData.capture(), eq(27),
|
||||||
eq(true), anyChannelPromise());
|
eq(true));
|
||||||
|
|
||||||
ByteBuf bb = bb("world");
|
ByteBuf bb = bb("world");
|
||||||
assertEquals(bb, outboundData.getValue());
|
assertEquals(bb, outboundData.getValue());
|
||||||
@ -280,7 +278,7 @@ public class Http2FrameCodecTest {
|
|||||||
outboundData.getValue().release();
|
outboundData.getValue().release();
|
||||||
|
|
||||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class),
|
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class),
|
||||||
anyInt(), anyLong(), anyChannelPromise());
|
anyInt(), anyLong());
|
||||||
assertTrue(channel.isActive());
|
assertTrue(channel.isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +299,7 @@ public class Http2FrameCodecTest {
|
|||||||
assertEquals(3, stream2.id());
|
assertEquals(3, stream2.id());
|
||||||
|
|
||||||
channel.writeOutbound(new DefaultHttp2ResetFrame(314 /* non-standard error */).stream(stream2));
|
channel.writeOutbound(new DefaultHttp2ResetFrame(314 /* non-standard error */).stream(stream2));
|
||||||
verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eq(3), eq(314L), anyChannelPromise());
|
verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eq(3), eq(314L));
|
||||||
assertEquals(State.CLOSED, stream.state());
|
assertEquals(State.CLOSED, stream.state());
|
||||||
assertTrue(channel.isActive());
|
assertTrue(channel.isActive());
|
||||||
}
|
}
|
||||||
@ -343,7 +341,7 @@ public class Http2FrameCodecTest {
|
|||||||
|
|
||||||
channel.writeOutbound(goAwayFrame);
|
channel.writeOutbound(goAwayFrame);
|
||||||
verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(7),
|
verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(7),
|
||||||
eq(NO_ERROR.code()), eq(expected), anyChannelPromise());
|
eq(NO_ERROR.code()), eq(expected));
|
||||||
assertEquals(State.OPEN, stream.state());
|
assertEquals(State.OPEN, stream.state());
|
||||||
assertTrue(channel.isActive());
|
assertTrue(channel.isActive());
|
||||||
expected.release();
|
expected.release();
|
||||||
@ -419,7 +417,7 @@ public class Http2FrameCodecTest {
|
|||||||
channel.writeOutbound(goAwayFrame);
|
channel.writeOutbound(goAwayFrame);
|
||||||
// When the last stream id computation overflows, the last stream id should just be set to 2^31 - 1.
|
// When the last stream id computation overflows, the last stream id should just be set to 2^31 - 1.
|
||||||
verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(Integer.MAX_VALUE),
|
verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(Integer.MAX_VALUE),
|
||||||
eq(NO_ERROR.code()), eq(debugData), anyChannelPromise());
|
eq(NO_ERROR.code()), eq(debugData));
|
||||||
debugData.release();
|
debugData.release();
|
||||||
assertEquals(State.OPEN, stream.state());
|
assertEquals(State.OPEN, stream.state());
|
||||||
assertTrue(channel.isActive());
|
assertTrue(channel.isActive());
|
||||||
@ -596,7 +594,7 @@ public class Http2FrameCodecTest {
|
|||||||
channel.write(unknownFrame);
|
channel.write(unknownFrame);
|
||||||
|
|
||||||
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq(unknownFrame.frameType()),
|
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq(unknownFrame.frameType()),
|
||||||
eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer), any(Promise.class));
|
eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -604,7 +602,7 @@ public class Http2FrameCodecTest {
|
|||||||
Http2Settings settings = new Http2Settings();
|
Http2Settings settings = new Http2Settings();
|
||||||
channel.write(new DefaultHttp2SettingsFrame(settings));
|
channel.write(new DefaultHttp2SettingsFrame(settings));
|
||||||
|
|
||||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), same(settings), any(Promise.class));
|
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), same(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -763,7 +761,7 @@ public class Http2FrameCodecTest {
|
|||||||
channel.writeAndFlush(new DefaultHttp2PingFrame(12345));
|
channel.writeAndFlush(new DefaultHttp2PingFrame(12345));
|
||||||
|
|
||||||
verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(false),
|
verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(false),
|
||||||
eq(12345L), anyChannelPromise());
|
eq(12345L));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -781,7 +779,7 @@ public class Http2FrameCodecTest {
|
|||||||
Http2Settings settings = new Http2Settings().maxConcurrentStreams(1);
|
Http2Settings settings = new Http2Settings().maxConcurrentStreams(1);
|
||||||
channel.writeAndFlush(new DefaultHttp2SettingsFrame(settings));
|
channel.writeAndFlush(new DefaultHttp2SettingsFrame(settings));
|
||||||
|
|
||||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), eq(settings), anyChannelPromise());
|
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), eq(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -837,21 +835,21 @@ public class Http2FrameCodecTest {
|
|||||||
Http2PingFrame frame = inboundHandler.readInbound();
|
Http2PingFrame frame = inboundHandler.readInbound();
|
||||||
assertFalse(frame.ack());
|
assertFalse(frame.ack());
|
||||||
assertEquals(8, frame.content());
|
assertEquals(8, frame.content());
|
||||||
verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L), anyChannelPromise());
|
verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void autoAckPingFalse() throws Exception {
|
public void autoAckPingFalse() throws Exception {
|
||||||
setUp(Http2FrameCodecBuilder.forServer().autoAckPingFrame(false), new Http2Settings());
|
setUp(Http2FrameCodecBuilder.forServer().autoAckPingFrame(false), new Http2Settings());
|
||||||
frameInboundWriter.writeInboundPing(false, 8);
|
frameInboundWriter.writeInboundPing(false, 8);
|
||||||
verify(frameWriter, never()).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L), anyChannelPromise());
|
verify(frameWriter, never()).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L));
|
||||||
Http2PingFrame frame = inboundHandler.readInbound();
|
Http2PingFrame frame = inboundHandler.readInbound();
|
||||||
assertFalse(frame.ack());
|
assertFalse(frame.ack());
|
||||||
assertEquals(8, frame.content());
|
assertEquals(8, frame.content());
|
||||||
|
|
||||||
// Now ack the frame manually.
|
// Now ack the frame manually.
|
||||||
channel.writeAndFlush(new DefaultHttp2PingFrame(8, true));
|
channel.writeAndFlush(new DefaultHttp2PingFrame(8, true));
|
||||||
verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L), anyChannelPromise());
|
verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -49,59 +49,59 @@ final class Http2FrameInboundWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundData(int streamId, ByteBuf data, int padding, boolean endStream) {
|
void writeInboundData(int streamId, ByteBuf data, int padding, boolean endStream) {
|
||||||
writer.writeData(ctx, streamId, data, padding, endStream, ctx.newPromise()).syncUninterruptibly();
|
writer.writeData(ctx, streamId, data, padding, endStream).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundHeaders(int streamId, Http2Headers headers,
|
void writeInboundHeaders(int streamId, Http2Headers headers,
|
||||||
int padding, boolean endStream) {
|
int padding, boolean endStream) {
|
||||||
writer.writeHeaders(ctx, streamId, headers, padding, endStream, ctx.newPromise()).syncUninterruptibly();
|
writer.writeHeaders(ctx, streamId, headers, padding, endStream).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundHeaders(int streamId, Http2Headers headers,
|
void writeInboundHeaders(int streamId, Http2Headers headers,
|
||||||
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) {
|
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) {
|
||||||
writer.writeHeaders(ctx, streamId, headers, streamDependency,
|
writer.writeHeaders(ctx, streamId, headers, streamDependency,
|
||||||
weight, exclusive, padding, endStream, ctx.newPromise()).syncUninterruptibly();
|
weight, exclusive, padding, endStream).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundPriority(int streamId, int streamDependency,
|
void writeInboundPriority(int streamId, int streamDependency,
|
||||||
short weight, boolean exclusive) {
|
short weight, boolean exclusive) {
|
||||||
writer.writePriority(ctx, streamId, streamDependency, weight,
|
writer.writePriority(ctx, streamId, streamDependency, weight,
|
||||||
exclusive, ctx.newPromise()).syncUninterruptibly();
|
exclusive).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundRstStream(int streamId, long errorCode) {
|
void writeInboundRstStream(int streamId, long errorCode) {
|
||||||
writer.writeRstStream(ctx, streamId, errorCode, ctx.newPromise()).syncUninterruptibly();
|
writer.writeRstStream(ctx, streamId, errorCode).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundSettings(Http2Settings settings) {
|
void writeInboundSettings(Http2Settings settings) {
|
||||||
writer.writeSettings(ctx, settings, ctx.newPromise()).syncUninterruptibly();
|
writer.writeSettings(ctx, settings).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundSettingsAck() {
|
void writeInboundSettingsAck() {
|
||||||
writer.writeSettingsAck(ctx, ctx.newPromise()).syncUninterruptibly();
|
writer.writeSettingsAck(ctx).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundPing(boolean ack, long data) {
|
void writeInboundPing(boolean ack, long data) {
|
||||||
writer.writePing(ctx, ack, data, ctx.newPromise()).syncUninterruptibly();
|
writer.writePing(ctx, ack, data).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writePushPromise(int streamId, int promisedStreamId,
|
void writePushPromise(int streamId, int promisedStreamId,
|
||||||
Http2Headers headers, int padding) {
|
Http2Headers headers, int padding) {
|
||||||
writer.writePushPromise(ctx, streamId, promisedStreamId,
|
writer.writePushPromise(ctx, streamId, promisedStreamId,
|
||||||
headers, padding, ctx.newPromise()).syncUninterruptibly();
|
headers, padding).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundGoAway(int lastStreamId, long errorCode, ByteBuf debugData) {
|
void writeInboundGoAway(int lastStreamId, long errorCode, ByteBuf debugData) {
|
||||||
writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, ctx.newPromise()).syncUninterruptibly();
|
writer.writeGoAway(ctx, lastStreamId, errorCode, debugData).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundWindowUpdate(int streamId, int windowSizeIncrement) {
|
void writeInboundWindowUpdate(int streamId, int windowSizeIncrement) {
|
||||||
writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, ctx.newPromise()).syncUninterruptibly();
|
writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeInboundFrame(byte frameType, int streamId,
|
void writeInboundFrame(byte frameType, int streamId,
|
||||||
Http2Flags flags, ByteBuf payload) {
|
Http2Flags flags, ByteBuf payload) {
|
||||||
writer.writeFrame(ctx, frameType, streamId, flags, payload, ctx.newPromise()).syncUninterruptibly();
|
writer.writeFrame(ctx, frameType, streamId, flags, payload).syncUninterruptibly();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class WriteInboundChannelHandlerContext
|
private static final class WriteInboundChannelHandlerContext
|
||||||
|
@ -59,7 +59,6 @@ import static org.mockito.Mockito.anyShort;
|
|||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.eq;
|
import static org.mockito.Mockito.eq;
|
||||||
import static org.mockito.Mockito.isA;
|
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@ -134,7 +133,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void emptyDataShouldMatch() throws Exception {
|
public void emptyDataShouldMatch() throws Exception {
|
||||||
final ByteBuf data = EMPTY_BUFFER;
|
final ByteBuf data = EMPTY_BUFFER;
|
||||||
writer.writeData(ctx, STREAM_ID, data.slice(), 0, false, ctx.newPromise());
|
writer.writeData(ctx, STREAM_ID, data.slice(), 0, false);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(0), eq(false));
|
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(0), eq(false));
|
||||||
}
|
}
|
||||||
@ -142,7 +141,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void dataShouldMatch() throws Exception {
|
public void dataShouldMatch() throws Exception {
|
||||||
final ByteBuf data = data(10);
|
final ByteBuf data = data(10);
|
||||||
writer.writeData(ctx, STREAM_ID, data.slice(), 1, false, ctx.newPromise());
|
writer.writeData(ctx, STREAM_ID, data.slice(), 1, false);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(1), eq(false));
|
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(1), eq(false));
|
||||||
}
|
}
|
||||||
@ -150,7 +149,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void dataWithPaddingShouldMatch() throws Exception {
|
public void dataWithPaddingShouldMatch() throws Exception {
|
||||||
final ByteBuf data = data(10);
|
final ByteBuf data = data(10);
|
||||||
writer.writeData(ctx, STREAM_ID, data.slice(), MAX_PADDING, true, ctx.newPromise());
|
writer.writeData(ctx, STREAM_ID, data.slice(), MAX_PADDING, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(MAX_PADDING), eq(true));
|
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(MAX_PADDING), eq(true));
|
||||||
}
|
}
|
||||||
@ -163,7 +162,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
final boolean endOfStream = true;
|
final boolean endOfStream = true;
|
||||||
|
|
||||||
writer.writeData(ctx, STREAM_ID, originalData.slice(), originalPadding,
|
writer.writeData(ctx, STREAM_ID, originalData.slice(), originalPadding,
|
||||||
endOfStream, ctx.newPromise());
|
endOfStream);
|
||||||
readFrames();
|
readFrames();
|
||||||
|
|
||||||
// Verify that at least one frame was sent with eos=false and exactly one with eos=true.
|
// Verify that at least one frame was sent with eos=false and exactly one with eos=true.
|
||||||
@ -196,7 +195,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void emptyHeadersShouldMatch() throws Exception {
|
public void emptyHeadersShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
|
final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
|
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
|
||||||
}
|
}
|
||||||
@ -204,7 +203,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void emptyHeadersWithPaddingShouldMatch() throws Exception {
|
public void emptyHeadersWithPaddingShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
|
final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true));
|
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true));
|
||||||
}
|
}
|
||||||
@ -212,7 +211,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void binaryHeadersWithoutPriorityShouldMatch() throws Exception {
|
public void binaryHeadersWithoutPriorityShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = binaryHeaders();
|
final Http2Headers headers = binaryHeaders();
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
|
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
|
||||||
}
|
}
|
||||||
@ -220,7 +219,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void headersFrameWithoutPriorityShouldMatch() throws Exception {
|
public void headersFrameWithoutPriorityShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = headers();
|
final Http2Headers headers = headers();
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
|
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
|
||||||
}
|
}
|
||||||
@ -228,7 +227,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void headersFrameWithPriorityShouldMatch() throws Exception {
|
public void headersFrameWithPriorityShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = headers();
|
final Http2Headers headers = headers();
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, 4, (short) 255, true, 0, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, 4, (short) 255, true, 0, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(4), eq((short) 255),
|
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(4), eq((short) 255),
|
||||||
eq(true), eq(0), eq(true));
|
eq(true), eq(0), eq(true));
|
||||||
@ -237,7 +236,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void headersWithPaddingWithoutPriorityShouldMatch() throws Exception {
|
public void headersWithPaddingWithoutPriorityShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = headers();
|
final Http2Headers headers = headers();
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true));
|
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true));
|
||||||
}
|
}
|
||||||
@ -245,7 +244,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void headersWithPaddingWithPriorityShouldMatch() throws Exception {
|
public void headersWithPaddingWithPriorityShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = headers();
|
final Http2Headers headers = headers();
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 1, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 1, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true),
|
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true),
|
||||||
eq(1), eq(true));
|
eq(1), eq(true));
|
||||||
@ -254,7 +253,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void continuedHeadersShouldMatch() throws Exception {
|
public void continuedHeadersShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = largeHeaders();
|
final Http2Headers headers = largeHeaders();
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 0, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 0, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener)
|
verify(listener)
|
||||||
.onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true), eq(0), eq(true));
|
.onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true), eq(0), eq(true));
|
||||||
@ -263,7 +262,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void continuedHeadersWithPaddingShouldMatch() throws Exception {
|
public void continuedHeadersWithPaddingShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = largeHeaders();
|
final Http2Headers headers = largeHeaders();
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true),
|
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true),
|
||||||
eq(MAX_PADDING), eq(true));
|
eq(MAX_PADDING), eq(true));
|
||||||
@ -275,7 +274,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
final int maxListSize = 100;
|
final int maxListSize = 100;
|
||||||
reader.configuration().headersConfiguration().maxHeaderListSize(maxListSize, maxListSize);
|
reader.configuration().headersConfiguration().maxHeaderListSize(maxListSize, maxListSize);
|
||||||
final Http2Headers headers = headersOfSize(maxListSize + 1);
|
final Http2Headers headers = headersOfSize(maxListSize + 1);
|
||||||
writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true, ctx.newPromise());
|
writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true);
|
||||||
assertThrows(Http2Exception.class, new Executable() {
|
assertThrows(Http2Exception.class, new Executable() {
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Throwable {
|
public void execute() throws Throwable {
|
||||||
@ -290,7 +289,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void emptyPushPromiseShouldMatch() throws Exception {
|
public void emptyPushPromiseShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
|
final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
|
||||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0, ctx.newPromise());
|
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0));
|
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0));
|
||||||
}
|
}
|
||||||
@ -298,7 +297,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void pushPromiseFrameShouldMatch() throws Exception {
|
public void pushPromiseFrameShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = headers();
|
final Http2Headers headers = headers();
|
||||||
writer.writePushPromise(ctx, STREAM_ID, 1, headers, 5, ctx.newPromise());
|
writer.writePushPromise(ctx, STREAM_ID, 1, headers, 5);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(1), eq(headers), eq(5));
|
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(1), eq(headers), eq(5));
|
||||||
}
|
}
|
||||||
@ -306,7 +305,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void pushPromiseWithPaddingShouldMatch() throws Exception {
|
public void pushPromiseWithPaddingShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = headers();
|
final Http2Headers headers = headers();
|
||||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, MAX_PADDING, ctx.newPromise());
|
writer.writePushPromise(ctx, STREAM_ID, 2, headers, MAX_PADDING);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(MAX_PADDING));
|
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(MAX_PADDING));
|
||||||
}
|
}
|
||||||
@ -314,7 +313,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void continuedPushPromiseShouldMatch() throws Exception {
|
public void continuedPushPromiseShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = largeHeaders();
|
final Http2Headers headers = largeHeaders();
|
||||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0, ctx.newPromise());
|
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0));
|
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0));
|
||||||
}
|
}
|
||||||
@ -322,7 +321,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void continuedPushPromiseWithPaddingShouldMatch() throws Exception {
|
public void continuedPushPromiseWithPaddingShouldMatch() throws Exception {
|
||||||
final Http2Headers headers = largeHeaders();
|
final Http2Headers headers = largeHeaders();
|
||||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0xFF, ctx.newPromise());
|
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0xFF);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0xFF));
|
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0xFF));
|
||||||
}
|
}
|
||||||
@ -332,7 +331,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
final String text = "test";
|
final String text = "test";
|
||||||
final ByteBuf data = buf(text.getBytes());
|
final ByteBuf data = buf(text.getBytes());
|
||||||
|
|
||||||
writer.writeGoAway(ctx, STREAM_ID, ERROR_CODE, data.slice(), ctx.newPromise());
|
writer.writeGoAway(ctx, STREAM_ID, ERROR_CODE, data.slice());
|
||||||
readFrames();
|
readFrames();
|
||||||
|
|
||||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||||
@ -342,7 +341,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pingFrameShouldMatch() throws Exception {
|
public void pingFrameShouldMatch() throws Exception {
|
||||||
writer.writePing(ctx, false, 1234567, ctx.newPromise());
|
writer.writePing(ctx, false, 1234567);
|
||||||
readFrames();
|
readFrames();
|
||||||
|
|
||||||
ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(long.class);
|
ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(long.class);
|
||||||
@ -352,7 +351,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pingAckFrameShouldMatch() throws Exception {
|
public void pingAckFrameShouldMatch() throws Exception {
|
||||||
writer.writePing(ctx, true, 1234567, ctx.newPromise());
|
writer.writePing(ctx, true, 1234567);
|
||||||
readFrames();
|
readFrames();
|
||||||
|
|
||||||
ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(long.class);
|
ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(long.class);
|
||||||
@ -362,14 +361,14 @@ public class Http2FrameRoundtripTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void priorityFrameShouldMatch() throws Exception {
|
public void priorityFrameShouldMatch() throws Exception {
|
||||||
writer.writePriority(ctx, STREAM_ID, 1, (short) 1, true, ctx.newPromise());
|
writer.writePriority(ctx, STREAM_ID, 1, (short) 1, true);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onPriorityRead(eq(ctx), eq(STREAM_ID), eq(1), eq((short) 1), eq(true));
|
verify(listener).onPriorityRead(eq(ctx), eq(STREAM_ID), eq(1), eq((short) 1), eq(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void rstStreamFrameShouldMatch() throws Exception {
|
public void rstStreamFrameShouldMatch() throws Exception {
|
||||||
writer.writeRstStream(ctx, STREAM_ID, ERROR_CODE, ctx.newPromise());
|
writer.writeRstStream(ctx, STREAM_ID, ERROR_CODE);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onRstStreamRead(eq(ctx), eq(STREAM_ID), eq(ERROR_CODE));
|
verify(listener).onRstStreamRead(eq(ctx), eq(STREAM_ID), eq(ERROR_CODE));
|
||||||
}
|
}
|
||||||
@ -377,7 +376,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
@Test
|
@Test
|
||||||
public void emptySettingsFrameShouldMatch() throws Exception {
|
public void emptySettingsFrameShouldMatch() throws Exception {
|
||||||
final Http2Settings settings = new Http2Settings();
|
final Http2Settings settings = new Http2Settings();
|
||||||
writer.writeSettings(ctx, settings, ctx.newPromise());
|
writer.writeSettings(ctx, settings);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onSettingsRead(eq(ctx), eq(settings));
|
verify(listener).onSettingsRead(eq(ctx), eq(settings));
|
||||||
}
|
}
|
||||||
@ -390,21 +389,21 @@ public class Http2FrameRoundtripTest {
|
|||||||
settings.initialWindowSize(123);
|
settings.initialWindowSize(123);
|
||||||
settings.maxConcurrentStreams(456);
|
settings.maxConcurrentStreams(456);
|
||||||
|
|
||||||
writer.writeSettings(ctx, settings, ctx.newPromise());
|
writer.writeSettings(ctx, settings);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onSettingsRead(eq(ctx), eq(settings));
|
verify(listener).onSettingsRead(eq(ctx), eq(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void settingsAckShouldMatch() throws Exception {
|
public void settingsAckShouldMatch() throws Exception {
|
||||||
writer.writeSettingsAck(ctx, ctx.newPromise());
|
writer.writeSettingsAck(ctx);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onSettingsAckRead(eq(ctx));
|
verify(listener).onSettingsAckRead(eq(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void windowUpdateFrameShouldMatch() throws Exception {
|
public void windowUpdateFrameShouldMatch() throws Exception {
|
||||||
writer.writeWindowUpdate(ctx, STREAM_ID, WINDOW_UPDATE, ctx.newPromise());
|
writer.writeWindowUpdate(ctx, STREAM_ID, WINDOW_UPDATE);
|
||||||
readFrames();
|
readFrames();
|
||||||
verify(listener).onWindowUpdateRead(eq(ctx), eq(STREAM_ID), eq(WINDOW_UPDATE));
|
verify(listener).onWindowUpdateRead(eq(ctx), eq(STREAM_ID), eq(WINDOW_UPDATE));
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import io.netty.handler.codec.http2.Http2Exception.StreamException;
|
|||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.hamcrest.CoreMatchers;
|
import org.hamcrest.CoreMatchers;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
@ -49,7 +50,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http2.Http2TestUtil.anyChannelPromise;
|
|
||||||
import static io.netty.handler.codec.http2.Http2TestUtil.anyHttp2Settings;
|
import static io.netty.handler.codec.http2.Http2TestUtil.anyHttp2Settings;
|
||||||
import static io.netty.handler.codec.http2.Http2TestUtil.assertEqualsAndRelease;
|
import static io.netty.handler.codec.http2.Http2TestUtil.assertEqualsAndRelease;
|
||||||
import static io.netty.handler.codec.http2.Http2TestUtil.bb;
|
import static io.netty.handler.codec.http2.Http2TestUtil.bb;
|
||||||
@ -110,7 +110,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
Http2Settings settings = new Http2Settings().initialWindowSize(initialRemoteStreamWindow);
|
Http2Settings settings = new Http2Settings().initialWindowSize(initialRemoteStreamWindow);
|
||||||
frameInboundWriter.writeInboundSettings(settings);
|
frameInboundWriter.writeInboundSettings(settings);
|
||||||
|
|
||||||
verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class), anyChannelPromise());
|
verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class));
|
||||||
|
|
||||||
frameInboundWriter.writeInboundSettingsAck();
|
frameInboundWriter.writeInboundSettingsAck();
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
// Handshake
|
// Handshake
|
||||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class),
|
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class),
|
||||||
anyHttp2Settings(), anyChannelPromise());
|
anyHttp2Settings());
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@ -151,7 +151,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
assertTrue(childChannel.isActive());
|
assertTrue(childChannel.isActive());
|
||||||
|
|
||||||
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq((byte) 99), eqStreamId(childChannel),
|
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq((byte) 99), eqStreamId(childChannel),
|
||||||
any(Http2Flags.class), any(ByteBuf.class), any(Promise.class));
|
any(Http2Flags.class), any(ByteBuf.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Http2StreamChannel newInboundStream(int streamId, boolean endStream, final ChannelHandler childHandler) {
|
private Http2StreamChannel newInboundStream(int streamId, boolean endStream, final ChannelHandler childHandler) {
|
||||||
@ -552,14 +552,13 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
childChannel.close();
|
childChannel.close();
|
||||||
verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class),
|
verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class),
|
||||||
eqStreamId(childChannel), eq(Http2Error.CANCEL.code()), anyChannelPromise());
|
eqStreamId(childChannel), eq(Http2Error.CANCEL.code()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() {
|
public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() {
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
any(Http2Headers.class), anyInt(), anyBoolean(),
|
any(Http2Headers.class), anyInt(), anyBoolean())).thenAnswer(new Answer<Future<Void>>() {
|
||||||
any(Promise.class))).thenAnswer(new Answer<Future<Void>>() {
|
|
||||||
|
|
||||||
private boolean headersWritten;
|
private boolean headersWritten;
|
||||||
@Override
|
@Override
|
||||||
@ -568,9 +567,9 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
// refuses to allocate a new stream due to having received a GOAWAY.
|
// refuses to allocate a new stream due to having received a GOAWAY.
|
||||||
if (!headersWritten) {
|
if (!headersWritten) {
|
||||||
headersWritten = true;
|
headersWritten = true;
|
||||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(new Exception("boom"));
|
return ImmediateEventExecutor.INSTANCE.newFailedFuture(new Exception("boom"));
|
||||||
}
|
}
|
||||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
|
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -587,7 +586,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
childChannel.close();
|
childChannel.close();
|
||||||
// The channel was never active so we should not generate a RST frame.
|
// The channel was never active so we should not generate a RST frame.
|
||||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class),
|
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class),
|
||||||
eqStreamId(childChannel), anyLong(), anyChannelPromise());
|
eqStreamId(childChannel), anyLong());
|
||||||
|
|
||||||
assertTrue(parentChannel.outboundMessages().isEmpty());
|
assertTrue(parentChannel.outboundMessages().isEmpty());
|
||||||
}
|
}
|
||||||
@ -603,7 +602,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
// A RST_STREAM frame should NOT be emitted, as we received a RST_STREAM.
|
// A RST_STREAM frame should NOT be emitted, as we received a RST_STREAM.
|
||||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(channel),
|
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(channel),
|
||||||
anyLong(), anyChannelPromise());
|
anyLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -633,11 +632,9 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
Http2Headers headers = new DefaultHttp2Headers();
|
Http2Headers headers = new DefaultHttp2Headers();
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
eq(headers), anyInt(), anyBoolean(),
|
eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock ->
|
||||||
any(Promise.class))).thenAnswer(invocationOnMock -> {
|
ImmediateEventExecutor.INSTANCE.newFailedFuture(
|
||||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(
|
new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed")));
|
||||||
new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed"));
|
|
||||||
});
|
|
||||||
final Future<Void> future = childChannel.writeAndFlush(
|
final Future<Void> future = childChannel.writeAndFlush(
|
||||||
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()));
|
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()));
|
||||||
|
|
||||||
@ -680,7 +677,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
// An active outbound stream should emit a RST_STREAM frame.
|
// An active outbound stream should emit a RST_STREAM frame.
|
||||||
verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(childChannel),
|
verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(childChannel),
|
||||||
anyLong(), anyChannelPromise());
|
anyLong());
|
||||||
|
|
||||||
assertFalse(childChannel.isOpen());
|
assertFalse(childChannel.isOpen());
|
||||||
assertFalse(childChannel.isActive());
|
assertFalse(childChannel.isActive());
|
||||||
@ -698,10 +695,8 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
Http2Headers headers = new DefaultHttp2Headers();
|
Http2Headers headers = new DefaultHttp2Headers();
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
eq(headers), anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(invocationOnMock -> {
|
eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock ->
|
||||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(
|
ImmediateEventExecutor.INSTANCE.newFailedFuture(new Http2NoMoreStreamIdsException()));
|
||||||
new Http2NoMoreStreamIdsException());
|
|
||||||
});
|
|
||||||
|
|
||||||
final Future<Void> future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
|
final Future<Void> future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
|
||||||
parentChannel.flush();
|
parentChannel.flush();
|
||||||
@ -782,9 +777,8 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
Http2Headers headers = new DefaultHttp2Headers();
|
Http2Headers headers = new DefaultHttp2Headers();
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
eq(headers), anyInt(), anyBoolean(),
|
eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock -> {
|
||||||
any(Promise.class))).thenAnswer(invocationOnMock -> {
|
Promise<Void> promise = ImmediateEventExecutor.INSTANCE.newPromise();
|
||||||
Promise<Void> promise = invocationOnMock.getArgument(5);
|
|
||||||
writePromises.offer(promise);
|
writePromises.offer(promise);
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
@ -1305,7 +1299,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
assertTrue(flushSniffer.checkFlush());
|
assertTrue(flushSniffer.checkFlush());
|
||||||
|
|
||||||
verify(frameWriter, never())
|
verify(frameWriter, never())
|
||||||
.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(), anyChannelPromise());
|
.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt());
|
||||||
// only the first one was read because it was legacy auto-read behavior.
|
// only the first one was read because it was legacy auto-read behavior.
|
||||||
verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 1);
|
verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 1);
|
||||||
assertFalse(flushSniffer.checkFlush());
|
assertFalse(flushSniffer.checkFlush());
|
||||||
@ -1317,15 +1311,15 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
// connection will collect the bytes and decide not to send a wire level frame until more are consumed.
|
// connection will collect the bytes and decide not to send a wire level frame until more are consumed.
|
||||||
assertTrue(flushSniffer.checkFlush());
|
assertTrue(flushSniffer.checkFlush());
|
||||||
verify(frameWriter, never())
|
verify(frameWriter, never())
|
||||||
.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(), anyChannelPromise());
|
.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt());
|
||||||
|
|
||||||
// Call read one more time which should trigger the writing of the flow control update.
|
// Call read one more time which should trigger the writing of the flow control update.
|
||||||
childChannel.read();
|
childChannel.read();
|
||||||
verify(frameWriter)
|
verify(frameWriter)
|
||||||
.writeWindowUpdate(any(ChannelHandlerContext.class), eq(0), eq(32 * 1024), anyChannelPromise());
|
.writeWindowUpdate(any(ChannelHandlerContext.class), eq(0), eq(32 * 1024));
|
||||||
verify(frameWriter)
|
verify(frameWriter)
|
||||||
.writeWindowUpdate(any(ChannelHandlerContext.class), eq(childChannel.stream().id()),
|
.writeWindowUpdate(any(ChannelHandlerContext.class), eq(childChannel.stream().id()),
|
||||||
eq(32 * 1024), anyChannelPromise());
|
eq(32 * 1024));
|
||||||
assertTrue(flushSniffer.checkFlush());
|
assertTrue(flushSniffer.checkFlush());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,10 +22,9 @@ import io.netty.channel.Channel;
|
|||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
@ -360,50 +359,45 @@ public final class Http2TestUtil {
|
|||||||
}).when(frameWriter).close();
|
}).when(frameWriter).close();
|
||||||
|
|
||||||
when(frameWriter.configuration()).thenReturn(configuration);
|
when(frameWriter.configuration()).thenReturn(configuration);
|
||||||
when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class),
|
when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class)))
|
||||||
any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
((Promise<Void>) invocationOnMock.getArgument(2)).setSuccess(null));
|
|
||||||
|
|
||||||
when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
|
when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class)))
|
||||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
((Promise<Void>) invocationOnMock.getArgument(1)).setSuccess(null));
|
|
||||||
|
|
||||||
|
when(frameWriter.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong()))
|
||||||
|
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
when(frameWriter.writeGoAway(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeGoAway(any(ChannelHandlerContext.class), anyInt(),
|
||||||
anyLong(), any(ByteBuf.class), any(Promise.class))).thenAnswer(invocationOnMock -> {
|
anyLong(), any(ByteBuf.class))).thenAnswer(invocationOnMock -> {
|
||||||
buffers.offer((ByteBuf) invocationOnMock.getArgument(3));
|
buffers.offer((ByteBuf) invocationOnMock.getArgument(3));
|
||||||
return ((Promise<Void>) invocationOnMock.getArgument(4)).setSuccess(null);
|
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||||
});
|
});
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
||||||
anyBoolean(), any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
anyBoolean())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null));
|
|
||||||
|
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
|
any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()))
|
||||||
any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
((Promise<Void>) invocationOnMock.getArgument(8)).setSuccess(null));
|
|
||||||
|
|
||||||
when(frameWriter.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(),
|
when(frameWriter.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(),
|
||||||
anyBoolean(), any(Promise.class))).thenAnswer(invocationOnMock -> {
|
anyBoolean())).thenAnswer(invocationOnMock -> {
|
||||||
buffers.offer((ByteBuf) invocationOnMock.getArgument(2));
|
buffers.offer((ByteBuf) invocationOnMock.getArgument(2));
|
||||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
|
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(),
|
||||||
anyLong(), any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
anyLong())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
((Promise<Void>) invocationOnMock.getArgument(3)).setSuccess(null));
|
|
||||||
|
|
||||||
when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
|
when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt()))
|
||||||
any(Promise.class))).then((Answer<Future<Void>>) invocationOnMock ->
|
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
((Promise<Void>) invocationOnMock.getArgument(3)).setSuccess(null));
|
|
||||||
|
|
||||||
when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class),
|
when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class),
|
||||||
anyInt(), anyChannelPromise())).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
anyInt())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||||
((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null));
|
|
||||||
|
|
||||||
when(frameWriter.writeFrame(any(ChannelHandlerContext.class), anyByte(), anyInt(), any(Http2Flags.class),
|
when(frameWriter.writeFrame(any(ChannelHandlerContext.class), anyByte(), anyInt(), any(Http2Flags.class),
|
||||||
any(ByteBuf.class), anyChannelPromise())).thenAnswer(invocationOnMock -> {
|
any(ByteBuf.class))).thenAnswer(invocationOnMock -> {
|
||||||
buffers.offer((ByteBuf) invocationOnMock.getArgument(4));
|
buffers.offer((ByteBuf) invocationOnMock.getArgument(4));
|
||||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
|
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||||
});
|
});
|
||||||
return frameWriter;
|
return frameWriter;
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
scheme(new AsciiString("https")).authority(new AsciiString("example.org"))
|
scheme(new AsciiString("https")).authority(new AsciiString("example.org"))
|
||||||
.path(new AsciiString("/some/path/resource2"));
|
.path(new AsciiString("/some/path/resource2"));
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitRequests();
|
awaitRequests();
|
||||||
@ -179,7 +179,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
.add(HttpHeaderNames.COOKIE, "c=d")
|
.add(HttpHeaderNames.COOKIE, "c=d")
|
||||||
.add(HttpHeaderNames.COOKIE, "e=f");
|
.add(HttpHeaderNames.COOKIE, "e=f");
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitRequests();
|
awaitRequests();
|
||||||
@ -211,7 +211,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
.add(HttpHeaderNames.COOKIE, "a=b; c=d")
|
.add(HttpHeaderNames.COOKIE, "a=b; c=d")
|
||||||
.add(HttpHeaderNames.COOKIE, "e=f");
|
.add(HttpHeaderNames.COOKIE, "e=f");
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitRequests();
|
awaitRequests();
|
||||||
@ -235,7 +235,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
.add(new AsciiString("çã".getBytes(CharsetUtil.UTF_8)),
|
.add(new AsciiString("çã".getBytes(CharsetUtil.UTF_8)),
|
||||||
new AsciiString("Ãã".getBytes(CharsetUtil.UTF_8)));
|
new AsciiString("Ãã".getBytes(CharsetUtil.UTF_8)));
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitResponses();
|
awaitResponses();
|
||||||
@ -257,9 +257,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
|
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
|
||||||
new AsciiString("/some/path/resource2"));
|
new AsciiString("/some/path/resource2"));
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true,
|
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true);
|
||||||
newPromiseClient());
|
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitRequests();
|
awaitRequests();
|
||||||
@ -288,12 +287,12 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
new AsciiString("/some/path/resource2"));
|
new AsciiString("/some/path/resource2"));
|
||||||
final int midPoint = text.length() / 2;
|
final int midPoint = text.length() / 2;
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||||
clientHandler.encoder().writeData(
|
clientHandler.encoder().writeData(
|
||||||
ctxClient(), 3, content.retainedSlice(0, midPoint), 0, false, newPromiseClient());
|
ctxClient(), 3, content.retainedSlice(0, midPoint), 0, false);
|
||||||
clientHandler.encoder().writeData(
|
clientHandler.encoder().writeData(
|
||||||
ctxClient(), 3, content.retainedSlice(midPoint, text.length() - midPoint),
|
ctxClient(), 3, content.retainedSlice(midPoint, text.length() - midPoint),
|
||||||
0, true, newPromiseClient());
|
0, true);
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitRequests();
|
awaitRequests();
|
||||||
@ -321,10 +320,10 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
|
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
|
||||||
new AsciiString("/some/path/resource2"));
|
new AsciiString("/some/path/resource2"));
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false, newPromiseClient());
|
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false);
|
||||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false, newPromiseClient());
|
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false);
|
||||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, true, newPromiseClient());
|
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, true);
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitRequests();
|
awaitRequests();
|
||||||
@ -360,10 +359,9 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
.set(new AsciiString("foo2"), new AsciiString("goo2"))
|
.set(new AsciiString("foo2"), new AsciiString("goo2"))
|
||||||
.add(new AsciiString("foo2"), new AsciiString("goo3"));
|
.add(new AsciiString("foo2"), new AsciiString("goo3"));
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, false,
|
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, false);
|
||||||
newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers2, 0, true);
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers2, 0, true, newPromiseClient());
|
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitRequests();
|
awaitRequests();
|
||||||
@ -402,14 +400,12 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
|
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
|
||||||
new AsciiString("/some/path/resource2"));
|
new AsciiString("/some/path/resource2"));
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 5, http2Headers2, 3, (short) 123, true, 0,
|
clientHandler.encoder().writeHeaders(ctxClient(), 5, http2Headers2, 3, (short) 123, true, 0,
|
||||||
false, newPromiseClient());
|
false);
|
||||||
clientChannel.flush(); // Headers are queued in the flow controller and so flush them.
|
clientChannel.flush(); // Headers are queued in the flow controller and so flush them.
|
||||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true,
|
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true);
|
||||||
newPromiseClient());
|
clientHandler.encoder().writeData(ctxClient(), 5, content2.retainedDuplicate(), 0, true);
|
||||||
clientHandler.encoder().writeData(ctxClient(), 5, content2.retainedDuplicate(), 0, true,
|
|
||||||
newPromiseClient());
|
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitRequests();
|
awaitRequests();
|
||||||
@ -456,7 +452,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(new AsciiString("GET"))
|
final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(new AsciiString("GET"))
|
||||||
.path(new AsciiString("/push/test"));
|
.path(new AsciiString("/push/test"));
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers3, 0, true, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers3, 0, true);
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
awaitRequests();
|
awaitRequests();
|
||||||
@ -475,12 +471,10 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
.scheme(new AsciiString("https"))
|
.scheme(new AsciiString("https"))
|
||||||
.authority(new AsciiString("example.org"));
|
.authority(new AsciiString("example.org"));
|
||||||
runInChannel(serverConnectedChannel, () -> {
|
runInChannel(serverConnectedChannel, () -> {
|
||||||
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2Headers, 0, false, newPromiseServer());
|
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2Headers, 0, false);
|
||||||
serverHandler.encoder().writePushPromise(ctxServer(), 3, 2, http2Headers2, 0, newPromiseServer());
|
serverHandler.encoder().writePushPromise(ctxServer(), 3, 2, http2Headers2, 0);
|
||||||
serverHandler.encoder().writeData(ctxServer(), 3, content.retainedDuplicate(), 0, true,
|
serverHandler.encoder().writeData(ctxServer(), 3, content.retainedDuplicate(), 0, true);
|
||||||
newPromiseServer());
|
serverHandler.encoder().writeData(ctxServer(), 5, content2.retainedDuplicate(), 0, true);
|
||||||
serverHandler.encoder().writeData(ctxServer(), 5, content2.retainedDuplicate(), 0, true,
|
|
||||||
newPromiseServer());
|
|
||||||
serverConnectedChannel.flush();
|
serverConnectedChannel.flush();
|
||||||
});
|
});
|
||||||
awaitResponses();
|
awaitResponses();
|
||||||
@ -518,7 +512,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -528,8 +522,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
|
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
|
||||||
final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(new AsciiString("100"));
|
final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(new AsciiString("100"));
|
||||||
runInChannel(serverConnectedChannel, () -> {
|
runInChannel(serverConnectedChannel, () -> {
|
||||||
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse, 0, false,
|
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse, 0, false);
|
||||||
newPromiseServer());
|
|
||||||
serverConnectedChannel.flush();
|
serverConnectedChannel.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -538,8 +531,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
|
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
|
||||||
httpHeaders.remove(HttpHeaderNames.EXPECT);
|
httpHeaders.remove(HttpHeaderNames.EXPECT);
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeData(ctxClient(), 3, payload.retainedDuplicate(), 0, true,
|
clientHandler.encoder().writeData(ctxClient(), 3, payload.retainedDuplicate(), 0, true);
|
||||||
newPromiseClient());
|
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -551,8 +543,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
|
|
||||||
final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(new AsciiString("200"));
|
final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(new AsciiString("200"));
|
||||||
runInChannel(serverConnectedChannel, () -> {
|
runInChannel(serverConnectedChannel, () -> {
|
||||||
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse2, 0, true,
|
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse2, 0, true);
|
||||||
newPromiseServer());
|
|
||||||
serverConnectedChannel.flush();
|
serverConnectedChannel.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -586,7 +577,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
boostrapEnv(1, 1, 2);
|
boostrapEnv(1, 1, 2);
|
||||||
final Http2Settings settings = new Http2Settings().pushEnabled(true);
|
final Http2Settings settings = new Http2Settings().pushEnabled(true);
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
clientHandler.encoder().writeSettings(ctxClient(), settings, newPromiseClient());
|
clientHandler.encoder().writeSettings(ctxClient(), settings);
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
});
|
});
|
||||||
assertTrue(settingsLatch.await(5, SECONDS));
|
assertTrue(settingsLatch.await(5, SECONDS));
|
||||||
|
@ -106,17 +106,16 @@ public class StreamBufferingEncoderTest {
|
|||||||
when(writer.configuration()).thenReturn(configuration);
|
when(writer.configuration()).thenReturn(configuration);
|
||||||
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
||||||
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
||||||
when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
|
when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()))
|
||||||
any(Promise.class))).thenAnswer(successAnswer());
|
.thenAnswer(successAnswer());
|
||||||
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class))).thenAnswer(
|
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong())).thenAnswer(
|
||||||
successAnswer());
|
successAnswer());
|
||||||
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
|
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)))
|
||||||
any(Promise.class)))
|
|
||||||
.thenAnswer(successAnswer());
|
.thenAnswer(successAnswer());
|
||||||
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
||||||
anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(noopAnswer());
|
anyInt(), anyBoolean())).thenAnswer(noopAnswer());
|
||||||
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
||||||
anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Promise.class)))
|
anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()))
|
||||||
.thenAnswer(noopAnswer());
|
.thenAnswer(noopAnswer());
|
||||||
|
|
||||||
connection = new DefaultHttp2Connection(false);
|
connection = new DefaultHttp2Connection(false);
|
||||||
@ -138,6 +137,13 @@ public class StreamBufferingEncoderTest {
|
|||||||
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(executor.inEventLoop()).thenReturn(true);
|
when(executor.inEventLoop()).thenReturn(true);
|
||||||
doAnswer((Answer<Promise<Void>>) invocation -> newPromise()).when(ctx).newPromise();
|
doAnswer((Answer<Promise<Void>>) invocation -> newPromise()).when(ctx).newPromise();
|
||||||
|
doAnswer((Answer<Future<Void>>) invocation -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null))
|
||||||
|
.when(ctx).newSucceededFuture();
|
||||||
|
|
||||||
|
doAnswer((Answer<Future<Void>>) invocation ->
|
||||||
|
ImmediateEventExecutor.INSTANCE.newFailedFuture(invocation.getArgument(0)))
|
||||||
|
.when(ctx).newFailedFuture(any(Throwable.class));
|
||||||
|
|
||||||
when(ctx.executor()).thenReturn(executor);
|
when(ctx.executor()).thenReturn(executor);
|
||||||
when(channel.isActive()).thenReturn(false);
|
when(channel.isActive()).thenReturn(false);
|
||||||
when(channel.config()).thenReturn(config);
|
when(channel.config()).thenReturn(config);
|
||||||
@ -159,34 +165,34 @@ public class StreamBufferingEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipleWritesToActiveStream() {
|
public void multipleWritesToActiveStream() {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3);
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
ByteBuf data = data();
|
ByteBuf data = data();
|
||||||
final int expectedBytes = data.readableBytes() * 3;
|
final int expectedBytes = data.readableBytes() * 3;
|
||||||
encoder.writeData(ctx, 3, data, 0, false, newPromise());
|
encoder.writeData(ctx, 3, data, 0, false);
|
||||||
encoder.writeData(ctx, 3, data(), 0, false, newPromise());
|
encoder.writeData(ctx, 3, data(), 0, false);
|
||||||
encoder.writeData(ctx, 3, data(), 0, false, newPromise());
|
encoder.writeData(ctx, 3, data(), 0, false);
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3);
|
||||||
|
|
||||||
writeVerifyWriteHeaders(times(1), 3);
|
writeVerifyWriteHeaders(times(1), 3);
|
||||||
// Contiguous data writes are coalesced
|
// Contiguous data writes are coalesced
|
||||||
ArgumentCaptor<ByteBuf> bufCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
ArgumentCaptor<ByteBuf> bufCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||||
verify(writer, times(1)).writeData(any(ChannelHandlerContext.class), eq(3),
|
verify(writer, times(1)).writeData(any(ChannelHandlerContext.class), eq(3),
|
||||||
bufCaptor.capture(), eq(0), eq(false), any(Promise.class));
|
bufCaptor.capture(), eq(0), eq(false));
|
||||||
assertEquals(expectedBytes, bufCaptor.getValue().readableBytes());
|
assertEquals(expectedBytes, bufCaptor.getValue().readableBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ensureCanCreateNextStreamWhenStreamCloses() {
|
public void ensureCanCreateNextStreamWhenStreamCloses() {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(1);
|
setMaxConcurrentStreams(1);
|
||||||
|
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3);
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
|
|
||||||
// This one gets buffered.
|
// This one gets buffered.
|
||||||
encoderWriteHeaders(5, newPromise());
|
encoderWriteHeaders(5);
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
@ -206,45 +212,44 @@ public class StreamBufferingEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void alternatingWritesToActiveAndBufferedStreams() {
|
public void alternatingWritesToActiveAndBufferedStreams() {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(1);
|
setMaxConcurrentStreams(1);
|
||||||
|
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3);
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
|
|
||||||
encoderWriteHeaders(5, newPromise());
|
encoderWriteHeaders(5);
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, false, newPromise());
|
encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, false);
|
||||||
writeVerifyWriteHeaders(times(1), 3);
|
writeVerifyWriteHeaders(times(1), 3);
|
||||||
encoder.writeData(ctx, 5, EMPTY_BUFFER, 0, false, newPromise());
|
encoder.writeData(ctx, 5, EMPTY_BUFFER, 0, false);
|
||||||
verify(writer, never())
|
verify(writer, never())
|
||||||
.writeData(eq(ctx), eq(5), any(ByteBuf.class), eq(0), eq(false), eq(newPromise()));
|
.writeData(eq(ctx), eq(5), any(ByteBuf.class), eq(0), eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bufferingNewStreamFailsAfterGoAwayReceived() throws Http2Exception {
|
public void bufferingNewStreamFailsAfterGoAwayReceived() throws Http2Exception {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(0);
|
setMaxConcurrentStreams(0);
|
||||||
connection.goAwayReceived(1, 8, EMPTY_BUFFER);
|
connection.goAwayReceived(1, 8, EMPTY_BUFFER);
|
||||||
|
|
||||||
Promise<Void> promise = newPromise();
|
Future<Void> future = encoderWriteHeaders(3);
|
||||||
encoderWriteHeaders(3, promise);
|
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
assertTrue(promise.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(promise.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void receivingGoAwayFailsBufferedStreams() throws Http2Exception {
|
public void receivingGoAwayFailsBufferedStreams() throws Http2Exception {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(5);
|
setMaxConcurrentStreams(5);
|
||||||
|
|
||||||
int streamId = 3;
|
int streamId = 3;
|
||||||
List<Future<Void>> futures = new ArrayList<>();
|
List<Future<Void>> futures = new ArrayList<>();
|
||||||
for (int i = 0; i < 9; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
futures.add(encoderWriteHeaders(streamId, newPromise()));
|
futures.add(encoderWriteHeaders(streamId));
|
||||||
streamId += 2;
|
streamId += 2;
|
||||||
}
|
}
|
||||||
assertEquals(5, connection.numActiveStreams());
|
assertEquals(5, connection.numActiveStreams());
|
||||||
@ -266,11 +271,11 @@ public class StreamBufferingEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void receivingGoAwayFailsNewStreamIfMaxConcurrentStreamsReached() throws Http2Exception {
|
public void receivingGoAwayFailsNewStreamIfMaxConcurrentStreamsReached() throws Http2Exception {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(1);
|
setMaxConcurrentStreams(1);
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3);
|
||||||
connection.goAwayReceived(11, 8, EMPTY_BUFFER);
|
connection.goAwayReceived(11, 8, EMPTY_BUFFER);
|
||||||
Future<Void> f = encoderWriteHeaders(5, newPromise());
|
Future<Void> f = encoderWriteHeaders(5);
|
||||||
|
|
||||||
assertTrue(f.awaitUninterruptibly().cause() instanceof Http2GoAwayException);
|
assertTrue(f.awaitUninterruptibly().cause() instanceof Http2GoAwayException);
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
@ -278,40 +283,41 @@ public class StreamBufferingEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sendingGoAwayShouldNotFailStreams() {
|
public void sendingGoAwayShouldNotFailStreams() {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(1);
|
setMaxConcurrentStreams(1);
|
||||||
|
|
||||||
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
||||||
anyBoolean(), any(Promise.class))).thenAnswer(successAnswer());
|
anyBoolean())).thenAnswer(successAnswer());
|
||||||
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
||||||
anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(successAnswer());
|
anyShort(), anyBoolean(), anyInt(), anyBoolean())).thenAnswer(successAnswer());
|
||||||
|
|
||||||
Future<Void> f1 = encoderWriteHeaders(3, newPromise());
|
Future<Void> f1 = encoderWriteHeaders(3);
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
Future<Void> f2 = encoderWriteHeaders(5, newPromise());
|
Future<Void> f2 = encoderWriteHeaders(5);
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
Future<Void> f3 = encoderWriteHeaders(7, newPromise());
|
Future<Void> f3 = encoderWriteHeaders(7);
|
||||||
assertEquals(2, encoder.numBufferedStreams());
|
assertEquals(2, encoder.numBufferedStreams());
|
||||||
|
|
||||||
ByteBuf empty = Unpooled.buffer(0);
|
ByteBuf empty = Unpooled.buffer(0);
|
||||||
encoder.writeGoAway(ctx, 3, CANCEL.code(), empty, newPromise());
|
encoder.writeGoAway(ctx, 3, CANCEL.code(), empty);
|
||||||
|
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
assertEquals(2, encoder.numBufferedStreams());
|
assertEquals(2, encoder.numBufferedStreams());
|
||||||
assertFalse(f1.isDone());
|
// As the first stream did exists the first future should be done.
|
||||||
|
assertTrue(f1.isDone());
|
||||||
assertFalse(f2.isDone());
|
assertFalse(f2.isDone());
|
||||||
assertFalse(f3.isDone());
|
assertFalse(f3.isDone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void endStreamDoesNotFailBufferedStream() {
|
public void endStreamDoesNotFailBufferedStream() {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(0);
|
setMaxConcurrentStreams(0);
|
||||||
|
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3);
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, true, newPromise());
|
encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, true);
|
||||||
|
|
||||||
assertEquals(0, connection.numActiveStreams());
|
assertEquals(0, connection.numActiveStreams());
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
@ -319,7 +325,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
// Simulate that we received a SETTINGS frame which
|
// Simulate that we received a SETTINGS frame which
|
||||||
// increased MAX_CONCURRENT_STREAMS to 1.
|
// increased MAX_CONCURRENT_STREAMS to 1.
|
||||||
setMaxConcurrentStreams(1);
|
setMaxConcurrentStreams(1);
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
|
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
@ -328,61 +334,60 @@ public class StreamBufferingEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void rstStreamClosesBufferedStream() {
|
public void rstStreamClosesBufferedStream() {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(0);
|
setMaxConcurrentStreams(0);
|
||||||
|
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3);
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
Promise<Void> rstStreamPromise = newPromise();
|
Future<Void> rstStreamFuture = encoder.writeRstStream(ctx, 3, CANCEL.code());
|
||||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstStreamPromise);
|
assertTrue(rstStreamFuture.isSuccess());
|
||||||
assertTrue(rstStreamPromise.isSuccess());
|
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bufferUntilActiveStreamsAreReset() throws Exception {
|
public void bufferUntilActiveStreamsAreReset() throws Exception {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(1);
|
setMaxConcurrentStreams(1);
|
||||||
|
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3);
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
encoderWriteHeaders(5, newPromise());
|
encoderWriteHeaders(5);
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
encoderWriteHeaders(7, newPromise());
|
encoderWriteHeaders(7);
|
||||||
assertEquals(2, encoder.numBufferedStreams());
|
assertEquals(2, encoder.numBufferedStreams());
|
||||||
|
|
||||||
writeVerifyWriteHeaders(times(1), 3);
|
writeVerifyWriteHeaders(times(1), 3);
|
||||||
writeVerifyWriteHeaders(never(), 5);
|
writeVerifyWriteHeaders(never(), 5);
|
||||||
writeVerifyWriteHeaders(never(), 7);
|
writeVerifyWriteHeaders(never(), 7);
|
||||||
|
|
||||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), newPromise());
|
encoder.writeRstStream(ctx, 3, CANCEL.code());
|
||||||
connection.remote().flowController().writePendingBytes();
|
connection.remote().flowController().writePendingBytes();
|
||||||
writeVerifyWriteHeaders(times(1), 5);
|
writeVerifyWriteHeaders(times(1), 5);
|
||||||
writeVerifyWriteHeaders(never(), 7);
|
writeVerifyWriteHeaders(never(), 7);
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
encoder.writeRstStream(ctx, 5, CANCEL.code(), newPromise());
|
encoder.writeRstStream(ctx, 5, CANCEL.code());
|
||||||
connection.remote().flowController().writePendingBytes();
|
connection.remote().flowController().writePendingBytes();
|
||||||
writeVerifyWriteHeaders(times(1), 7);
|
writeVerifyWriteHeaders(times(1), 7);
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
|
|
||||||
encoder.writeRstStream(ctx, 7, CANCEL.code(), newPromise());
|
encoder.writeRstStream(ctx, 7, CANCEL.code());
|
||||||
assertEquals(0, connection.numActiveStreams());
|
assertEquals(0, connection.numActiveStreams());
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bufferUntilMaxStreamsIncreased() {
|
public void bufferUntilMaxStreamsIncreased() {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(2);
|
setMaxConcurrentStreams(2);
|
||||||
|
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3);
|
||||||
encoderWriteHeaders(5, newPromise());
|
encoderWriteHeaders(5);
|
||||||
encoderWriteHeaders(7, newPromise());
|
encoderWriteHeaders(7);
|
||||||
encoderWriteHeaders(9, newPromise());
|
encoderWriteHeaders(9);
|
||||||
assertEquals(2, encoder.numBufferedStreams());
|
assertEquals(2, encoder.numBufferedStreams());
|
||||||
|
|
||||||
writeVerifyWriteHeaders(times(1), 3);
|
writeVerifyWriteHeaders(times(1), 3);
|
||||||
@ -393,13 +398,13 @@ public class StreamBufferingEncoderTest {
|
|||||||
// Simulate that we received a SETTINGS frame which
|
// Simulate that we received a SETTINGS frame which
|
||||||
// increased MAX_CONCURRENT_STREAMS to 5.
|
// increased MAX_CONCURRENT_STREAMS to 5.
|
||||||
setMaxConcurrentStreams(5);
|
setMaxConcurrentStreams(5);
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
|
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
writeVerifyWriteHeaders(times(1), 7);
|
writeVerifyWriteHeaders(times(1), 7);
|
||||||
writeVerifyWriteHeaders(times(1), 9);
|
writeVerifyWriteHeaders(times(1), 9);
|
||||||
|
|
||||||
encoderWriteHeaders(11, newPromise());
|
encoderWriteHeaders(11);
|
||||||
|
|
||||||
writeVerifyWriteHeaders(times(1), 11);
|
writeVerifyWriteHeaders(times(1), 11);
|
||||||
|
|
||||||
@ -411,7 +416,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS;
|
int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS;
|
||||||
int numStreams = initialLimit * 2;
|
int numStreams = initialLimit * 2;
|
||||||
for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) {
|
for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) {
|
||||||
encoderWriteHeaders(nextStreamId, newPromise());
|
encoderWriteHeaders(nextStreamId);
|
||||||
if (ix < initialLimit) {
|
if (ix < initialLimit) {
|
||||||
writeVerifyWriteHeaders(times(1), nextStreamId);
|
writeVerifyWriteHeaders(times(1), nextStreamId);
|
||||||
} else {
|
} else {
|
||||||
@ -432,7 +437,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS;
|
int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS;
|
||||||
int numStreams = initialLimit * 2;
|
int numStreams = initialLimit * 2;
|
||||||
for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) {
|
for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) {
|
||||||
encoderWriteHeaders(nextStreamId, newPromise());
|
encoderWriteHeaders(nextStreamId);
|
||||||
if (ix < initialLimit) {
|
if (ix < initialLimit) {
|
||||||
writeVerifyWriteHeaders(times(1), nextStreamId);
|
writeVerifyWriteHeaders(times(1), nextStreamId);
|
||||||
} else {
|
} else {
|
||||||
@ -452,13 +457,13 @@ public class StreamBufferingEncoderTest {
|
|||||||
public void exhaustedStreamsDoNotBuffer() throws Http2Exception {
|
public void exhaustedStreamsDoNotBuffer() throws Http2Exception {
|
||||||
// Write the highest possible stream ID for the client.
|
// Write the highest possible stream ID for the client.
|
||||||
// This will cause the next stream ID to be negative.
|
// This will cause the next stream ID to be negative.
|
||||||
encoderWriteHeaders(Integer.MAX_VALUE, newPromise());
|
encoderWriteHeaders(Integer.MAX_VALUE);
|
||||||
|
|
||||||
// Disallow any further streams.
|
// Disallow any further streams.
|
||||||
setMaxConcurrentStreams(0);
|
setMaxConcurrentStreams(0);
|
||||||
|
|
||||||
// Simulate numeric overflow for the next stream ID.
|
// Simulate numeric overflow for the next stream ID.
|
||||||
Future<Void> f = encoderWriteHeaders(-1, newPromise());
|
Future<Void> f = encoderWriteHeaders(-1);
|
||||||
|
|
||||||
// Verify that the write fails.
|
// Verify that the write fails.
|
||||||
assertNotNull(f.awaitUninterruptibly().cause());
|
assertNotNull(f.awaitUninterruptibly().cause());
|
||||||
@ -466,18 +471,17 @@ public class StreamBufferingEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void closedBufferedStreamReleasesByteBuf() {
|
public void closedBufferedStreamReleasesByteBuf() {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
setMaxConcurrentStreams(0);
|
setMaxConcurrentStreams(0);
|
||||||
ByteBuf data = mock(ByteBuf.class);
|
ByteBuf data = mock(ByteBuf.class);
|
||||||
Future<Void> f1 = encoderWriteHeaders(3, newPromise());
|
Future<Void> f1 = encoderWriteHeaders(3);
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
Future<Void> f2 = encoder.writeData(ctx, 3, data, 0, false, newPromise());
|
Future<Void> f2 = encoder.writeData(ctx, 3, data, 0, false);
|
||||||
|
|
||||||
Promise<Void> rstPromise = mock(Promise.class);
|
Future<Void> rstFuture = encoder.writeRstStream(ctx, 3, CANCEL.code());
|
||||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstPromise);
|
|
||||||
|
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
verify(rstPromise).setSuccess(null);
|
assertTrue(rstFuture.isSuccess());
|
||||||
assertTrue(f1.isSuccess());
|
assertTrue(f1.isSuccess());
|
||||||
assertTrue(f2.isSuccess());
|
assertTrue(f2.isSuccess());
|
||||||
verify(data).release();
|
verify(data).release();
|
||||||
@ -485,12 +489,12 @@ public class StreamBufferingEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void closeShouldCancelAllBufferedStreams() throws Http2Exception {
|
public void closeShouldCancelAllBufferedStreams() throws Http2Exception {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
connection.local().maxActiveStreams(0);
|
connection.local().maxActiveStreams(0);
|
||||||
|
|
||||||
Future<Void> f1 = encoderWriteHeaders(3, newPromise());
|
Future<Void> f1 = encoderWriteHeaders(3);
|
||||||
Future<Void> f2 = encoderWriteHeaders(5, newPromise());
|
Future<Void> f2 = encoderWriteHeaders(5);
|
||||||
Future<Void> f3 = encoderWriteHeaders(7, newPromise());
|
Future<Void> f3 = encoderWriteHeaders(7);
|
||||||
|
|
||||||
encoder.close();
|
encoder.close();
|
||||||
assertNotNull(f1.awaitUninterruptibly().cause());
|
assertNotNull(f1.awaitUninterruptibly().cause());
|
||||||
@ -500,10 +504,10 @@ public class StreamBufferingEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersAfterCloseShouldImmediatelyFail() {
|
public void headersAfterCloseShouldImmediatelyFail() {
|
||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx);
|
||||||
encoder.close();
|
encoder.close();
|
||||||
|
|
||||||
Future<Void> f = encoderWriteHeaders(3, newPromise());
|
Future<Void> f = encoderWriteHeaders(3);
|
||||||
assertNotNull(f.cause());
|
assertNotNull(f.cause());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,12 +521,13 @@ public class StreamBufferingEncoderTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Future<Void> encoderWriteHeaders(int streamId, Promise<Void> promise) {
|
private Future<Void> encoderWriteHeaders(int streamId) {
|
||||||
|
Future<Void> future =
|
||||||
encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT,
|
encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT,
|
||||||
false, 0, false, promise);
|
false, 0, false);
|
||||||
try {
|
try {
|
||||||
encoder.flowController().writePendingBytes();
|
encoder.flowController().writePendingBytes();
|
||||||
return promise;
|
return future;
|
||||||
} catch (Http2Exception e) {
|
} catch (Http2Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@ -531,7 +536,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
private void writeVerifyWriteHeaders(VerificationMode mode, int streamId) {
|
private void writeVerifyWriteHeaders(VerificationMode mode, int streamId) {
|
||||||
verify(writer, mode).writeHeaders(eq(ctx), eq(streamId), any(Http2Headers.class), eq(0),
|
verify(writer, mode).writeHeaders(eq(ctx), eq(streamId), any(Http2Headers.class), eq(0),
|
||||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0),
|
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0),
|
||||||
eq(false), any(Promise.class));
|
eq(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Answer<Future<Void>> successAnswer() {
|
private static Answer<Future<Void>> successAnswer() {
|
||||||
|
@ -88,8 +88,8 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
|
|||||||
private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {
|
private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {
|
||||||
// Send a frame for the response status
|
// Send a frame for the response status
|
||||||
Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
|
Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
|
||||||
encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());
|
encoder().writeHeaders(ctx, streamId, headers, 0, false);
|
||||||
encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise());
|
encoder().writeData(ctx, streamId, payload, 0, true);
|
||||||
|
|
||||||
// no need to call flush as channelReadComplete(...) will take care of it.
|
// no need to call flush as channelReadComplete(...) will take care of it.
|
||||||
}
|
}
|
||||||
|
@ -602,17 +602,8 @@ public class SslHandler extends ByteToMessageDecoder {
|
|||||||
* {@link ChannelHandlerContext#close()}
|
* {@link ChannelHandlerContext#close()}
|
||||||
*/
|
*/
|
||||||
public Future<Void> closeOutbound() {
|
public Future<Void> closeOutbound() {
|
||||||
return closeOutbound(ctx.newPromise());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends an SSL {@code close_notify} message to the specified channel and
|
|
||||||
* destroys the underlying {@link SSLEngine}. This will <strong>not</strong> close the underlying
|
|
||||||
* {@link Channel}. If you want to also close the {@link Channel} use {@link Channel#close()} or
|
|
||||||
* {@link ChannelHandlerContext#close()}
|
|
||||||
*/
|
|
||||||
public Future<Void> closeOutbound(final Promise<Void> promise) {
|
|
||||||
final ChannelHandlerContext ctx = this.ctx;
|
final ChannelHandlerContext ctx = this.ctx;
|
||||||
|
Promise<Void> promise = ctx.newPromise();
|
||||||
if (ctx.executor().inEventLoop()) {
|
if (ctx.executor().inEventLoop()) {
|
||||||
closeOutbound0(promise);
|
closeOutbound0(promise);
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,14 +101,14 @@ public class Http2FrameWriterDataBenchmark extends AbstractMicrobenchmark {
|
|||||||
@Benchmark
|
@Benchmark
|
||||||
@BenchmarkMode(Mode.AverageTime)
|
@BenchmarkMode(Mode.AverageTime)
|
||||||
public void newWriter() {
|
public void newWriter() {
|
||||||
writer.writeData(ctx, 3, payload.retain(), padding, true, ctx.newPromise());
|
writer.writeData(ctx, 3, payload.retain(), padding, true);
|
||||||
ctx.flush();
|
ctx.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
@BenchmarkMode(Mode.AverageTime)
|
@BenchmarkMode(Mode.AverageTime)
|
||||||
public void oldWriter() {
|
public void oldWriter() {
|
||||||
oldWriter.writeData(ctx, 3, payload.retain(), padding, true, ctx.newPromise());
|
oldWriter.writeData(ctx, 3, payload.retain(), padding, true);
|
||||||
ctx.flush();
|
ctx.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,9 +118,9 @@ public class Http2FrameWriterDataBenchmark extends AbstractMicrobenchmark {
|
|||||||
private final int maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
|
private final int maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
|
||||||
@Override
|
@Override
|
||||||
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
||||||
int padding, boolean endStream, Promise<Void> promise) {
|
int padding, boolean endStream) {
|
||||||
final Http2CodecUtil.SimpleChannelPromiseAggregator promiseAggregator =
|
final Http2CodecUtil.SimpleChannelPromiseAggregator promiseAggregator =
|
||||||
new Http2CodecUtil.SimpleChannelPromiseAggregator(promise, ctx.executor());
|
new Http2CodecUtil.SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||||
final DataFrameHeader header = new DataFrameHeader(ctx, streamId);
|
final DataFrameHeader header = new DataFrameHeader(ctx, streamId);
|
||||||
boolean needToReleaseHeaders = true;
|
boolean needToReleaseHeaders = true;
|
||||||
boolean needToReleaseData = true;
|
boolean needToReleaseData = true;
|
||||||
|
@ -88,8 +88,8 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
|
|||||||
private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {
|
private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {
|
||||||
// Send a frame for the response status
|
// Send a frame for the response status
|
||||||
Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
|
Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
|
||||||
encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());
|
encoder().writeHeaders(ctx, streamId, headers, 0, false);
|
||||||
encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise());
|
encoder().writeData(ctx, streamId, payload, 0, true);
|
||||||
|
|
||||||
// no need to call flush as channelReadComplete(...) will take care of it.
|
// no need to call flush as channelReadComplete(...) will take care of it.
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user