8266900: java/net/httpclient/ShortResponseBody.java fails on windows with java.io.IOException: Unable to establish loopback connection

Reviewed-by: dfuchs
This commit is contained in:
Daniel Jeliński 2022-10-24 06:07:10 +00:00
parent aad81f2eba
commit 329b49a938
3 changed files with 96 additions and 143 deletions

@ -34,7 +34,6 @@ import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
@ -44,6 +43,7 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.test.lib.net.SimpleSSLContext;
import org.testng.ITestContext;
import org.testng.ITestResult;
@ -84,7 +84,10 @@ public abstract class ShortResponseBody {
SSLContext sslContext;
SSLParameters sslParameters;
HttpClient client;
int numberOfRequests;
static final int REQUESTS_PER_CLIENT = 10; // create new client every 10 requests
static final long PAUSE_FOR_GC = 5; // 5ms to let gc work
static final long PAUSE_FOR_PEER = 5; // 5ms to let server react
@ -119,12 +122,18 @@ public abstract class ShortResponseBody {
@BeforeMethod
void beforeMethod(ITestContext context) {
System.gc();
try {
Thread.sleep(PAUSE_FOR_GC);
} catch (InterruptedException x) {
if (client == null || numberOfRequests == REQUESTS_PER_CLIENT) {
numberOfRequests = 0;
out.println("--- new client");
client = newHttpClient();
System.gc();
try {
Thread.sleep(PAUSE_FOR_GC);
} catch (InterruptedException x) {
}
}
numberOfRequests++;
if (context.getFailedTests().size() > 0) {
if (skiptests.get() == null) {
SkipException skip = new SkipException("some tests failed");
@ -168,7 +177,6 @@ public abstract class ShortResponseBody {
@Test(dataProvider = "sanity")
void sanity(String url) throws Exception {
HttpClient client = newHttpClient();
url = uniqueURL(url);
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
out.println("Request: " + request);
@ -192,7 +200,6 @@ public abstract class ShortResponseBody {
@Test(dataProvider = "sanityBadRequest")
void sanityBadRequest(String url) throws Exception {
HttpClient client = newHttpClient();
url = uniqueURL(url);
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
out.println("Request: " + request);
@ -268,18 +275,9 @@ public abstract class ShortResponseBody {
return new Object[0][];
}
List<Object[]> list = new ArrayList<>();
Arrays.asList(cases).stream()
.map(e -> new Object[] {e[0], e[1], true}) // reuse client
.forEach(list::add);
Arrays.asList(cases).stream()
.map(e -> new Object[] {e[0], e[1], false}) // do not reuse client
.forEach(list::add);
return list.stream().toArray(Object[][]::new);
return cases;
}
static final int ITERATION_COUNT = 3;
HttpClient newHttpClient() {
return HttpClient.newBuilder()
.proxy(NO_PROXY)
@ -289,18 +287,6 @@ public abstract class ShortResponseBody {
.build();
}
HttpClient sharedClient = null;
HttpClient newHttpClient(boolean shared) {
if (shared) {
HttpClient sharedClient = this.sharedClient;
if (sharedClient == null) {
sharedClient = this.sharedClient = newHttpClient();
}
return sharedClient;
}
return newHttpClient();
}
// can be used to prolong request body publication
static final class InfiniteInputStream extends InputStream {
int count = 0;
@ -555,10 +541,8 @@ public abstract class ShortResponseBody {
private static void writeResponse(Socket socket, String response, int len) throws IOException {
OutputStream os = socket.getOutputStream();
byte[] responseBytes = response.getBytes(US_ASCII);
for (int i = 0; i < len; ++i) {
os.write(responseBytes[i]);
os.flush();
}
os.write(responseBytes, 0, len);
os.flush();
}
static final byte[] requestEnd = new byte[] { '\r', '\n', '\r', '\n' };
@ -703,7 +687,6 @@ public abstract class ShortResponseBody {
@AfterTest
public void teardown() throws Exception {
if (sharedClient != null) sharedClient = null;
closeImmediatelyServer.close();
closeImmediatelyHttpsServer.close();
variableLengthServer.close();

@ -35,74 +35,59 @@
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.ExecutionException;
import org.testng.annotations.Test;
import static java.lang.System.out;
import static java.net.http.HttpResponse.BodyHandlers.ofString;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
public class ShortResponseBodyGet extends ShortResponseBody {
@Test(dataProvider = "uris")
void testSynchronousGET(String urlp, String expectedMsg, boolean sameClient)
void testSynchronousGET(String urlp, String expectedMsg)
throws Exception
{
checkSkip();
out.print("---\n");
HttpClient client = null;
for (int i=0; i< ITERATION_COUNT; i++) {
String url = uniqueURL(urlp);
if (client == null)
client = newHttpClient(sameClient);
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
out.println("Request: " + request);
try {
HttpResponse<String> response = client.send(request, ofString());
String body = response.body();
out.println(response + ": " + body);
fail("UNEXPECTED RESPONSE: " + response);
} catch (IOException ioe) {
out.println("Caught expected exception:" + ioe);
assertExpectedMessage(request, ioe, expectedMsg);
// synchronous API must have the send method on the stack
assertSendMethodOnStack(ioe);
assertNoConnectionExpiredException(ioe);
}
String url = uniqueURL(urlp);
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
out.println("Request: " + request);
try {
HttpResponse<String> response = client.send(request, ofString());
String body = response.body();
out.println(response + ": " + body);
fail("UNEXPECTED RESPONSE: " + response);
} catch (IOException ioe) {
out.println("Caught expected exception:" + ioe);
assertExpectedMessage(request, ioe, expectedMsg);
// synchronous API must have the send method on the stack
assertSendMethodOnStack(ioe);
assertNoConnectionExpiredException(ioe);
}
}
@Test(dataProvider = "uris")
void testAsynchronousGET(String urlp, String expectedMsg, boolean sameClient)
void testAsynchronousGET(String urlp, String expectedMsg)
throws Exception
{
checkSkip();
out.print("---\n");
HttpClient client = null;
for (int i=0; i< ITERATION_COUNT; i++) {
String url = uniqueURL(urlp);
if (client == null)
client = newHttpClient(sameClient);
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
out.println("Request: " + request);
try {
HttpResponse<String> response = client.sendAsync(request, ofString()).get();
String body = response.body();
out.println(response + ": " + body);
fail("UNEXPECTED RESPONSE: " + response);
} catch (ExecutionException ee) {
if (ee.getCause() instanceof IOException) {
IOException ioe = (IOException) ee.getCause();
out.println("Caught expected exception:" + ioe);
assertExpectedMessage(request, ioe, expectedMsg);
assertNoConnectionExpiredException(ioe);
} else {
throw ee;
}
String url = uniqueURL(urlp);
HttpRequest request = HttpRequest.newBuilder(URI.create(url)).build();
out.println("Request: " + request);
try {
HttpResponse<String> response = client.sendAsync(request, ofString()).get();
String body = response.body();
out.println(response + ": " + body);
fail("UNEXPECTED RESPONSE: " + response);
} catch (ExecutionException ee) {
if (ee.getCause() instanceof IOException) {
IOException ioe = (IOException) ee.getCause();
out.println("Caught expected exception:" + ioe);
assertExpectedMessage(request, ioe, expectedMsg);
assertNoConnectionExpiredException(ioe);
} else {
throw ee;
}
}
}

@ -36,7 +36,6 @@
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
@ -46,8 +45,6 @@ import java.util.concurrent.ExecutionException;
import org.testng.annotations.Test;
import static java.lang.System.out;
import static java.net.http.HttpResponse.BodyHandlers.ofString;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
public class ShortResponseBodyPost extends ShortResponseBody {
@ -60,76 +57,64 @@ public class ShortResponseBodyPost extends ShortResponseBody {
@Test(dataProvider = "uris")
void testSynchronousPOST(String urlp, String expectedMsg, boolean sameClient)
void testSynchronousPOST(String urlp, String expectedMsg)
throws Exception
{
checkSkip();
out.print("---\n");
HttpClient client = null;
for (int i=0; i< ITERATION_COUNT; i++) {
String url = uniqueURL(urlp);
if (client == null)
client = newHttpClient(sameClient);
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.POST(BodyPublishers.ofInputStream(() -> new InfiniteInputStream()))
.build();
out.println("Request: " + request);
try {
HttpResponse<String> response = client.send(request, ofString());
String body = response.body();
out.println(response + ": " + body);
fail("UNEXPECTED RESPONSE: " + response);
} catch (IOException ioe) {
String url = uniqueURL(urlp);
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.POST(BodyPublishers.ofInputStream(() -> new InfiniteInputStream()))
.build();
out.println("Request: " + request);
try {
HttpResponse<String> response = client.send(request, ofString());
String body = response.body();
out.println(response + ": " + body);
fail("UNEXPECTED RESPONSE: " + response);
} catch (IOException ioe) {
out.println("Caught expected exception:" + ioe);
List<String> expectedMessages = new ArrayList<>();
expectedMessages.add(expectedMsg);
MSGS_ORDER.stream().takeWhile(s -> !s.equals(expectedMsg))
.forEach(expectedMessages::add);
assertExpectedMessage(request, ioe, expectedMessages);
// synchronous API must have the send method on the stack
assertSendMethodOnStack(ioe);
assertNoConnectionExpiredException(ioe);
}
}
@Test(dataProvider = "uris")
void testAsynchronousPOST(String urlp, String expectedMsg)
throws Exception
{
checkSkip();
String url = uniqueURL(urlp);
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.POST(BodyPublishers.ofInputStream(() -> new InfiniteInputStream()))
.build();
out.println("Request: " + request);
try {
HttpResponse<String> response = client.sendAsync(request, ofString()).get();
String body = response.body();
out.println(response + ": " + body);
fail("UNEXPECTED RESPONSE: " + response);
} catch (ExecutionException ee) {
if (ee.getCause() instanceof IOException) {
IOException ioe = (IOException) ee.getCause();
out.println("Caught expected exception:" + ioe);
List<String> expectedMessages = new ArrayList<>();
expectedMessages.add(expectedMsg);
MSGS_ORDER.stream().takeWhile(s -> !s.equals(expectedMsg))
.forEach(expectedMessages::add);
.forEach(expectedMessages::add);
assertExpectedMessage(request, ioe, expectedMessages);
// synchronous API must have the send method on the stack
assertSendMethodOnStack(ioe);
assertNoConnectionExpiredException(ioe);
}
}
}
@Test(dataProvider = "uris")
void testAsynchronousPOST(String urlp, String expectedMsg, boolean sameClient)
throws Exception
{
checkSkip();
out.print("---\n");
HttpClient client = null;
for (int i=0; i< ITERATION_COUNT; i++) {
String url = uniqueURL(urlp);
if (client == null)
client = newHttpClient(sameClient);
HttpRequest request = HttpRequest.newBuilder(URI.create(url))
.POST(BodyPublishers.ofInputStream(() -> new InfiniteInputStream()))
.build();
out.println("Request: " + request);
try {
HttpResponse<String> response = client.sendAsync(request, ofString()).get();
String body = response.body();
out.println(response + ": " + body);
fail("UNEXPECTED RESPONSE: " + response);
} catch (ExecutionException ee) {
if (ee.getCause() instanceof IOException) {
IOException ioe = (IOException) ee.getCause();
out.println("Caught expected exception:" + ioe);
List<String> expectedMessages = new ArrayList<>();
expectedMessages.add(expectedMsg);
MSGS_ORDER.stream().takeWhile(s -> !s.equals(expectedMsg))
.forEach(expectedMessages::add);
assertExpectedMessage(request, ioe, expectedMessages);
assertNoConnectionExpiredException(ioe);
} else {
throw ee;
}
} else {
throw ee;
}
}
}