Skip to content

Commit 04b0e78

Browse files
committed
8307648: java/net/httpclient/ExpectContinueTest.java timed out
Reviewed-by: djelinski
1 parent 6b90b05 commit 04b0e78

File tree

3 files changed

+68
-10
lines changed

3 files changed

+68
-10
lines changed

src/java.net.http/share/classes/jdk/internal/net/http/Stream.java

+29-5
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ void incoming(Http2Frame frame) throws IOException {
475475
if ((frame instanceof HeaderFrame hf)) {
476476
if (hf.endHeaders()) {
477477
Log.logTrace("handling response (streamid={0})", streamid);
478-
handleResponse();
478+
handleResponse(hf);
479479
}
480480
if (hf.getFlag(HeaderFrame.END_STREAM)) {
481481
if (debug.on()) debug.log("handling END_STREAM: %d", streamid);
@@ -511,16 +511,28 @@ DecodingCallback rspHeadersConsumer() {
511511
return rspHeadersConsumer::onDecoded;
512512
}
513513

514-
protected void handleResponse() throws IOException {
514+
protected void handleResponse(HeaderFrame hf) throws IOException {
515515
HttpHeaders responseHeaders = responseHeadersBuilder.build();
516516

517517
if (!finalResponseCodeReceived) {
518518
responseCode = (int) responseHeaders
519519
.firstValueAsLong(":status")
520520
.orElseThrow(() -> new IOException("no statuscode in response"));
521521
// If informational code, response is partially complete
522-
if (responseCode < 100 || responseCode > 199)
522+
if (responseCode < 100 || responseCode > 199) {
523523
this.finalResponseCodeReceived = true;
524+
} else if (hf.getFlag(HeaderFrame.END_STREAM)) {
525+
// see RFC 9113 section 8.1:
526+
// A HEADERS frame with the END_STREAM flag set that carries an
527+
// informational status code is malformed
528+
String msg = ("Stream %s PROTOCOL_ERROR: " +
529+
"HEADERS frame with status %s has END_STREAM flag set")
530+
.formatted(streamid, responseCode);
531+
if (debug.on()) {
532+
debug.log(msg);
533+
}
534+
cancelImpl(new IOException(msg), ResetFrame.PROTOCOL_ERROR);
535+
}
524536

525537
response = new Response(
526538
request, exchange, responseHeaders, connection(),
@@ -567,12 +579,20 @@ void incoming_reset(ResetFrame frame) {
567579
// response to be read before the Reset is handled in the case where the client's
568580
// input stream is partially consumed or not consumed at all by the server.
569581
if (frame.getErrorCode() != ResetFrame.NO_ERROR) {
582+
if (debug.on()) {
583+
debug.log("completing requestBodyCF exceptionally due to received" +
584+
" RESET(%s) (stream=%s)", frame.getErrorCode(), streamid);
585+
}
570586
requestBodyCF.completeExceptionally(new IOException("RST_STREAM received"));
571587
} else {
588+
if (debug.on()) {
589+
debug.log("completing requestBodyCF normally due to received" +
590+
" RESET(NO_ERROR) (stream=%s)", streamid);
591+
}
572592
requestBodyCF.complete(null);
573593
}
574594
}
575-
if (response == null && subscriber == null) {
595+
if ((response == null || !finalResponseCodeReceived) && subscriber == null) {
576596
// we haven't received the headers yet, and won't receive any!
577597
// handle reset now.
578598
handleReset(frame, null);
@@ -950,6 +970,10 @@ public void onNext(ByteBuffer item) {
950970
private void onNextImpl(ByteBuffer item) {
951971
// Got some more request body bytes to send.
952972
if (requestBodyCF.isDone()) {
973+
if (debug.on()) {
974+
debug.log("RequestSubscriber: requestBodyCf is done: " +
975+
"cancelling subscription");
976+
}
953977
// stream already cancelled, probably in timeout
954978
sendScheduler.stop();
955979
subscription.cancel();
@@ -1528,7 +1552,7 @@ void completeResponseExceptionally(Throwable t) {
15281552

15291553
// create and return the PushResponseImpl
15301554
@Override
1531-
protected void handleResponse() {
1555+
protected void handleResponse(HeaderFrame hf) {
15321556
HttpHeaders responseHeaders = responseHeadersBuilder.build();
15331557

15341558
if (!finalPushResponseCodeReceived) {

test/jdk/java/net/httpclient/ExpectContinueTest.java

+36-5
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
* @summary Tests that when the httpclient sends a 100 Expect Continue header and receives
2929
* a response code of 417 Expectation Failed, that the client does not hang
3030
* indefinitely and closes the connection.
31-
* @bug 8286171
31+
* @bug 8286171 8307648
3232
* @library /test/lib /test/jdk/java/net/httpclient/lib
3333
* @build jdk.httpclient.test.lib.common.HttpServerAdapters
3434
* @run testng/othervm -Djdk.internal.httpclient.debug=err ExpectContinueTest
@@ -56,6 +56,7 @@
5656
import java.net.Socket;
5757
import java.net.URI;
5858
import java.net.http.HttpClient;
59+
import java.net.http.HttpClient.Builder;
5960
import java.net.http.HttpRequest;
6061
import java.net.http.HttpResponse;
6162
import java.util.StringTokenizer;
@@ -106,6 +107,10 @@ public void setup() throws Exception {
106107
h2postUri = URI.create("http://" + http2TestServer.serverAuthority() + "/http2/post");
107108
h2hangUri = URI.create("http://" + http2TestServer.serverAuthority() + "/http2/hang");
108109

110+
System.out.println("HTTP/1.1 server listening at: " + http1TestServer.serverAuthority());
111+
System.out.println("HTTP/1.1 hang server listening at: " + hangUri.getRawAuthority());
112+
System.out.println("HTTP/2 clear server listening at: " + http2TestServer.serverAuthority());
113+
109114
http1TestServer.start();
110115
http1HangServer.start();
111116
http2TestServer.start();
@@ -124,8 +129,10 @@ static class GetHandler implements HttpTestHandler {
124129
public void handle(HttpTestExchange exchange) throws IOException {
125130
try (InputStream is = exchange.getRequestBody();
126131
OutputStream os = exchange.getResponseBody()) {
132+
System.err.println("Server reading body");
127133
is.readAllBytes();
128134
byte[] bytes = "RESPONSE_BODY".getBytes(UTF_8);
135+
System.err.println("Server sending 200 (length="+bytes.length+")");
129136
exchange.sendResponseHeaders(200, bytes.length);
130137
os.write(bytes);
131138
}
@@ -139,13 +146,16 @@ public void handle(HttpTestExchange exchange) throws IOException {
139146
// Http1 server has already sent 100 response at this point but not Http2 server
140147
if (exchange.getExchangeVersion().equals(HttpClient.Version.HTTP_2)) {
141148
// Send 100 Headers, tell client that we're ready for body
149+
System.err.println("Server sending 100 (length = 0)");
142150
exchange.sendResponseHeaders(100, 0);
143151
}
144152

145153
// Read body from client and acknowledge with 200
146154
try (InputStream is = exchange.getRequestBody();
147155
OutputStream os = exchange.getResponseBody()) {
156+
System.err.println("Server reading body");
148157
is.readAllBytes();
158+
System.err.println("Server send 200 (length=0)");
149159
exchange.sendResponseHeaders(200, 0);
150160
}
151161
}
@@ -159,6 +169,7 @@ public void handle(HttpTestExchange exchange) throws IOException {
159169
try (InputStream is = exchange.getRequestBody();
160170
OutputStream os = exchange.getResponseBody()) {
161171
byte[] bytes = EXPECTATION_FAILED_417.getBytes();
172+
System.err.println("Server send 417 (length="+bytes.length+")");
162173
exchange.sendResponseHeaders(417, bytes.length);
163174
os.write(bytes);
164175
}
@@ -184,11 +195,14 @@ static class Http1HangServer extends Thread implements Closeable {
184195
public void run() {
185196
byte[] bytes = EXPECTATION_FAILED_417.getBytes();
186197

198+
boolean closed = this.closed;
187199
while (!closed) {
188200
try {
189201
// Not using try with resources here as we expect the client to close resources when
190202
// 417 is received
191-
client = ss.accept();
203+
System.err.println("Http1HangServer accepting connections");
204+
var client = this.client = ss.accept();
205+
System.err.println("Http1HangServer accepted connection: " + client);
192206
InputStream is = client.getInputStream();
193207
OutputStream os = client.getOutputStream();
194208

@@ -213,7 +227,8 @@ public void run() {
213227
&& version.equals("HTTP/1.1");
214228
// If correct request, send 417 reply. Otherwise, wait for correct one
215229
if (validRequest) {
216-
closed = true;
230+
System.err.println("Http1HangServer sending 417");
231+
closed = this.closed = true;
217232
response.append("HTTP/1.1 417 Expectation Failed\r\n")
218233
.append("Content-Length: ")
219234
.append(0)
@@ -224,17 +239,25 @@ public void run() {
224239
os.write(bytes);
225240
os.flush();
226241
} else {
242+
System.err.println("Http1HangServer received invalid request: closing");
227243
client.close();
228244
}
229245
} catch (IOException e) {
230-
closed = true;
246+
closed = this.closed = true;
231247
e.printStackTrace();
248+
} finally {
249+
if (closed = this.closed) {
250+
System.err.println("Http1HangServer: finished");
251+
} else {
252+
System.err.println("Http1HangServer: looping for accepting next connection");
253+
}
232254
}
233255
}
234256
}
235257

236258
@Override
237259
public void close() throws IOException {
260+
var client = this.client;
238261
if (client != null) client.close();
239262
if (ss != null) ss.close();
240263
}
@@ -244,13 +267,15 @@ public void close() throws IOException {
244267
public Object[][] urisData() {
245268
return new Object[][]{
246269
{ getUri, postUri, hangUri, HTTP_1_1 },
247-
{ h2getUri, h2postUri, h2hangUri, HttpClient.Version.HTTP_2 }
270+
{ h2getUri, h2postUri, h2hangUri, HTTP_2 }
248271
};
249272
}
250273

251274
@Test(dataProvider = "uris")
252275
public void test(URI getUri, URI postUri, URI hangUri, HttpClient.Version version) throws IOException, InterruptedException {
276+
System.out.println("Testing with version: " + version);
253277
HttpClient client = HttpClient.newBuilder()
278+
.proxy(Builder.NO_PROXY)
254279
.version(version)
255280
.build();
256281

@@ -268,18 +293,24 @@ public void test(URI getUri, URI postUri, URI hangUri, HttpClient.Version versio
268293
.expectContinue(true)
269294
.build();
270295

296+
System.out.printf("Sending request (%s): %s%n", version, getRequest);
297+
System.err.println("Sending request: " + getRequest);
271298
CompletableFuture<HttpResponse<String>> cf = client.sendAsync(getRequest, HttpResponse.BodyHandlers.ofString());
272299
HttpResponse<String> resp = cf.join();
273300
System.err.println("Response Headers: " + resp.headers());
274301
System.err.println("Response Status Code: " + resp.statusCode());
275302
assertEquals(resp.statusCode(), 200);
276303

304+
System.out.printf("Sending request (%s): %s%n", version, postRequest);
305+
System.err.println("Sending request: " + postRequest);
277306
cf = client.sendAsync(postRequest, HttpResponse.BodyHandlers.ofString());
278307
resp = cf.join();
279308
System.err.println("Response Headers: " + resp.headers());
280309
System.err.println("Response Status Code: " + resp.statusCode());
281310
assertEquals(resp.statusCode(), 200);
282311

312+
System.out.printf("Sending request (%s): %s%n", version, hangRequest);
313+
System.err.println("Sending request: " + hangRequest);
283314
cf = client.sendAsync(hangRequest, HttpResponse.BodyHandlers.ofString());
284315
resp = cf.join();
285316
System.err.println("Response Headers: " + resp.headers());

test/jdk/java/net/httpclient/lib/jdk/httpclient/test/lib/http2/Http2TestExchangeImpl.java

+3
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ public OutputStream getResponseBody() {
133133

134134
@Override
135135
public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
136+
// Do not set Content-Length for 100, and do not set END_STREAM
137+
if (rCode == 100) responseLength = 0;
138+
136139
this.responseLength = responseLength;
137140
if (responseLength !=0 && rCode != 204 && !isHeadRequest()) {
138141
long clen = responseLength > 0 ? responseLength : 0;

0 commit comments

Comments
 (0)