diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java index e1fea7b8b7e..1ca753c1d8f 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/AbstractHttpClientServerTest.java @@ -70,16 +70,25 @@ public abstract class AbstractHttpClientServerTest } if (server == null) - server = new Server(); + { + QueuedThreadPool serverThreads = new QueuedThreadPool(); + serverThreads.setName("server"); + server = new Server(serverThreads); + } connector = new ServerConnector(server, sslContextFactory); server.addConnector(connector); server.setHandler(handler); server.start(); - QueuedThreadPool executor = new QueuedThreadPool(); - executor.setName(executor.getName() + "-client"); + startClient(); + } + + protected void startClient() throws Exception + { + QueuedThreadPool clientThreads = new QueuedThreadPool(); + clientThreads.setName("client"); client = new HttpClient(sslContextFactory); - client.setExecutor(executor); + client.setExecutor(clientThreads); client.start(); } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientContinueTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientContinueTest.java index 4c449850df6..a7ed98cecf1 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientContinueTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientContinueTest.java @@ -20,6 +20,11 @@ package org.eclipse.jetty.client; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Iterator; @@ -645,4 +650,75 @@ public class HttpClientContinueTest extends AbstractHttpClientServerTest Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } + + @Test + public void test_Expect100Continue_WithTwoResponsesInOneRead() throws Exception + { + // There is a chance that the server replies with the 100 Continue response + // and immediately after with the "normal" response, say a 200 OK. + // These may be read by the client in a single read, and must be handled correctly. + + startClient(); + + try (ServerSocket server = new ServerSocket()) + { + server.bind(new InetSocketAddress("localhost", 0)); + + final CountDownLatch latch = new CountDownLatch(1); + client.newRequest("localhost", server.getLocalPort()) + .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) + .content(new BytesContentProvider(new byte[]{0})) + .send(new Response.CompleteListener() + { + @Override + public void onComplete(Result result) + { + Assert.assertTrue(result.toString(), result.isSucceeded()); + Assert.assertEquals(200, result.getResponse().getStatus()); + latch.countDown(); + } + }); + + try (Socket socket = server.accept()) + { + // Read the request headers. + InputStream input = socket.getInputStream(); + int crlfs = 0; + while (true) + { + int read = input.read(); + if (read == '\r' || read == '\n') + ++crlfs; + else + crlfs = 0; + if (crlfs == 4) + break; + } + + OutputStream output = socket.getOutputStream(); + String responses = "" + + "HTTP/1.1 100 Continue\r\n" + + "\r\n" + + "HTTP/1.1 200 OK\r\n" + + "Transfer-Encoding: chunked\r\n" + + "\r\n" + + "10\r\n" + + "0123456789ABCDEF\r\n"; + output.write(responses.getBytes(StandardCharsets.UTF_8)); + output.flush(); + + Thread.sleep(1000); + + String content = "" + + "10\r\n" + + "0123456789ABCDEF\r\n" + + "0\r\n" + + "\r\n"; + output.write(content.getBytes(StandardCharsets.UTF_8)); + output.flush(); + + Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); + } + } + } }