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) {
|
||||
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();
|
||||
HttpResponseDecoder decoder = pipeline.get(HttpResponseDecoder.class);
|
||||
if (decoder == null) {
|
||||
HttpClientCodec codec = pipeline.get(HttpClientCodec.class);
|
||||
if (codec == null) {
|
||||
promise.setFailure(new IllegalStateException("ChannelPipeline does not contain " +
|
||||
return channel.newFailedFuture(new IllegalStateException("ChannelPipeline does not contain " +
|
||||
"an HttpResponseDecoder or HttpClientCodec"));
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
FullHttpRequest request = newHandshakeRequest();
|
||||
|
||||
Promise<Void> promise = channel.newPromise();
|
||||
channel.writeAndFlush(request).addListener(channel, (ch, future) -> {
|
||||
if (future.isSuccess()) {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
@ -385,29 +373,12 @@ public abstract class WebSocketClientHandshaker {
|
||||
* the {@link Future} which is notified once the handshake completes.
|
||||
*/
|
||||
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) {
|
||||
try {
|
||||
finishHandshake(channel, (FullHttpResponse) response);
|
||||
promise.setSuccess(null);
|
||||
return channel.newSucceededFuture();
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
return channel.newFailedFuture(cause);
|
||||
}
|
||||
} else {
|
||||
ChannelPipeline p = channel.pipeline();
|
||||
@ -415,10 +386,12 @@ public abstract class WebSocketClientHandshaker {
|
||||
if (ctx == null) {
|
||||
ctx = p.context(HttpClientCodec.class);
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
Promise<Void> promise = channel.newPromise();
|
||||
// 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.
|
||||
//
|
||||
@ -459,8 +432,8 @@ public abstract class WebSocketClientHandshaker {
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,7 +169,7 @@ public abstract class WebSocketServerHandshaker {
|
||||
* The {@link Future} which is notified once the opening handshake completes
|
||||
*/
|
||||
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
|
||||
* @param responseHeaders
|
||||
* 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
|
||||
* the {@link Future} which is notified when the opening handshake is done
|
||||
*/
|
||||
public final Future<Void> handshake(Channel channel, FullHttpRequest req,
|
||||
HttpHeaders responseHeaders, final Promise<Void> promise) {
|
||||
public final Future<Void> handshake(Channel channel, FullHttpRequest req, HttpHeaders responseHeaders) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{} WebSocket version {} server handshake", channel, version());
|
||||
@ -208,9 +205,8 @@ public abstract class WebSocketServerHandshaker {
|
||||
// this means the user use an HttpServerCodec
|
||||
ctx = p.context(HttpServerCodec.class);
|
||||
if (ctx == null) {
|
||||
promise.setFailure(
|
||||
return channel.newFailedFuture(
|
||||
new IllegalStateException("No HttpDecoder and no HttpServerCodec in the pipeline"));
|
||||
return promise;
|
||||
}
|
||||
p.addBefore(ctx.name(), "wsencoder", newWebSocketEncoder());
|
||||
p.addBefore(ctx.name(), "wsdecoder", newWebsocketDecoder());
|
||||
@ -221,16 +217,12 @@ public abstract class WebSocketServerHandshaker {
|
||||
encoderName = p.context(HttpResponseEncoder.class).name();
|
||||
p.addBefore(encoderName, "wsencoder", newWebSocketEncoder());
|
||||
}
|
||||
channel.writeAndFlush(response).addListener(channel, (ch, future) -> {
|
||||
return channel.writeAndFlush(response).addListener(channel, (ch, future) -> {
|
||||
if (future.isSuccess()) {
|
||||
ChannelPipeline p1 = ch.pipeline();
|
||||
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
|
||||
*/
|
||||
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
|
||||
* @param responseHeaders
|
||||
* 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
|
||||
* the {@link Future} which is notified when the opening handshake is done
|
||||
*/
|
||||
public final Future<Void> handshake(final Channel channel, HttpRequest req,
|
||||
final HttpHeaders responseHeaders, final Promise<Void> promise) {
|
||||
final HttpHeaders responseHeaders) {
|
||||
|
||||
if (req instanceof FullHttpRequest) {
|
||||
return handshake(channel, (FullHttpRequest) req, responseHeaders, promise);
|
||||
return handshake(channel, (FullHttpRequest) req, responseHeaders);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("{} WebSocket version {} server handshake", channel, version());
|
||||
@ -279,11 +269,12 @@ public abstract class WebSocketServerHandshaker {
|
||||
// this means the user use an HttpServerCodec
|
||||
ctx = p.context(HttpServerCodec.class);
|
||||
if (ctx == null) {
|
||||
promise.setFailure(
|
||||
return channel.newFailedFuture(
|
||||
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
|
||||
// enough for the websockets handshake payload.
|
||||
//
|
||||
@ -295,7 +286,7 @@ public abstract class WebSocketServerHandshaker {
|
||||
protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
|
||||
// Remove ourself and do the actual handshake
|
||||
ctx.pipeline().remove(this);
|
||||
handshake(channel, msg, responseHeaders, promise);
|
||||
handshake(channel, msg, responseHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -100,7 +100,7 @@ public abstract class WebSocketServerHandshakerTest {
|
||||
"ws://example.com/chat");
|
||||
request.headers().set("x-client-header", "value");
|
||||
try {
|
||||
serverHandshaker.handshake(null, request, null, null);
|
||||
serverHandshaker.handshake(null, request, null);
|
||||
} catch (WebSocketServerHandshakeException exception) {
|
||||
assertNotNull(exception.getMessage());
|
||||
assertEquals(request.headers(), exception.request().headers());
|
||||
|
@ -143,12 +143,12 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
||||
|
||||
@Override
|
||||
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 EmbeddedChannel channel = stream == null ? null : (EmbeddedChannel) stream.getProperty(propertyKey);
|
||||
if (channel == null) {
|
||||
// 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 {
|
||||
@ -161,13 +161,13 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
||||
buf = nextReadableBuf(channel);
|
||||
}
|
||||
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.
|
||||
promise.setSuccess(null);
|
||||
return promise;
|
||||
return ctx.newSucceededFuture();
|
||||
}
|
||||
|
||||
Promise<Void> promise = ctx.newPromise();
|
||||
PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
|
||||
for (;;) {
|
||||
ByteBuf nextBuf = nextReadableBuf(channel);
|
||||
@ -177,9 +177,8 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
||||
compressedEndOfStream = nextBuf == null;
|
||||
}
|
||||
|
||||
Promise<Void> bufPromise = ctx.newPromise();
|
||||
combiner.add(bufPromise);
|
||||
super.writeData(ctx, streamId, buf, padding, compressedEndOfStream, bufPromise);
|
||||
Future<Void> future = super.writeData(ctx, streamId, buf, padding, compressedEndOfStream);
|
||||
combiner.add(future);
|
||||
if (nextBuf == null) {
|
||||
break;
|
||||
}
|
||||
@ -188,56 +187,54 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
||||
buf = nextBuf;
|
||||
}
|
||||
combiner.finish(promise);
|
||||
return promise;
|
||||
} catch (Throwable cause) {
|
||||
promise.tryFailure(cause);
|
||||
return ctx.newFailedFuture(cause);
|
||||
} finally {
|
||||
if (endOfStream) {
|
||||
cleanup(stream, channel);
|
||||
}
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||
boolean endStream, Promise<Void> promise) {
|
||||
boolean endStream) {
|
||||
try {
|
||||
// Determine if compression is required and sanitize the headers.
|
||||
EmbeddedChannel compressor = newCompressor(ctx, headers, endStream);
|
||||
|
||||
// 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.
|
||||
bindCompressorToStream(compressor, streamId);
|
||||
|
||||
return future;
|
||||
} catch (Throwable e) {
|
||||
promise.tryFailure(e);
|
||||
return ctx.newFailedFuture(e);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 boolean endOfStream, final Promise<Void> promise) {
|
||||
final boolean endOfStream) {
|
||||
try {
|
||||
// Determine if compression is required and sanitize the headers.
|
||||
EmbeddedChannel compressor = newCompressor(ctx, headers, endOfStream);
|
||||
|
||||
// Write the headers and create the stream object.
|
||||
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.
|
||||
bindCompressorToStream(compressor, streamId);
|
||||
|
||||
return future;
|
||||
} 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.channel.ChannelHandlerContext;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
@ -35,73 +34,70 @@ public class DecoratingHttp2FrameWriter implements Http2FrameWriter {
|
||||
|
||||
@Override
|
||||
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
||||
boolean endStream, Promise<Void> promise) {
|
||||
return delegate.writeData(ctx, streamId, data, padding, endStream, promise);
|
||||
boolean endStream) {
|
||||
return delegate.writeData(ctx, streamId, data, padding, endStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||
boolean endStream, Promise<Void> promise) {
|
||||
return delegate.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
||||
boolean endStream) {
|
||||
return delegate.writeHeaders(ctx, streamId, headers, padding, endStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||
int streamDependency, short weight, boolean exclusive, int padding,
|
||||
boolean endStream, Promise<Void> promise) {
|
||||
boolean endStream) {
|
||||
return delegate
|
||||
.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream, promise);
|
||||
.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
||||
boolean exclusive, Promise<Void> promise) {
|
||||
return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
||||
boolean exclusive) {
|
||||
return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||
Promise<Void> promise) {
|
||||
return delegate.writeRstStream(ctx, streamId, errorCode, promise);
|
||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||
return delegate.writeRstStream(ctx, streamId, errorCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings, Promise<Void> promise) {
|
||||
return delegate.writeSettings(ctx, settings, promise);
|
||||
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings) {
|
||||
return delegate.writeSettings(ctx, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||
return delegate.writeSettingsAck(ctx, promise);
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||
return delegate.writeSettingsAck(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
||||
return delegate.writePing(ctx, ack, data, promise);
|
||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data) {
|
||||
return delegate.writePing(ctx, ack, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||
Http2Headers headers, int padding, Promise<Void> promise) {
|
||||
return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise);
|
||||
Http2Headers headers, int padding) {
|
||||
return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
|
||||
Promise<Void> promise) {
|
||||
return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) {
|
||||
return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
|
||||
Promise<Void> promise) {
|
||||
return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
|
||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) {
|
||||
return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
||||
ByteBuf payload, Promise<Void> promise) {
|
||||
return delegate.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
||||
ByteBuf payload) {
|
||||
return delegate.writeFrame(ctx, frameType, streamId, flags, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -20,7 +20,6 @@ import io.netty.handler.codec.http2.Http2Stream.State;
|
||||
import io.netty.util.collection.IntObjectHashMap;
|
||||
import io.netty.util.collection.IntObjectMap;
|
||||
import io.netty.util.collection.IntObjectMap.PrimitiveEntry;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.concurrent.UnaryPromiseNotifier;
|
||||
import io.netty.util.internal.EmptyArrays;
|
||||
@ -116,7 +115,7 @@ public class DefaultHttp2Connection implements Http2Connection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> close(final Promise<Void> promise) {
|
||||
public void close(final Promise<Void> promise) {
|
||||
requireNonNull(promise, "promise");
|
||||
// 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.
|
||||
@ -131,7 +130,7 @@ public class DefaultHttp2Connection implements Http2Connection {
|
||||
}
|
||||
if (isStreamMapEmpty()) {
|
||||
promise.trySuccess(null);
|
||||
return promise;
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<PrimitiveEntry<Http2Stream>> itr = streamMap.entries().iterator();
|
||||
@ -162,7 +161,6 @@ public class DefaultHttp2Connection implements Http2Connection {
|
||||
}
|
||||
}
|
||||
}
|
||||
return closePromise;
|
||||
}
|
||||
|
||||
@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
|
||||
// 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.
|
||||
encoder.writeSettingsAck(ctx, ctx.newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
|
||||
encoder.remoteSettings(settings);
|
||||
} else {
|
||||
@ -512,7 +512,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder {
|
||||
public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
|
||||
if (autoAckPing) {
|
||||
// Send an ack back to the remote client.
|
||||
encoder.writePing(ctx, true, data, ctx.newPromise());
|
||||
encoder.writePing(ctx, true, data);
|
||||
}
|
||||
listener.onPingRead(ctx, data);
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
|
||||
@Override
|
||||
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;
|
||||
try {
|
||||
stream = requireStream(streamId);
|
||||
@ -136,9 +136,10 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
data.release();
|
||||
return promise.setFailure(e);
|
||||
return ctx.newFailedFuture(e);
|
||||
}
|
||||
|
||||
Promise<Void> promise = ctx.newPromise();
|
||||
// Hand control of the frame to the flow controller.
|
||||
flowController().addFlowControlled(stream,
|
||||
new FlowControlledData(stream, data, padding, endOfStream, promise, ctx.channel()));
|
||||
@ -147,8 +148,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
|
||||
@Override
|
||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||
boolean endStream, Promise<Void> promise) {
|
||||
return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream, promise);
|
||||
boolean endStream) {
|
||||
return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream);
|
||||
}
|
||||
|
||||
private static boolean validateHeadersSentState(Http2Stream stream, Http2Headers headers, boolean isServer,
|
||||
@ -163,10 +164,9 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
@Override
|
||||
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 boolean endOfStream,
|
||||
Promise<Void> promise) {
|
||||
final boolean exclusive, final int padding, final boolean endOfStream) {
|
||||
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,
|
||||
int streamDependency, final short weight,
|
||||
boolean exclusive, final int padding,
|
||||
boolean endOfStream, Promise<Void> promise) {
|
||||
boolean endOfStream) {
|
||||
if (hasPriority) {
|
||||
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,
|
||||
final Http2Headers headers, final boolean hasPriority,
|
||||
final int streamDependency, final short weight,
|
||||
final boolean exclusive, final int padding,
|
||||
final boolean endOfStream, Promise<Void> promise) {
|
||||
final boolean endOfStream) {
|
||||
try {
|
||||
Http2Stream stream = connection.stream(streamId);
|
||||
if (stream == null) {
|
||||
@ -202,8 +202,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
stream = connection.local().createStream(streamId, /*endOfStream*/ false);
|
||||
} catch (Http2Exception cause) {
|
||||
if (connection.remote().mayHaveCreatedStream(streamId)) {
|
||||
promise.tryFailure(new IllegalStateException("Stream no longer exists: " + streamId, cause));
|
||||
return promise;
|
||||
return ctx.newFailedFuture(
|
||||
new IllegalStateException("Stream no longer exists: " + streamId, cause));
|
||||
}
|
||||
throw cause;
|
||||
}
|
||||
@ -231,7 +231,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream);
|
||||
|
||||
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.
|
||||
|
||||
@ -261,6 +261,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
|
||||
return future;
|
||||
} else {
|
||||
Promise<Void> promise = ctx.newPromise();
|
||||
// Pass headers to the flow-controller so it can maintain their sequence relative to DATA frames.
|
||||
flowController.addFlowControlled(stream,
|
||||
new FlowControlledHeaders(stream, headers, hasPriority, streamDependency,
|
||||
@ -269,27 +270,24 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
lifecycleManager.onError(ctx, true, t);
|
||||
promise.tryFailure(t);
|
||||
return promise;
|
||||
return ctx.newFailedFuture(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
||||
boolean exclusive, Promise<Void> promise) {
|
||||
return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
||||
boolean exclusive) {
|
||||
return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||
Promise<Void> promise) {
|
||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||
// 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
|
||||
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
||||
Promise<Void> promise) {
|
||||
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings) {
|
||||
outstandingLocalSettingsQueue.add(settings);
|
||||
try {
|
||||
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");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
return promise.setFailure(e);
|
||||
return ctx.newFailedFuture(e);
|
||||
}
|
||||
|
||||
return frameWriter.writeSettings(ctx, settings, promise);
|
||||
return frameWriter.writeSettings(ctx, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||
if (outstandingRemoteSettingsQueue == null) {
|
||||
return frameWriter.writeSettingsAck(ctx, promise);
|
||||
return frameWriter.writeSettingsAck(ctx);
|
||||
}
|
||||
Http2Settings settings = outstandingRemoteSettingsQueue.poll();
|
||||
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"));
|
||||
}
|
||||
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
|
||||
// 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.
|
||||
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
|
||||
// account independently.
|
||||
@ -333,13 +332,13 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
||||
return frameWriter.writePing(ctx, ack, data, promise);
|
||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data) {
|
||||
return frameWriter.writePing(ctx, ack, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||
Http2Headers headers, int padding, Promise<Void> promise) {
|
||||
Http2Headers headers, int padding) {
|
||||
try {
|
||||
if (connection.goAwayReceived()) {
|
||||
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.
|
||||
connection.local().reservePushStream(promisedStreamId, stream);
|
||||
|
||||
Future<Void> future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding,
|
||||
promise);
|
||||
Future<Void> future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding);
|
||||
// Writing headers may fail during the encode state if they violate HPACK limits.
|
||||
if (future.isSuccess() || !future.isDone()) {
|
||||
// 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;
|
||||
} catch (Throwable t) {
|
||||
lifecycleManager.onError(ctx, true, t);
|
||||
promise.tryFailure(t);
|
||||
return promise;
|
||||
return ctx.newFailedFuture(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
|
||||
Promise<Void> promise) {
|
||||
return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData, promise);
|
||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) {
|
||||
return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
|
||||
Promise<Void> promise) {
|
||||
return promise.setFailure(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" +
|
||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) {
|
||||
return ctx.newFailedFuture(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" +
|
||||
" objects to control window sizes"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
||||
ByteBuf payload, Promise<Void> promise) {
|
||||
return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
||||
ByteBuf payload) {
|
||||
return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -424,7 +419,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
@Override
|
||||
public void consumeReceivedSettings(Http2Settings settings) {
|
||||
if (outstandingRemoteSettingsQueue == null) {
|
||||
outstandingRemoteSettingsQueue = new ArrayDeque<Http2Settings>(2);
|
||||
outstandingRemoteSettingsQueue = new ArrayDeque<>(2);
|
||||
}
|
||||
outstandingRemoteSettingsQueue.add(settings);
|
||||
}
|
||||
@ -508,7 +503,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
|
||||
// Write the frame(s).
|
||||
frameWriter().writeData(ctx, stream.id(), toWrite, writablePadding,
|
||||
endOfStream && size() == 0, writePromise);
|
||||
endOfStream && size() == 0).cascadeTo(writePromise);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -580,7 +575,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
promise.addListener(this);
|
||||
|
||||
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.
|
||||
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
|
||||
@ -621,7 +617,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationComplete(Future<? extends Void> future) throws Exception {
|
||||
public void operationComplete(Future<? extends Void> future) {
|
||||
if (future.isFailed()) {
|
||||
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.Http2HeadersEncoder.SensitivityDetector;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import static io.netty.buffer.Unpooled.directBuffer;
|
||||
@ -133,9 +132,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
||||
|
||||
@Override
|
||||
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
||||
int padding, boolean endStream, Promise<Void> promise) {
|
||||
int padding, boolean endStream) {
|
||||
final SimpleChannelPromiseAggregator promiseAggregator =
|
||||
new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||
ByteBuf frameHeader = null;
|
||||
try {
|
||||
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
|
||||
// necessary above because we internally allocate frameHeader.
|
||||
try {
|
||||
if (data != null) {
|
||||
if (data != null &&
|
||||
// Check if the data was released already.
|
||||
data.refCnt() > 0) {
|
||||
data.release();
|
||||
}
|
||||
} finally {
|
||||
@ -256,22 +257,22 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
||||
|
||||
@Override
|
||||
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,
|
||||
false, 0, (short) 0, false, promise);
|
||||
false, 0, (short) 0, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||
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,
|
||||
true, streamDependency, weight, exclusive, promise);
|
||||
true, streamDependency, weight, exclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId,
|
||||
int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
|
||||
int streamDependency, short weight, boolean exclusive) {
|
||||
try {
|
||||
verifyStreamId(streamId, STREAM_ID);
|
||||
verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY);
|
||||
@ -282,15 +283,14 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
||||
buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency);
|
||||
// Adjust the weight so that it fits into a single byte on the wire.
|
||||
buf.writeByte(weight - 1);
|
||||
return ctx.write(buf).cascadeTo(promise);
|
||||
return ctx.write(buf);
|
||||
} catch (Throwable t) {
|
||||
return promise.setFailure(t);
|
||||
return ctx.newFailedFuture(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||
Promise<Void> promise) {
|
||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||
try {
|
||||
verifyStreamId(streamId, STREAM_ID);
|
||||
verifyErrorCode(errorCode);
|
||||
@ -298,15 +298,14 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
||||
ByteBuf buf = ctx.alloc().buffer(RST_STREAM_FRAME_LENGTH);
|
||||
writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, RST_STREAM, new Http2Flags(), streamId);
|
||||
buf.writeInt((int) errorCode);
|
||||
return ctx.write(buf).cascadeTo(promise);
|
||||
return ctx.write(buf);
|
||||
} catch (Throwable t) {
|
||||
return promise.setFailure(t);
|
||||
return ctx.newFailedFuture(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
||||
Promise<Void> promise) {
|
||||
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings) {
|
||||
try {
|
||||
requireNonNull(settings, "settings");
|
||||
int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
|
||||
@ -316,39 +315,40 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
||||
buf.writeChar(entry.key());
|
||||
buf.writeInt(entry.value().intValue());
|
||||
}
|
||||
return ctx.write(buf).cascadeTo(promise);
|
||||
return ctx.write(buf);
|
||||
} catch (Throwable t) {
|
||||
return promise.setFailure(t);
|
||||
return ctx.newFailedFuture(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||
try {
|
||||
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
||||
writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0);
|
||||
return ctx.write(buf).cascadeTo(promise);
|
||||
return ctx.write(buf);
|
||||
} catch (Throwable t) {
|
||||
return promise.setFailure(t);
|
||||
return ctx.newFailedFuture(t);
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
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
|
||||
// in the catch block.
|
||||
writeFrameHeaderInternal(buf, PING_FRAME_PAYLOAD_LENGTH, PING, flags, 0);
|
||||
buf.writeLong(data);
|
||||
return ctx.write(buf).cascadeTo(promise);
|
||||
return ctx.write(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||
Http2Headers headers, int padding, Promise<Void> promise) {
|
||||
Http2Headers headers, int padding) {
|
||||
ByteBuf headerBlock = null;
|
||||
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||
SimpleChannelPromiseAggregator promiseAggregator =
|
||||
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||
try {
|
||||
verifyStreamId(streamId, STREAM_ID);
|
||||
verifyStreamId(promisedStreamId, "Promised Stream ID");
|
||||
@ -403,8 +403,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
||||
|
||||
@Override
|
||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||
ByteBuf debugData, Promise<Void> promise) {
|
||||
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||
ByteBuf debugData) {
|
||||
SimpleChannelPromiseAggregator promiseAggregator =
|
||||
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||
try {
|
||||
verifyStreamOrConnectionId(lastStreamId, "Last Stream ID");
|
||||
verifyErrorCode(errorCode);
|
||||
@ -437,7 +438,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
||||
|
||||
@Override
|
||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
|
||||
int windowSizeIncrement, Promise<Void> promise) {
|
||||
int windowSizeIncrement) {
|
||||
try {
|
||||
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
||||
verifyWindowSizeIncrement(windowSizeIncrement);
|
||||
@ -445,16 +446,17 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
||||
ByteBuf buf = ctx.alloc().buffer(WINDOW_UPDATE_FRAME_LENGTH);
|
||||
writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, WINDOW_UPDATE, new Http2Flags(), streamId);
|
||||
buf.writeInt(windowSizeIncrement);
|
||||
return ctx.write(buf).cascadeTo(promise);
|
||||
return ctx.write(buf);
|
||||
} catch (Throwable t) {
|
||||
return promise.setFailure(t);
|
||||
return ctx.newFailedFuture(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||
Http2Flags flags, ByteBuf payload, Promise<Void> promise) {
|
||||
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||
Http2Flags flags, ByteBuf payload) {
|
||||
SimpleChannelPromiseAggregator promiseAggregator =
|
||||
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||
try {
|
||||
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
||||
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
||||
@ -481,9 +483,10 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
||||
|
||||
private Future<Void> writeHeadersInternal(ChannelHandlerContext ctx,
|
||||
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;
|
||||
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||
SimpleChannelPromiseAggregator promiseAggregator =
|
||||
new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||
try {
|
||||
verifyStreamId(streamId, STREAM_ID);
|
||||
if (hasPriority) {
|
||||
|
@ -483,7 +483,7 @@ public class DefaultHttp2LocalFlowController implements Http2LocalFlowController
|
||||
}
|
||||
|
||||
// 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.
|
||||
* <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.
|
||||
*
|
||||
* @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}.
|
||||
|
@ -61,9 +61,8 @@ public interface Http2ConnectionEncoder extends Http2FrameWriter {
|
||||
/**
|
||||
* Writes the given data to the internal {@link Http2FrameWriter} without performing any
|
||||
* state checks on the connection/stream.
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
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.
|
||||
encoder.writeSettings(ctx, initialSettings, ctx.newPromise())
|
||||
encoder.writeSettings(ctx, initialSettings)
|
||||
.addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
|
||||
|
||||
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
|
||||
// flushed to the OS.
|
||||
// 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();
|
||||
Promise<Void> promise = ctx.newPromise();
|
||||
doGracefulShutdown(ctx, f, promise);
|
||||
@ -626,7 +626,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
||||
}
|
||||
|
||||
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) {
|
||||
doGracefulShutdown(ctx, future, promise);
|
||||
} else {
|
||||
@ -663,7 +663,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
||||
try {
|
||||
stream = encoder.connection().remote().createStream(streamId, true);
|
||||
} catch (Http2Exception e) {
|
||||
resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
|
||||
resetUnknownStream(ctx, streamId, http2Ex.error().code());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -680,10 +680,10 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
||||
|
||||
if (stream == null) {
|
||||
if (!outbound || connection().local().mayHaveCreatedStream(streamId)) {
|
||||
resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise());
|
||||
resetUnknownStream(ctx, streamId, http2Ex.error().code());
|
||||
}
|
||||
} 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
|
||||
*/
|
||||
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() {
|
||||
@ -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
|
||||
* we could create a new stream.
|
||||
*/
|
||||
private Future<Void> resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||
Promise<Void> promise) {
|
||||
Future<Void> future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise);
|
||||
private Future<Void> resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||
Future<Void> future = frameWriter().writeRstStream(ctx, streamId, errorCode);
|
||||
if (future.isDone()) {
|
||||
closeConnectionOnError(ctx, future);
|
||||
} else {
|
||||
@ -719,21 +718,20 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||
Promise<Void> promise) {
|
||||
public Future<Void> resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||
final Http2Stream stream = connection().stream(streamId);
|
||||
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,
|
||||
long errorCode, Promise<Void> promise) {
|
||||
long errorCode) {
|
||||
if (stream.isResetSent()) {
|
||||
// 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
|
||||
// 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.
|
||||
if (stream.state() == IDLE ||
|
||||
connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) {
|
||||
future = promise.setSuccess(null);
|
||||
future = ctx.newSucceededFuture();
|
||||
} else {
|
||||
future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise);
|
||||
future = frameWriter().writeRstStream(ctx, stream.id(), errorCode);
|
||||
}
|
||||
if (future.isDone()) {
|
||||
processRstStreamWriteResult(ctx, stream, future);
|
||||
@ -762,24 +760,22 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
||||
|
||||
@Override
|
||||
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();
|
||||
try {
|
||||
if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) {
|
||||
debugData.release();
|
||||
promise.trySuccess(null);
|
||||
return promise;
|
||||
return ctx.newSucceededFuture();
|
||||
}
|
||||
} catch (Throwable cause) {
|
||||
debugData.release();
|
||||
promise.tryFailure(cause);
|
||||
return promise;
|
||||
return ctx.newFailedFuture(cause);
|
||||
}
|
||||
|
||||
// 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.
|
||||
debugData.retain();
|
||||
Future<Void> future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
||||
Future<Void> future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData);
|
||||
|
||||
if (future.isDone()) {
|
||||
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
|
||||
* 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();
|
||||
int lastKnownStream;
|
||||
if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) {
|
||||
@ -827,7 +823,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
||||
} else {
|
||||
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")
|
||||
|
@ -17,7 +17,6 @@ package io.netty.handler.codec.http2;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.FutureListener;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.internal.ObjectUtil;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
@ -49,38 +48,41 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||
Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
|
||||
if (newPromise == null) {
|
||||
return promise;
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||
FutureListener<Void> listener = handleOutstandingControlFrames(ctx);
|
||||
Future<Void> f = super.writeSettingsAck(ctx);
|
||||
if (listener != null) {
|
||||
f.addListener(listener);
|
||||
}
|
||||
return super.writeSettingsAck(ctx, newPromise);
|
||||
return f;
|
||||
}
|
||||
|
||||
@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.
|
||||
if (ack) {
|
||||
Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
|
||||
if (newPromise == null) {
|
||||
return promise;
|
||||
FutureListener<Void> listener = handleOutstandingControlFrames(ctx);
|
||||
Future<Void> f = super.writePing(ctx, ack, data);
|
||||
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
|
||||
public Future<Void> writeRstStream(
|
||||
ChannelHandlerContext ctx, int streamId, long errorCode, Promise<Void> promise) {
|
||||
Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
|
||||
if (newPromise == null) {
|
||||
return promise;
|
||||
ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||
FutureListener<Void> listener = handleOutstandingControlFrames(ctx);
|
||||
Future<Void> f = super.writeRstStream(ctx, streamId, errorCode);
|
||||
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 (outstandingControlFrames == maxOutstandingControlFrames) {
|
||||
// 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.
|
||||
lifecycleManager.onError(ctx, true, exception);
|
||||
ctx.close();
|
||||
return null;
|
||||
}
|
||||
outstandingControlFrames++;
|
||||
|
||||
// We did not reach the limit yet, add the listener to decrement the number of outstanding control frames
|
||||
// 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
|
||||
* appended to the end of the frame.
|
||||
* @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.
|
||||
*/
|
||||
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
|
||||
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
|
||||
Promise<Void> promise = ctx.newPromise();
|
||||
if (msg instanceof Http2DataFrame) {
|
||||
Http2DataFrame dataFrame = (Http2DataFrame) msg;
|
||||
encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(),
|
||||
dataFrame.padding(), dataFrame.isEndStream(), promise);
|
||||
return encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(),
|
||||
dataFrame.padding(), dataFrame.isEndStream());
|
||||
} else if (msg instanceof Http2HeadersFrame) {
|
||||
writeHeadersFrame(ctx, (Http2HeadersFrame) msg, promise);
|
||||
return writeHeadersFrame(ctx, (Http2HeadersFrame) msg);
|
||||
} else if (msg instanceof Http2WindowUpdateFrame) {
|
||||
Http2WindowUpdateFrame frame = (Http2WindowUpdateFrame) msg;
|
||||
Http2FrameStream frameStream = frame.stream();
|
||||
@ -300,9 +299,9 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
||||
} else {
|
||||
consumeBytes(frameStream.id(), frame.windowSizeIncrement());
|
||||
}
|
||||
promise.setSuccess(null);
|
||||
return ctx.newSucceededFuture();
|
||||
} catch (Throwable t) {
|
||||
promise.setFailure(t);
|
||||
return ctx.newFailedFuture(t);
|
||||
}
|
||||
} else if (msg instanceof Http2ResetFrame) {
|
||||
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
|
||||
// stream in an invalid state and cause a connection error.
|
||||
if (connection().streamMayHaveExisted(id)) {
|
||||
encoder().writeRstStream(ctx, rstFrame.stream().id(), rstFrame.errorCode(), promise);
|
||||
return encoder().writeRstStream(ctx, rstFrame.stream().id(), rstFrame.errorCode());
|
||||
} else {
|
||||
ReferenceCountUtil.release(rstFrame);
|
||||
promise.setFailure(Http2Exception.streamError(
|
||||
return ctx.newFailedFuture(Http2Exception.streamError(
|
||||
rstFrame.stream().id(), Http2Error.PROTOCOL_ERROR, "Stream never existed"));
|
||||
}
|
||||
} else if (msg instanceof Http2PingFrame) {
|
||||
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) {
|
||||
encoder().writeSettings(ctx, ((Http2SettingsFrame) msg).settings(), promise);
|
||||
return encoder().writeSettings(ctx, ((Http2SettingsFrame) msg).settings());
|
||||
} else if (msg instanceof Http2SettingsAckFrame) {
|
||||
// In the event of manual SETTINGS ACK, it is assumed the encoder will apply the earliest received but not
|
||||
// yet ACKed settings.
|
||||
encoder().writeSettingsAck(ctx, promise);
|
||||
return encoder().writeSettingsAck(ctx);
|
||||
} else if (msg instanceof Http2GoAwayFrame) {
|
||||
writeGoAwayFrame(ctx, (Http2GoAwayFrame) msg, promise);
|
||||
return writeGoAwayFrame(ctx, (Http2GoAwayFrame) msg);
|
||||
} else if (msg instanceof Http2PushPromiseFrame) {
|
||||
Http2PushPromiseFrame pushPromiseFrame = (Http2PushPromiseFrame) msg;
|
||||
writePushPromise(ctx, pushPromiseFrame, promise);
|
||||
return writePushPromise(ctx, pushPromiseFrame);
|
||||
} else if (msg instanceof Http2PriorityFrame) {
|
||||
Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg;
|
||||
encoder().writePriority(ctx, priorityFrame.stream().id(), priorityFrame.streamDependency(),
|
||||
priorityFrame.weight(), priorityFrame.exclusive(), promise);
|
||||
return encoder().writePriority(ctx, priorityFrame.stream().id(), priorityFrame.streamDependency(),
|
||||
priorityFrame.weight(), priorityFrame.exclusive());
|
||||
} else if (msg instanceof Http2UnknownFrame) {
|
||||
Http2UnknownFrame unknownFrame = (Http2UnknownFrame) msg;
|
||||
encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(),
|
||||
unknownFrame.flags(), unknownFrame.content(), promise);
|
||||
return encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(),
|
||||
unknownFrame.flags(), unknownFrame.content());
|
||||
} else if (!(msg instanceof Http2Frame)) {
|
||||
ctx.write(msg).cascadeTo(promise);
|
||||
return ctx.write(msg);
|
||||
} else {
|
||||
ReferenceCountUtil.release(msg);
|
||||
promise.setFailure(new UnsupportedMessageTypeException(msg));
|
||||
return ctx.newFailedFuture(new UnsupportedMessageTypeException(msg));
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
private void increaseInitialConnectionWindow(int deltaBytes) throws Http2Exception {
|
||||
@ -366,10 +364,10 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
||||
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) {
|
||||
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();
|
||||
@ -378,66 +376,72 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
||||
if (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,
|
||||
final Promise<Void> promise) {
|
||||
private Future<Void> writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame) {
|
||||
|
||||
if (isStreamIdValid(headersFrame.stream().id())) {
|
||||
encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(),
|
||||
headersFrame.isEndStream(), promise);
|
||||
} else if (initializeNewStream(ctx, (DefaultHttp2FrameStream) headersFrame.stream(), promise)) {
|
||||
final int streamId = headersFrame.stream().id();
|
||||
return encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(),
|
||||
headersFrame.padding(), headersFrame.isEndStream());
|
||||
} else {
|
||||
Future<Void> future = initializeNewStream(ctx, (DefaultHttp2FrameStream) headersFrame.stream());
|
||||
if (future == null) {
|
||||
final int streamId = headersFrame.stream().id();
|
||||
|
||||
encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(),
|
||||
headersFrame.isEndStream(), promise);
|
||||
future = encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(),
|
||||
headersFrame.isEndStream());
|
||||
|
||||
if (!promise.isDone()) {
|
||||
numBufferedStreams++;
|
||||
// Clean up the stream being initialized if writing the headers fails and also
|
||||
// decrement the number of buffered streams.
|
||||
promise.addListener(channelFuture -> {
|
||||
numBufferedStreams--;
|
||||
if (!future.isDone()) {
|
||||
numBufferedStreams++;
|
||||
// Clean up the stream being initialized if writing the headers fails and also
|
||||
// decrement the number of buffered streams.
|
||||
future.addListener(channelFuture -> {
|
||||
numBufferedStreams--;
|
||||
|
||||
handleHeaderFuture(channelFuture, streamId);
|
||||
});
|
||||
} else {
|
||||
handleHeaderFuture(promise, streamId);
|
||||
handleHeaderFuture(channelFuture, streamId);
|
||||
});
|
||||
} else {
|
||||
handleHeaderFuture(future, streamId);
|
||||
}
|
||||
}
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
private void writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame,
|
||||
final Promise<Void> promise) {
|
||||
private Future<Void> writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame) {
|
||||
if (isStreamIdValid(pushPromiseFrame.pushStream().id())) {
|
||||
encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(),
|
||||
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise);
|
||||
} else if (initializeNewStream(ctx, (DefaultHttp2FrameStream) pushPromiseFrame.pushStream(), promise)) {
|
||||
final int streamId = pushPromiseFrame.stream().id();
|
||||
encoder().writePushPromise(ctx, streamId, pushPromiseFrame.pushStream().id(),
|
||||
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise);
|
||||
return encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(),
|
||||
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding());
|
||||
} else {
|
||||
Future<Void> future = initializeNewStream(ctx, (DefaultHttp2FrameStream) pushPromiseFrame.pushStream());
|
||||
if (future == null) {
|
||||
final int streamId = pushPromiseFrame.stream().id();
|
||||
future = encoder().writePushPromise(ctx, streamId, pushPromiseFrame.pushStream().id(),
|
||||
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding());
|
||||
|
||||
if (promise.isDone()) {
|
||||
handleHeaderFuture(promise, streamId);
|
||||
} else {
|
||||
numBufferedStreams++;
|
||||
// Clean up the stream being initialized if writing the headers fails and also
|
||||
// decrement the number of buffered streams.
|
||||
promise.addListener(f -> {
|
||||
numBufferedStreams--;
|
||||
handleHeaderFuture(f, streamId);
|
||||
});
|
||||
if (future.isDone()) {
|
||||
handleHeaderFuture(future, streamId);
|
||||
} else {
|
||||
numBufferedStreams++;
|
||||
// Clean up the stream being initialized if writing the headers fails and also
|
||||
// decrement the number of buffered streams.
|
||||
future.addListener(f -> {
|
||||
numBufferedStreams--;
|
||||
handleHeaderFuture(f, streamId);
|
||||
});
|
||||
}
|
||||
return future;
|
||||
}
|
||||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream,
|
||||
Promise<Void> promise) {
|
||||
private Future<Void> initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream) {
|
||||
final Http2Connection connection = connection();
|
||||
final int streamId = connection.local().incrementAndGetNextStreamId();
|
||||
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
|
||||
// valid stream ID for the current peer.
|
||||
@ -445,7 +449,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
||||
Integer.MAX_VALUE - 1, NO_ERROR.code(),
|
||||
writeAscii(ctx.alloc(), "Stream IDs exhausted on local stream creation")));
|
||||
|
||||
return false;
|
||||
return f;
|
||||
}
|
||||
http2FrameStream.id = streamId;
|
||||
|
||||
@ -458,7 +462,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
||||
|
||||
// We should not re-use ids.
|
||||
assert old == null;
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
|
||||
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.channel.ChannelHandlerContext;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
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
|
||||
* 256 (inclusive).
|
||||
* @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.
|
||||
* <a href="https://tools.ietf.org/html/rfc7540#section-10.5.1">Section 10.5.1</a> states the following:
|
||||
* <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.
|
||||
*/
|
||||
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.
|
||||
@ -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
|
||||
* 256 (inclusive).
|
||||
* @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.
|
||||
* <a href="https://tools.ietf.org/html/rfc7540#section-10.5.1">Section 10.5.1</a> states the following:
|
||||
* <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.
|
||||
*/
|
||||
Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream,
|
||||
Promise<Void> promise);
|
||||
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream);
|
||||
|
||||
/**
|
||||
* Writes a PRIORITY frame to the remote endpoint.
|
||||
@ -103,11 +99,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
||||
* depend on the connection.
|
||||
* @param weight the weight for this stream.
|
||||
* @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.
|
||||
*/
|
||||
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.
|
||||
@ -115,31 +110,26 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
||||
* @param ctx the context to use for writing.
|
||||
* @param streamId the stream for which to send the frame.
|
||||
* @param errorCode the error code indicating the nature of the failure.
|
||||
* @param promise the promise for the write.
|
||||
* @return the future for the write.
|
||||
*/
|
||||
Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||
Promise<Void> promise);
|
||||
Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode);
|
||||
|
||||
/**
|
||||
* Writes a SETTINGS frame to the remote endpoint.
|
||||
*
|
||||
* @param ctx the context to use for writing.
|
||||
* @param settings the settings to be sent.
|
||||
* @param promise the promise for the write.
|
||||
* @return the future for the write.
|
||||
*/
|
||||
Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
||||
Promise<Void> promise);
|
||||
Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings);
|
||||
|
||||
/**
|
||||
* Writes a SETTINGS acknowledgment to the remote endpoint.
|
||||
*
|
||||
* @param ctx the context to use for writing.
|
||||
* @param promise the promise 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.
|
||||
@ -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
|
||||
* remote endpoint.
|
||||
* @param data the payload of the frame.
|
||||
* @param promise the promise for the write.
|
||||
* @return the future for the write.
|
||||
*/
|
||||
Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data,
|
||||
Promise<Void> promise);
|
||||
Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data);
|
||||
|
||||
/**
|
||||
* 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 padding additional bytes that should be added to obscure the true content size. Must be between 0 and
|
||||
* 256 (inclusive).
|
||||
* @param promise the promise 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:
|
||||
* <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.
|
||||
*/
|
||||
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.
|
||||
@ -183,11 +170,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
||||
* @param lastStreamId the last known stream of this endpoint.
|
||||
* @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 promise the promise for the write.
|
||||
* @return the future for the write.
|
||||
*/
|
||||
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.
|
||||
@ -196,11 +182,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
||||
* @param streamId the stream for which to send the frame.
|
||||
* @param windowSizeIncrement the number of bytes by which the local inbound flow control window
|
||||
* is increasing.
|
||||
* @param promise the promise for the write.
|
||||
* @return the future for the write.
|
||||
*/
|
||||
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.
|
||||
@ -210,11 +195,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
||||
* @param streamId the stream for which to send the 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 promise the promise for the write.
|
||||
* @return the future for the write.
|
||||
*/
|
||||
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}
|
||||
|
@ -59,13 +59,11 @@ public interface Http2LifecycleManager {
|
||||
* @param ctx The context used for communication and buffer allocation if necessary.
|
||||
* @param streamId The identifier of the stream to reset.
|
||||
* @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
|
||||
* {@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.
|
||||
*/
|
||||
Future<Void> resetStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||
Promise<Void> promise);
|
||||
Future<Void> resetStream(ChannelHandlerContext ctx, int streamId, long errorCode);
|
||||
|
||||
/**
|
||||
* 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 errorCode The rational as to why the connection is being closed. See {@link Http2Error}.
|
||||
* @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
|
||||
* {@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.
|
||||
*/
|
||||
Future<Void> goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||
ByteBuf debugData, Promise<Void> promise);
|
||||
ByteBuf debugData);
|
||||
|
||||
/**
|
||||
* Processes the given error.
|
||||
|
@ -18,7 +18,6 @@ package io.netty.handler.codec.http2;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import static io.netty.handler.codec.http2.Http2FrameLogger.Direction.OUTBOUND;
|
||||
@ -40,93 +39,92 @@ public class Http2OutboundFrameLogger implements Http2FrameWriter {
|
||||
|
||||
@Override
|
||||
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);
|
||||
return writer.writeData(ctx, streamId, data, padding, endStream, promise);
|
||||
return writer.writeData(ctx, streamId, data, padding, endStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
return writer.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
||||
return writer.writeHeaders(ctx, streamId, headers, padding, endStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||
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,
|
||||
padding, endStream);
|
||||
return writer.writeHeaders(ctx, streamId, headers, streamDependency, weight,
|
||||
exclusive, padding, endStream, promise);
|
||||
exclusive, padding, endStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
return writer.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
||||
return writer.writePriority(ctx, streamId, streamDependency, weight, exclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx,
|
||||
int streamId, long errorCode, Promise<Void> promise) {
|
||||
int streamId, long errorCode) {
|
||||
logger.logRstStream(OUTBOUND, ctx, streamId, errorCode);
|
||||
return writer.writeRstStream(ctx, streamId, errorCode, promise);
|
||||
return writer.writeRstStream(ctx, streamId, errorCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeSettings(ChannelHandlerContext ctx,
|
||||
Http2Settings settings, Promise<Void> promise) {
|
||||
Http2Settings settings) {
|
||||
logger.logSettings(OUTBOUND, ctx, settings);
|
||||
return writer.writeSettings(ctx, settings, promise);
|
||||
return writer.writeSettings(ctx, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx) {
|
||||
logger.logSettingsAck(OUTBOUND, ctx);
|
||||
return writer.writeSettingsAck(ctx, promise);
|
||||
return writer.writeSettingsAck(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack,
|
||||
long data, Promise<Void> promise) {
|
||||
long data) {
|
||||
if (ack) {
|
||||
logger.logPingAck(OUTBOUND, ctx, data);
|
||||
} else {
|
||||
logger.logPing(OUTBOUND, ctx, data);
|
||||
}
|
||||
return writer.writePing(ctx, ack, data, promise);
|
||||
return writer.writePing(ctx, ack, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId,
|
||||
int promisedStreamId, Http2Headers headers, int padding,
|
||||
Promise<Void> promise) {
|
||||
int promisedStreamId, Http2Headers headers, int 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
|
||||
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||
ByteBuf debugData, Promise<Void> promise) {
|
||||
ByteBuf debugData) {
|
||||
logger.logGoAway(OUTBOUND, ctx, lastStreamId, errorCode, debugData);
|
||||
return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
||||
return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx,
|
||||
int streamId, int windowSizeIncrement, Promise<Void> promise) {
|
||||
int streamId, int windowSizeIncrement) {
|
||||
logger.logWindowsUpdate(OUTBOUND, ctx, streamId, windowSizeIncrement);
|
||||
return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
|
||||
return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
return writer.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
||||
return writer.writeFrame(ctx, frameType, streamId, flags, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,7 +106,7 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
|
||||
Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders);
|
||||
endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable();
|
||||
writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers,
|
||||
endStream, promiseAggregator);
|
||||
endStream).cascadeTo(promiseAggregator.newPromise());
|
||||
}
|
||||
|
||||
if (!endStream && msg instanceof HttpContent) {
|
||||
@ -125,12 +125,14 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
|
||||
// Write the data
|
||||
final ByteBuf content = ((HttpContent) msg).content();
|
||||
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;
|
||||
|
||||
if (!trailers.isEmpty()) {
|
||||
// Write trailing headers.
|
||||
writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator);
|
||||
writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true)
|
||||
.cascadeTo(promiseAggregator.newPromise());
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
@ -145,14 +147,13 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
|
||||
return promise;
|
||||
}
|
||||
|
||||
private static void writeHeaders(ChannelHandlerContext ctx, Http2ConnectionEncoder encoder, int streamId,
|
||||
HttpHeaders headers, Http2Headers http2Headers, boolean endStream,
|
||||
SimpleChannelPromiseAggregator promiseAggregator) {
|
||||
private static Future<Void> writeHeaders(ChannelHandlerContext ctx, Http2ConnectionEncoder encoder, int streamId,
|
||||
HttpHeaders headers, Http2Headers http2Headers, boolean endStream) {
|
||||
int dependencyId = headers.getInt(
|
||||
HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 0);
|
||||
short weight = headers.getShort(
|
||||
HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT);
|
||||
encoder.writeHeaders(ctx, streamId, http2Headers, dependencyId, weight, false,
|
||||
0, endStream, promiseAggregator.newPromise());
|
||||
return encoder.writeHeaders(ctx, streamId, http2Headers, dependencyId, weight, false,
|
||||
0, endStream);
|
||||
}
|
||||
}
|
||||
|
@ -154,40 +154,41 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
||||
|
||||
@Override
|
||||
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,
|
||||
false, padding, endStream, promise);
|
||||
false, padding, endStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||
int streamDependency, short weight, boolean exclusive,
|
||||
int padding, boolean endOfStream, Promise<Void> promise) {
|
||||
int padding, boolean endOfStream) {
|
||||
if (closed) {
|
||||
return promise.setFailure(new Http2ChannelClosedException());
|
||||
return ctx.newFailedFuture(new Http2ChannelClosedException());
|
||||
}
|
||||
if (isExistingStream(streamId) || canCreateStream()) {
|
||||
return super.writeHeaders(ctx, streamId, headers, streamDependency, weight,
|
||||
exclusive, padding, endOfStream, promise);
|
||||
exclusive, padding, endOfStream);
|
||||
}
|
||||
if (goAwayDetail != null) {
|
||||
return promise.setFailure(new Http2GoAwayException(goAwayDetail));
|
||||
return ctx.newFailedFuture(new Http2GoAwayException(goAwayDetail));
|
||||
}
|
||||
PendingStream pendingStream = pendingStreams.get(streamId);
|
||||
if (pendingStream == null) {
|
||||
pendingStream = new PendingStream(ctx, streamId);
|
||||
pendingStreams.put(streamId, pendingStream);
|
||||
}
|
||||
Promise<Void> promise = ctx.newPromise();
|
||||
|
||||
pendingStream.frames.add(new HeadersFrame(headers, streamDependency, weight, exclusive,
|
||||
padding, endOfStream, promise));
|
||||
return promise;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||
Promise<Void> promise) {
|
||||
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) {
|
||||
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
|
||||
// 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
|
||||
// error handling routines.
|
||||
stream.close(null);
|
||||
promise.setSuccess(null);
|
||||
return ctx.newSucceededFuture();
|
||||
} 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
|
||||
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
||||
int padding, boolean endOfStream, Promise<Void> promise) {
|
||||
int padding, boolean endOfStream) {
|
||||
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);
|
||||
if (pendingStream != null) {
|
||||
Promise<Void> promise = ctx.newPromise();
|
||||
pendingStream.frames.add(new DataFrame(data, padding, endOfStream, promise));
|
||||
return promise;
|
||||
} else {
|
||||
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
|
||||
@ -352,7 +353,8 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
||||
|
||||
@Override
|
||||
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
|
||||
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);
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, true, newPromiseClient());
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, true);
|
||||
clientHandler.flush(ctxClient());
|
||||
});
|
||||
awaitServer();
|
||||
@ -157,8 +157,8 @@ public class DataCompressionHttp2Test {
|
||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||
clientHandler.flush(ctxClient());
|
||||
});
|
||||
awaitServer();
|
||||
@ -178,8 +178,8 @@ public class DataCompressionHttp2Test {
|
||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||
clientHandler.flush(ctxClient());
|
||||
});
|
||||
awaitServer();
|
||||
@ -201,9 +201,9 @@ public class DataCompressionHttp2Test {
|
||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
||||
clientEncoder.writeData(ctxClient(), 3, data1.retain(), 0, false, newPromiseClient());
|
||||
clientEncoder.writeData(ctxClient(), 3, data2.retain(), 0, true, newPromiseClient());
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||
clientEncoder.writeData(ctxClient(), 3, data1.retain(), 0, false);
|
||||
clientEncoder.writeData(ctxClient(), 3, data2.retain(), 0, true);
|
||||
clientHandler.flush(ctxClient());
|
||||
});
|
||||
awaitServer();
|
||||
@ -224,8 +224,8 @@ public class DataCompressionHttp2Test {
|
||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR);
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||
clientHandler.flush(ctxClient());
|
||||
});
|
||||
awaitServer();
|
||||
@ -245,8 +245,8 @@ public class DataCompressionHttp2Test {
|
||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR);
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||
clientHandler.flush(ctxClient());
|
||||
});
|
||||
awaitServer();
|
||||
@ -266,8 +266,8 @@ public class DataCompressionHttp2Test {
|
||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD);
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||
clientHandler.flush(ctxClient());
|
||||
});
|
||||
awaitServer();
|
||||
@ -287,8 +287,8 @@ public class DataCompressionHttp2Test {
|
||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD);
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||
clientHandler.flush(ctxClient());
|
||||
});
|
||||
awaitServer();
|
||||
@ -310,8 +310,8 @@ public class DataCompressionHttp2Test {
|
||||
.set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.DEFLATE);
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
|
||||
clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
|
||||
clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
|
||||
clientHandler.flush(ctxClient());
|
||||
});
|
||||
awaitServer();
|
||||
|
@ -79,7 +79,6 @@ public class DefaultHttp2ConnectionDecoderTest {
|
||||
private static final int STATE_RECV_TRAILERS = 1 << 1;
|
||||
|
||||
private Http2ConnectionDecoder decoder;
|
||||
private Promise<Void> promise;
|
||||
|
||||
@Mock
|
||||
private Http2Connection connection;
|
||||
@ -130,7 +129,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
||||
public void setup() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||
Promise<Void> promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||
|
||||
final AtomicInteger headersReceivedState = new AtomicInteger();
|
||||
when(channel.isActive()).thenReturn(true);
|
||||
@ -209,7 +208,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
||||
decode().onSettingsRead(ctx, new Http2Settings());
|
||||
verify(listener).onSettingsRead(eq(ctx), eq(new Http2Settings()));
|
||||
assertTrue(decoder.prefaceReceived());
|
||||
verify(encoder).writeSettingsAck(eq(ctx), eq(promise));
|
||||
verify(encoder).writeSettingsAck(eq(ctx));
|
||||
|
||||
// Simulate receiving the SETTINGS ACK for the initial settings.
|
||||
decode().onSettingsAckRead(ctx);
|
||||
@ -793,7 +792,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
||||
@Test
|
||||
public void pingReadShouldReplyWithAck() throws Exception {
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.DefaultChannelConfig;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
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.ImmediateEventExecutor;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
@ -126,25 +125,23 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
when(channel.unsafe()).thenReturn(unsafe);
|
||||
ChannelConfig config = new DefaultChannelConfig(channel);
|
||||
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(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy);
|
||||
when(frameSizePolicy.maxFrameSize()).thenReturn(64);
|
||||
doAnswer((Answer<Future<Void>>) in -> ((Promise<Void>) in.getArguments()[2])
|
||||
.setSuccess(null)).when(writer).writeSettings(eq(ctx), any(Http2Settings.class), any(Promise.class));
|
||||
doAnswer((Answer<Future<Void>>) in -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null))
|
||||
.when(writer).writeSettings(eq(ctx), any(Http2Settings.class));
|
||||
doAnswer((Answer<Future<Void>>) in -> {
|
||||
((ByteBuf) in.getArguments()[3]).release();
|
||||
return ((Promise<Void>) in.getArguments()[4]).setSuccess(null);
|
||||
}).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class), any(Promise.class));
|
||||
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||
}).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class));
|
||||
|
||||
writtenData = new ArrayList<>();
|
||||
writtenPadding = new ArrayList<>();
|
||||
when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
|
||||
any(Promise.class))).then((Answer<Future<Void>>) in -> {
|
||||
when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()))
|
||||
.then((Answer<Future<Void>>) in -> {
|
||||
// Make sure we only receive stream closure on the last frame and that void promises
|
||||
// are used for all writes except the last one.
|
||||
Promise<Void> promise = (Promise<Void>) in.getArguments()[5];
|
||||
if (streamClosed) {
|
||||
fail("Stream already closed");
|
||||
} else {
|
||||
@ -156,39 +153,41 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
// Release the buffer just as DefaultHttp2FrameWriter does
|
||||
data.release();
|
||||
// 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(),
|
||||
anyInt(), anyBoolean(), any(Promise.class)))
|
||||
anyInt(), anyBoolean()))
|
||||
.then((Answer<Future<Void>>) invocationOnMock -> {
|
||||
Promise<Void> promise = invocationOnMock.getArgument(8);
|
||||
if (streamClosed) {
|
||||
fail("Stream already closed");
|
||||
} else {
|
||||
streamClosed = (Boolean) invocationOnMock.getArguments()[5];
|
||||
}
|
||||
return promise.setSuccess(null);
|
||||
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||
});
|
||||
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class),
|
||||
anyInt(), anyBoolean(), any(Promise.class)))
|
||||
anyInt(), anyBoolean()))
|
||||
.then((Answer<Future<Void>>) invocationOnMock -> {
|
||||
Promise<Void> promise = invocationOnMock.getArgument(5);
|
||||
if (streamClosed) {
|
||||
fail("Stream already closed");
|
||||
} else {
|
||||
streamClosed = invocationOnMock.getArgument(4);
|
||||
}
|
||||
return promise.setSuccess(null);
|
||||
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||
});
|
||||
payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class);
|
||||
doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture());
|
||||
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||
when(ctx.channel()).thenReturn(channel);
|
||||
doAnswer((Answer<Promise<Void>>) in -> newPromise()).when(ctx).newPromise();
|
||||
doAnswer((Answer<Future<Void>>) in -> newSucceededFuture()).when(ctx).newSucceededFuture();
|
||||
doAnswer((Answer<Promise<Void>>) in -> ImmediateEventExecutor.INSTANCE.newPromise()).when(ctx).newPromise();
|
||||
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(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.
|
||||
connection = new DefaultHttp2Connection(true);
|
||||
connection.remote().flowController(remoteFlow);
|
||||
@ -210,8 +209,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
private void dataWriteShouldSignalThatFrameWasConsumedOnError0(boolean endOfStream) throws Exception {
|
||||
createStream(STREAM_ID, false);
|
||||
final ByteBuf data = dummyData();
|
||||
Promise<Void> p = newPromise();
|
||||
encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream, p);
|
||||
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream);
|
||||
|
||||
FlowControlled controlled = payloadCaptor.getValue();
|
||||
assertEquals(8, controlled.size());
|
||||
@ -224,21 +222,20 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
assertEquals(0, controlled.size());
|
||||
assertEquals("abcd", writtenData.get(0));
|
||||
assertEquals(0, data.refCnt());
|
||||
assertSame(error, p.cause());
|
||||
assertSame(error, f.cause());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataWriteShouldSucceed() throws Exception {
|
||||
createStream(STREAM_ID, false);
|
||||
final ByteBuf data = dummyData();
|
||||
Promise<Void> p = newPromise();
|
||||
encoder.writeData(ctx, STREAM_ID, data, 0, true, p);
|
||||
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 0, true);
|
||||
assertEquals(8, payloadCaptor.getValue().size());
|
||||
payloadCaptor.getValue().write(ctx, 8);
|
||||
assertEquals(0, payloadCaptor.getValue().size());
|
||||
assertEquals("abcdefgh", writtenData.get(0));
|
||||
assertEquals(0, data.refCnt());
|
||||
assertTrue(p.isSuccess());
|
||||
assertTrue(f.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -246,35 +243,33 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
createStream(STREAM_ID, false);
|
||||
final ByteBuf data = dummyData().retain();
|
||||
|
||||
Promise<Void> promise1 = newPromise();
|
||||
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise1);
|
||||
Promise<Void> promise2 = newPromise();
|
||||
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise2);
|
||||
Future<Void> future1 = encoder.writeData(ctx, STREAM_ID, data, 0, true);
|
||||
Future<Void> future2 = encoder.writeData(ctx, STREAM_ID, data, 0, true);
|
||||
|
||||
// Now merge the two payloads.
|
||||
List<FlowControlled> capturedWrites = payloadCaptor.getAllValues();
|
||||
FlowControlled mergedPayload = capturedWrites.get(0);
|
||||
mergedPayload.merge(ctx, capturedWrites.get(1));
|
||||
assertEquals(16, mergedPayload.size());
|
||||
assertFalse(promise1.isDone());
|
||||
assertFalse(promise2.isDone());
|
||||
assertFalse(future1.isDone());
|
||||
assertFalse(future2.isDone());
|
||||
|
||||
// Write the merged payloads and verify it was written correctly.
|
||||
mergedPayload.write(ctx, 16);
|
||||
assertEquals(0, mergedPayload.size());
|
||||
assertEquals("abcdefghabcdefgh", writtenData.get(0));
|
||||
assertEquals(0, data.refCnt());
|
||||
assertTrue(promise1.isSuccess());
|
||||
assertTrue(promise2.isSuccess());
|
||||
assertTrue(future1.isSuccess());
|
||||
assertTrue(future2.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataFramesDontMergeWithHeaders() throws Exception {
|
||||
createStream(STREAM_ID, false);
|
||||
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);
|
||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, newPromise());
|
||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||
List<FlowControlled> capturedWrites = payloadCaptor.getAllValues();
|
||||
assertFalse(capturedWrites.get(0).merge(ctx, capturedWrites.get(1)));
|
||||
}
|
||||
@ -289,8 +284,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
private void assertSplitPaddingOnEmptyBuffer(ByteBuf data) throws Exception {
|
||||
createStream(STREAM_ID, false);
|
||||
when(frameSizePolicy.maxFrameSize()).thenReturn(5);
|
||||
Promise<Void> p = newPromise();
|
||||
encoder.writeData(ctx, STREAM_ID, data, 10, true, p);
|
||||
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 10, true);
|
||||
assertEquals(10, payloadCaptor.getValue().size());
|
||||
payloadCaptor.getValue().write(ctx, 10);
|
||||
// writer was called 2 times
|
||||
@ -298,18 +292,17 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
assertEquals("", writtenData.get(0));
|
||||
assertEquals(10, (int) writtenPadding.get(0));
|
||||
assertEquals(0, data.refCnt());
|
||||
assertTrue(p.isSuccess());
|
||||
assertTrue(f.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWriteForUnknownStreamShouldCreateStream() throws Exception {
|
||||
writeAllFlowControlledFrames();
|
||||
final int streamId = 6;
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||
Future<Void> f = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
|
||||
eq(false), eq(promise));
|
||||
assertTrue(promise.isSuccess());
|
||||
eq(false));
|
||||
assertTrue(f.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -318,46 +311,41 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
Http2Stream parent = createStream(STREAM_ID, false);
|
||||
reservePushStream(PUSH_STREAM_ID, parent);
|
||||
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state());
|
||||
verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
||||
eq(0), eq(false), eq(promise));
|
||||
eq(0), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trailersDoNotEndStreamThrows() {
|
||||
writeAllFlowControlledFrames();
|
||||
final int streamId = 6;
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
|
||||
Promise<Void> promise2 = newPromise();
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
assertTrue(future.isDone());
|
||||
assertFalse(future.isSuccess());
|
||||
|
||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||
eq(0), eq(false), eq(promise));
|
||||
eq(0), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trailersDoNotEndStreamWithDataThrows() {
|
||||
writeAllFlowControlledFrames();
|
||||
final int streamId = 6;
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
|
||||
Http2Stream stream = connection.stream(streamId);
|
||||
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
||||
|
||||
Promise<Void> promise2 = newPromise();
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
assertTrue(future.isDone());
|
||||
assertFalse(future.isSuccess());
|
||||
|
||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||
eq(0), eq(false), eq(promise));
|
||||
eq(0), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -373,20 +361,17 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
private void tooManyHeadersThrows(boolean eos) {
|
||||
writeAllFlowControlledFrames();
|
||||
final int streamId = 6;
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||
Promise<Void> promise2 = newPromise();
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||
|
||||
Promise<Void> promise3 = newPromise();
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
|
||||
assertTrue(future.isDone());
|
||||
assertFalse(future.isSuccess());
|
||||
|
||||
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),
|
||||
eq(0), eq(true), eq(promise2));
|
||||
eq(0), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -414,23 +399,21 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
final int streamId = 6;
|
||||
Http2Headers infoHeaders = informationalHeaders();
|
||||
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, promise2);
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
|
||||
Promise<Void> promise3 = newPromise();
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
|
||||
assertTrue(future.isDone());
|
||||
assertEquals(eos, future.isSuccess());
|
||||
|
||||
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),
|
||||
eq(0), eq(false), eq(promise2));
|
||||
eq(0), eq(false));
|
||||
if (eos) {
|
||||
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) {
|
||||
writeAllFlowControlledFrames();
|
||||
final int streamId = 6;
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
|
||||
Http2Stream stream = connection.stream(streamId);
|
||||
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
||||
|
||||
Promise<Void> promise2 = newPromise();
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||
|
||||
Promise<Void> promise3 = newPromise();
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
|
||||
assertTrue(future.isDone());
|
||||
assertFalse(future.isSuccess());
|
||||
|
||||
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),
|
||||
eq(0), eq(true), eq(promise2));
|
||||
eq(0), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -498,27 +478,25 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
final int streamId = 6;
|
||||
Http2Headers infoHeaders = informationalHeaders();
|
||||
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);
|
||||
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
||||
|
||||
Promise<Void> promise2 = newPromise();
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
|
||||
Promise<Void> promise3 = newPromise();
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
||||
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
|
||||
assertTrue(future.isDone());
|
||||
assertEquals(eos, future.isSuccess());
|
||||
|
||||
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),
|
||||
eq(0), eq(false), eq(promise2));
|
||||
eq(0), eq(false));
|
||||
if (eos) {
|
||||
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 {
|
||||
createStream(STREAM_ID, false);
|
||||
goAwayReceived(0);
|
||||
Future<Void> future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0,
|
||||
newPromise());
|
||||
Future<Void> future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0);
|
||||
assertTrue(future.isDone());
|
||||
assertFalse(future.isSuccess());
|
||||
}
|
||||
@ -535,42 +512,38 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
@Test
|
||||
public void pushPromiseWriteShouldReserveStream() throws Exception {
|
||||
createStream(STREAM_ID, false);
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, promise);
|
||||
encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0);
|
||||
assertEquals(RESERVED_LOCAL, stream(PUSH_STREAM_ID).state());
|
||||
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
|
||||
public void priorityWriteAfterGoAwayShouldSucceed() throws Exception {
|
||||
createStream(STREAM_ID, false);
|
||||
goAwayReceived(Integer.MAX_VALUE);
|
||||
Promise<Void> promise = newPromise();
|
||||
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), eq(promise));
|
||||
encoder.writePriority(ctx, STREAM_ID, 0, (short) 255, true);
|
||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void priorityWriteShouldSetPriorityForStream() throws Exception {
|
||||
Promise<Void> promise = newPromise();
|
||||
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.
|
||||
Http2Stream stream = stream(STREAM_ID);
|
||||
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
|
||||
public void priorityWriteOnPreviouslyExistingStreamShouldSucceed() throws Exception {
|
||||
createStream(STREAM_ID, false).close();
|
||||
Promise<Void> promise = newPromise();
|
||||
short weight = 255;
|
||||
encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
|
||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true), eq(promise));
|
||||
encoder.writePriority(ctx, STREAM_ID, 0, weight, true);
|
||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -579,53 +552,47 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
createStream(STREAM_ID, false);
|
||||
createStream(parentStreamId, false).close();
|
||||
|
||||
Promise<Void> promise = newPromise();
|
||||
short weight = 255;
|
||||
encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true, promise);
|
||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true), eq(promise));
|
||||
encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true);
|
||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rstStreamWriteForUnknownStreamShouldIgnore() throws Exception {
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code(), promise);
|
||||
verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong(), eq(promise));
|
||||
encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code());
|
||||
verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rstStreamShouldCloseStream() throws Exception {
|
||||
// Create the stream and send headers.
|
||||
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.
|
||||
stream(STREAM_ID);
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
||||
verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong(), eq(promise));
|
||||
encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
|
||||
verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pingWriteAfterGoAwayShouldSucceed() throws Exception {
|
||||
Promise<Void> promise = newPromise();
|
||||
goAwayReceived(0);
|
||||
encoder.writePing(ctx, false, 0L, promise);
|
||||
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
|
||||
encoder.writePing(ctx, false, 0L);
|
||||
verify(writer).writePing(eq(ctx), eq(false), eq(0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pingWriteShouldSucceed() throws Exception {
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writePing(ctx, false, 0L, promise);
|
||||
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
|
||||
encoder.writePing(ctx, false, 0L);
|
||||
verify(writer).writePing(eq(ctx), eq(false), eq(0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingsWriteAfterGoAwayShouldSucceed() throws Exception {
|
||||
goAwayReceived(0);
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeSettings(ctx, new Http2Settings(), promise);
|
||||
verify(writer).writeSettings(eq(ctx), any(Http2Settings.class), eq(promise));
|
||||
encoder.writeSettings(ctx, new Http2Settings());
|
||||
verify(writer).writeSettings(eq(ctx), any(Http2Settings.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -635,9 +602,8 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
settings.maxConcurrentStreams(1000);
|
||||
settings.headerTableSize(2000);
|
||||
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeSettings(ctx, settings, promise);
|
||||
verify(writer).writeSettings(eq(ctx), eq(settings), eq(promise));
|
||||
encoder.writeSettings(ctx, settings);
|
||||
verify(writer).writeSettings(eq(ctx), eq(settings));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -646,11 +612,10 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
|
||||
Http2Stream stream = createStream(STREAM_ID, false);
|
||||
ByteBuf data = dummyData();
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true, promise);
|
||||
assertTrue(promise.isSuccess());
|
||||
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true);
|
||||
assertTrue(f.isSuccess());
|
||||
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));
|
||||
data.release();
|
||||
}
|
||||
@ -659,11 +624,10 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
public void headersWriteShouldHalfCloseStream() throws Exception {
|
||||
writeAllFlowControlledFrames();
|
||||
createStream(STREAM_ID, false);
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
||||
Future<Void> f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||
|
||||
assertTrue(promise.isSuccess());
|
||||
verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise));
|
||||
assertTrue(f.isSuccess());
|
||||
verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(f));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -671,64 +635,54 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
writeAllFlowControlledFrames();
|
||||
Http2Stream parent = createStream(STREAM_ID, false);
|
||||
Http2Stream stream = reservePushStream(PUSH_STREAM_ID, parent);
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
||||
Future<Void> f = encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
|
||||
assertEquals(HALF_CLOSED_REMOTE, stream.state());
|
||||
assertTrue(promise.isSuccess());
|
||||
verify(lifecycleManager).closeStreamLocal(eq(stream), eq(promise));
|
||||
assertTrue(f.isSuccess());
|
||||
verify(lifecycleManager).closeStreamLocal(eq(stream), eq(f));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception {
|
||||
final Promise<Void> promise = newPromise();
|
||||
final Throwable ex = new RuntimeException();
|
||||
// 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)))
|
||||
.thenAnswer((Answer<Future<Void>>) invocation -> {
|
||||
promise.setFailure(ex);
|
||||
return promise;
|
||||
});
|
||||
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true)))
|
||||
.thenReturn(ImmediateEventExecutor.INSTANCE.newFailedFuture(ex));
|
||||
|
||||
writeAllFlowControlledFrames();
|
||||
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());
|
||||
assertFalse(promise.isSuccess());
|
||||
assertTrue(f.isDone());
|
||||
assertFalse(f.isSuccess());
|
||||
assertFalse(stream.isHeadersSent());
|
||||
InOrder inOrder = inOrder(lifecycleManager);
|
||||
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
|
||||
public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception {
|
||||
final Promise<Void> promise = newPromise();
|
||||
final Throwable ex = new RuntimeException();
|
||||
// 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)))
|
||||
.thenAnswer((Answer<Future<Void>>) invocation -> {
|
||||
promise.setFailure(ex);
|
||||
return promise;
|
||||
});
|
||||
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true)))
|
||||
.thenReturn(ImmediateEventExecutor.INSTANCE.newFailedFuture(ex));
|
||||
|
||||
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());
|
||||
assertFalse(promise.isSuccess());
|
||||
assertTrue(f.isDone());
|
||||
assertFalse(f.isSuccess());
|
||||
assertFalse(stream(STREAM_ID).isHeadersSent());
|
||||
InOrder inOrder = inOrder(lifecycleManager);
|
||||
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
|
||||
public void encoderDelegatesGoAwayToLifeCycleManager() {
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null, promise);
|
||||
encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null);
|
||||
verify(lifecycleManager).goAway(eq(ctx), eq(STREAM_ID), eq(Http2Error.INTERNAL_ERROR.code()),
|
||||
eq((ByteBuf) null), eq(promise));
|
||||
eq((ByteBuf) null));
|
||||
verifyNoMoreInteractions(writer);
|
||||
}
|
||||
|
||||
@ -736,11 +690,10 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
public void dataWriteToClosedStreamShouldFail() throws Exception {
|
||||
createStream(STREAM_ID, false).close();
|
||||
ByteBuf data = mock(ByteBuf.class);
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
||||
assertTrue(promise.isDone());
|
||||
assertFalse(promise.isSuccess());
|
||||
assertThat(promise.cause(), instanceOf(IllegalArgumentException.class));
|
||||
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 0, false);
|
||||
assertTrue(f.isDone());
|
||||
assertFalse(f.isSuccess());
|
||||
assertThat(f.cause(), instanceOf(IllegalArgumentException.class));
|
||||
verify(data).release();
|
||||
}
|
||||
|
||||
@ -748,11 +701,10 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
public void dataWriteToHalfClosedLocalStreamShouldFail() throws Exception {
|
||||
createStream(STREAM_ID, true);
|
||||
ByteBuf data = mock(ByteBuf.class);
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
||||
assertTrue(promise.isDone());
|
||||
assertFalse(promise.isSuccess());
|
||||
assertThat(promise.cause(), instanceOf(IllegalStateException.class));
|
||||
Future<Void> f = encoder.writeData(ctx, STREAM_ID, data, 0, false);
|
||||
assertTrue(f.isDone());
|
||||
assertFalse(f.isSuccess());
|
||||
assertThat(f.cause(), instanceOf(IllegalStateException.class));
|
||||
verify(data).release();
|
||||
}
|
||||
|
||||
@ -761,7 +713,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
Http2Stream stream = createStream(STREAM_ID, false);
|
||||
connection.goAwaySent(0, 0, EMPTY_BUFFER);
|
||||
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));
|
||||
}
|
||||
|
||||
@ -770,10 +722,9 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
writeAllFlowControlledFrames();
|
||||
createStream(STREAM_ID, false);
|
||||
goAwaySent(0);
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
||||
eq(0), eq(false), eq(promise));
|
||||
eq(0), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -781,7 +732,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
Http2Stream stream = createStream(STREAM_ID, false);
|
||||
goAwayReceived(STREAM_ID);
|
||||
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));
|
||||
}
|
||||
|
||||
@ -789,31 +740,28 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception {
|
||||
writeAllFlowControlledFrames();
|
||||
goAwayReceived(STREAM_ID);
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
||||
eq(0), eq(false), eq(promise));
|
||||
eq(0), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWithNoPriority() {
|
||||
writeAllFlowControlledFrames();
|
||||
final int streamId = 6;
|
||||
Promise<Void> promise = newPromise();
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
|
||||
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||
eq(0), eq(false), eq(promise));
|
||||
eq(0), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWithPriority() {
|
||||
writeAllFlowControlledFrames();
|
||||
final int streamId = 6;
|
||||
Promise<Void> promise = newPromise();
|
||||
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),
|
||||
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() {
|
||||
@ -845,14 +793,6 @@ public class DefaultHttp2ConnectionEncoderTest {
|
||||
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() {
|
||||
// The buffer is purposely 8 bytes so it will even work for a ping frame.
|
||||
return wrappedBuffer("abcdefgh".getBytes(UTF_8));
|
||||
|
@ -22,8 +22,6 @@ import io.netty.channel.MultithreadEventLoopGroup;
|
||||
import io.netty.channel.local.LocalHandler;
|
||||
import io.netty.handler.codec.http2.Http2Connection.Endpoint;
|
||||
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 org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@ -157,10 +155,9 @@ public class DefaultHttp2ConnectionTest {
|
||||
final Promise<Void> promise = group.next().newPromise();
|
||||
final CountDownLatch latch = new CountDownLatch(client.numActiveStreams());
|
||||
client.forEachActiveStream(stream -> {
|
||||
client.close(promise).addListener((FutureListener<Void>) future -> {
|
||||
assertTrue(promise.isDone());
|
||||
client.close(promise.addListener(future -> {
|
||||
latch.countDown();
|
||||
});
|
||||
}));
|
||||
return true;
|
||||
});
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
@ -186,10 +183,9 @@ public class DefaultHttp2ConnectionTest {
|
||||
return true;
|
||||
});
|
||||
} catch (Http2Exception ignored) {
|
||||
client.close(promise).addListener((FutureListener<Void>) future -> {
|
||||
assertTrue(promise.isDone());
|
||||
client.close(promise.addListener(future -> {
|
||||
latch.countDown();
|
||||
});
|
||||
}));
|
||||
}
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
@ -636,10 +632,9 @@ public class DefaultHttp2ConnectionTest {
|
||||
private void testRemoveAllStreams() throws InterruptedException {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final Promise<Void> promise = group.next().newPromise();
|
||||
client.close(promise).addListener((FutureListener<Void>) future -> {
|
||||
assertTrue(promise.isDone());
|
||||
client.close(promise.addListener(future -> {
|
||||
latch.countDown();
|
||||
});
|
||||
}));
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
|
@ -20,10 +20,8 @@ import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.concurrent.DefaultPromise;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -50,8 +48,6 @@ public class DefaultHttp2FrameWriterTest {
|
||||
|
||||
private ByteBuf expectedOutbound;
|
||||
|
||||
private Promise<Void> promise;
|
||||
|
||||
private Http2HeadersEncoder http2HeadersEncoder;
|
||||
|
||||
@Mock
|
||||
@ -76,8 +72,6 @@ public class DefaultHttp2FrameWriterTest {
|
||||
|
||||
expectedOutbound = Unpooled.EMPTY_BUFFER;
|
||||
|
||||
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||
|
||||
Answer<Object> answer = var1 -> {
|
||||
Object msg = var1.getArgument(0);
|
||||
if (msg instanceof ByteBuf) {
|
||||
@ -90,6 +84,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||
when(ctx.channel()).thenReturn(channel);
|
||||
when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE);
|
||||
when(ctx.newPromise()).thenReturn(ImmediateEventExecutor.INSTANCE.newPromise());
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@ -105,7 +100,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
Http2Headers headers = new DefaultHttp2Headers()
|
||||
.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[] expectedFrameBytes = {
|
||||
@ -124,7 +119,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
Http2Headers headers = new DefaultHttp2Headers()
|
||||
.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[] expectedFrameBytes = {
|
||||
@ -143,7 +138,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
Http2Headers headers = new DefaultHttp2Headers()
|
||||
.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[] expectedFrameBytes = {
|
||||
@ -170,7 +165,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE);
|
||||
frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE);
|
||||
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);
|
||||
|
||||
@ -211,7 +206,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE);
|
||||
frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE);
|
||||
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,
|
||||
Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
|
||||
@ -245,7 +240,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
|
||||
@Test
|
||||
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) 0x00, (byte) 0x00, (byte) 0x00, // payload length
|
||||
@ -264,7 +259,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
|
||||
// will auto release after frameWriter.writeFrame succeed
|
||||
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) 0x00, (byte) 0x00, (byte) 0x05, // payload length
|
||||
@ -279,7 +274,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
@Test
|
||||
public void 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[] {
|
||||
(byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5
|
||||
@ -295,7 +290,7 @@ public class DefaultHttp2FrameWriterTest {
|
||||
@Test
|
||||
public void writePriorityDefaults() {
|
||||
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[] {
|
||||
(byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5
|
||||
|
@ -417,17 +417,16 @@ public class DefaultHttp2LocalFlowControllerTest {
|
||||
}
|
||||
|
||||
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) {
|
||||
verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt(), eq(promise));
|
||||
verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void verifyWindowUpdateNotSent() {
|
||||
verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
|
||||
any(Promise.class));
|
||||
verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt());
|
||||
}
|
||||
|
||||
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.UTF_8;
|
||||
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.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.any;
|
||||
@ -157,7 +159,7 @@ public class Http2ConnectionHandlerTest {
|
||||
buf.release();
|
||||
return future;
|
||||
}).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 -> {
|
||||
Object o = invocation.getArguments()[0];
|
||||
if (o instanceof FutureListener) {
|
||||
@ -167,6 +169,7 @@ public class Http2ConnectionHandlerTest {
|
||||
}).when(future).addListener(any(FutureListener.class));
|
||||
when(future.cause()).thenReturn(fakeException);
|
||||
when(channel.isActive()).thenReturn(true);
|
||||
when(future.isFailed()).thenReturn(true);
|
||||
when(channel.pipeline()).thenReturn(pipeline);
|
||||
when(connection.remote()).thenReturn(remote);
|
||||
when(remote.flowController()).thenReturn(remoteFlowController);
|
||||
@ -185,10 +188,13 @@ public class Http2ConnectionHandlerTest {
|
||||
when(connection.goAwaySent(anyInt(), anyLong(), any(ByteBuf.class))).thenReturn(true);
|
||||
when(stream.open(anyBoolean())).thenReturn(stream);
|
||||
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.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.write(any())).thenReturn(future);
|
||||
when(ctx.executor()).thenReturn(executor);
|
||||
@ -254,7 +260,7 @@ public class Http2ConnectionHandlerTest {
|
||||
final Answer<Object> verifier = in -> {
|
||||
assertEquals(in.getArgument(0), evt); // sanity check...
|
||||
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);
|
||||
return null;
|
||||
};
|
||||
@ -292,7 +298,7 @@ public class Http2ConnectionHandlerTest {
|
||||
handler.channelRead(ctx, copiedBuffer("BAD_PREFACE", UTF_8));
|
||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.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());
|
||||
}
|
||||
|
||||
@ -303,7 +309,7 @@ public class Http2ConnectionHandlerTest {
|
||||
handler.channelRead(ctx, copiedBuffer("GET /path HTTP/1.1", US_ASCII));
|
||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
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());
|
||||
assertTrue(goAwayDebugCap.contains("/path"));
|
||||
}
|
||||
@ -319,7 +325,7 @@ public class Http2ConnectionHandlerTest {
|
||||
handler.channelRead(ctx, buf);
|
||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.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());
|
||||
}
|
||||
|
||||
@ -373,7 +379,7 @@ public class Http2ConnectionHandlerTest {
|
||||
handler.exceptionCaught(ctx, e);
|
||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(frameWriter).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()),
|
||||
captor.capture(), eq(promise));
|
||||
captor.capture());
|
||||
captor.getValue().release();
|
||||
}
|
||||
|
||||
@ -389,16 +395,16 @@ public class Http2ConnectionHandlerTest {
|
||||
when(stream.isHeadersSent()).thenReturn(false);
|
||||
when(remote.lastStreamCreated()).thenReturn(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);
|
||||
|
||||
ArgumentCaptor<Http2Headers> captor = ArgumentCaptor.forClass(Http2Headers.class);
|
||||
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();
|
||||
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
|
||||
@ -413,13 +419,13 @@ public class Http2ConnectionHandlerTest {
|
||||
when(stream.isHeadersSent()).thenReturn(false);
|
||||
when(remote.lastStreamCreated()).thenReturn(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);
|
||||
|
||||
verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
|
||||
any(Http2Headers.class), eq(padding), eq(true), eq(promise));
|
||||
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
||||
any(Http2Headers.class), eq(padding), eq(true));
|
||||
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -434,13 +440,13 @@ public class Http2ConnectionHandlerTest {
|
||||
when(stream.isHeadersSent()).thenReturn(false);
|
||||
when(remote.lastStreamCreated()).thenReturn(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);
|
||||
|
||||
verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
|
||||
any(Http2Headers.class), eq(padding), eq(true), eq(promise));
|
||||
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
||||
any(Http2Headers.class), eq(padding), eq(true));
|
||||
verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -448,7 +454,7 @@ public class Http2ConnectionHandlerTest {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
handler = new Http2ConnectionHandler(decoder, encoder, new Http2Settings()) {
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) {
|
||||
latch.countDown();
|
||||
}
|
||||
@ -470,13 +476,13 @@ public class Http2ConnectionHandlerTest {
|
||||
when(stream.isHeadersSent()).thenReturn(true);
|
||||
when(remote.lastStreamCreated()).thenReturn(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);
|
||||
|
||||
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
|
||||
@ -494,14 +500,14 @@ public class Http2ConnectionHandlerTest {
|
||||
when(stream.isHeadersSent()).thenReturn(false);
|
||||
when(remote.lastStreamCreated()).thenReturn(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);
|
||||
|
||||
verify(remote).createStream(STREAM_ID, true);
|
||||
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
|
||||
@ -518,9 +524,9 @@ public class Http2ConnectionHandlerTest {
|
||||
public void writeRstOnNonExistantStreamShouldSucceed() throws Exception {
|
||||
handler = newHandler();
|
||||
when(frameWriter.writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID),
|
||||
eq(STREAM_CLOSED.code()), eq(promise))).thenReturn(future);
|
||||
handler.resetStream(ctx, NON_EXISTANT_STREAM_ID, STREAM_CLOSED.code(), promise);
|
||||
verify(frameWriter).writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID), eq(STREAM_CLOSED.code()), eq(promise));
|
||||
eq(STREAM_CLOSED.code()))).thenReturn(future);
|
||||
handler.resetStream(ctx, NON_EXISTANT_STREAM_ID, STREAM_CLOSED.code());
|
||||
verify(frameWriter).writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID), eq(STREAM_CLOSED.code()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -528,21 +534,21 @@ public class Http2ConnectionHandlerTest {
|
||||
handler = newHandler();
|
||||
when(stream.id()).thenReturn(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.isHeadersSent()).thenReturn(true);
|
||||
// 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.
|
||||
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
|
||||
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
|
||||
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code());
|
||||
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeRstOnIdleStreamShouldNotWriteButStillSucceed() throws Exception {
|
||||
handler = newHandler();
|
||||
when(stream.state()).thenReturn(IDLE);
|
||||
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
|
||||
verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
|
||||
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code());
|
||||
verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong());
|
||||
verify(stream).close();
|
||||
}
|
||||
|
||||
@ -585,11 +591,10 @@ public class Http2ConnectionHandlerTest {
|
||||
return null;
|
||||
}).when(future).addListener(any(FutureListener.class));
|
||||
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(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(ctx).close();
|
||||
assertEquals(0, data.refCnt());
|
||||
}
|
||||
@ -600,13 +605,12 @@ public class Http2ConnectionHandlerTest {
|
||||
ByteBuf data = dummyData();
|
||||
long errorCode = Http2Error.INTERNAL_ERROR.code();
|
||||
|
||||
handler.goAway(ctx, STREAM_ID + 2, errorCode, data.retain(), promise);
|
||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data),
|
||||
eq(promise));
|
||||
handler.goAway(ctx, STREAM_ID + 2, errorCode, data.retain());
|
||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data));
|
||||
verify(connection).goAwaySent(eq(STREAM_ID + 2), eq(errorCode), eq(data));
|
||||
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||
handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
|
||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data), eq(promise));
|
||||
handler.goAway(ctx, STREAM_ID, errorCode, data);
|
||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data));
|
||||
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
|
||||
assertEquals(0, data.refCnt());
|
||||
}
|
||||
@ -617,20 +621,24 @@ public class Http2ConnectionHandlerTest {
|
||||
ByteBuf data = dummyData();
|
||||
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(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.
|
||||
assertFalse(promise.isDone());
|
||||
assertFalse(future.isDone());
|
||||
|
||||
when(connection.goAwaySent()).thenReturn(true);
|
||||
when(remote.lastStreamKnownByPeer()).thenReturn(STREAM_ID);
|
||||
|
||||
Exception ex = new IllegalStateException();
|
||||
doAnswer((Answer<Boolean>) invocationOnMock -> {
|
||||
throw new IllegalStateException();
|
||||
throw ex;
|
||||
}).when(connection).goAwaySent(anyInt(), anyLong(), any(ByteBuf.class));
|
||||
handler.goAway(ctx, STREAM_ID + 2, errorCode, data, promise);
|
||||
assertTrue(promise.isDone());
|
||||
assertFalse(promise.isSuccess());
|
||||
Future<Void> future2 = handler.goAway(ctx, STREAM_ID + 2, errorCode, data);
|
||||
assertTrue(future2.isDone());
|
||||
assertFalse(future2.isSuccess());
|
||||
assertSame(ex, future2.cause());
|
||||
|
||||
assertEquals(0, data.refCnt());
|
||||
verifyNoMoreInteractions(frameWriter);
|
||||
}
|
||||
@ -657,9 +665,8 @@ public class Http2ConnectionHandlerTest {
|
||||
when(channel.isActive()).thenReturn(false);
|
||||
handler.channelInactive(ctx);
|
||||
verify(frameWriter, never()).writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(),
|
||||
any(ByteBuf.class), any(Promise.class));
|
||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong(),
|
||||
any(Promise.class));
|
||||
any(ByteBuf.class));
|
||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -729,22 +736,14 @@ public class Http2ConnectionHandlerTest {
|
||||
return stream;
|
||||
});
|
||||
when(stream.isResetSent()).then((Answer<Boolean>) invocationOnMock -> resetSent.get());
|
||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class)))
|
||||
.then((Answer<Future<Void>>) invocationOnMock -> {
|
||||
Promise<Void> promise = invocationOnMock.getArgument(3);
|
||||
return promise.setSuccess(null);
|
||||
});
|
||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong()))
|
||||
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
|
||||
Promise<Void> promise =
|
||||
new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||
final Promise<Void> promise2 =
|
||||
new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||
promise.addListener(future -> handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise2));
|
||||
|
||||
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());
|
||||
Future<Void> f1 = handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code());
|
||||
Future<Void> f2 = handler.resetStream(ctx, STREAM_ID, CANCEL.code());
|
||||
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong());
|
||||
assertTrue(f1.isSuccess());
|
||||
assertTrue(f2.isSuccess());
|
||||
}
|
||||
|
||||
private static ByteBuf dummyData() {
|
||||
|
@ -35,6 +35,7 @@ import io.netty.util.AsciiString;
|
||||
import io.netty.util.IllegalReferenceCountException;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@ -152,8 +153,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
(Integer) invocationOnMock.getArgument(1),
|
||||
(Http2Headers) invocationOnMock.getArgument(2),
|
||||
0,
|
||||
false,
|
||||
ctx.newPromise());
|
||||
false);
|
||||
http2Server.flush(ctx);
|
||||
return null;
|
||||
}).when(serverListener).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
||||
@ -169,14 +169,14 @@ public class Http2ConnectionRoundtripTest {
|
||||
final short weight = 16;
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
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.encoder().writeRstStream(ctx(), 3, Http2Error.INTERNAL_ERROR.code(), newPromise());
|
||||
http2Client.encoder().writeRstStream(ctx(), 3, Http2Error.INTERNAL_ERROR.code());
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
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());
|
||||
});
|
||||
|
||||
@ -191,8 +191,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
final short weight = 16;
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, true,
|
||||
newPromise());
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, true);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -250,18 +249,17 @@ public class Http2ConnectionRoundtripTest {
|
||||
runInChannel(serverConnectedChannel, () -> {
|
||||
http2Server.encoder().writeSettings(serverCtx(),
|
||||
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
||||
.maxHeaderListSize(100),
|
||||
serverNewPromise());
|
||||
.maxHeaderListSize(100));
|
||||
http2Server.flush(serverCtx());
|
||||
});
|
||||
|
||||
assertTrue(serverSettingsAckLatch1.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
||||
|
||||
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()));
|
||||
// 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 -> {
|
||||
clientDataWriteException.set(future.cause());
|
||||
clientDataWrite.countDown();
|
||||
@ -277,8 +275,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
runInChannel(serverConnectedChannel, () -> {
|
||||
http2Server.encoder().writeSettings(serverCtx(),
|
||||
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
||||
.maxHeaderListSize(Http2CodecUtil.MAX_HEADER_LIST_SIZE),
|
||||
serverNewPromise());
|
||||
.maxHeaderListSize(Http2CodecUtil.MAX_HEADER_LIST_SIZE));
|
||||
http2Server.flush(serverCtx());
|
||||
});
|
||||
|
||||
@ -286,8 +283,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
assertTrue(serverSettingsAckLatch2.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true,
|
||||
newPromise()).addListener(future -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true).addListener(future -> {
|
||||
clientHeadersWriteException2.set(future.cause());
|
||||
clientHeadersLatch.countDown();
|
||||
});
|
||||
@ -344,8 +340,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
runInChannel(serverConnectedChannel, () -> {
|
||||
http2Server.encoder().writeSettings(serverCtx(),
|
||||
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
||||
.initialWindowSize(0),
|
||||
serverNewPromise());
|
||||
.initialWindowSize(0));
|
||||
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
|
||||
// controller.
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
||||
newPromise());
|
||||
http2Client.encoder().writeData(ctx(), 3, Unpooled.wrappedBuffer(data), 0, true, newPromise());
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||
http2Client.encoder().writeData(ctx(), 3, Unpooled.wrappedBuffer(data), 0, true);
|
||||
http2Client.flush(ctx());
|
||||
clientWriteDataLatch.countDown();
|
||||
});
|
||||
@ -367,8 +361,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
runInChannel(serverConnectedChannel, () -> {
|
||||
http2Server.encoder().writeSettings(serverCtx(),
|
||||
new Http2Settings().copyFrom(http2Server.decoder().localSettings())
|
||||
.initialWindowSize(data.length),
|
||||
serverNewPromise());
|
||||
.initialWindowSize(data.length));
|
||||
http2Server.flush(serverCtx());
|
||||
});
|
||||
|
||||
@ -391,9 +384,8 @@ public class Http2ConnectionRoundtripTest {
|
||||
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writePriority(ctx(), 5, 3, (short) 14, false, newPromise());
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
||||
newPromise());
|
||||
http2Client.encoder().writePriority(ctx(), 5, 3, (short) 14, false);
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -420,10 +412,8 @@ public class Http2ConnectionRoundtripTest {
|
||||
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
runInChannel(clientChannel, () -> {
|
||||
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,
|
||||
newPromise());
|
||||
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, false);
|
||||
http2Client.encoder().frameWriter().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -465,8 +455,8 @@ public class Http2ConnectionRoundtripTest {
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), streamId, headers, CONNECTION_STREAM_ID,
|
||||
DEFAULT_PRIORITY_WEIGHT, false, 0, false, newPromise());
|
||||
http2Client.encoder().writeRstStream(ctx(), streamId, Http2Error.CANCEL.code(), newPromise());
|
||||
DEFAULT_PRIORITY_WEIGHT, false, 0, false);
|
||||
http2Client.encoder().writeRstStream(ctx(), streamId, Http2Error.CANCEL.code());
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -478,7 +468,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
|
||||
// Now have the server attempt to send a headers frame simulating some asynchronous work.
|
||||
runInChannel(serverConnectedChannel, () -> {
|
||||
http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true, serverNewPromise())
|
||||
http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true)
|
||||
.addListener(future -> {
|
||||
serverWriteHeadersCauseRef.set(future.cause());
|
||||
serverWriteHeadersLatch.countDown();
|
||||
@ -510,8 +500,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
// Create a single stream by sending a HEADERS frame to the server.
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
||||
newPromise());
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -547,8 +536,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
|
||||
// Create a single stream by sending a HEADERS frame to the server.
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
||||
newPromise());
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -592,41 +580,42 @@ public class Http2ConnectionRoundtripTest {
|
||||
throws Exception {
|
||||
bootstrapEnv(1, 1, 2, 1);
|
||||
|
||||
final Promise<Void> emptyDataPromise = newPromise();
|
||||
Promise<Void> promise = ImmediateEventExecutor.INSTANCE.newPromise();
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
|
||||
newPromise());
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false);
|
||||
ByteBuf emptyBuf = Unpooled.buffer();
|
||||
emptyBuf.release();
|
||||
final Future<Void> future;
|
||||
switch (mode) {
|
||||
case SINGLE_END_OF_STREAM:
|
||||
http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, true, emptyDataPromise);
|
||||
future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, true);
|
||||
break;
|
||||
case SECOND_END_OF_STREAM:
|
||||
http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false, emptyDataPromise);
|
||||
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, true, newPromise());
|
||||
future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false);
|
||||
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, true);
|
||||
break;
|
||||
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,
|
||||
(short) 16, false, 0, true, newPromise());
|
||||
(short) 16, false, 0, true);
|
||||
break;
|
||||
case SECOND_WITH_TRAILERS:
|
||||
http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false, emptyDataPromise);
|
||||
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false, newPromise());
|
||||
future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false);
|
||||
http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false);
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0,
|
||||
(short) 16, false, 0, true, newPromise());
|
||||
(short) 16, false, 0, true);
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
http2Client.flush(ctx());
|
||||
future.cascadeTo(promise);
|
||||
});
|
||||
|
||||
ExecutionException e = assertThrows(ExecutionException.class, new Executable() {
|
||||
@Override
|
||||
public void execute() throws Throwable {
|
||||
emptyDataPromise.get();
|
||||
promise.get();
|
||||
}
|
||||
});
|
||||
assertThat(e.getCause(), is(instanceOf(IllegalReferenceCountException.class)));
|
||||
@ -641,8 +630,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
final Promise<Void> assertPromise = newPromise();
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
|
||||
newPromise());
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false);
|
||||
clientChannel.pipeline().addFirst(new ChannelHandler() {
|
||||
@Override
|
||||
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
|
||||
@ -660,7 +648,8 @@ public class Http2ConnectionRoundtripTest {
|
||||
});
|
||||
|
||||
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()
|
||||
.hasFlowControlled(http2Client.connection().stream(3)));
|
||||
|
||||
@ -697,8 +686,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
// Create a single stream by sending a HEADERS frame to the server.
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
||||
newPromise());
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -733,7 +721,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
||||
true, newPromise());
|
||||
true);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -741,7 +729,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), MAX_VALUE + 1, headers, 0, (short) 16, false, 0,
|
||||
true, newPromise());
|
||||
true);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -768,7 +756,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
||||
false, newPromise());
|
||||
false);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -778,7 +766,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
||||
|
||||
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());
|
||||
});
|
||||
|
||||
@ -791,7 +779,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1);
|
||||
runInChannel(clientChannel, () -> {
|
||||
Future<Void> f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0,
|
||||
true, newPromise());
|
||||
true);
|
||||
clientWriteAfterGoAwayFutureRef.set(f);
|
||||
http2Client.flush(ctx());
|
||||
f.addListener(future -> clientWriteAfterGoAwayLatch.countDown());
|
||||
@ -839,9 +827,9 @@ public class Http2ConnectionRoundtripTest {
|
||||
final Http2Headers headers = dummyHeaders();
|
||||
runInChannel(clientChannel, () -> {
|
||||
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,
|
||||
false, newPromise());
|
||||
false);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -851,7 +839,7 @@ public class Http2ConnectionRoundtripTest {
|
||||
assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
||||
|
||||
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());
|
||||
});
|
||||
|
||||
@ -911,12 +899,12 @@ public class Http2ConnectionRoundtripTest {
|
||||
// Create the stream and send all of the data at once.
|
||||
runInChannel(clientChannel, () -> {
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
||||
false, newPromise());
|
||||
http2Client.encoder().writeData(ctx(), 3, data.retainedDuplicate(), 0, false, newPromise());
|
||||
false);
|
||||
http2Client.encoder().writeData(ctx(), 3, data.retainedDuplicate(), 0, false);
|
||||
|
||||
// Write trailers.
|
||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
|
||||
true, newPromise());
|
||||
true);
|
||||
http2Client.flush(ctx());
|
||||
});
|
||||
|
||||
@ -989,14 +977,13 @@ public class Http2ConnectionRoundtripTest {
|
||||
for (int streamId = 3; streamId < upperLimit; streamId += 2) {
|
||||
// Send a bunch of data on each stream.
|
||||
http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16,
|
||||
false, 0, false, newPromise());
|
||||
http2Client.encoder().writePing(ctx(), false, pingData,
|
||||
newPromise());
|
||||
false, 0, false);
|
||||
http2Client.encoder().writePing(ctx(), false, pingData);
|
||||
http2Client.encoder().writeData(ctx(), streamId, data.retainedSlice(), 0,
|
||||
false, newPromise());
|
||||
false);
|
||||
// Write trailers.
|
||||
http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16,
|
||||
false, 0, true, newPromise());
|
||||
false, 0, true);
|
||||
http2Client.flush(ctx());
|
||||
}
|
||||
});
|
||||
|
@ -33,7 +33,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
@ -59,7 +58,6 @@ import static org.mockito.Mockito.when;
|
||||
/**
|
||||
* Tests for {@link Http2ControlFrameLimitEncoder}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Http2ControlFrameLimitEncoderTest {
|
||||
|
||||
private Http2ControlFrameLimitEncoder encoder;
|
||||
@ -101,22 +99,22 @@ public class Http2ControlFrameLimitEncoderTest {
|
||||
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
||||
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
||||
|
||||
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class)))
|
||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise(invocationOnMock, 3));
|
||||
when(writer.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
|
||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise(invocationOnMock, 1));
|
||||
when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong(), any(Promise.class)))
|
||||
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong()))
|
||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise());
|
||||
when(writer.writeSettingsAck(any(ChannelHandlerContext.class)))
|
||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise());
|
||||
when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong()))
|
||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
|
||||
Promise<Void> promise = handlePromise(invocationOnMock, 3);
|
||||
Promise<Void> promise = handlePromise();
|
||||
if (invocationOnMock.getArgument(1) == Boolean.FALSE) {
|
||||
promise.trySuccess(null);
|
||||
}
|
||||
return promise;
|
||||
});
|
||||
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
|
||||
any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
|
||||
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)))
|
||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
|
||||
ReferenceCountUtil.release(invocationOnMock.getArgument(3));
|
||||
Promise<Void> promise = invocationOnMock.getArgument(4);
|
||||
Promise<Void> promise = ImmediateEventExecutor.INSTANCE.newPromise();
|
||||
goAwayPromises.offer(promise);
|
||||
return promise;
|
||||
});
|
||||
@ -139,7 +137,12 @@ public class Http2ControlFrameLimitEncoderTest {
|
||||
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||
when(executor.inEventLoop()).thenReturn(true);
|
||||
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.close()).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
when(channel.isActive()).thenReturn(false);
|
||||
when(channel.config()).thenReturn(config);
|
||||
when(channel.isWritable()).thenReturn(true);
|
||||
@ -152,12 +155,12 @@ public class Http2ControlFrameLimitEncoderTest {
|
||||
handler.handlerAdded(ctx);
|
||||
}
|
||||
|
||||
private Promise<Void> handlePromise(InvocationOnMock invocationOnMock, int promiseIdx) {
|
||||
Promise<Void> promise = invocationOnMock.getArgument(promiseIdx);
|
||||
private Promise<Void> handlePromise() {
|
||||
Promise<Void> p = ImmediateEventExecutor.INSTANCE.newPromise();
|
||||
if (++numWrites == 2) {
|
||||
promise.setSuccess(null);
|
||||
p.setSuccess(null);
|
||||
}
|
||||
return promise;
|
||||
return p;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@ -178,75 +181,75 @@ public class Http2ControlFrameLimitEncoderTest {
|
||||
|
||||
@Test
|
||||
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
|
||||
// not count to the number of queued frames.
|
||||
assertTrue(encoder.writeSettingsAck(ctx, newPromise()).isSuccess());
|
||||
assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
|
||||
assertTrue(encoder.writeSettingsAck(ctx).isSuccess());
|
||||
assertFalse(encoder.writeSettingsAck(ctx).isDone());
|
||||
|
||||
verifyFlushAndClose(0, false);
|
||||
|
||||
assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
|
||||
assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
|
||||
assertFalse(encoder.writeSettingsAck(ctx).isDone());
|
||||
assertFalse(encoder.writeSettingsAck(ctx).isDone());
|
||||
|
||||
verifyFlushAndClose(1, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
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
|
||||
// not count to the number of queued frames.
|
||||
assertTrue(encoder.writePing(ctx, true, 8, newPromise()).isSuccess());
|
||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
|
||||
assertTrue(encoder.writePing(ctx, true, 8).isSuccess());
|
||||
assertFalse(encoder.writePing(ctx, true, 8).isDone());
|
||||
|
||||
verifyFlushAndClose(0, false);
|
||||
|
||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
|
||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
|
||||
assertFalse(encoder.writePing(ctx, true, 8).isDone());
|
||||
assertFalse(encoder.writePing(ctx, true, 8).isDone());
|
||||
|
||||
verifyFlushAndClose(1, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotLimitPing() {
|
||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
||||
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||
|
||||
verifyFlushAndClose(0, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
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
|
||||
// not count to the number of queued frames.
|
||||
assertTrue(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isSuccess());
|
||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
||||
assertTrue(encoder.writeRstStream(ctx, 1, CANCEL.code()).isSuccess());
|
||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||
|
||||
verifyFlushAndClose(0, false);
|
||||
|
||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||
|
||||
verifyFlushAndClose(1, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
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
|
||||
// not count to the number of queued frames.
|
||||
assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
|
||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isSuccess());
|
||||
assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
|
||||
assertFalse(encoder.writePing(ctx, true, 8).isSuccess());
|
||||
|
||||
verifyFlushAndClose(0, false);
|
||||
|
||||
assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
|
||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
|
||||
assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isSuccess());
|
||||
assertFalse(encoder.writeSettingsAck(ctx).isDone());
|
||||
assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
|
||||
assertFalse(encoder.writePing(ctx, true, 8).isSuccess());
|
||||
|
||||
verifyFlushAndClose(1, true);
|
||||
}
|
||||
@ -256,7 +259,7 @@ public class Http2ControlFrameLimitEncoderTest {
|
||||
verify(ctx, times(invocations)).close();
|
||||
if (failed) {
|
||||
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.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.assertEqualsAndRelease;
|
||||
import static io.netty.handler.codec.http2.Http2TestUtil.bb;
|
||||
@ -82,7 +81,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
/**
|
||||
* Unit tests for {@link Http2FrameCodec}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Http2FrameCodecTest {
|
||||
|
||||
// For verifying outbound frames
|
||||
@ -141,13 +139,13 @@ public class Http2FrameCodecTest {
|
||||
channel.pipeline().fireChannelActive();
|
||||
|
||||
// Handshake
|
||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), anyHttp2Settings(), anyChannelPromise());
|
||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), anyHttp2Settings());
|
||||
verifyNoMoreInteractions(frameWriter);
|
||||
channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf());
|
||||
|
||||
frameInboundWriter.writeInboundSettings(initialRemoteSettings);
|
||||
|
||||
verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class), anyChannelPromise());
|
||||
verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class));
|
||||
|
||||
frameInboundWriter.writeInboundSettingsAck();
|
||||
|
||||
@ -178,9 +176,9 @@ public class Http2FrameCodecTest {
|
||||
channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
|
||||
verify(frameWriter).writeHeaders(
|
||||
any(ChannelHandlerContext.class), eq(1), eq(response),
|
||||
eq(27), eq(true), anyChannelPromise());
|
||||
eq(27), eq(true));
|
||||
verify(frameWriter, never()).writeRstStream(
|
||||
any(ChannelHandlerContext.class), anyInt(), anyLong(), anyChannelPromise());
|
||||
any(ChannelHandlerContext.class), anyInt(), anyLong());
|
||||
|
||||
assertEquals(State.CLOSED, stream.state());
|
||||
event = inboundHandler.readInboundMessageOrUserEvent();
|
||||
@ -207,9 +205,9 @@ public class Http2FrameCodecTest {
|
||||
channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
|
||||
verify(frameWriter).writeHeaders(
|
||||
any(ChannelHandlerContext.class), eq(1), eq(response),
|
||||
eq(27), eq(true), anyChannelPromise());
|
||||
eq(27), eq(true));
|
||||
verify(frameWriter, never()).writeRstStream(
|
||||
any(ChannelHandlerContext.class), anyInt(), anyLong(), anyChannelPromise());
|
||||
any(ChannelHandlerContext.class), anyInt(), anyLong());
|
||||
|
||||
assertEquals(State.CLOSED, stream.state());
|
||||
assertTrue(channel.isActive());
|
||||
@ -266,12 +264,12 @@ public class Http2FrameCodecTest {
|
||||
|
||||
inboundHandler.writeOutbound(new DefaultHttp2HeadersFrame(response, false).stream(stream2));
|
||||
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));
|
||||
ArgumentCaptor<ByteBuf> outboundData = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(frameWriter).writeData(any(ChannelHandlerContext.class), eq(1), outboundData.capture(), eq(27),
|
||||
eq(true), anyChannelPromise());
|
||||
eq(true));
|
||||
|
||||
ByteBuf bb = bb("world");
|
||||
assertEquals(bb, outboundData.getValue());
|
||||
@ -280,7 +278,7 @@ public class Http2FrameCodecTest {
|
||||
outboundData.getValue().release();
|
||||
|
||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class),
|
||||
anyInt(), anyLong(), anyChannelPromise());
|
||||
anyInt(), anyLong());
|
||||
assertTrue(channel.isActive());
|
||||
}
|
||||
|
||||
@ -301,7 +299,7 @@ public class Http2FrameCodecTest {
|
||||
assertEquals(3, stream2.id());
|
||||
|
||||
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());
|
||||
assertTrue(channel.isActive());
|
||||
}
|
||||
@ -343,7 +341,7 @@ public class Http2FrameCodecTest {
|
||||
|
||||
channel.writeOutbound(goAwayFrame);
|
||||
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());
|
||||
assertTrue(channel.isActive());
|
||||
expected.release();
|
||||
@ -419,7 +417,7 @@ public class Http2FrameCodecTest {
|
||||
channel.writeOutbound(goAwayFrame);
|
||||
// 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),
|
||||
eq(NO_ERROR.code()), eq(debugData), anyChannelPromise());
|
||||
eq(NO_ERROR.code()), eq(debugData));
|
||||
debugData.release();
|
||||
assertEquals(State.OPEN, stream.state());
|
||||
assertTrue(channel.isActive());
|
||||
@ -596,7 +594,7 @@ public class Http2FrameCodecTest {
|
||||
channel.write(unknownFrame);
|
||||
|
||||
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
|
||||
@ -604,7 +602,7 @@ public class Http2FrameCodecTest {
|
||||
Http2Settings settings = new Http2Settings();
|
||||
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
|
||||
@ -763,7 +761,7 @@ public class Http2FrameCodecTest {
|
||||
channel.writeAndFlush(new DefaultHttp2PingFrame(12345));
|
||||
|
||||
verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(false),
|
||||
eq(12345L), anyChannelPromise());
|
||||
eq(12345L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -781,7 +779,7 @@ public class Http2FrameCodecTest {
|
||||
Http2Settings settings = new Http2Settings().maxConcurrentStreams(1);
|
||||
channel.writeAndFlush(new DefaultHttp2SettingsFrame(settings));
|
||||
|
||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), eq(settings), anyChannelPromise());
|
||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), eq(settings));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -837,21 +835,21 @@ public class Http2FrameCodecTest {
|
||||
Http2PingFrame frame = inboundHandler.readInbound();
|
||||
assertFalse(frame.ack());
|
||||
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
|
||||
public void autoAckPingFalse() throws Exception {
|
||||
setUp(Http2FrameCodecBuilder.forServer().autoAckPingFrame(false), new Http2Settings());
|
||||
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();
|
||||
assertFalse(frame.ack());
|
||||
assertEquals(8, frame.content());
|
||||
|
||||
// Now ack the frame manually.
|
||||
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
|
||||
|
@ -49,59 +49,59 @@ final class Http2FrameInboundWriter {
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) {
|
||||
writer.writeHeaders(ctx, streamId, headers, streamDependency,
|
||||
weight, exclusive, padding, endStream, ctx.newPromise()).syncUninterruptibly();
|
||||
weight, exclusive, padding, endStream).syncUninterruptibly();
|
||||
}
|
||||
|
||||
void writeInboundPriority(int streamId, int streamDependency,
|
||||
short weight, boolean exclusive) {
|
||||
writer.writePriority(ctx, streamId, streamDependency, weight,
|
||||
exclusive, ctx.newPromise()).syncUninterruptibly();
|
||||
exclusive).syncUninterruptibly();
|
||||
}
|
||||
|
||||
void writeInboundRstStream(int streamId, long errorCode) {
|
||||
writer.writeRstStream(ctx, streamId, errorCode, ctx.newPromise()).syncUninterruptibly();
|
||||
writer.writeRstStream(ctx, streamId, errorCode).syncUninterruptibly();
|
||||
}
|
||||
|
||||
void writeInboundSettings(Http2Settings settings) {
|
||||
writer.writeSettings(ctx, settings, ctx.newPromise()).syncUninterruptibly();
|
||||
writer.writeSettings(ctx, settings).syncUninterruptibly();
|
||||
}
|
||||
|
||||
void writeInboundSettingsAck() {
|
||||
writer.writeSettingsAck(ctx, ctx.newPromise()).syncUninterruptibly();
|
||||
writer.writeSettingsAck(ctx).syncUninterruptibly();
|
||||
}
|
||||
|
||||
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,
|
||||
Http2Headers headers, int padding) {
|
||||
writer.writePushPromise(ctx, streamId, promisedStreamId,
|
||||
headers, padding, ctx.newPromise()).syncUninterruptibly();
|
||||
headers, padding).syncUninterruptibly();
|
||||
}
|
||||
|
||||
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) {
|
||||
writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, ctx.newPromise()).syncUninterruptibly();
|
||||
writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement).syncUninterruptibly();
|
||||
}
|
||||
|
||||
void writeInboundFrame(byte frameType, int streamId,
|
||||
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
|
||||
|
@ -59,7 +59,6 @@ import static org.mockito.Mockito.anyShort;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.isA;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@ -134,7 +133,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void emptyDataShouldMatch() throws Exception {
|
||||
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();
|
||||
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(0), eq(false));
|
||||
}
|
||||
@ -142,7 +141,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void dataShouldMatch() throws Exception {
|
||||
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();
|
||||
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(1), eq(false));
|
||||
}
|
||||
@ -150,7 +149,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void dataWithPaddingShouldMatch() throws Exception {
|
||||
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();
|
||||
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;
|
||||
|
||||
writer.writeData(ctx, STREAM_ID, originalData.slice(), originalPadding,
|
||||
endOfStream, ctx.newPromise());
|
||||
endOfStream);
|
||||
readFrames();
|
||||
|
||||
// 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
|
||||
public void emptyHeadersShouldMatch() throws Exception {
|
||||
final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
|
||||
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true, ctx.newPromise());
|
||||
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true);
|
||||
readFrames();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
|
||||
}
|
||||
@ -204,7 +203,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void emptyHeadersWithPaddingShouldMatch() throws Exception {
|
||||
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();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true));
|
||||
}
|
||||
@ -212,7 +211,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void binaryHeadersWithoutPriorityShouldMatch() throws Exception {
|
||||
final Http2Headers headers = binaryHeaders();
|
||||
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true, ctx.newPromise());
|
||||
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true);
|
||||
readFrames();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
|
||||
}
|
||||
@ -220,7 +219,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void headersFrameWithoutPriorityShouldMatch() throws Exception {
|
||||
final Http2Headers headers = headers();
|
||||
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true, ctx.newPromise());
|
||||
writer.writeHeaders(ctx, STREAM_ID, headers, 0, true);
|
||||
readFrames();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
|
||||
}
|
||||
@ -228,7 +227,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void headersFrameWithPriorityShouldMatch() throws Exception {
|
||||
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();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(4), eq((short) 255),
|
||||
eq(true), eq(0), eq(true));
|
||||
@ -237,7 +236,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void headersWithPaddingWithoutPriorityShouldMatch() throws Exception {
|
||||
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();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true));
|
||||
}
|
||||
@ -245,7 +244,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void headersWithPaddingWithPriorityShouldMatch() throws Exception {
|
||||
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();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true),
|
||||
eq(1), eq(true));
|
||||
@ -254,7 +253,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void continuedHeadersShouldMatch() throws Exception {
|
||||
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();
|
||||
verify(listener)
|
||||
.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
|
||||
public void continuedHeadersWithPaddingShouldMatch() throws Exception {
|
||||
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();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true),
|
||||
eq(MAX_PADDING), eq(true));
|
||||
@ -275,7 +274,7 @@ public class Http2FrameRoundtripTest {
|
||||
final int maxListSize = 100;
|
||||
reader.configuration().headersConfiguration().maxHeaderListSize(maxListSize, maxListSize);
|
||||
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() {
|
||||
@Override
|
||||
public void execute() throws Throwable {
|
||||
@ -290,7 +289,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void emptyPushPromiseShouldMatch() throws Exception {
|
||||
final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
|
||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0, ctx.newPromise());
|
||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0);
|
||||
readFrames();
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0));
|
||||
}
|
||||
@ -298,7 +297,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void pushPromiseFrameShouldMatch() throws Exception {
|
||||
final Http2Headers headers = headers();
|
||||
writer.writePushPromise(ctx, STREAM_ID, 1, headers, 5, ctx.newPromise());
|
||||
writer.writePushPromise(ctx, STREAM_ID, 1, headers, 5);
|
||||
readFrames();
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(1), eq(headers), eq(5));
|
||||
}
|
||||
@ -306,7 +305,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void pushPromiseWithPaddingShouldMatch() throws Exception {
|
||||
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();
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(MAX_PADDING));
|
||||
}
|
||||
@ -314,7 +313,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void continuedPushPromiseShouldMatch() throws Exception {
|
||||
final Http2Headers headers = largeHeaders();
|
||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0, ctx.newPromise());
|
||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0);
|
||||
readFrames();
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0));
|
||||
}
|
||||
@ -322,7 +321,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void continuedPushPromiseWithPaddingShouldMatch() throws Exception {
|
||||
final Http2Headers headers = largeHeaders();
|
||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0xFF, ctx.newPromise());
|
||||
writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0xFF);
|
||||
readFrames();
|
||||
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 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();
|
||||
|
||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
@ -342,7 +341,7 @@ public class Http2FrameRoundtripTest {
|
||||
|
||||
@Test
|
||||
public void pingFrameShouldMatch() throws Exception {
|
||||
writer.writePing(ctx, false, 1234567, ctx.newPromise());
|
||||
writer.writePing(ctx, false, 1234567);
|
||||
readFrames();
|
||||
|
||||
ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(long.class);
|
||||
@ -352,7 +351,7 @@ public class Http2FrameRoundtripTest {
|
||||
|
||||
@Test
|
||||
public void pingAckFrameShouldMatch() throws Exception {
|
||||
writer.writePing(ctx, true, 1234567, ctx.newPromise());
|
||||
writer.writePing(ctx, true, 1234567);
|
||||
readFrames();
|
||||
|
||||
ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(long.class);
|
||||
@ -362,14 +361,14 @@ public class Http2FrameRoundtripTest {
|
||||
|
||||
@Test
|
||||
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();
|
||||
verify(listener).onPriorityRead(eq(ctx), eq(STREAM_ID), eq(1), eq((short) 1), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rstStreamFrameShouldMatch() throws Exception {
|
||||
writer.writeRstStream(ctx, STREAM_ID, ERROR_CODE, ctx.newPromise());
|
||||
writer.writeRstStream(ctx, STREAM_ID, ERROR_CODE);
|
||||
readFrames();
|
||||
verify(listener).onRstStreamRead(eq(ctx), eq(STREAM_ID), eq(ERROR_CODE));
|
||||
}
|
||||
@ -377,7 +376,7 @@ public class Http2FrameRoundtripTest {
|
||||
@Test
|
||||
public void emptySettingsFrameShouldMatch() throws Exception {
|
||||
final Http2Settings settings = new Http2Settings();
|
||||
writer.writeSettings(ctx, settings, ctx.newPromise());
|
||||
writer.writeSettings(ctx, settings);
|
||||
readFrames();
|
||||
verify(listener).onSettingsRead(eq(ctx), eq(settings));
|
||||
}
|
||||
@ -390,21 +389,21 @@ public class Http2FrameRoundtripTest {
|
||||
settings.initialWindowSize(123);
|
||||
settings.maxConcurrentStreams(456);
|
||||
|
||||
writer.writeSettings(ctx, settings, ctx.newPromise());
|
||||
writer.writeSettings(ctx, settings);
|
||||
readFrames();
|
||||
verify(listener).onSettingsRead(eq(ctx), eq(settings));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingsAckShouldMatch() throws Exception {
|
||||
writer.writeSettingsAck(ctx, ctx.newPromise());
|
||||
writer.writeSettingsAck(ctx);
|
||||
readFrames();
|
||||
verify(listener).onSettingsAckRead(eq(ctx));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void windowUpdateFrameShouldMatch() throws Exception {
|
||||
writer.writeWindowUpdate(ctx, STREAM_ID, WINDOW_UPDATE, ctx.newPromise());
|
||||
writer.writeWindowUpdate(ctx, STREAM_ID, WINDOW_UPDATE);
|
||||
readFrames();
|
||||
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.AttributeKey;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
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.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.assertEqualsAndRelease;
|
||||
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);
|
||||
frameInboundWriter.writeInboundSettings(settings);
|
||||
|
||||
verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class), anyChannelPromise());
|
||||
verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class));
|
||||
|
||||
frameInboundWriter.writeInboundSettingsAck();
|
||||
|
||||
@ -121,7 +121,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
||||
|
||||
// Handshake
|
||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class),
|
||||
anyHttp2Settings(), anyChannelPromise());
|
||||
anyHttp2Settings());
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@ -151,7 +151,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
||||
assertTrue(childChannel.isActive());
|
||||
|
||||
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) {
|
||||
@ -552,14 +552,13 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
||||
|
||||
childChannel.close();
|
||||
verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class),
|
||||
eqStreamId(childChannel), eq(Http2Error.CANCEL.code()), anyChannelPromise());
|
||||
eqStreamId(childChannel), eq(Http2Error.CANCEL.code()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() {
|
||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||
any(Http2Headers.class), anyInt(), anyBoolean(),
|
||||
any(Promise.class))).thenAnswer(new Answer<Future<Void>>() {
|
||||
any(Http2Headers.class), anyInt(), anyBoolean())).thenAnswer(new Answer<Future<Void>>() {
|
||||
|
||||
private boolean headersWritten;
|
||||
@Override
|
||||
@ -568,9 +567,9 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
||||
// refuses to allocate a new stream due to having received a GOAWAY.
|
||||
if (!headersWritten) {
|
||||
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();
|
||||
// The channel was never active so we should not generate a RST frame.
|
||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class),
|
||||
eqStreamId(childChannel), anyLong(), anyChannelPromise());
|
||||
eqStreamId(childChannel), anyLong());
|
||||
|
||||
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.
|
||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(channel),
|
||||
anyLong(), anyChannelPromise());
|
||||
anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -633,11 +632,9 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
||||
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||
eq(headers), anyInt(), anyBoolean(),
|
||||
any(Promise.class))).thenAnswer(invocationOnMock -> {
|
||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(
|
||||
new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed"));
|
||||
});
|
||||
eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock ->
|
||||
ImmediateEventExecutor.INSTANCE.newFailedFuture(
|
||||
new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed")));
|
||||
final Future<Void> future = childChannel.writeAndFlush(
|
||||
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.
|
||||
verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(childChannel),
|
||||
anyLong(), anyChannelPromise());
|
||||
anyLong());
|
||||
|
||||
assertFalse(childChannel.isOpen());
|
||||
assertFalse(childChannel.isActive());
|
||||
@ -698,10 +695,8 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
||||
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||
eq(headers), anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(invocationOnMock -> {
|
||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(
|
||||
new Http2NoMoreStreamIdsException());
|
||||
});
|
||||
eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock ->
|
||||
ImmediateEventExecutor.INSTANCE.newFailedFuture(new Http2NoMoreStreamIdsException()));
|
||||
|
||||
final Future<Void> future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
|
||||
parentChannel.flush();
|
||||
@ -782,9 +777,8 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
||||
|
||||
Http2Headers headers = new DefaultHttp2Headers();
|
||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||
eq(headers), anyInt(), anyBoolean(),
|
||||
any(Promise.class))).thenAnswer(invocationOnMock -> {
|
||||
Promise<Void> promise = invocationOnMock.getArgument(5);
|
||||
eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock -> {
|
||||
Promise<Void> promise = ImmediateEventExecutor.INSTANCE.newPromise();
|
||||
writePromises.offer(promise);
|
||||
return promise;
|
||||
});
|
||||
@ -1305,7 +1299,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
||||
assertTrue(flushSniffer.checkFlush());
|
||||
|
||||
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.
|
||||
verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 1);
|
||||
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.
|
||||
assertTrue(flushSniffer.checkFlush());
|
||||
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.
|
||||
childChannel.read();
|
||||
verify(frameWriter)
|
||||
.writeWindowUpdate(any(ChannelHandlerContext.class), eq(0), eq(32 * 1024), anyChannelPromise());
|
||||
.writeWindowUpdate(any(ChannelHandlerContext.class), eq(0), eq(32 * 1024));
|
||||
verify(frameWriter)
|
||||
.writeWindowUpdate(any(ChannelHandlerContext.class), eq(childChannel.stream().id()),
|
||||
eq(32 * 1024), anyChannelPromise());
|
||||
eq(32 * 1024));
|
||||
assertTrue(flushSniffer.checkFlush());
|
||||
}
|
||||
|
||||
|
@ -22,10 +22,9 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.AsciiString;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
@ -360,50 +359,45 @@ public final class Http2TestUtil {
|
||||
}).when(frameWriter).close();
|
||||
|
||||
when(frameWriter.configuration()).thenReturn(configuration);
|
||||
when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class),
|
||||
any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||
((Promise<Void>) invocationOnMock.getArgument(2)).setSuccess(null));
|
||||
when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class)))
|
||||
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
|
||||
when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
|
||||
.thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||
((Promise<Void>) invocationOnMock.getArgument(1)).setSuccess(null));
|
||||
when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class)))
|
||||
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
|
||||
when(frameWriter.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong()))
|
||||
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
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));
|
||||
return ((Promise<Void>) invocationOnMock.getArgument(4)).setSuccess(null);
|
||||
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||
});
|
||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
||||
anyBoolean(), any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||
((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null));
|
||||
anyBoolean())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
|
||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||
any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
|
||||
any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||
((Promise<Void>) invocationOnMock.getArgument(8)).setSuccess(null));
|
||||
any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()))
|
||||
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
|
||||
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));
|
||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
|
||||
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||
});
|
||||
|
||||
when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(),
|
||||
anyLong(), any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||
((Promise<Void>) invocationOnMock.getArgument(3)).setSuccess(null));
|
||||
anyLong())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
|
||||
when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
|
||||
any(Promise.class))).then((Answer<Future<Void>>) invocationOnMock ->
|
||||
((Promise<Void>) invocationOnMock.getArgument(3)).setSuccess(null));
|
||||
when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt()))
|
||||
.thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
|
||||
when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class),
|
||||
anyInt(), anyChannelPromise())).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||
((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null));
|
||||
anyInt())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
|
||||
|
||||
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));
|
||||
return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
|
||||
return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
|
||||
});
|
||||
return frameWriter;
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
scheme(new AsciiString("https")).authority(new AsciiString("example.org"))
|
||||
.path(new AsciiString("/some/path/resource2"));
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitRequests();
|
||||
@ -179,7 +179,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
.add(HttpHeaderNames.COOKIE, "c=d")
|
||||
.add(HttpHeaderNames.COOKIE, "e=f");
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitRequests();
|
||||
@ -211,7 +211,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
.add(HttpHeaderNames.COOKIE, "a=b; c=d")
|
||||
.add(HttpHeaderNames.COOKIE, "e=f");
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitRequests();
|
||||
@ -235,7 +235,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
.add(new AsciiString("çã".getBytes(CharsetUtil.UTF_8)),
|
||||
new AsciiString("Ãã".getBytes(CharsetUtil.UTF_8)));
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitResponses();
|
||||
@ -257,9 +257,8 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
|
||||
new AsciiString("/some/path/resource2"));
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true,
|
||||
newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitRequests();
|
||||
@ -288,12 +287,12 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
new AsciiString("/some/path/resource2"));
|
||||
final int midPoint = text.length() / 2;
|
||||
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.retainedSlice(0, midPoint), 0, false, newPromiseClient());
|
||||
ctxClient(), 3, content.retainedSlice(0, midPoint), 0, false);
|
||||
clientHandler.encoder().writeData(
|
||||
ctxClient(), 3, content.retainedSlice(midPoint, text.length() - midPoint),
|
||||
0, true, newPromiseClient());
|
||||
0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitRequests();
|
||||
@ -321,10 +320,10 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
|
||||
new AsciiString("/some/path/resource2"));
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false, newPromiseClient());
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false, newPromiseClient());
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, true, newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false);
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false);
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitRequests();
|
||||
@ -360,10 +359,9 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
.set(new AsciiString("foo2"), new AsciiString("goo2"))
|
||||
.add(new AsciiString("foo2"), new AsciiString("goo3"));
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, false,
|
||||
newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers2, 0, true, newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, false);
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers2, 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitRequests();
|
||||
@ -402,14 +400,12 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
|
||||
new AsciiString("/some/path/resource2"));
|
||||
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,
|
||||
false, newPromiseClient());
|
||||
false);
|
||||
clientChannel.flush(); // Headers are queued in the flow controller and so flush them.
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true,
|
||||
newPromiseClient());
|
||||
clientHandler.encoder().writeData(ctxClient(), 5, content2.retainedDuplicate(), 0, true,
|
||||
newPromiseClient());
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true);
|
||||
clientHandler.encoder().writeData(ctxClient(), 5, content2.retainedDuplicate(), 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitRequests();
|
||||
@ -456,7 +452,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(new AsciiString("GET"))
|
||||
.path(new AsciiString("/push/test"));
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers3, 0, true, newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers3, 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
awaitRequests();
|
||||
@ -475,12 +471,10 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
.scheme(new AsciiString("https"))
|
||||
.authority(new AsciiString("example.org"));
|
||||
runInChannel(serverConnectedChannel, () -> {
|
||||
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2Headers, 0, false, newPromiseServer());
|
||||
serverHandler.encoder().writePushPromise(ctxServer(), 3, 2, http2Headers2, 0, newPromiseServer());
|
||||
serverHandler.encoder().writeData(ctxServer(), 3, content.retainedDuplicate(), 0, true,
|
||||
newPromiseServer());
|
||||
serverHandler.encoder().writeData(ctxServer(), 5, content2.retainedDuplicate(), 0, true,
|
||||
newPromiseServer());
|
||||
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2Headers, 0, false);
|
||||
serverHandler.encoder().writePushPromise(ctxServer(), 3, 2, http2Headers2, 0);
|
||||
serverHandler.encoder().writeData(ctxServer(), 3, content.retainedDuplicate(), 0, true);
|
||||
serverHandler.encoder().writeData(ctxServer(), 5, content2.retainedDuplicate(), 0, true);
|
||||
serverConnectedChannel.flush();
|
||||
});
|
||||
awaitResponses();
|
||||
@ -518,7 +512,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
|
||||
try {
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
|
||||
clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
|
||||
clientChannel.flush();
|
||||
});
|
||||
|
||||
@ -528,8 +522,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
|
||||
final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(new AsciiString("100"));
|
||||
runInChannel(serverConnectedChannel, () -> {
|
||||
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse, 0, false,
|
||||
newPromiseServer());
|
||||
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse, 0, false);
|
||||
serverConnectedChannel.flush();
|
||||
});
|
||||
|
||||
@ -538,8 +531,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
|
||||
httpHeaders.remove(HttpHeaderNames.EXPECT);
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, payload.retainedDuplicate(), 0, true,
|
||||
newPromiseClient());
|
||||
clientHandler.encoder().writeData(ctxClient(), 3, payload.retainedDuplicate(), 0, true);
|
||||
clientChannel.flush();
|
||||
});
|
||||
|
||||
@ -551,8 +543,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
|
||||
final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(new AsciiString("200"));
|
||||
runInChannel(serverConnectedChannel, () -> {
|
||||
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse2, 0, true,
|
||||
newPromiseServer());
|
||||
serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse2, 0, true);
|
||||
serverConnectedChannel.flush();
|
||||
});
|
||||
|
||||
@ -586,7 +577,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
||||
boostrapEnv(1, 1, 2);
|
||||
final Http2Settings settings = new Http2Settings().pushEnabled(true);
|
||||
runInChannel(clientChannel, () -> {
|
||||
clientHandler.encoder().writeSettings(ctxClient(), settings, newPromiseClient());
|
||||
clientHandler.encoder().writeSettings(ctxClient(), settings);
|
||||
clientChannel.flush();
|
||||
});
|
||||
assertTrue(settingsLatch.await(5, SECONDS));
|
||||
|
@ -106,17 +106,16 @@ public class StreamBufferingEncoderTest {
|
||||
when(writer.configuration()).thenReturn(configuration);
|
||||
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
||||
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
||||
when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
|
||||
any(Promise.class))).thenAnswer(successAnswer());
|
||||
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class))).thenAnswer(
|
||||
when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()))
|
||||
.thenAnswer(successAnswer());
|
||||
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong())).thenAnswer(
|
||||
successAnswer());
|
||||
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
|
||||
any(Promise.class)))
|
||||
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)))
|
||||
.thenAnswer(successAnswer());
|
||||
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),
|
||||
anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Promise.class)))
|
||||
anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()))
|
||||
.thenAnswer(noopAnswer());
|
||||
|
||||
connection = new DefaultHttp2Connection(false);
|
||||
@ -138,6 +137,13 @@ public class StreamBufferingEncoderTest {
|
||||
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||
when(executor.inEventLoop()).thenReturn(true);
|
||||
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(channel.isActive()).thenReturn(false);
|
||||
when(channel.config()).thenReturn(config);
|
||||
@ -159,34 +165,34 @@ public class StreamBufferingEncoderTest {
|
||||
|
||||
@Test
|
||||
public void multipleWritesToActiveStream() {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoderWriteHeaders(3, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
encoderWriteHeaders(3);
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
ByteBuf data = data();
|
||||
final int expectedBytes = data.readableBytes() * 3;
|
||||
encoder.writeData(ctx, 3, data, 0, false, newPromise());
|
||||
encoder.writeData(ctx, 3, data(), 0, false, newPromise());
|
||||
encoder.writeData(ctx, 3, data(), 0, false, newPromise());
|
||||
encoderWriteHeaders(3, newPromise());
|
||||
encoder.writeData(ctx, 3, data, 0, false);
|
||||
encoder.writeData(ctx, 3, data(), 0, false);
|
||||
encoder.writeData(ctx, 3, data(), 0, false);
|
||||
encoderWriteHeaders(3);
|
||||
|
||||
writeVerifyWriteHeaders(times(1), 3);
|
||||
// Contiguous data writes are coalesced
|
||||
ArgumentCaptor<ByteBuf> bufCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureCanCreateNextStreamWhenStreamCloses() {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(1);
|
||||
|
||||
encoderWriteHeaders(3, newPromise());
|
||||
encoderWriteHeaders(3);
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
|
||||
// This one gets buffered.
|
||||
encoderWriteHeaders(5, newPromise());
|
||||
encoderWriteHeaders(5);
|
||||
assertEquals(1, connection.numActiveStreams());
|
||||
assertEquals(1, encoder.numBufferedStreams());
|
||||
|
||||
@ -206,45 +212,44 @@ public class StreamBufferingEncoderTest {
|
||||
|
||||
@Test
|
||||
public void alternatingWritesToActiveAndBufferedStreams() {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(1);
|
||||
|
||||
encoderWriteHeaders(3, newPromise());
|
||||
encoderWriteHeaders(3);
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
|
||||
encoderWriteHeaders(5, newPromise());
|
||||
encoderWriteHeaders(5);
|
||||
assertEquals(1, connection.numActiveStreams());
|
||||
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);
|
||||
encoder.writeData(ctx, 5, EMPTY_BUFFER, 0, false, newPromise());
|
||||
encoder.writeData(ctx, 5, EMPTY_BUFFER, 0, false);
|
||||
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
|
||||
public void bufferingNewStreamFailsAfterGoAwayReceived() throws Http2Exception {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(0);
|
||||
connection.goAwayReceived(1, 8, EMPTY_BUFFER);
|
||||
|
||||
Promise<Void> promise = newPromise();
|
||||
encoderWriteHeaders(3, promise);
|
||||
Future<Void> future = encoderWriteHeaders(3);
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
assertTrue(promise.isDone());
|
||||
assertFalse(promise.isSuccess());
|
||||
assertTrue(future.isDone());
|
||||
assertFalse(future.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void receivingGoAwayFailsBufferedStreams() throws Http2Exception {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(5);
|
||||
|
||||
int streamId = 3;
|
||||
List<Future<Void>> futures = new ArrayList<>();
|
||||
for (int i = 0; i < 9; i++) {
|
||||
futures.add(encoderWriteHeaders(streamId, newPromise()));
|
||||
futures.add(encoderWriteHeaders(streamId));
|
||||
streamId += 2;
|
||||
}
|
||||
assertEquals(5, connection.numActiveStreams());
|
||||
@ -266,11 +271,11 @@ public class StreamBufferingEncoderTest {
|
||||
|
||||
@Test
|
||||
public void receivingGoAwayFailsNewStreamIfMaxConcurrentStreamsReached() throws Http2Exception {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(1);
|
||||
encoderWriteHeaders(3, newPromise());
|
||||
encoderWriteHeaders(3);
|
||||
connection.goAwayReceived(11, 8, EMPTY_BUFFER);
|
||||
Future<Void> f = encoderWriteHeaders(5, newPromise());
|
||||
Future<Void> f = encoderWriteHeaders(5);
|
||||
|
||||
assertTrue(f.awaitUninterruptibly().cause() instanceof Http2GoAwayException);
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
@ -278,40 +283,41 @@ public class StreamBufferingEncoderTest {
|
||||
|
||||
@Test
|
||||
public void sendingGoAwayShouldNotFailStreams() {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(1);
|
||||
|
||||
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(),
|
||||
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());
|
||||
Future<Void> f2 = encoderWriteHeaders(5, newPromise());
|
||||
Future<Void> f2 = encoderWriteHeaders(5);
|
||||
assertEquals(1, encoder.numBufferedStreams());
|
||||
Future<Void> f3 = encoderWriteHeaders(7, newPromise());
|
||||
Future<Void> f3 = encoderWriteHeaders(7);
|
||||
assertEquals(2, encoder.numBufferedStreams());
|
||||
|
||||
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(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(f3.isDone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endStreamDoesNotFailBufferedStream() {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(0);
|
||||
|
||||
encoderWriteHeaders(3, newPromise());
|
||||
encoderWriteHeaders(3);
|
||||
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(1, encoder.numBufferedStreams());
|
||||
@ -319,7 +325,7 @@ public class StreamBufferingEncoderTest {
|
||||
// Simulate that we received a SETTINGS frame which
|
||||
// increased MAX_CONCURRENT_STREAMS to 1.
|
||||
setMaxConcurrentStreams(1);
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
|
||||
assertEquals(1, connection.numActiveStreams());
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
@ -328,61 +334,60 @@ public class StreamBufferingEncoderTest {
|
||||
|
||||
@Test
|
||||
public void rstStreamClosesBufferedStream() {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(0);
|
||||
|
||||
encoderWriteHeaders(3, newPromise());
|
||||
encoderWriteHeaders(3);
|
||||
assertEquals(1, encoder.numBufferedStreams());
|
||||
|
||||
Promise<Void> rstStreamPromise = newPromise();
|
||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstStreamPromise);
|
||||
assertTrue(rstStreamPromise.isSuccess());
|
||||
Future<Void> rstStreamFuture = encoder.writeRstStream(ctx, 3, CANCEL.code());
|
||||
assertTrue(rstStreamFuture.isSuccess());
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bufferUntilActiveStreamsAreReset() throws Exception {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(1);
|
||||
|
||||
encoderWriteHeaders(3, newPromise());
|
||||
encoderWriteHeaders(3);
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
encoderWriteHeaders(5, newPromise());
|
||||
encoderWriteHeaders(5);
|
||||
assertEquals(1, encoder.numBufferedStreams());
|
||||
encoderWriteHeaders(7, newPromise());
|
||||
encoderWriteHeaders(7);
|
||||
assertEquals(2, encoder.numBufferedStreams());
|
||||
|
||||
writeVerifyWriteHeaders(times(1), 3);
|
||||
writeVerifyWriteHeaders(never(), 5);
|
||||
writeVerifyWriteHeaders(never(), 7);
|
||||
|
||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), newPromise());
|
||||
encoder.writeRstStream(ctx, 3, CANCEL.code());
|
||||
connection.remote().flowController().writePendingBytes();
|
||||
writeVerifyWriteHeaders(times(1), 5);
|
||||
writeVerifyWriteHeaders(never(), 7);
|
||||
assertEquals(1, connection.numActiveStreams());
|
||||
assertEquals(1, encoder.numBufferedStreams());
|
||||
|
||||
encoder.writeRstStream(ctx, 5, CANCEL.code(), newPromise());
|
||||
encoder.writeRstStream(ctx, 5, CANCEL.code());
|
||||
connection.remote().flowController().writePendingBytes();
|
||||
writeVerifyWriteHeaders(times(1), 7);
|
||||
assertEquals(1, connection.numActiveStreams());
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
|
||||
encoder.writeRstStream(ctx, 7, CANCEL.code(), newPromise());
|
||||
encoder.writeRstStream(ctx, 7, CANCEL.code());
|
||||
assertEquals(0, connection.numActiveStreams());
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bufferUntilMaxStreamsIncreased() {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(2);
|
||||
|
||||
encoderWriteHeaders(3, newPromise());
|
||||
encoderWriteHeaders(5, newPromise());
|
||||
encoderWriteHeaders(7, newPromise());
|
||||
encoderWriteHeaders(9, newPromise());
|
||||
encoderWriteHeaders(3);
|
||||
encoderWriteHeaders(5);
|
||||
encoderWriteHeaders(7);
|
||||
encoderWriteHeaders(9);
|
||||
assertEquals(2, encoder.numBufferedStreams());
|
||||
|
||||
writeVerifyWriteHeaders(times(1), 3);
|
||||
@ -393,13 +398,13 @@ public class StreamBufferingEncoderTest {
|
||||
// Simulate that we received a SETTINGS frame which
|
||||
// increased MAX_CONCURRENT_STREAMS to 5.
|
||||
setMaxConcurrentStreams(5);
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
writeVerifyWriteHeaders(times(1), 7);
|
||||
writeVerifyWriteHeaders(times(1), 9);
|
||||
|
||||
encoderWriteHeaders(11, newPromise());
|
||||
encoderWriteHeaders(11);
|
||||
|
||||
writeVerifyWriteHeaders(times(1), 11);
|
||||
|
||||
@ -411,7 +416,7 @@ public class StreamBufferingEncoderTest {
|
||||
int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS;
|
||||
int numStreams = initialLimit * 2;
|
||||
for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) {
|
||||
encoderWriteHeaders(nextStreamId, newPromise());
|
||||
encoderWriteHeaders(nextStreamId);
|
||||
if (ix < initialLimit) {
|
||||
writeVerifyWriteHeaders(times(1), nextStreamId);
|
||||
} else {
|
||||
@ -432,7 +437,7 @@ public class StreamBufferingEncoderTest {
|
||||
int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS;
|
||||
int numStreams = initialLimit * 2;
|
||||
for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) {
|
||||
encoderWriteHeaders(nextStreamId, newPromise());
|
||||
encoderWriteHeaders(nextStreamId);
|
||||
if (ix < initialLimit) {
|
||||
writeVerifyWriteHeaders(times(1), nextStreamId);
|
||||
} else {
|
||||
@ -452,13 +457,13 @@ public class StreamBufferingEncoderTest {
|
||||
public void exhaustedStreamsDoNotBuffer() throws Http2Exception {
|
||||
// Write the highest possible stream ID for the client.
|
||||
// This will cause the next stream ID to be negative.
|
||||
encoderWriteHeaders(Integer.MAX_VALUE, newPromise());
|
||||
encoderWriteHeaders(Integer.MAX_VALUE);
|
||||
|
||||
// Disallow any further streams.
|
||||
setMaxConcurrentStreams(0);
|
||||
|
||||
// Simulate numeric overflow for the next stream ID.
|
||||
Future<Void> f = encoderWriteHeaders(-1, newPromise());
|
||||
Future<Void> f = encoderWriteHeaders(-1);
|
||||
|
||||
// Verify that the write fails.
|
||||
assertNotNull(f.awaitUninterruptibly().cause());
|
||||
@ -466,18 +471,17 @@ public class StreamBufferingEncoderTest {
|
||||
|
||||
@Test
|
||||
public void closedBufferedStreamReleasesByteBuf() {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
setMaxConcurrentStreams(0);
|
||||
ByteBuf data = mock(ByteBuf.class);
|
||||
Future<Void> f1 = encoderWriteHeaders(3, newPromise());
|
||||
Future<Void> f1 = encoderWriteHeaders(3);
|
||||
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);
|
||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstPromise);
|
||||
Future<Void> rstFuture = encoder.writeRstStream(ctx, 3, CANCEL.code());
|
||||
|
||||
assertEquals(0, encoder.numBufferedStreams());
|
||||
verify(rstPromise).setSuccess(null);
|
||||
assertTrue(rstFuture.isSuccess());
|
||||
assertTrue(f1.isSuccess());
|
||||
assertTrue(f2.isSuccess());
|
||||
verify(data).release();
|
||||
@ -485,12 +489,12 @@ public class StreamBufferingEncoderTest {
|
||||
|
||||
@Test
|
||||
public void closeShouldCancelAllBufferedStreams() throws Http2Exception {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
connection.local().maxActiveStreams(0);
|
||||
|
||||
Future<Void> f1 = encoderWriteHeaders(3, newPromise());
|
||||
Future<Void> f2 = encoderWriteHeaders(5, newPromise());
|
||||
Future<Void> f3 = encoderWriteHeaders(7, newPromise());
|
||||
Future<Void> f1 = encoderWriteHeaders(3);
|
||||
Future<Void> f2 = encoderWriteHeaders(5);
|
||||
Future<Void> f3 = encoderWriteHeaders(7);
|
||||
|
||||
encoder.close();
|
||||
assertNotNull(f1.awaitUninterruptibly().cause());
|
||||
@ -500,10 +504,10 @@ public class StreamBufferingEncoderTest {
|
||||
|
||||
@Test
|
||||
public void headersAfterCloseShouldImmediatelyFail() {
|
||||
encoder.writeSettingsAck(ctx, newPromise());
|
||||
encoder.writeSettingsAck(ctx);
|
||||
encoder.close();
|
||||
|
||||
Future<Void> f = encoderWriteHeaders(3, newPromise());
|
||||
Future<Void> f = encoderWriteHeaders(3);
|
||||
assertNotNull(f.cause());
|
||||
}
|
||||
|
||||
@ -517,12 +521,13 @@ public class StreamBufferingEncoderTest {
|
||||
}
|
||||
}
|
||||
|
||||
private Future<Void> encoderWriteHeaders(int streamId, Promise<Void> promise) {
|
||||
encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT,
|
||||
false, 0, false, promise);
|
||||
private Future<Void> encoderWriteHeaders(int streamId) {
|
||||
Future<Void> future =
|
||||
encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT,
|
||||
false, 0, false);
|
||||
try {
|
||||
encoder.flowController().writePendingBytes();
|
||||
return promise;
|
||||
return future;
|
||||
} catch (Http2Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -531,7 +536,7 @@ public class StreamBufferingEncoderTest {
|
||||
private void writeVerifyWriteHeaders(VerificationMode mode, int streamId) {
|
||||
verify(writer, mode).writeHeaders(eq(ctx), eq(streamId), any(Http2Headers.class), eq(0),
|
||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0),
|
||||
eq(false), any(Promise.class));
|
||||
eq(false));
|
||||
}
|
||||
|
||||
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) {
|
||||
// Send a frame for the response status
|
||||
Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
|
||||
encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());
|
||||
encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise());
|
||||
encoder().writeHeaders(ctx, streamId, headers, 0, false);
|
||||
encoder().writeData(ctx, streamId, payload, 0, true);
|
||||
|
||||
// no need to call flush as channelReadComplete(...) will take care of it.
|
||||
}
|
||||
|
@ -602,17 +602,8 @@ public class SslHandler extends ByteToMessageDecoder {
|
||||
* {@link ChannelHandlerContext#close()}
|
||||
*/
|
||||
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;
|
||||
Promise<Void> promise = ctx.newPromise();
|
||||
if (ctx.executor().inEventLoop()) {
|
||||
closeOutbound0(promise);
|
||||
} else {
|
||||
|
@ -101,14 +101,14 @@ public class Http2FrameWriterDataBenchmark extends AbstractMicrobenchmark {
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
public void newWriter() {
|
||||
writer.writeData(ctx, 3, payload.retain(), padding, true, ctx.newPromise());
|
||||
writer.writeData(ctx, 3, payload.retain(), padding, true);
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
public void oldWriter() {
|
||||
oldWriter.writeData(ctx, 3, payload.retain(), padding, true, ctx.newPromise());
|
||||
oldWriter.writeData(ctx, 3, payload.retain(), padding, true);
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@ -118,9 +118,9 @@ public class Http2FrameWriterDataBenchmark extends AbstractMicrobenchmark {
|
||||
private final int maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
|
||||
@Override
|
||||
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 =
|
||||
new Http2CodecUtil.SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||
new Http2CodecUtil.SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
|
||||
final DataFrameHeader header = new DataFrameHeader(ctx, streamId);
|
||||
boolean needToReleaseHeaders = true;
|
||||
boolean needToReleaseData = true;
|
||||
|
@ -88,8 +88,8 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
|
||||
private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {
|
||||
// Send a frame for the response status
|
||||
Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
|
||||
encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());
|
||||
encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise());
|
||||
encoder().writeHeaders(ctx, streamId, headers, 0, false);
|
||||
encoder().writeData(ctx, streamId, payload, 0, true);
|
||||
|
||||
// no need to call flush as channelReadComplete(...) will take care of it.
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user