Issue #5605 - java.io.IOException: unconsumed input during http request parsing.

Fixed test after merge.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2020-11-19 16:47:36 +01:00
parent cb10c35daf
commit 88c03ac1dc
2 changed files with 43 additions and 39 deletions

View File

@ -42,11 +42,11 @@ public class HttpInput extends ServletInputStream implements Runnable
private final byte[] _oneByteBuffer = new byte[1];
private final BlockingContentProducer _blockingContentProducer;
private final AsyncContentProducer _asyncContentProducer;
private final HttpChannelState _channelState;
private ContentProducer _contentProducer;
private boolean _consumedEof;
private ReadListener _readListener;
private long _contentConsumed;
public HttpInput(HttpChannelState state)
{
@ -56,8 +56,6 @@ public class HttpInput extends ServletInputStream implements Runnable
_contentProducer = _blockingContentProducer;
}
/* HttpInput */
public void recycle()
{
if (LOG.isDebugEnabled())
@ -66,6 +64,7 @@ public class HttpInput extends ServletInputStream implements Runnable
_contentProducer = _blockingContentProducer;
_consumedEof = false;
_readListener = null;
_contentConsumed = 0;
}
/**
@ -112,6 +111,18 @@ public class HttpInput extends ServletInputStream implements Runnable
}
}
public int get(Content content, byte[] bytes, int offset, int length)
{
int consumed = content.get(bytes, offset, length);
_contentConsumed += consumed;
return consumed;
}
public long getContentConsumed()
{
return _contentConsumed;
}
public long getContentReceived()
{
return _contentProducer.getRawContentArrived();
@ -216,7 +227,7 @@ public class HttpInput extends ServletInputStream implements Runnable
throw new IllegalStateException("read on unready input");
if (!content.isSpecial())
{
int read = content.get(b, off, len);
int read = get(content, b, off, len);
if (LOG.isDebugEnabled())
LOG.debug("read produced {} byte(s)", read);
if (content.isEmpty())

View File

@ -38,9 +38,8 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.client.util.AsyncRequestContent;
import org.eclipse.jetty.client.util.BytesRequestContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.HttpChannel;
@ -77,10 +76,6 @@ public class GzipWithSendErrorTest
private HttpClient client;
private ServerConnector connector;
private static void onComplete(Result result)
{
}
@BeforeEach
public void setup() throws Exception
{
@ -129,8 +124,8 @@ public class GzipWithSendErrorTest
response = client.newRequest(serverURI.resolve("/submit"))
.method(HttpMethod.POST)
.header(HttpHeader.CONTENT_ENCODING, "gzip")
.content(new BytesContentProvider("text/plain", compressed("normal-A")))
.headers(fields -> fields.put(HttpHeader.CONTENT_ENCODING, "gzip"))
.body(new BytesRequestContent("text/plain", compressed("normal-A")))
.send();
assertEquals(200, response.getStatus(), "Response status on /submit (normal-A)");
@ -138,8 +133,8 @@ public class GzipWithSendErrorTest
response = client.newRequest(serverURI.resolve("/fail"))
.method(HttpMethod.POST)
.header(HttpHeader.CONTENT_ENCODING, "gzip")
.content(new BytesContentProvider("text/plain", compressed("normal-B")))
.headers(fields -> fields.put(HttpHeader.CONTENT_ENCODING, "gzip"))
.body(new BytesRequestContent("text/plain", compressed("normal-B")))
.send();
assertEquals(400, response.getStatus(), "Response status on /fail (normal-B)");
@ -147,8 +142,8 @@ public class GzipWithSendErrorTest
response = client.newRequest(serverURI.resolve("/submit"))
.method(HttpMethod.POST)
.header(HttpHeader.CONTENT_ENCODING, "gzip")
.content(new BytesContentProvider("text/plain", compressed("normal-C")))
.headers(fields -> fields.put(HttpHeader.CONTENT_ENCODING, "gzip"))
.body(new BytesRequestContent("text/plain", compressed("normal-C")))
.send();
assertEquals(200, response.getStatus(), "Response status on /submit (normal-C)");
@ -218,7 +213,7 @@ public class GzipWithSendErrorTest
int sizeActuallySent = compressedRequest.length / 2;
ByteBuffer start = ByteBuffer.wrap(compressedRequest, 0, sizeActuallySent);
DeferredContentProvider contentProvider = new DeferredContentProvider(start)
AsyncRequestContent content = new AsyncRequestContent(start)
{
@Override
public long getLength()
@ -232,9 +227,9 @@ public class GzipWithSendErrorTest
client.newRequest(serverURI.resolve("/fail"))
.method(HttpMethod.POST)
.header(HttpHeader.CONTENT_TYPE, "application/octet-stream")
.header(HttpHeader.CONTENT_ENCODING, "gzip")
.content(contentProvider)
.headers(fields -> fields.put(HttpHeader.CONTENT_TYPE, "application/octet-stream"))
.headers(fields -> fields.put(HttpHeader.CONTENT_ENCODING, "gzip"))
.body(content)
.onResponseSuccess((response) ->
{
clientResponseRef.set(response);
@ -255,17 +250,17 @@ public class GzipWithSendErrorTest
// System.out.printf("Input Content Received: %,d%n", inputContentReceived.get());
// System.out.printf("Input BytesIn Count: %,d%n", inputBytesIn.get());
// Servlet didn't read body content
assertThat("Request Input Content Consumed not have been used", inputContentConsumed.get(), is(0L));
// Network reads
assertThat("Request Input Content Received should have seen content", inputContentReceived.get(), greaterThan(0L));
// Servlet didn't read body content.
assertThat("Request Input Content Consumed none", inputContentConsumed.get(), is(0L));
// Content arrived to the HttpChannel, but not to the HttpInput.
assertThat("Request Input Content Received", inputContentReceived.get(), is(0L));
assertThat("Request Input Content Received less then initial buffer", inputContentReceived.get(), lessThanOrEqualTo((long)sizeActuallySent));
assertThat("Request Connection BytesIn should have some minimal data", inputBytesIn.get(), greaterThanOrEqualTo(1024L));
assertThat("Request Connection BytesIn read should not have read all of the data", inputBytesIn.get(), lessThanOrEqualTo((long)sizeActuallySent));
// Now provide rest
contentProvider.offer(ByteBuffer.wrap(compressedRequest, sizeActuallySent, compressedRequest.length - sizeActuallySent));
contentProvider.close();
content.offer(ByteBuffer.wrap(compressedRequest, sizeActuallySent, compressedRequest.length - sizeActuallySent));
content.close();
assertTrue(clientResultComplete.await(5, TimeUnit.SECONDS));
}
@ -323,7 +318,7 @@ public class GzipWithSendErrorTest
int sizeActuallySent = compressedRequest.length / 2;
ByteBuffer start = ByteBuffer.wrap(compressedRequest, 0, sizeActuallySent);
DeferredContentProvider contentProvider = new DeferredContentProvider(start);
AsyncRequestContent content = new AsyncRequestContent(start);
AtomicReference<Response> clientResponseRef = new AtomicReference<>();
CountDownLatch clientResponseSuccessLatch = new CountDownLatch(1);
CountDownLatch clientResultComplete = new CountDownLatch(1);
@ -332,9 +327,9 @@ public class GzipWithSendErrorTest
client.newRequest(uri)
.method(HttpMethod.POST)
.header(HttpHeader.CONTENT_TYPE, "application/octet-stream")
.header(HttpHeader.CONTENT_ENCODING, "gzip")
.content(contentProvider)
.headers(fields -> fields.put(HttpHeader.CONTENT_TYPE, "application/octet-stream"))
.headers(fields -> fields.put(HttpHeader.CONTENT_ENCODING, "gzip"))
.body(content)
.onResponseSuccess((response) ->
{
clientResponseRef.set(response);
@ -355,19 +350,17 @@ public class GzipWithSendErrorTest
// System.out.printf("Input Content Received: %,d%n", inputContentReceived.get());
// System.out.printf("Input BytesIn Count: %,d%n", inputBytesIn.get());
long readCount = read ? 1L : 0L;
// Servlet read of body content
assertThat("Request Input Content Consumed not have been used", inputContentConsumed.get(), is(readCount));
// Network reads
assertThat("Request Input Content Received should have seen content", inputContentReceived.get(), greaterThan(0L));
// Servlet read of body content.
assertThat("Request Input Content Consumed " + (read ? "some" : "none"), inputContentConsumed.get(), is(read ? 1L : 0L));
// Content arrived to the HttpChannel, but not to the HttpInput.
assertThat("Request Input Content Received", inputContentReceived.get(), read ? greaterThan(0L) : is(0L));
assertThat("Request Input Content Received less then initial buffer", inputContentReceived.get(), lessThanOrEqualTo((long)sizeActuallySent));
assertThat("Request Connection BytesIn should have some minimal data", inputBytesIn.get(), greaterThanOrEqualTo(1024L));
assertThat("Request Connection BytesIn read should not have read all of the data", inputBytesIn.get(), lessThanOrEqualTo((long)sizeActuallySent));
// Now provide rest
contentProvider.offer(ByteBuffer.wrap(compressedRequest, sizeActuallySent, compressedRequest.length - sizeActuallySent));
contentProvider.close();
content.offer(ByteBuffer.wrap(compressedRequest, sizeActuallySent, compressedRequest.length - sizeActuallySent));
content.close();
assertTrue(clientResultComplete.await(5, TimeUnit.SECONDS));
}