diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java index 7f8ecc9e9e29..3bdaaa45a28d 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/HttpClientTransport.java @@ -22,6 +22,7 @@ import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import org.apache.hc.client5.http.classic.HttpClient; import org.apache.hc.client5.http.classic.methods.HttpDelete; @@ -33,6 +34,7 @@ import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.io.entity.AbstractHttpEntity; import org.springframework.boot.buildpack.platform.io.Content; @@ -146,14 +148,37 @@ private Response execute(HttpUriRequest request) { try { ClassicHttpResponse response = this.client.executeOpen(this.host, request, null); int statusCode = response.getCode(); + if (statusCode >= 400 && statusCode <= 500) { byte[] content = readContent(response); response.close(); + + if (statusCode == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) { + String detail = null; + + // Some Docker endpoints may still send a JSON body; prefer it if + // present, + // otherwise fall back to plain text. + Message json = deserializeMessage(content); + if (json != null && StringUtils.hasText(json.getMessage())) { + detail = json.getMessage(); + } + else if (content != null && content.length > 0) { + detail = new String(content, StandardCharsets.UTF_8); + } + + String msg = "Proxy authentication required for host: " + this.host.toHostString() + ", uri: " + + request.getUri() + (StringUtils.hasText(detail) ? " - " + detail : ""); + + throw new ProxyAuthenticationException(msg); + } + Errors errors = (statusCode != 500) ? deserializeErrors(content) : null; Message message = deserializeMessage(content); throw new DockerEngineException(this.host.toHostString(), request.getUri(), statusCode, response.getReasonPhrase(), errors, message); } + return new HttpClientResponse(response); } catch (IOException | URISyntaxException ex) { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/ProxyAuthenticationException.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/ProxyAuthenticationException.java new file mode 100644 index 000000000000..3211ec132be0 --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/ProxyAuthenticationException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.buildpack.platform.docker.transport; + +public class ProxyAuthenticationException extends RuntimeException { + + public ProxyAuthenticationException(String message) { + super(message); + } + + public ProxyAuthenticationException(String message, Throwable cause) { + super(message, cause); + } + +}