Skip to content

Commit 33cd4b4

Browse files
author
Alexey Bakhtin
committed
8296410: HttpClient throws java.io.IOException: no statuscode in response for HTTP2
Reviewed-by: mbaesken Backport-of: f4b140b
1 parent 35d9f97 commit 33cd4b4

File tree

5 files changed

+417
-42
lines changed

5 files changed

+417
-42
lines changed

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

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -135,6 +135,7 @@ class Stream<T> extends ExchangeImpl<T> {
135135
private volatile boolean remotelyClosed;
136136
private volatile boolean closed;
137137
private volatile boolean endStreamSent;
138+
private volatile boolean finalResponseCodeReceived;
138139
// Indicates the first reason that was invoked when sending a ResetFrame
139140
// to the server. A streamState of 0 indicates that no reset was sent.
140141
// (see markStream(int code)
@@ -464,30 +465,44 @@ DecodingCallback rspHeadersConsumer() {
464465

465466
protected void handleResponse() throws IOException {
466467
HttpHeaders responseHeaders = responseHeadersBuilder.build();
467-
responseCode = (int)responseHeaders
468-
.firstValueAsLong(":status")
469-
.orElseThrow(() -> new IOException("no statuscode in response"));
470468

471-
response = new Response(
472-
request, exchange, responseHeaders, connection(),
473-
responseCode, HttpClient.Version.HTTP_2);
469+
if (!finalResponseCodeReceived) {
470+
responseCode = (int) responseHeaders
471+
.firstValueAsLong(":status")
472+
.orElseThrow(() -> new IOException("no statuscode in response"));
473+
// If informational code, response is partially complete
474+
if (responseCode < 100 || responseCode > 199)
475+
this.finalResponseCodeReceived = true;
476+
477+
response = new Response(
478+
request, exchange, responseHeaders, connection(),
479+
responseCode, HttpClient.Version.HTTP_2);
474480

475481
/* TODO: review if needs to be removed
476482
the value is not used, but in case `content-length` doesn't parse as
477483
long, there will be NumberFormatException. If left as is, make sure
478484
code up the stack handles NFE correctly. */
479-
responseHeaders.firstValueAsLong("content-length");
485+
responseHeaders.firstValueAsLong("content-length");
480486

481-
if (Log.headers()) {
482-
StringBuilder sb = new StringBuilder("RESPONSE HEADERS:\n");
483-
Log.dumpHeaders(sb, " ", responseHeaders);
484-
Log.logHeaders(sb.toString());
485-
}
487+
if (Log.headers()) {
488+
StringBuilder sb = new StringBuilder("RESPONSE HEADERS:\n");
489+
Log.dumpHeaders(sb, " ", responseHeaders);
490+
Log.logHeaders(sb.toString());
491+
}
492+
493+
// this will clear the response headers
494+
rspHeadersConsumer.reset();
486495

487-
// this will clear the response headers
488-
rspHeadersConsumer.reset();
496+
completeResponse(response);
497+
} else {
498+
if (Log.headers()) {
499+
StringBuilder sb = new StringBuilder("TRAILING HEADERS:\n");
500+
Log.dumpHeaders(sb, " ", responseHeaders);
501+
Log.logHeaders(sb.toString());
502+
}
503+
rspHeadersConsumer.reset();
504+
}
489505

490-
completeResponse(response);
491506
}
492507

493508
void incoming_reset(ResetFrame frame) {
@@ -1303,6 +1318,7 @@ static class PushedStream<T> extends Stream<T> {
13031318
CompletableFuture<HttpResponse<T>> responseCF;
13041319
final HttpRequestImpl pushReq;
13051320
HttpResponse.BodyHandler<T> pushHandler;
1321+
private volatile boolean finalPushResponseCodeReceived;
13061322

13071323
PushedStream(PushGroup<T> pushGroup,
13081324
Http2Connection connection,
@@ -1399,35 +1415,48 @@ void completeResponseExceptionally(Throwable t) {
13991415
@Override
14001416
protected void handleResponse() {
14011417
HttpHeaders responseHeaders = responseHeadersBuilder.build();
1402-
responseCode = (int)responseHeaders
1403-
.firstValueAsLong(":status")
1404-
.orElse(-1);
14051418

1406-
if (responseCode == -1) {
1407-
completeResponseExceptionally(new IOException("No status code"));
1408-
}
1419+
if (!finalPushResponseCodeReceived) {
1420+
responseCode = (int)responseHeaders
1421+
.firstValueAsLong(":status")
1422+
.orElse(-1);
14091423

1410-
this.response = new Response(
1411-
pushReq, exchange, responseHeaders, connection(),
1412-
responseCode, HttpClient.Version.HTTP_2);
1424+
if (responseCode == -1) {
1425+
completeResponseExceptionally(new IOException("No status code"));
1426+
}
14131427

1414-
/* TODO: review if needs to be removed
1415-
the value is not used, but in case `content-length` doesn't parse
1416-
as long, there will be NumberFormatException. If left as is, make
1417-
sure code up the stack handles NFE correctly. */
1418-
responseHeaders.firstValueAsLong("content-length");
1428+
this.finalPushResponseCodeReceived = true;
14191429

1420-
if (Log.headers()) {
1421-
StringBuilder sb = new StringBuilder("RESPONSE HEADERS");
1422-
sb.append(" (streamid=").append(streamid).append("):\n");
1423-
Log.dumpHeaders(sb, " ", responseHeaders);
1424-
Log.logHeaders(sb.toString());
1425-
}
1430+
this.response = new Response(
1431+
pushReq, exchange, responseHeaders, connection(),
1432+
responseCode, HttpClient.Version.HTTP_2);
14261433

1427-
rspHeadersConsumer.reset();
1434+
/* TODO: review if needs to be removed
1435+
the value is not used, but in case `content-length` doesn't parse
1436+
as long, there will be NumberFormatException. If left as is, make
1437+
sure code up the stack handles NFE correctly. */
1438+
responseHeaders.firstValueAsLong("content-length");
14281439

1429-
// different implementations for normal streams and pushed streams
1430-
completeResponse(response);
1440+
if (Log.headers()) {
1441+
StringBuilder sb = new StringBuilder("RESPONSE HEADERS");
1442+
sb.append(" (streamid=").append(streamid).append("):\n");
1443+
Log.dumpHeaders(sb, " ", responseHeaders);
1444+
Log.logHeaders(sb.toString());
1445+
}
1446+
1447+
rspHeadersConsumer.reset();
1448+
1449+
// different implementations for normal streams and pushed streams
1450+
completeResponse(response);
1451+
} else {
1452+
if (Log.headers()) {
1453+
StringBuilder sb = new StringBuilder("TRAILING HEADERS");
1454+
sb.append(" (streamid=").append(streamid).append("):\n");
1455+
Log.dumpHeaders(sb, " ", responseHeaders);
1456+
Log.logHeaders(sb.toString());
1457+
}
1458+
rspHeadersConsumer.reset();
1459+
}
14311460
}
14321461
}
14331462

0 commit comments

Comments
 (0)