Skip to content

Commit 35f9763

Browse files
committed
Fixes #13896 - listener.onContent(...) clears that ByteBuffer before the call.
Now the buffer used to notify request content listener is sliced, so that the clear() performed before invoking the listeners has no effect. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
1 parent 8f487bc commit 35f9763

File tree

2 files changed

+61
-8
lines changed

2 files changed

+61
-8
lines changed

jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpSender.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ private class ContentSender extends IteratingCallback
480480
private volatile boolean expect100;
481481
// Fields only used internally.
482482
private Content.Chunk chunk;
483-
private ByteBuffer contentBuffer;
483+
private ByteBuffer notifyBuffer;
484484
private boolean committed;
485485
private boolean success;
486486
private boolean complete;
@@ -494,7 +494,7 @@ public boolean reset()
494494
proceedAction = null;
495495
expect100 = false;
496496
chunk = null;
497-
contentBuffer = null;
497+
notifyBuffer = null;
498498
committed = false;
499499
success = false;
500500
complete = false;
@@ -567,7 +567,8 @@ protected Action process() throws Throwable
567567
}
568568

569569
ByteBuffer buffer = chunk.getByteBuffer();
570-
contentBuffer = buffer.asReadOnlyBuffer();
570+
// Save the buffer used to notify request content listeners.
571+
notifyBuffer = buffer.slice().asReadOnlyBuffer();
571572
boolean last = chunk.isLast();
572573
if (committed)
573574
sendContent(exchange, buffer, last, this);
@@ -590,8 +591,8 @@ protected void onSuccess()
590591
boolean proceed = true;
591592
if (committed)
592593
{
593-
if (contentBuffer.hasRemaining())
594-
proceed = someToContent(exchange, contentBuffer);
594+
if (notifyBuffer.hasRemaining())
595+
proceed = someToContent(exchange, notifyBuffer);
595596
}
596597
else
597598
{
@@ -600,8 +601,8 @@ protected void onSuccess()
600601
if (proceed)
601602
{
602603
// Was any content sent while committing?
603-
if (contentBuffer.hasRemaining())
604-
proceed = someToContent(exchange, contentBuffer);
604+
if (notifyBuffer.hasRemaining())
605+
proceed = someToContent(exchange, notifyBuffer);
605606
}
606607
}
607608

jetty-core/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,59 @@ public void onComplete(Result result)
11401140

11411141
@ParameterizedTest
11421142
@ArgumentsSource(ScenarioProvider.class)
1143-
public void setOnCompleteCallbackWithBlockingSend(Scenario scenario) throws Exception
1143+
public void testRequestContentListenerBytesCounting(Scenario scenario) throws Exception
1144+
{
1145+
start(scenario, new EmptyServerHandler());
1146+
1147+
int length = 41;
1148+
ByteBuffer requestByteBuffer = BufferUtil.allocate(length * 2).limit(length);
1149+
AtomicInteger requestContentLength = new AtomicInteger();
1150+
1151+
class NonSlicingRequestContent implements Request.Content
1152+
{
1153+
private boolean read;
1154+
1155+
@Override
1156+
public Content.Chunk read()
1157+
{
1158+
if (read)
1159+
return Content.Chunk.EOF;
1160+
read = true;
1161+
return Content.Chunk.from(requestByteBuffer, true);
1162+
}
1163+
1164+
@Override
1165+
public void demand(Runnable demandCallback)
1166+
{
1167+
demandCallback.run();
1168+
}
1169+
1170+
@Override
1171+
public long getLength()
1172+
{
1173+
return length;
1174+
}
1175+
1176+
@Override
1177+
public void fail(Throwable failure)
1178+
{
1179+
}
1180+
}
1181+
1182+
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
1183+
.scheme(scenario.getScheme())
1184+
.onRequestContent((rq, bb) -> requestContentLength.addAndGet(bb.remaining()))
1185+
.body(new NonSlicingRequestContent())
1186+
.timeout(5, TimeUnit.SECONDS)
1187+
.send();
1188+
1189+
assertThat(response.getStatus(), is(HttpStatus.OK_200));
1190+
assertThat(requestContentLength.get(), is(length));
1191+
}
1192+
1193+
@ParameterizedTest
1194+
@ArgumentsSource(ScenarioProvider.class)
1195+
public void testSetOnCompleteCallbackWithBlockingSend(Scenario scenario) throws Exception
11441196
{
11451197
byte[] content = new byte[512];
11461198
new Random().nextBytes(content);

0 commit comments

Comments
 (0)