Skip to content
Permalink
Browse files
8233403: Improve verbosity of some httpclient tests
Improve the verbosity of some httpclient tests to help diagnosis of intermittent failures. Also fixes ShortRequestBody test.

Backport-of: eaba9fe
  • Loading branch information
GoeLin committed Sep 22, 2021
1 parent 8c2cecc commit d78b4c78b12effeceeeb2cb704e6aea4ce93e911
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,10 @@
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
import jdk.testlibrary.SimpleSSLContext;
import org.testng.ITestContext;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -132,6 +134,17 @@ public void execute(Runnable command) {
}
}

protected boolean stopAfterFirstFailure() {
return Boolean.getBoolean("jdk.internal.httpclient.debug");
}

@BeforeMethod
void beforeMethod(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
throw new RuntimeException("some tests failed");
}
}

@AfterClass
static final void printFailedTests() {
out.println("\n=========================");
@@ -217,52 +230,73 @@ public void accept(Where where) {
}

@DataProvider(name = "subscribeProvider")
public Object[][] subscribeProvider() {
public Object[][] subscribeProvider(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
return variants(List.of(
new UncheckedCustomExceptionThrower(),
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.BEFORE_SUBSCRIBE, Where.AFTER_SUBSCRIBE));
}

@DataProvider(name = "requestProvider")
public Object[][] requestProvider() {
public Object[][] requestProvider(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
return variants(List.of(
new UncheckedCustomExceptionThrower(),
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.BEFORE_REQUEST, Where.AFTER_REQUEST));
}

@DataProvider(name = "nextRequestProvider")
public Object[][] nextRequestProvider() {
public Object[][] nextRequestProvider(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
return variants(List.of(
new UncheckedCustomExceptionThrower(),
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.BEFORE_NEXT_REQUEST, Where.AFTER_NEXT_REQUEST));
}

@DataProvider(name = "beforeCancelProviderIO")
public Object[][] beforeCancelProviderIO() {
public Object[][] beforeCancelProviderIO(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
return variants(List.of(
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.BEFORE_CANCEL));
}

@DataProvider(name = "afterCancelProviderIO")
public Object[][] afterCancelProviderIO() {
public Object[][] afterCancelProviderIO(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
return variants(List.of(
new UncheckedIOExceptionThrower()),
EnumSet.of(Where.AFTER_CANCEL));
}

@DataProvider(name = "beforeCancelProviderCustom")
public Object[][] beforeCancelProviderCustom() {
public Object[][] beforeCancelProviderCustom(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
return variants(List.of(
new UncheckedCustomExceptionThrower()),
EnumSet.of(Where.BEFORE_CANCEL));
}

@DataProvider(name = "afterCancelProviderCustom")
public Object[][] afterCancelProvider() {
public Object[][] afterCancelProvider(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
return variants(List.of(
new UncheckedCustomExceptionThrower()),
EnumSet.of(Where.AFTER_CANCEL));
@@ -41,11 +41,12 @@
*/

import jdk.testlibrary.SimpleSSLContext;
import org.testng.ITestContext;
import org.testng.annotations.AfterTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
@@ -145,6 +146,17 @@ public void execute(Runnable command) {
}
}

protected boolean stopAfterFirstFailure() {
return Boolean.getBoolean("jdk.internal.httpclient.debug");
}

@BeforeMethod
void beforeMethod(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
throw new RuntimeException("some tests failed");
}
}

@AfterClass
static final void printFailedTests() {
out.println("\n=========================");
@@ -207,27 +219,38 @@ public void accept(Where where) {

private Object[][] variants(List<Thrower> throwers) {
String[] uris = uris();
Object[][] result = new Object[uris.length * 2 * throwers.size()][];
// reduce traces by always using the same client if
// stopAfterFirstFailure is requested.
List<Boolean> sameClients = stopAfterFirstFailure()
? List.of(true)
: List.of(false, true);
Object[][] result = new Object[uris.length * sameClients.size() * throwers.size()][];
int i = 0;
for (Thrower thrower : throwers) {
for (boolean sameClient : List.of(false, true)) {
for (boolean sameClient : sameClients) {
for (String uri : uris()) {
result[i++] = new Object[]{uri, sameClient, thrower};
}
}
}
assert i == uris.length * 2 * throwers.size();
assert i == uris.length * sameClients.size() * throwers.size();
return result;
}

@DataProvider(name = "ioVariants")
public Object[][] ioVariants() {
public Object[][] ioVariants(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
return variants(List.of(
new UncheckedIOExceptionThrower()));
}

@DataProvider(name = "customVariants")
public Object[][] customVariants() {
public Object[][] customVariants(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
return variants(List.of(
new UncheckedCustomExceptionThrower()));
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,10 @@
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
import jdk.testlibrary.SimpleSSLContext;
import org.testng.ITestContext;
import org.testng.annotations.AfterTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -131,6 +133,17 @@ public void execute(Runnable command) {
}
}

protected boolean stopAfterFirstFailure() {
return Boolean.getBoolean("jdk.internal.httpclient.debug");
}

@BeforeMethod
void beforeMethod(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
throw new RuntimeException("some tests failed");
}
}

@AfterClass
static final void printFailedTests() {
out.println("\n=========================");
@@ -182,7 +195,10 @@ static final void printFailedTests() {
}

@DataProvider(name = "variants")
public Object[][] variants() {
public Object[][] variants(ITestContext context) {
if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {
return new Object[0][];
}
String[] uris = uris();
Object[][] result = new Object[uris.length * 2 * 2][];
int i = 0;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
@@ -76,6 +77,7 @@
BYTE_ARRAY_BODY.length,
fileSize(FILE_BODY) };
static final int[] BODY_OFFSETS = new int[] { 0, +1, -1, +2, -2, +3, -3 };
static final String MARKER = "ShortRequestBody";

// A delegating Body Publisher. Subtypes will have a concrete body type.
static abstract class AbstractDelegateRequestBody
@@ -134,7 +136,7 @@ public static void main(String[] args) throws Exception {
try (Server server = new Server()) {
for (Supplier<HttpClient> cs : clientSuppliers) {
err.println("\n---- next supplier ----\n");
URI uri = new URI("http://localhost:" + server.getPort() + "/");
URI uri = new URI("http://localhost:" + server.getPort() + "/" + MARKER);

// sanity ( 6 requests to keep client and server offsets easy to workout )
success(cs, uri, new StringRequestBody(STRING_BODY, 0));
@@ -248,44 +250,56 @@ public void run() {
int offset = 0;

while (!closed) {
err.println("Server: waiting for connection");
try (Socket s = ss.accept()) {
err.println("Server: got connection");
InputStream is = s.getInputStream();
readRequestHeaders(is);
try {
String headers = readRequestHeaders(is);
if (headers == null) continue;
} catch (SocketException ex) {
err.println("Ignoring unexpected exception while reading headers: " + ex);
ex.printStackTrace(err);
// proceed in order to update count etc..., even though
// we know that read() will fail;
}
byte[] ba = new byte[1024];

int length = BODY_LENGTHS[count % 3];
length += BODY_OFFSETS[offset];
err.println("Server: count=" + count + ", offset=" + offset);
err.println("Server: expecting " +length+ " bytes");
int read = is.readNBytes(ba, 0, length);
err.println("Server: actually read " + read + " bytes");

// Update the counts before replying, to prevent the
// client-side racing reset with this thread.
count++;
if (count % 6 == 0) // 6 is the number of failure requests per offset
offset++;
if (count % 42 == 0) {
count = 0; // reset, for second iteration
offset = 0;
int read = 0;
try {
read = is.readNBytes(ba, 0, length);
err.println("Server: actually read " + read + " bytes");
} finally {
// Update the counts before replying, to prevent the
// client-side racing reset with this thread.
count++;
if (count % 6 == 0) // 6 is the number of failure requests per offset
offset++;
if (count % 42 == 0) {
count = 0; // reset, for second iteration
offset = 0;
}
}

if (read < length) {
// no need to reply, client has already closed
// ensure closed
if (is.read() != -1)
new AssertionError("Unexpected read");
new AssertionError("Unexpected read: " + read);
} else {
OutputStream os = s.getOutputStream();
err.println("Server: writing "
+ RESPONSE.getBytes(US_ASCII).length + " bytes");
os.write(RESPONSE.getBytes(US_ASCII));
}

} catch (IOException e) {
if (!closed)
System.out.println("Unexpected" + e);
} catch (Throwable e) {
if (!closed) {
err.println("Unexpected: " + e);
e.printStackTrace();
}
}
}
}
@@ -306,9 +320,14 @@ public void close() {
static final byte[] requestEnd = new byte[] {'\r', '\n', '\r', '\n' };

// Read until the end of a HTTP request headers
static void readRequestHeaders(InputStream is) throws IOException {
int requestEndCount = 0, r;
static String readRequestHeaders(InputStream is) throws IOException {
int requestEndCount = 0, r, eol = -1;
StringBuilder headers = new StringBuilder();
while ((r = is.read()) != -1) {
if (r == '\r' && eol < 0) {
eol = headers.length();
}
headers.append((char) r);
if (r == requestEnd[requestEndCount]) {
requestEndCount++;
if (requestEndCount == 4) {
@@ -318,6 +337,11 @@ static void readRequestHeaders(InputStream is) throws IOException {
requestEndCount = 0;
}
}

if (eol <= 0) return null;
String requestLine = headers.toString().substring(0, eol);
if (!requestLine.contains(MARKER)) return null;
return headers.toString();
}

static int fileSize(Path p) {

1 comment on commit d78b4c7

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on d78b4c7 Sep 22, 2021

Please sign in to comment.