Skip to content

Commit f4dfd01

Browse files
committed
HTTP client stream should handle a write to a stream which has been reset without having being allocated.
Motivation: Vert.x HTTP client stream does not allocate a stream when the stream has been reset by the application before its allocation. When such stream is being written, the stream behaves normally and fails since the internal state is not correct. Changes: Record the reset state of a stream and guard against writes in such case.
1 parent 001a07c commit f4dfd01

File tree

3 files changed

+78
-33
lines changed

3 files changed

+78
-33
lines changed

vertx-core/src/main/java/io/vertx/core/http/impl/Http1xClientConnection.java

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,11 @@ private void endRequest(Stream s) {
317317
* @param stream to reset
318318
* @return whether the stream should be considered as closed
319319
*/
320-
private boolean reset(Stream stream) {
320+
private Boolean reset(Stream stream) {
321+
if (stream.reset) {
322+
return null;
323+
}
324+
stream.reset = true;
321325
if (!responses.contains(stream)) {
322326
requests.remove(stream);
323327
return true;
@@ -326,16 +330,20 @@ private boolean reset(Stream stream) {
326330
return false;
327331
}
328332

329-
private void writeHead(Stream stream, HttpRequestHead request, boolean chunked, ByteBuf buf, boolean end, boolean connect, PromiseInternal<Void> handler) {
333+
private void writeHead(Stream stream, HttpRequestHead request, boolean chunked, ByteBuf buf, boolean end, boolean connect, PromiseInternal<Void> listener) {
330334
writeToChannel(new MessageWrite() {
331335
@Override
332336
public void write() {
337+
if (stream.reset) {
338+
listener.fail("Stream reset");
339+
return;
340+
}
333341
stream.request = request;
334-
beginRequest(stream, request, chunked, buf, end, connect, handler);
342+
beginRequest(stream, request, chunked, buf, end, connect, listener);
335343
}
336344
@Override
337345
public void cancel(Throwable cause) {
338-
handler.fail(cause);
346+
listener.fail(cause);
339347
}
340348
});
341349
}
@@ -344,6 +352,10 @@ private void writeBuffer(Stream stream, ByteBuf buff, boolean end, PromiseIntern
344352
writeToChannel(new MessageWrite() {
345353
@Override
346354
public void write() {
355+
if (stream.reset) {
356+
listener.fail("Stream reset");
357+
return;
358+
}
347359
writeBuffer(stream, buff, end, (FutureListener<Void>)listener);
348360
}
349361

@@ -368,7 +380,7 @@ private abstract static class Stream {
368380
private boolean responseEnded;
369381
private long bytesRead;
370382
private long bytesWritten;
371-
383+
private boolean reset;
372384

373385
Stream(ContextInternal context, Promise<HttpClientStream> promise, int id) {
374386
this.context = context;
@@ -404,7 +416,6 @@ private static class StreamImpl extends Stream implements HttpClientStream {
404416

405417
private final Http1xClientConnection conn;
406418
private final InboundMessageQueue<Object> queue;
407-
private boolean reset;
408419
private boolean closed;
409420
private Handler<HttpResponseHead> headHandler;
410421
private Handler<Buffer> chunkHandler;
@@ -431,18 +442,16 @@ protected void handlePause() {
431442
}
432443
@Override
433444
protected void handleMessage(Object item) {
434-
if (!reset) {
435-
if (item instanceof MultiMap) {
436-
Handler<MultiMap> handler = endHandler;
437-
if (handler != null) {
438-
context.dispatch((MultiMap) item, handler);
439-
}
440-
} else {
441-
Buffer buffer = (Buffer) item;
442-
Handler<Buffer> handler = chunkHandler;
443-
if (handler != null) {
444-
context.dispatch(buffer, handler);
445-
}
445+
if (item instanceof MultiMap) {
446+
Handler<MultiMap> handler = endHandler;
447+
if (handler != null) {
448+
context.dispatch((MultiMap) item, handler);
449+
}
450+
} else {
451+
Buffer buffer = (Buffer) item;
452+
Handler<Buffer> handler = chunkHandler;
453+
if (handler != null) {
454+
context.dispatch(buffer, handler);
446455
}
447456
}
448457
}
@@ -584,26 +593,24 @@ public Future<Void> reset(Throwable cause) {
584593
Promise<Void> promise = context.promise();
585594
EventLoop eventLoop = conn.context.nettyEventLoop();
586595
if (eventLoop.inEventLoop()) {
587-
_reset(cause, promise);
596+
reset(cause, promise);
588597
} else {
589-
eventLoop.execute(() -> _reset(cause, promise));
598+
eventLoop.execute(() -> reset(cause, promise));
590599
}
591600
return promise.future();
592601
}
593602

594-
private void _reset(Throwable cause, Promise<Void> promise) {
595-
if (reset) {
603+
private void reset(Throwable cause, Promise<Void> promise) {
604+
Boolean removed = conn.reset(this);
605+
if (removed == null) {
596606
promise.fail("Stream already reset");
597-
return;
598-
}
599-
reset = true;
600-
boolean removed = conn.reset(this);
601-
if (removed) {
602-
context.execute(cause, this::handleClosed);
603607
} else {
604-
context.execute(cause, this::handleException);
605-
}
606-
promise.complete();
608+
if (removed) {
609+
context.execute(cause, this::handleClosed);
610+
} else {
611+
context.execute(cause, this::handleException);
612+
}
613+
promise.complete(); }
607614
}
608615

609616
@Override
@@ -856,7 +863,9 @@ private void handleResponseChunk(Stream stream, ByteBuf chunk) {
856863
Buffer buff = BufferInternal.safeBuffer(chunk);
857864
int len = buff.length();
858865
stream.bytesRead += len;
859-
stream.handleChunk(buff);
866+
if (!stream.reset) {
867+
stream.handleChunk(buff);
868+
}
860869
}
861870

862871
private void handleResponseEnd(Stream stream, LastHttpContent trailer) {
@@ -908,7 +917,9 @@ private void handleResponseEnd(Stream stream, LastHttpContent trailer) {
908917
checkLifecycle();
909918
}
910919
lastResponseReceivedTimestamp = System.currentTimeMillis();
911-
stream.handleEnd(trailer);
920+
if (!stream.reset) {
921+
stream.handleEnd(trailer);
922+
}
912923
if (stream.requestEnded) {
913924
stream.handleClosed(null);
914925
}

vertx-core/src/main/java/io/vertx/core/http/impl/VertxHttp2Stream.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ abstract class VertxHttp2Stream<C extends Http2ConnectionBase> {
5353
private long bytesWritten;
5454
protected boolean isConnect;
5555
private Throwable failure;
56+
private long reset = -1L;
5657

5758
VertxHttp2Stream(C conn, ContextInternal context) {
5859
this.conn = conn;
@@ -126,6 +127,7 @@ void onException(Throwable cause) {
126127
}
127128

128129
void onReset(long code) {
130+
reset = code;
129131
context.emit(code, this::handleReset);
130132
}
131133

@@ -246,6 +248,12 @@ public void cancel(Throwable cause) {
246248
}
247249

248250
void doWriteHeaders(Http2Headers headers, boolean end, boolean checkFlush, Promise<Void> promise) {
251+
if (reset != -1L) {
252+
if (promise != null) {
253+
promise.fail("Stream reset");
254+
}
255+
return;
256+
}
249257
if (end) {
250258
endWritten();
251259
}
@@ -273,6 +281,10 @@ public void cancel(Throwable cause) {
273281
}
274282

275283
void doWriteData(ByteBuf buf, boolean end, Promise<Void> promise) {
284+
if (reset != -1L) {
285+
promise.fail("Stream reset");
286+
return;
287+
}
276288
ByteBuf chunk;
277289
if (buf == null && end) {
278290
chunk = Unpooled.EMPTY_BUFFER;
@@ -289,6 +301,9 @@ void doWriteData(ByteBuf buf, boolean end, Promise<Void> promise) {
289301
}
290302

291303
final Future<Void> writeReset(long code) {
304+
if (code < 0L) {
305+
throw new IllegalArgumentException("Invalid reset code value");
306+
}
292307
Promise<Void> promise = context.promise();
293308
EventLoop eventLoop = conn.context().nettyEventLoop();
294309
if (eventLoop.inEventLoop()) {
@@ -300,6 +315,11 @@ final Future<Void> writeReset(long code) {
300315
}
301316

302317
protected void doWriteReset(long code, Promise<Void> promise) {
318+
if (reset != -1L) {
319+
promise.fail("Stream already reset");
320+
return;
321+
}
322+
reset = code;
303323
int streamId;
304324
synchronized (this) {
305325
streamId = stream != null ? stream.id() : -1;

vertx-core/src/test/java/io/vertx/tests/http/HttpTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5863,6 +5863,20 @@ public void testResetClientRequestResponseInProgress() throws Exception {
58635863
await();
58645864
}
58655865

5866+
@Test
5867+
public void testResetPartialClientRequest() throws Exception {
5868+
server.requestHandler(req -> {
5869+
});
5870+
startServer(testAddress);
5871+
client.request(requestOptions).onComplete(onSuccess(req -> {
5872+
assertTrue(req.reset().succeeded());
5873+
req.end("body").onComplete(onFailure(err -> {
5874+
testComplete();
5875+
}));
5876+
}));
5877+
await();
5878+
}
5879+
58665880
@Test
58675881
public void testSimpleCookie() throws Exception {
58685882
testCookies("foo=bar", req -> {

0 commit comments

Comments
 (0)