|
1 | 1 | /* |
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. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | 4 | * |
5 | 5 | * This code is free software; you can redistribute it and/or modify it |
@@ -135,6 +135,7 @@ class Stream<T> extends ExchangeImpl<T> { |
135 | 135 | private volatile boolean remotelyClosed; |
136 | 136 | private volatile boolean closed; |
137 | 137 | private volatile boolean endStreamSent; |
| 138 | + private volatile boolean finalResponseCodeReceived; |
138 | 139 | // Indicates the first reason that was invoked when sending a ResetFrame |
139 | 140 | // to the server. A streamState of 0 indicates that no reset was sent. |
140 | 141 | // (see markStream(int code) |
@@ -464,30 +465,44 @@ DecodingCallback rspHeadersConsumer() { |
464 | 465 |
|
465 | 466 | protected void handleResponse() throws IOException { |
466 | 467 | HttpHeaders responseHeaders = responseHeadersBuilder.build(); |
467 | | - responseCode = (int)responseHeaders |
468 | | - .firstValueAsLong(":status") |
469 | | - .orElseThrow(() -> new IOException("no statuscode in response")); |
470 | 468 |
|
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); |
474 | 480 |
|
475 | 481 | /* TODO: review if needs to be removed |
476 | 482 | the value is not used, but in case `content-length` doesn't parse as |
477 | 483 | long, there will be NumberFormatException. If left as is, make sure |
478 | 484 | code up the stack handles NFE correctly. */ |
479 | | - responseHeaders.firstValueAsLong("content-length"); |
| 485 | + responseHeaders.firstValueAsLong("content-length"); |
480 | 486 |
|
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(); |
486 | 495 |
|
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 | + } |
489 | 505 |
|
490 | | - completeResponse(response); |
491 | 506 | } |
492 | 507 |
|
493 | 508 | void incoming_reset(ResetFrame frame) { |
@@ -1303,6 +1318,7 @@ static class PushedStream<T> extends Stream<T> { |
1303 | 1318 | CompletableFuture<HttpResponse<T>> responseCF; |
1304 | 1319 | final HttpRequestImpl pushReq; |
1305 | 1320 | HttpResponse.BodyHandler<T> pushHandler; |
| 1321 | + private volatile boolean finalPushResponseCodeReceived; |
1306 | 1322 |
|
1307 | 1323 | PushedStream(PushGroup<T> pushGroup, |
1308 | 1324 | Http2Connection connection, |
@@ -1399,35 +1415,48 @@ void completeResponseExceptionally(Throwable t) { |
1399 | 1415 | @Override |
1400 | 1416 | protected void handleResponse() { |
1401 | 1417 | HttpHeaders responseHeaders = responseHeadersBuilder.build(); |
1402 | | - responseCode = (int)responseHeaders |
1403 | | - .firstValueAsLong(":status") |
1404 | | - .orElse(-1); |
1405 | 1418 |
|
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); |
1409 | 1423 |
|
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 | + } |
1413 | 1427 |
|
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; |
1419 | 1429 |
|
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); |
1426 | 1433 |
|
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"); |
1428 | 1439 |
|
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 | + } |
1431 | 1460 | } |
1432 | 1461 | } |
1433 | 1462 |
|
|
0 commit comments