diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/LocalHttpClientTransport.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/LocalHttpClientTransport.java index f9f6707a533..a097e0f9066 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/LocalHttpClientTransport.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/transport/LocalHttpClientTransport.java @@ -40,8 +40,8 @@ import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.util.TimeValue; import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost; -import org.springframework.boot.buildpack.platform.socket.DomainSocket; import org.springframework.boot.buildpack.platform.socket.NamedPipeSocket; +import org.springframework.boot.buildpack.platform.socket.UnixDomainSocket; /** * {@link HttpClientTransport} that talks to local Docker. @@ -129,7 +129,7 @@ final class LocalHttpClientTransport extends HttpClientTransport { if (this.host.startsWith(NPIPE_PREFIX)) { return NamedPipeSocket.get(this.host.substring(NPIPE_PREFIX.length())); } - return (!Platform.isWindows()) ? DomainSocket.get(this.host) : NamedPipeSocket.get(this.host); + return (!Platform.isWindows()) ? UnixDomainSocket.get(this.host) : NamedPipeSocket.get(this.host); } @Override diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/BsdDomainSocket.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/BsdDomainSocket.java deleted file mode 100644 index 37b597250d2..00000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/BsdDomainSocket.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012-2024 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.socket; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.List; - -import com.sun.jna.LastErrorException; -import com.sun.jna.Native; -import com.sun.jna.Platform; -import com.sun.jna.Structure; - -import org.springframework.util.Assert; - -/** - * {@link DomainSocket} implementation for BSD based platforms. - * - * @author Phillip Webb - */ -class BsdDomainSocket extends DomainSocket { - - private static final int MAX_PATH_LENGTH = 104; - - static { - Native.register(Platform.C_LIBRARY_NAME); - } - - BsdDomainSocket(String path) throws IOException { - super(path); - } - - @Override - protected void connect(String path, int handle) { - SockaddrUn address = new SockaddrUn(AF_LOCAL, path.getBytes(StandardCharsets.UTF_8)); - connect(handle, address, address.size()); - } - - private native int connect(int fd, SockaddrUn address, int addressLen) throws LastErrorException; - - /** - * Native {@code sockaddr_un} structure as defined in {@code sys/un.h}. - */ - public static class SockaddrUn extends Structure implements Structure.ByReference { - - public byte sunLen; - - public byte sunFamily; - - public byte[] sunPath = new byte[MAX_PATH_LENGTH]; - - private SockaddrUn(byte sunFamily, byte[] path) { - Assert.isTrue(path.length < MAX_PATH_LENGTH, () -> "Path cannot exceed " + MAX_PATH_LENGTH + " bytes"); - System.arraycopy(path, 0, this.sunPath, 0, path.length); - this.sunPath[path.length] = 0; - this.sunLen = (byte) (fieldOffset("sunPath") + path.length); - this.sunFamily = sunFamily; - allocateMemory(); - } - - @Override - protected List getFieldOrder() { - return Arrays.asList("sunLen", "sunFamily", "sunPath"); - } - - } - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/DomainSocket.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/DomainSocket.java deleted file mode 100644 index 0c587f6b5f1..00000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/DomainSocket.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2012-2024 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.socket; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.nio.ByteBuffer; - -import com.sun.jna.LastErrorException; -import com.sun.jna.Native; -import com.sun.jna.Platform; - -import org.springframework.boot.buildpack.platform.socket.FileDescriptor.Handle; - -/** - * A {@link Socket} implementation for Linux of BSD domain sockets. - * - * @author Phillip Webb - * @since 2.3.0 - */ -public abstract class DomainSocket extends AbstractSocket { - - private static final int SHUT_RD = 0; - - private static final int SHUT_WR = 1; - - protected static final int PF_LOCAL = 1; - - protected static final byte AF_LOCAL = 1; - - protected static final int SOCK_STREAM = 1; - - private final FileDescriptor fileDescriptor; - - private final InputStream inputStream; - - private final OutputStream outputStream; - - static { - Native.register(Platform.C_LIBRARY_NAME); - } - - DomainSocket(String path) throws IOException { - try { - this.fileDescriptor = open(path); - this.inputStream = new DomainSocketInputStream(); - this.outputStream = new DomainSocketOutputStream(); - } - catch (LastErrorException ex) { - throw new IOException(ex); - } - } - - private FileDescriptor open(String path) { - int handle = socket(PF_LOCAL, SOCK_STREAM, 0); - try { - connect(path, handle); - return new FileDescriptor(handle, this::close); - } - catch (RuntimeException ex) { - close(handle); - throw ex; - } - } - - private int read(ByteBuffer buffer) throws IOException { - try (Handle handle = this.fileDescriptor.acquire()) { - if (handle.isClosed()) { - return -1; - } - try { - return read(handle.intValue(), buffer, buffer.remaining()); - } - catch (LastErrorException ex) { - throw new IOException(ex); - } - } - } - - public void write(ByteBuffer buffer) throws IOException { - try (Handle handle = this.fileDescriptor.acquire()) { - if (!handle.isClosed()) { - try { - write(handle.intValue(), buffer, buffer.remaining()); - } - catch (LastErrorException ex) { - throw new IOException(ex); - } - } - } - } - - @Override - public InputStream getInputStream() { - return this.inputStream; - } - - @Override - public OutputStream getOutputStream() { - return this.outputStream; - } - - @Override - public void close() throws IOException { - super.close(); - try { - this.fileDescriptor.close(); - } - catch (LastErrorException ex) { - throw new IOException(ex); - } - } - - protected abstract void connect(String path, int handle); - - private native int socket(int domain, int type, int protocol) throws LastErrorException; - - private native int read(int fd, ByteBuffer buffer, int count) throws LastErrorException; - - private native int write(int fd, ByteBuffer buffer, int count) throws LastErrorException; - - private native int close(int fd) throws LastErrorException; - - /** - * Return a new {@link DomainSocket} for the given path. - * @param path the path to the domain socket - * @return a {@link DomainSocket} instance - * @throws IOException if the socket cannot be opened - */ - public static DomainSocket get(String path) throws IOException { - if (Platform.isMac() || isBsdPlatform()) { - return new BsdDomainSocket(path); - } - return new LinuxDomainSocket(path); - } - - private static boolean isBsdPlatform() { - return Platform.isFreeBSD() || Platform.iskFreeBSD() || Platform.isNetBSD() || Platform.isOpenBSD(); - } - - /** - * {@link InputStream} returned from the {@link DomainSocket}. - */ - private final class DomainSocketInputStream extends InputStream { - - @Override - public int read() throws IOException { - ByteBuffer buffer = ByteBuffer.allocate(1); - int amountRead = DomainSocket.this.read(buffer); - return (amountRead != 1) ? -1 : buffer.get() & 0xFF; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (len == 0) { - return 0; - } - int amountRead = DomainSocket.this.read(ByteBuffer.wrap(b, off, len)); - return (amountRead > 0) ? amountRead : -1; - } - - } - - /** - * {@link OutputStream} returned from the {@link DomainSocket}. - */ - private final class DomainSocketOutputStream extends OutputStream { - - @Override - public void write(int b) throws IOException { - ByteBuffer buffer = ByteBuffer.allocate(1); - buffer.put(0, (byte) (b & 0xFF)); - DomainSocket.this.write(buffer); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - if (len != 0) { - DomainSocket.this.write(ByteBuffer.wrap(b, off, len)); - } - } - - } - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/LinuxDomainSocket.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/LinuxDomainSocket.java deleted file mode 100644 index 13490f17fa6..00000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/LinuxDomainSocket.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2012-2024 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.socket; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.List; - -import com.sun.jna.LastErrorException; -import com.sun.jna.Native; -import com.sun.jna.Platform; -import com.sun.jna.Structure; - -import org.springframework.util.Assert; - -/** - * {@link DomainSocket} implementation for Linux based platforms. - * - * @author Phillip Webb - */ -class LinuxDomainSocket extends DomainSocket { - - static { - Native.register(Platform.C_LIBRARY_NAME); - } - - LinuxDomainSocket(String path) throws IOException { - super(path); - } - - private static final int MAX_PATH_LENGTH = 108; - - @Override - protected void connect(String path, int handle) { - SockaddrUn address = new SockaddrUn(AF_LOCAL, path.getBytes(StandardCharsets.UTF_8)); - connect(handle, address, address.size()); - } - - private native int connect(int fd, SockaddrUn address, int addressLen) throws LastErrorException; - - /** - * Native {@code sockaddr_un} structure as defined in {@code sys/un.h}. - */ - public static class SockaddrUn extends Structure implements Structure.ByReference { - - public short sunFamily; - - public byte[] sunPath = new byte[MAX_PATH_LENGTH]; - - private SockaddrUn(byte sunFamily, byte[] path) { - Assert.isTrue(path.length < MAX_PATH_LENGTH, () -> "Path cannot exceed " + MAX_PATH_LENGTH + " bytes"); - System.arraycopy(path, 0, this.sunPath, 0, path.length); - this.sunPath[path.length] = 0; - this.sunFamily = sunFamily; - allocateMemory(); - } - - @Override - protected List getFieldOrder() { - return Arrays.asList("sunFamily", "sunPath"); - } - - } - -} diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/UnixDomainSocket.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/UnixDomainSocket.java new file mode 100644 index 00000000000..276397c2c5b --- /dev/null +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/socket/UnixDomainSocket.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012-2024 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.socket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.Channels; +import java.nio.channels.SocketChannel; + +/** + * A {@link Socket} implementation for Unix domain sockets. + * + * @author Scott Frederick + * @since 3.4.0 + */ +public final class UnixDomainSocket extends AbstractSocket { + + /** + * Create a new {@link Socket} for the given path. + * @param path the path to the domain socket + * @return a {@link Socket} instance + * @throws IOException if the socket cannot be opened + */ + public static Socket get(String path) throws IOException { + return new UnixDomainSocket(path); + } + + private final SocketAddress socketAddress; + + private final SocketChannel socketChannel; + + private UnixDomainSocket(String path) throws IOException { + this.socketAddress = UnixDomainSocketAddress.of(path); + this.socketChannel = SocketChannel.open(this.socketAddress); + } + + @Override + public InputStream getInputStream() throws IOException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + if (!isConnected()) { + throw new SocketException("Socket is not connected"); + } + if (isInputShutdown()) { + throw new SocketException("Socket input is shutdown"); + } + + return Channels.newInputStream(this.socketChannel); + } + + @Override + public OutputStream getOutputStream() throws IOException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + if (!isConnected()) { + throw new SocketException("Socket is not connected"); + } + if (isOutputShutdown()) { + throw new SocketException("Socket output is shutdown"); + } + + return Channels.newOutputStream(this.socketChannel); + } + + @Override + public SocketAddress getLocalSocketAddress() { + return this.socketAddress; + } + + @Override + public SocketAddress getRemoteSocketAddress() { + return this.socketAddress; + } + + @Override + public void close() throws IOException { + super.close(); + this.socketChannel.close(); + } + +}