Skip to content

Commit 2e74ce8

Browse files
committed
Merge branch '3.3.x' into 3.4.x
Closes gh-44630
2 parents e06244d + a807a07 commit 2e74ce8

File tree

3 files changed

+60
-13
lines changed

3 files changed

+60
-13
lines changed

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java

+31-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2024 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,7 +36,6 @@
3636
import org.apache.hc.core5.http.HttpEntity;
3737
import org.apache.hc.core5.http.HttpHost;
3838
import org.apache.hc.core5.http.io.entity.AbstractHttpEntity;
39-
import org.apache.hc.core5.http.message.StatusLine;
4039

4140
import org.springframework.boot.buildpack.platform.io.Content;
4241
import org.springframework.boot.buildpack.platform.io.IOConsumer;
@@ -51,6 +50,7 @@
5150
* @author Phillip Webb
5251
* @author Mike Smithson
5352
* @author Scott Frederick
53+
* @author Moritz Halbritter
5454
*/
5555
abstract class HttpClientTransport implements HttpTransport {
5656

@@ -159,12 +159,12 @@ private Response execute(HttpUriRequest request) {
159159
ClassicHttpResponse response = this.client.executeOpen(this.host, request, null);
160160
int statusCode = response.getCode();
161161
if (statusCode >= 400 && statusCode <= 500) {
162-
HttpEntity entity = response.getEntity();
163-
Errors errors = (statusCode != 500) ? getErrorsFromResponse(entity) : null;
164-
Message message = getMessageFromResponse(entity);
165-
StatusLine statusLine = new StatusLine(response);
162+
byte[] content = readContent(response);
163+
response.close();
164+
Errors errors = (statusCode != 500) ? deserializeErrors(content) : null;
165+
Message message = deserializeMessage(content);
166166
throw new DockerEngineException(this.host.toHostString(), request.getUri(), statusCode,
167-
statusLine.getReasonPhrase(), errors, message);
167+
response.getReasonPhrase(), errors, message);
168168
}
169169
return new HttpClientResponse(response);
170170
}
@@ -173,19 +173,38 @@ private Response execute(HttpUriRequest request) {
173173
}
174174
}
175175

176-
private Errors getErrorsFromResponse(HttpEntity entity) {
176+
private byte[] readContent(ClassicHttpResponse response) throws IOException {
177+
HttpEntity entity = response.getEntity();
178+
if (entity == null) {
179+
return null;
180+
}
181+
try (InputStream stream = entity.getContent()) {
182+
if (stream == null) {
183+
return null;
184+
}
185+
return stream.readAllBytes();
186+
}
187+
}
188+
189+
private Errors deserializeErrors(byte[] content) {
190+
if (content == null) {
191+
return null;
192+
}
177193
try {
178-
return SharedObjectMapper.get().readValue(entity.getContent(), Errors.class);
194+
return SharedObjectMapper.get().readValue(content, Errors.class);
179195
}
180196
catch (IOException ex) {
181197
return null;
182198
}
183199
}
184200

185-
private Message getMessageFromResponse(HttpEntity entity) {
201+
private Message deserializeMessage(byte[] content) {
202+
if (content == null) {
203+
return null;
204+
}
186205
try {
187-
return (entity.getContent() != null)
188-
? SharedObjectMapper.get().readValue(entity.getContent(), Message.class) : null;
206+
Message message = SharedObjectMapper.get().readValue(content, Message.class);
207+
return (message.getMessage() != null) ? message : null;
189208
}
190209
catch (IOException ex) {
191210
return null;

spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransportTests.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -57,6 +57,7 @@
5757
* @author Phillip Webb
5858
* @author Mike Smithson
5959
* @author Scott Frederick
60+
* @author Moritz Halbritter
6061
*/
6162
@ExtendWith(MockitoExtension.class)
6263
class HttpClientTransportTests {
@@ -309,6 +310,18 @@ void executeWhenResponseIsIn500RangeWithOtherContentShouldThrowDockerException()
309310
});
310311
}
311312

313+
@Test
314+
void shouldReturnErrorsAndMessage() throws IOException {
315+
givenClientWillReturnResponse();
316+
given(this.entity.getContent()).willReturn(getClass().getResourceAsStream("message-and-errors.json"));
317+
given(this.response.getCode()).willReturn(404);
318+
assertThatExceptionOfType(DockerEngineException.class).isThrownBy(() -> this.http.get(this.uri))
319+
.satisfies((ex) -> {
320+
assertThat(ex.getErrors()).hasSize(2);
321+
assertThat(ex.getResponseMessage().getMessage()).contains("test message");
322+
});
323+
}
324+
312325
@Test
313326
void executeWhenClientThrowsIOExceptionRethrowsAsDockerException() throws IOException {
314327
given(this.client.executeOpen(any(HttpHost.class), any(HttpUriRequest.class), isNull()))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"message": "test message",
3+
"errors": [
4+
{
5+
"code": "TEST1",
6+
"message": "Test One",
7+
"detail": 123
8+
},
9+
{
10+
"code": "TEST2",
11+
"message": "Test Two",
12+
"detail": "fail"
13+
}
14+
]
15+
}

0 commit comments

Comments
 (0)