[Git][java-team/jnr-unixsocket][upstream] Imported Upstream version 0.38.21
Miguel Landaeta (@nomadium)
gitlab at salsa.debian.org
Sat Dec 2 14:22:18 GMT 2023
Miguel Landaeta pushed to branch upstream at Debian Java Maintainers / jnr-unixsocket
Commits:
7390f438 by Miguel Landaeta at 2023-11-25T22:48:40+00:00
Imported Upstream version 0.38.21
- - - - -
24 changed files:
- + .github/workflows/ci.yml
- − .travis.yml
- LICENSE
- README.md
- pom.xml
- src/main/java/jnr/unixsocket/Common.java
- src/main/java/jnr/unixsocket/Native.java
- src/main/java/jnr/unixsocket/UnixDatagramChannel.java
- src/main/java/jnr/unixsocket/UnixServerSocketChannel.java
- src/main/java/jnr/unixsocket/UnixSocket.java
- src/main/java/jnr/unixsocket/UnixSocketChannel.java
- src/main/java/jnr/unixsocket/UnixSocketOptions.java
- src/main/java/jnr/enxio/channels/AbstractNativeDatagramChannel.java → src/main/java/jnr/unixsocket/impl/AbstractNativeDatagramChannel.java
- + src/main/java/jnr/unixsocket/impl/AbstractNativeServerSocketChannel.java
- src/main/java/jnr/enxio/channels/AbstractNativeSocketChannel.java → src/main/java/jnr/unixsocket/impl/AbstractNativeSocketChannel.java
- src/main/java/jnr/enxio/channels/Common.java → src/main/java/jnr/unixsocket/impl/Common.java
- src/test/java/jnr/unixsocket/BasicFunctionalityTest.java
- + src/test/java/jnr/unixsocket/SocketInteropTest.java
- + src/test/java/jnr/unixsocket/TcpChannelsApiSocketPair.java
- + src/test/java/jnr/unixsocket/TcpSocketsApiSocketPair.java
- + src/test/java/jnr/unixsocket/TestSocketPair.java
- src/test/java/jnr/unixsocket/UnixSocketChannelTest.java
- + src/test/java/jnr/unixsocket/UnixSocketPair.java
- src/test/java/jnr/unixsocket/example/UnixServer.java
Changes:
=====================================
.github/workflows/ci.yml
=====================================
@@ -0,0 +1,37 @@
+# This workflow will build a Java project with Maven
+# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
+
+name: Java CI with Maven
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ jdk8:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout at v2
+ - name: Set up JDK 8
+ uses: actions/setup-java at v1.4.3
+ with:
+ java-version: 8
+ - name: Build with Maven
+ run: mvn -B package --file pom.xml
+
+ jdk11:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout at v2
+ - name: Set up JDK 11
+ uses: actions/setup-java at v1.4.3
+ with:
+ java-version: 11
+ - name: Build with Maven
+ run: mvn -B package --file pom.xml
=====================================
.travis.yml deleted
=====================================
@@ -1,13 +0,0 @@
-language: java
-jdk:
- - oraclejdk7
- - openjdk7
-notifications:
- irc:
- channels:
- - "irc.freenode.org#jnr"
- on_success: change
- on_failure: always
- template:
- - "%{repository} (%{branch}:%{commit} by %{author}): %{message} (%{build_url})"
-sudo: false
=====================================
LICENSE
=====================================
@@ -1,12 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
- 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
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
- http://www.apache.org/licenses/LICENSE-2.0
+ 1. Definitions.
- 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.
\ No newline at end of file
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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
+
+ http://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.
=====================================
README.md
=====================================
@@ -1,3 +1,8 @@
+[![][Build Status img]][Build Status]
+[![][license img]][license]
+[![][Maven Central img]][Maven Central]
+[![][Javadocs img]][Javadocs]
+
jnr-unixsocket
==============
@@ -5,3 +10,14 @@ Native I/O access for java.
Check out the [examples](https://github.com/jnr/jnr-unixsocket/tree/master/src/test/java/jnr/unixsocket/example) for more information.
+[Build Status]:https://travis-ci.org/jnr/jnr-unixsocket
+[Build Status img]:https://travis-ci.org/jnr/jnr-unixsocket.svg?branch=master
+
+[license]:LICENSE
+[license img]:https://img.shields.io/badge/license-Apache%202-blue.svg
+
+[Maven Central]:https://maven-badges.herokuapp.com/maven-central/com.github.jnr/jnr-unixsocket
+[Maven Central img]:https://maven-badges.herokuapp.com/maven-central/com.github.jnr/jnr-unixsocket/badge.svg
+
+[Javadocs]:http://javadoc.io/doc/com.github.jnr/jnr-unixsocket
+[Javadocs img]:http://javadoc.io/badge/com.github.jnr/jnr-unixsocket.svg
=====================================
pom.xml
=====================================
@@ -10,9 +10,9 @@
<groupId>com.github.jnr</groupId>
<artifactId>jnr-unixsocket</artifactId>
<packaging>jar</packaging>
- <version>0.18</version>
+ <version>0.38.21</version>
<name>jnr-unixsocket</name>
- <description>Native I/O access for java</description>
+ <description>UNIX socket channels for java</description>
<url>http://github.com/jnr/jnr-unixsocket</url>
<licenses>
@@ -43,35 +43,41 @@
</developer>
</developers>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>8</maven.compiler.source>
+ <maven.compiler.target>8</maven.compiler.target>
+ </properties>
+
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.8.2</version>
+ <version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-ffi</artifactId>
- <version>2.1.4</version>
+ <version>2.2.15</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-constants</artifactId>
- <version>0.9.8</version>
+ <version>0.10.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-enxio</artifactId>
- <version>0.16</version>
+ <version>0.32.16</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-posix</artifactId>
- <version>3.0.35</version>
+ <version>3.1.18</version>
<scope>compile</scope>
</dependency>
</dependencies>
@@ -109,12 +115,20 @@
</executions>
</plugin>
- <!-- run findbugs check -->
+ <!-- run spotbugs check -->
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>findbugs-maven-plugin</artifactId>
- <version>3.0.3</version>
- <executions>
+ <groupId>com.github.spotbugs</groupId>
+ <artifactId>spotbugs-maven-plugin</artifactId>
+ <version>3.1.12.2</version>
+ <dependencies>
+ <!-- overwrite dependency on spotbugs if you want to specify the version of spotbugs -->
+ <dependency>
+ <groupId>com.github.spotbugs</groupId>
+ <artifactId>spotbugs</artifactId>
+ <version>4.0.0-beta4</version>
+ </dependency>
+ </dependencies>
+ <executions>
<execution>
<phase>process-test-classes</phase>
<goals>
@@ -124,9 +138,9 @@
</executions>
<configuration>
<effort>Max</effort>
- <includeTests>true</includeTests>
- <threshold>Low</threshold>
- <xmlOutput>true</xmlOutput>
+ <includeTests>false</includeTests>
+ <relaxed>true</relaxed>
+ <spotbugsXmlOutput>true</spotbugsXmlOutput>
<failOnError>false</failOnError>
</configuration>
</plugin>
@@ -134,7 +148,7 @@
<!-- run PMD check -->
<plugin>
<artifactId>maven-pmd-plugin</artifactId>
- <version>3.6</version>
+ <version>3.13.0</version>
<configuration>
<verbose>true</verbose>
<linkXRef>false</linkXRef>
@@ -163,15 +177,6 @@
</executions>
</plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.0.2</version>
- <configuration>
- <source>1.7</source>
- <target>1.7</target>
- </configuration>
- </plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
@@ -180,6 +185,7 @@
<instructions>
<_nouses>true</_nouses>
<Import-Package>*,jnr.ffi.mapper,jnr.ffi.provider.converters,jnr.ffi.provider.jffi,com.kenai.jffi</Import-Package>
+ <Automatic-Module-Name>jnr.unixsocket</Automatic-Module-Name>
</instructions>
</configuration>
<executions>
@@ -199,6 +205,9 @@
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+ <manifestEntries>
+ <Automatic-Module-Name>org.jnrproject.unixsocket</Automatic-Module-Name>
+ </manifestEntries>
</archive>
</configuration>
<executions>
@@ -223,19 +232,6 @@
</execution>
</executions>
</plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <version>2.10.3</version>
- <executions>
- <execution>
- <id>attach-javadocs</id>
- <goals>
- <goal>jar</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
@@ -255,6 +251,7 @@
</execution>
</executions>
</plugin>
+<!--
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
@@ -296,6 +293,12 @@
</execution>
</executions>
</plugin>
+-->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.8.1</version>
+ </plugin>
</plugins>
<extensions>
<extension>
@@ -304,5 +307,17 @@
</extension>
</extensions>
</build>
+
+ <profiles>
+ <profile>
+ <id>java9</id>
+ <activation>
+ <jdk>[9,)</jdk>
+ </activation>
+ <properties>
+ <maven.compiler.release>8</maven.compiler.release>
+ </properties>
+ </profile>
+ </profiles>
</project>
<!-- vim: set sw=2 ts=2 et: -->
=====================================
src/main/java/jnr/unixsocket/Common.java
=====================================
@@ -157,6 +157,8 @@ final class Common {
wMap.put(UnixSocketOptions.SO_RCVTIMEO, jnr.constants.platform.SocketOption.SO_RCVTIMEO);
wMap.put(UnixSocketOptions.SO_SNDTIMEO, jnr.constants.platform.SocketOption.SO_SNDTIMEO);
wMap.put(UnixSocketOptions.SO_KEEPALIVE, jnr.constants.platform.SocketOption.SO_KEEPALIVE);
+ wMap.put(UnixSocketOptions.SO_PASSCRED, jnr.constants.platform.SocketOption.SO_PASSCRED);
+
rMap.putAll(wMap);
rMap.put(UnixSocketOptions.SO_PEERCRED, jnr.constants.platform.SocketOption.SO_PEERCRED);
}
=====================================
src/main/java/jnr/unixsocket/Native.java
=====================================
@@ -152,7 +152,7 @@ class Native {
return libsocket().setsockopt(s, level.intValue(), optname.intValue(), t, DefaultNativeTimeval.size(t));
} else {
ByteBuffer buf = ByteBuffer.allocate(4);
- buf.order(ByteOrder.BIG_ENDIAN);
+ buf.order(ByteOrder.nativeOrder());
buf.putInt(optval).flip();
return libsocket().setsockopt(s, level.intValue(), optname.intValue(), buf, buf.remaining());
}
@@ -167,7 +167,7 @@ class Native {
return (t.tv_sec.intValue() * 1000 + t.tv_usec.intValue() / 1000);
} else {
ByteBuffer buf = ByteBuffer.allocate(4);
- buf.order(ByteOrder.BIG_ENDIAN);
+ buf.order(ByteOrder.nativeOrder());
ref = new IntByReference(4);
Native.libsocket().getsockopt(s, level.intValue(), optname, buf, ref);
return buf.getInt();
=====================================
src/main/java/jnr/unixsocket/UnixDatagramChannel.java
=====================================
@@ -40,7 +40,7 @@ import java.util.Set;
import jnr.constants.platform.ProtocolFamily;
import jnr.constants.platform.Sock;
-import jnr.enxio.channels.AbstractNativeDatagramChannel;
+import jnr.unixsocket.impl.AbstractNativeDatagramChannel;
public class UnixDatagramChannel extends AbstractNativeDatagramChannel {
static enum State {
@@ -58,6 +58,10 @@ public class UnixDatagramChannel extends AbstractNativeDatagramChannel {
return new UnixDatagramChannel();
}
+ public static final UnixDatagramChannel open(ProtocolFamily domain, int protocol) throws IOException {
+ return new UnixDatagramChannel(domain, protocol);
+ }
+
public static final UnixDatagramChannel[] pair() throws IOException {
int[] sockets = { -1, -1 };
Native.socketpair(ProtocolFamily.PF_UNIX, Sock.SOCK_DGRAM, 0, sockets);
@@ -71,6 +75,11 @@ public class UnixDatagramChannel extends AbstractNativeDatagramChannel {
this(Native.socket(ProtocolFamily.PF_UNIX, Sock.SOCK_DGRAM, 0));
}
+ UnixDatagramChannel(ProtocolFamily domain, int protocol) throws IOException
+ {
+ this(Native.socket(domain, Sock.SOCK_DGRAM, protocol));
+ }
+
UnixDatagramChannel(int fd) {
this(fd, State.IDLE, false);
}
@@ -78,9 +87,12 @@ public class UnixDatagramChannel extends AbstractNativeDatagramChannel {
UnixDatagramChannel(int fd, State initialState, boolean initialBoundState) {
super(fd);
stateLock.writeLock().lock();
- state = initialState;
- bindHandler = new BindHandler(initialBoundState);
- stateLock.writeLock().unlock();
+ try {
+ state = initialState;
+ bindHandler = new BindHandler(initialBoundState);
+ } finally {
+ stateLock.writeLock().unlock();
+ }
}
UnixDatagramChannel(int fd, UnixSocketAddress remote) throws IOException {
=====================================
src/main/java/jnr/unixsocket/UnixServerSocketChannel.java
=====================================
@@ -20,17 +20,22 @@ package jnr.unixsocket;
import jnr.constants.platform.ProtocolFamily;
import jnr.constants.platform.Sock;
-import jnr.enxio.channels.NativeServerSocketChannel;
+import jnr.unixsocket.impl.AbstractNativeServerSocketChannel;
import jnr.ffi.byref.IntByReference;
import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetBoundException;
import java.nio.channels.SelectionKey;
import java.nio.channels.spi.SelectorProvider;
+import static jnr.unixsocket.Native.getLastError;
+import static jnr.unixsocket.Native.getLastErrorString;
+
/**
*
*/
-public class UnixServerSocketChannel extends NativeServerSocketChannel {
+public class UnixServerSocketChannel extends AbstractNativeServerSocketChannel {
private final UnixServerSocket socket;
@@ -54,11 +59,24 @@ public class UnixServerSocketChannel extends NativeServerSocketChannel {
int maxLength = addr.getMaximumLength();
IntByReference len = new IntByReference(maxLength);
- int clientfd = Native.accept(getFD(), addr, len);
+ int clientfd = -1;
+ begin();
+ try {
+ clientfd = Native.accept(getFD(), addr, len);
+ } finally {
+ end(clientfd >= 0);
+ }
if (clientfd < 0) {
if (isBlocking()) {
- throw new IOException("accept failed: " + Native.getLastErrorString());
+ switch (getLastError()) {
+ case EBADF:
+ throw new ClosedChannelException();
+ case EINVAL:
+ throw new NotYetBoundException();
+ default:
+ throw new IOException("accept failed: " + getLastErrorString());
+ }
}
return null;
=====================================
src/main/java/jnr/unixsocket/UnixSocket.java
=====================================
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2009 Wayne Meissner
* Copyright (C) 2016 Marcus Linke
- *
+ *
* (ported from https://github.com/softprops/unisockets/blob/master/unisockets-core/src/main/scala/Socket.scala)
*
* This file is part of the JNR project.
@@ -17,7 +17,7 @@
* 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 jnr.unixsocket;
@@ -27,8 +27,12 @@ import java.io.OutputStream;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketException;
+import java.nio.ByteBuffer;
import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SelectableChannel;
import java.nio.channels.SocketChannel;
+import java.nio.channels.WritableByteChannel;
import java.util.concurrent.atomic.AtomicBoolean;
public class UnixSocket extends java.net.Socket {
@@ -44,8 +48,8 @@ public class UnixSocket extends java.net.Socket {
public UnixSocket(UnixSocketChannel chan) {
this.chan = chan;
- in = Channels.newInputStream(chan);
- out = Channels.newOutputStream(chan);
+ in = Channels.newInputStream(new UnselectableByteChannel(chan));
+ out = Channels.newOutputStream(new UnselectableByteChannel(chan));
}
@Override
@@ -81,7 +85,8 @@ public class UnixSocket extends java.net.Socket {
connect(addr, 0);
}
- public void connect(SocketAddress addr, Integer timeout) throws IOException {
+ @Override
+ public void connect(SocketAddress addr, int timeout) throws IOException {
if (addr instanceof UnixSocketAddress) {
chan.connect((UnixSocketAddress) addr);
} else {
@@ -101,6 +106,7 @@ public class UnixSocket extends java.net.Socket {
return null;
}
+ @Override
public InputStream getInputStream() throws IOException {
if (chan.isConnected()) {
return in;
@@ -111,12 +117,7 @@ public class UnixSocket extends java.net.Socket {
@Override
public SocketAddress getLocalSocketAddress() {
- UnixSocketAddress address = chan.getLocalSocketAddress();
- if (address != null) {
- return address;
- } else {
- return null;
- }
+ return chan.getLocalSocketAddress();
}
@Override
@@ -276,7 +277,43 @@ public class UnixSocket extends java.net.Socket {
throw (SocketException)new SocketException().initCause(e);
}
}
-
+
private void ignore() {
}
+
+ /**
+ * A byte channel that doesn't implement {@link SelectableChannel}. Though
+ * that type isn't in the public API, if the channel passed in implements
+ * that interface then unwanted synchronization is performed which can harm
+ * concurrency and can cause deadlocks.
+ *
+ * https://bugs.openjdk.java.net/browse/JDK-4774871
+ */
+ static final class UnselectableByteChannel implements ReadableByteChannel, WritableByteChannel {
+ private final UnixSocketChannel channel;
+
+ UnselectableByteChannel(UnixSocketChannel channel) {
+ this.channel = channel;
+ }
+
+ @Override
+ public int write(ByteBuffer src) throws IOException {
+ return channel.write(src);
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ return channel.read(dst);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return channel.isOpen();
+ }
+
+ @Override
+ public void close() throws IOException {
+ channel.close();
+ }
+ }
}
=====================================
src/main/java/jnr/unixsocket/UnixSocketChannel.java
=====================================
@@ -35,7 +35,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import jnr.constants.platform.Errno;
import jnr.constants.platform.ProtocolFamily;
import jnr.constants.platform.Sock;
-import jnr.enxio.channels.AbstractNativeSocketChannel;
+import jnr.unixsocket.impl.AbstractNativeSocketChannel;
import jnr.ffi.LastError;
/**
@@ -80,7 +80,7 @@ public class UnixSocketChannel extends AbstractNativeSocketChannel {
public static final UnixSocketChannel[] pair() throws IOException {
int[] sockets = { -1, -1 };
Native.socketpair(ProtocolFamily.PF_UNIX, Sock.SOCK_STREAM, 0, sockets);
- return new UnixSocketChannel[] {
+ return new UnixSocketChannel[] {
new UnixSocketChannel(sockets[0], State.CONNECTED, true),
new UnixSocketChannel(sockets[1], State.CONNECTED, true) };
}
@@ -100,7 +100,7 @@ public class UnixSocketChannel extends AbstractNativeSocketChannel {
UnixSocketChannel() throws IOException {
this(Native.socket(ProtocolFamily.PF_UNIX, Sock.SOCK_STREAM, 0));
}
-
+
UnixSocketChannel(int fd) {
this(fd, State.CONNECTED, false);
}
@@ -108,9 +108,12 @@ public class UnixSocketChannel extends AbstractNativeSocketChannel {
UnixSocketChannel(int fd, State initialState, boolean initialBoundState) {
super(fd);
stateLock.writeLock().lock();
- state = initialState;
- bindHandler = new BindHandler(initialBoundState);
- stateLock.writeLock().unlock();
+ try {
+ state = initialState;
+ bindHandler = new BindHandler(initialBoundState);
+ } finally {
+ stateLock.writeLock().unlock();
+ }
}
private boolean doConnect(SockAddrUnix remote) throws IOException {
@@ -146,7 +149,7 @@ public class UnixSocketChannel extends AbstractNativeSocketChannel {
return true;
}
}
-
+
boolean isBound() {
return bindHandler.isBound();
}
@@ -287,6 +290,7 @@ public class UnixSocketChannel extends AbstractNativeSocketChannel {
set.add(UnixSocketOptions.SO_RCVTIMEO);
set.add(UnixSocketOptions.SO_PEERCRED);
set.add(UnixSocketOptions.SO_KEEPALIVE);
+ set.add(UnixSocketOptions.SO_PASSCRED);
return Collections.unmodifiableSet(set);
}
}
=====================================
src/main/java/jnr/unixsocket/UnixSocketOptions.java
=====================================
@@ -73,5 +73,11 @@ public final class UnixSocketOptions {
public static final SocketOption<Credentials> SO_PEERCRED =
new GenericOption<Credentials>("SO_PEERCRED", Credentials.class);
+ /**
+ * Enable credential transmission.
+ */
+ public static final SocketOption<Boolean> SO_PASSCRED =
+ new GenericOption<Boolean>("SO_PASSCRED", Boolean.class);
+
}
=====================================
src/main/java/jnr/enxio/channels/AbstractNativeDatagramChannel.java → src/main/java/jnr/unixsocket/impl/AbstractNativeDatagramChannel.java
=====================================
@@ -16,7 +16,11 @@
* limitations under the License.
*/
-package jnr.enxio.channels;
+package jnr.unixsocket.impl;
+
+import jnr.enxio.channels.Native;
+import jnr.enxio.channels.NativeSelectableChannel;
+import jnr.enxio.channels.NativeSelectorProvider;
import java.io.IOException;
import java.nio.ByteBuffer;
=====================================
src/main/java/jnr/unixsocket/impl/AbstractNativeServerSocketChannel.java
=====================================
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 Jesse Wilson
+ *
+ * This file is part of the JNR project.
+ *
+ * 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
+ *
+ * http://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 jnr.unixsocket.impl;
+
+import jnr.constants.platform.Shutdown;
+import jnr.enxio.channels.Native;
+import jnr.enxio.channels.NativeServerSocketChannel;
+
+import java.io.IOException;
+import java.nio.channels.spi.SelectorProvider;
+
+public abstract class AbstractNativeServerSocketChannel extends NativeServerSocketChannel {
+ public AbstractNativeServerSocketChannel(int fd) {
+ super(fd);
+ }
+
+ public AbstractNativeServerSocketChannel(SelectorProvider provider, int fd, int ops) {
+ super(provider, fd, ops);
+ }
+
+ @Override
+ protected void implCloseSelectableChannel() throws IOException {
+ // Shutdown to interrupt any potentially blocked threads. This is necessary on Linux.
+ Native.shutdown(getFD(), SHUT_RD);
+ Native.close(getFD());
+ }
+
+ private static final int SHUT_RD = Shutdown.SHUT_RD.intValue();
+}
=====================================
src/main/java/jnr/enxio/channels/AbstractNativeSocketChannel.java → src/main/java/jnr/unixsocket/impl/AbstractNativeSocketChannel.java
=====================================
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-package jnr.enxio.channels;
+package jnr.unixsocket.impl;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -24,7 +24,12 @@ import java.nio.channels.ByteChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
+import jnr.constants.platform.Errno;
import jnr.constants.platform.Shutdown;
+import jnr.enxio.channels.Native;
+import jnr.enxio.channels.NativeException;
+import jnr.enxio.channels.NativeSelectableChannel;
+import jnr.enxio.channels.NativeSelectorProvider;
public abstract class AbstractNativeSocketChannel extends SocketChannel
implements ByteChannel, NativeSelectableChannel {
@@ -50,6 +55,11 @@ public abstract class AbstractNativeSocketChannel extends SocketChannel
@Override
protected void implCloseSelectableChannel() throws IOException {
+ if (this.isConnected()) {
+ this.shutdownInput();
+ this.shutdownOutput();
+ }
+
Native.close(common.getFD());
}
@@ -81,8 +91,8 @@ public abstract class AbstractNativeSocketChannel extends SocketChannel
@Override
public SocketChannel shutdownInput() throws IOException {
int n = Native.shutdown(common.getFD(), SHUT_RD);
- if (n < 0) {
- throw new IOException(Native.getLastErrorString());
+ if (n < 0 && Native.getLastError() != Errno.ENOTCONN) {
+ throw new NativeException(Native.getLastErrorString(), Native.getLastError());
}
return this;
}
@@ -90,8 +100,8 @@ public abstract class AbstractNativeSocketChannel extends SocketChannel
@Override
public SocketChannel shutdownOutput() throws IOException {
int n = Native.shutdown(common.getFD(), SHUT_WR);
- if (n < 0) {
- throw new IOException(Native.getLastErrorString());
+ if (n < 0 && Native.getLastError() != Errno.ENOTCONN) {
+ throw new NativeException(Native.getLastErrorString(), Native.getLastError());
}
return this;
}
=====================================
src/main/java/jnr/enxio/channels/Common.java → src/main/java/jnr/unixsocket/impl/Common.java
=====================================
@@ -16,12 +16,14 @@
* limitations under the License.
*/
-package jnr.enxio.channels;
+package jnr.unixsocket.impl;
import java.io.IOException;
import java.nio.ByteBuffer;
import jnr.constants.platform.Errno;
+import jnr.enxio.channels.Native;
+import jnr.enxio.channels.NativeException;
/**
* Helper class, providing common methods.
@@ -64,7 +66,7 @@ final class Common {
return 0;
default:
- throw new IOException(Native.getLastErrorString());
+ throw new NativeException(Native.getLastErrorString(), lastError);
}
default: {
@@ -92,35 +94,54 @@ final class Common {
int write(ByteBuffer src) throws IOException {
- ByteBuffer buffer = ByteBuffer.allocate(src.remaining());
-
+ int r = src.remaining();
+
+ ByteBuffer buffer = ByteBuffer.allocate(r);
+
buffer.put(src);
-
+
buffer.position(0);
int n = Native.write(_fd, buffer);
- if (n < 0) {
- switch (Native.getLastError()) {
+ if (n >=0 ) {
+ if (n < r) {
+ src.position(src.position()-(r-n));
+ }
+ } else {
+ Errno lastError = Native.getLastError();
+ switch (lastError) {
case EAGAIN:
case EWOULDBLOCK:
+ src.position(src.position()-r);
return 0;
default:
- throw new IOException(Native.getLastErrorString());
+ throw new NativeException(Native.getLastErrorString(), lastError);
}
}
return n;
}
- long write(ByteBuffer[] srcs, int offset, int length)
- throws IOException {
+ long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
long result = 0;
- int index = 0;
- for (index = offset; index < length; index++) {
- result += write(srcs[index]);
+ for (int index = offset; index < length; ++index) {
+ ByteBuffer buffer = srcs[index];
+ int remaining = buffer.remaining();
+ int written = 0;
+ while (true) {
+ int w = write(buffer);
+ written += w;
+ if (w == 0 || written == remaining) {
+ break;
+ }
+ }
+ result += written;
+ if (written < remaining) {
+ break;
+ }
}
return result;
=====================================
src/test/java/jnr/unixsocket/BasicFunctionalityTest.java
=====================================
@@ -1,10 +1,11 @@
package jnr.unixsocket;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static junit.framework.Assert.*;
+import jnr.enxio.channels.NativeSelectorProvider;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
-import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.SocketException;
@@ -16,19 +17,30 @@ import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Set;
-import jnr.enxio.channels.NativeSelectorProvider;
-
-import org.junit.Test;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
public class BasicFunctionalityTest {
- private static final File SOCKADDR = new File("/tmp/jnr-unixsocket-test" + System.currentTimeMillis() + ".sock");
- static { SOCKADDR.deleteOnExit(); }
- private static final UnixSocketAddress ADDRESS = new UnixSocketAddress(SOCKADDR);
private static final String DATA = "blah blah";
+ private UnixSocketPair socketPair;
private Thread server;
private volatile Exception serverException;
-
+
+ @Before
+ public void setUp() throws Exception {
+ socketPair = new UnixSocketPair();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ socketPair.close();
+ }
+
@Test
public void doubleBindTest() throws Exception {
UnixSocketChannel ch = UnixSocketChannel.open().bind(null);
@@ -44,7 +56,7 @@ public class BasicFunctionalityTest {
}
}
}
-
+
@Test
public void pairTest() throws Exception {
UnixSocketChannel[] sp = UnixSocketChannel.pair();
@@ -61,7 +73,7 @@ public class BasicFunctionalityTest {
final UnixServerSocketChannel channel = UnixServerSocketChannel.open();
final Selector sel = NativeSelectorProvider.getInstance().openSelector();
channel.configureBlocking(false);
- channel.socket().bind(ADDRESS);
+ channel.socket().bind(socketPair.socketAddress());
channel.register(sel, SelectionKey.OP_ACCEPT, new ServerActor(channel, sel));
// TODO: This is ugly but simple enough. Many failures on server side will cause client to hang.
@@ -92,9 +104,9 @@ public class BasicFunctionalityTest {
server.start();
// client logic
- UnixSocketChannel channel2 = UnixSocketChannel.open(ADDRESS);
+ UnixSocketChannel channel2 = UnixSocketChannel.open(socketPair.socketAddress());
- assertEquals(ADDRESS, channel2.getRemoteSocketAddress());
+ assertEquals(socketPair.socketAddress(), channel2.getRemoteSocketAddress());
Channels.newOutputStream(channel2).write(DATA.getBytes(UTF_8));
@@ -132,7 +144,7 @@ public class BasicFunctionalityTest {
// nonblocking result
return false;
}
- assertEquals(ADDRESS, client.getLocalSocketAddress());
+ assertEquals(socketPair.socketAddress(), client.getLocalSocketAddress());
assertEquals("", client.getRemoteSocketAddress().getStruct().getPath());
client.configureBlocking(false);
=====================================
src/test/java/jnr/unixsocket/SocketInteropTest.java
=====================================
@@ -0,0 +1,212 @@
+package jnr.unixsocket;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.SocketException;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.NotYetBoundException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+/**
+ * Confirm that UNIX sockets work similarly to TCP sockets.
+ */
+ at RunWith(Parameterized.class)
+public class SocketInteropTest {
+ @Rule
+ public Timeout timeout = new Timeout(5, TimeUnit.SECONDS);
+
+ @Parameter
+ public TestSocketPair.Factory socketPairFactory;
+
+ private TestSocketPair socketPair;
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> parameters() {
+ return Arrays.asList(
+ new Object[] { UnixSocketPair.FACTORY },
+ new Object[] { TcpSocketsApiSocketPair.FACTORY },
+ new Object[] { TcpChannelsApiSocketPair.FACTORY }
+ );
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ socketPair = socketPairFactory.createUnconnected();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ socketPair.close();
+ }
+
+ @Test
+ public void serverWritesAndClientReads() throws IOException {
+ socketPair.connectBlocking();
+
+ OutputStream serverOut = socketPair.server().getOutputStream();
+ serverOut.write("message from server to client\n".getBytes(UTF_8));
+ serverOut.flush();
+
+ InputStream clientIn = socketPair.client().getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(clientIn, UTF_8));
+ assertEquals("message from server to client", reader.readLine());
+ }
+
+ @Test
+ public void clientWritesAndServerReads() throws IOException {
+ socketPair.connectBlocking();
+
+ OutputStream clientOut = socketPair.client().getOutputStream();
+ clientOut.write("message from client to server\n".getBytes(UTF_8));
+ clientOut.flush();
+
+ InputStream serverIn = socketPair.server().getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(serverIn, UTF_8));
+ assertEquals("message from client to server", reader.readLine());
+ }
+
+ @Test
+ public void acceptThrowsWhenServerSocketIsNotYetBound() throws IOException {
+ try {
+ socketPair.serverAccept();
+ fail();
+ } catch (NotYetBoundException expected) {
+ // Thrown by channels APIs.
+ } catch (SocketException expected) {
+ // Thrown by sockets APIs.
+ }
+ }
+
+ @Test
+ public void acceptThrowsWhenServerSocketIsClosed() throws IOException {
+ socketPair.serverBind();
+ socketPair.close();
+ try {
+ socketPair.serverAccept();
+ fail();
+ } catch (ClosedChannelException expected) {
+ // Thrown by channels APIs.
+ } catch (SocketException expected) {
+ // Thrown by sockets APIs.
+ }
+ }
+
+ @Test
+ public void acceptThrowsWhenServerSocketIsAsynchronouslyClosed() throws IOException {
+ socketPair.serverBind();
+ closeLater(socketPair, 500, TimeUnit.MILLISECONDS);
+ try {
+ socketPair.serverAccept();
+ fail();
+ } catch (AsynchronousCloseException expected) {
+ // Thrown by channels APIs.
+ } catch (SocketException expected) {
+ // Thrown by sockets APIs.
+ }
+ }
+
+ private void closeLater(final Closeable closeable, final long delay, final TimeUnit timeUnit) {
+ new Thread(getClass().getName() + ".closeLater") {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(timeUnit.toMillis(delay));
+ closeable.close();
+ } catch (IOException | InterruptedException ignored) {
+ }
+ }
+ }.start();
+ }
+
+ @Test
+ public void acceptThrowsWhenAcceptingThreadIsInterrupted() throws IOException {
+ // https://bugs.openjdk.java.net/browse/JDK-4386498
+ assumeTrue("the TCP sockets API doesn't support Thread.interrupt()",
+ socketPairFactory != TcpSocketsApiSocketPair.FACTORY);
+
+ socketPair.serverBind();
+ interruptLater(Thread.currentThread(), 500, TimeUnit.MILLISECONDS);
+ try {
+ socketPair.serverAccept();
+ fail();
+ } catch (ClosedByInterruptException expected) {
+ }
+ // This has a side-effect of clearing the interrupted state. Otherwise later tests may fail!
+ assertTrue(Thread.interrupted());
+ }
+
+ private void interruptLater(final Thread target, final long delay, final TimeUnit timeUnit) {
+ new Thread(getClass().getName() + ".interruptLater") {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(timeUnit.toMillis(delay));
+ target.interrupt();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ }.start();
+ }
+
+ @Test
+ public void concurrentReadAndWrite() throws IOException {
+ // https://bugs.openjdk.java.net/browse/JDK-4774871
+ assumeTrue("the TCP channels API doesn't support concurrent read and write",
+ socketPairFactory != TcpChannelsApiSocketPair.FACTORY);
+
+ socketPair.connectBlocking();
+
+ // This thread runs later. It writes messages on each socket.
+ new Thread(getClass().getName() + ".concurrentReadAndWrite") {
+ @Override
+ public void run() {
+ try {
+ // Sleep to guarantee that the reads are in-flight before the writes are attempted.
+ Thread.sleep(500);
+
+ OutputStream clientOut = socketPair.client().getOutputStream();
+ clientOut.write("message from client to server\n".getBytes(UTF_8));
+ clientOut.flush();
+
+ OutputStream serverOut = socketPair.server().getOutputStream();
+ serverOut.write("message from server to client\n".getBytes(UTF_8));
+ serverOut.flush();
+ } catch (InterruptedException | IOException ignored) {
+ }
+ }
+ }.start();
+
+ // This thread runs earlier. It reads messages on each socket.
+ InputStream clientIn = socketPair.client().getInputStream();
+ BufferedReader clientReader = new BufferedReader(new InputStreamReader(clientIn, UTF_8));
+ assertEquals("message from server to client", clientReader.readLine());
+
+ InputStream serverIn = socketPair.server().getInputStream();
+ BufferedReader serverReader = new BufferedReader(new InputStreamReader(serverIn, UTF_8));
+ assertEquals("message from client to server", serverReader.readLine());
+ }
+}
=====================================
src/test/java/jnr/unixsocket/TcpChannelsApiSocketPair.java
=====================================
@@ -0,0 +1,84 @@
+package jnr.unixsocket;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+/**
+ * TCP sockets created with the java.nio channels APIs.
+ */
+class TcpChannelsApiSocketPair extends TestSocketPair {
+ static final Factory FACTORY = new Factory() {
+ @Override
+ TestSocketPair createUnconnected() throws IOException {
+ return new TcpChannelsApiSocketPair();
+ }
+ };
+
+ private final ServerSocketChannel serverSocketChannel;
+ private InetSocketAddress serverAddress;
+ private SocketChannel serverChannel;
+ private SocketChannel clientChannel;
+
+ TcpChannelsApiSocketPair() throws IOException {
+ serverSocketChannel = ServerSocketChannel.open();
+ }
+
+ @Override
+ void serverBind() throws IOException {
+ if (serverAddress != null) {
+ throw new IllegalStateException("already bound");
+ }
+
+ ServerSocket serverSocket = serverSocketChannel.socket();
+ serverSocket.setReuseAddress(true);
+ serverSocketChannel.bind(new InetSocketAddress(0));
+ serverSocketChannel.configureBlocking(true);
+ serverAddress = new InetSocketAddress(serverSocket.getInetAddress(), serverSocket.getLocalPort());
+ }
+
+ @Override
+ void clientConnect() throws IOException {
+ if (clientChannel != null) {
+ throw new IllegalStateException("already connected");
+ }
+
+ clientChannel = SocketChannel.open();
+ clientChannel.connect(serverAddress);
+ }
+
+ @Override
+ void serverAccept() throws IOException {
+ if (serverChannel != null) {
+ throw new IllegalStateException("already accepted");
+ }
+
+ serverChannel = serverSocketChannel.accept();
+ }
+
+ @Override
+ SocketAddress socketAddress() {
+ return serverAddress;
+ }
+
+ @Override
+ Socket server() {
+ return serverChannel.socket();
+ }
+
+ @Override
+ Socket client() {
+ return clientChannel.socket();
+ }
+
+ @Override
+ public void close() throws IOException {
+ closeQuietly(serverSocketChannel);
+ closeQuietly(serverChannel);
+ closeQuietly(clientChannel);
+ }
+}
=====================================
src/test/java/jnr/unixsocket/TcpSocketsApiSocketPair.java
=====================================
@@ -0,0 +1,81 @@
+package jnr.unixsocket;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+
+/**
+ * TCP sockets created with the java.io sockets APIs. The sockets returned by
+ * this class do not have channels.
+ */
+class TcpSocketsApiSocketPair extends TestSocketPair {
+ static final Factory FACTORY = new Factory() {
+ @Override
+ TestSocketPair createUnconnected() throws IOException {
+ return new TcpSocketsApiSocketPair();
+ }
+ };
+
+ private final ServerSocket serverSocket;
+ private InetSocketAddress serverAddress;
+ private Socket server;
+ private Socket client;
+
+ public TcpSocketsApiSocketPair() throws IOException {
+ serverSocket = new ServerSocket();
+ }
+
+ @Override
+ void serverBind() throws IOException {
+ if (serverAddress != null) {
+ throw new IllegalStateException("already bound");
+ }
+
+ serverSocket.setReuseAddress(true);
+ serverSocket.bind(new InetSocketAddress(0));
+ serverAddress = new InetSocketAddress(serverSocket.getInetAddress(), serverSocket.getLocalPort());
+ }
+
+ @Override
+ void clientConnect() throws IOException {
+ if (client != null) {
+ throw new IllegalStateException("already connected");
+ }
+
+ client = new Socket();
+ client.connect(serverAddress);
+ }
+
+ @Override
+ void serverAccept() throws IOException {
+ if (server != null) {
+ throw new IllegalStateException("already accepted");
+ }
+
+ server = serverSocket.accept();
+ }
+
+ @Override
+ SocketAddress socketAddress() {
+ return serverAddress;
+ }
+
+ @Override
+ Socket server() {
+ return server;
+ }
+
+ @Override
+ Socket client() {
+ return client;
+ }
+
+ @Override
+ public void close() throws IOException {
+ closeQuietly(serverSocket);
+ closeQuietly(server);
+ closeQuietly(client);
+ }
+}
=====================================
src/test/java/jnr/unixsocket/TestSocketPair.java
=====================================
@@ -0,0 +1,43 @@
+package jnr.unixsocket;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.Socket;
+import java.net.SocketAddress;
+
+/**
+ * A TCP or UNIX socket pair for testing.
+ */
+abstract class TestSocketPair implements Closeable {
+ void connectBlocking() throws IOException {
+ serverBind();
+ clientConnect();
+ serverAccept();
+ }
+
+ abstract void serverBind() throws IOException;
+
+ abstract void serverAccept() throws IOException;
+
+ abstract void clientConnect() throws IOException;
+
+ abstract SocketAddress socketAddress();
+
+ abstract Socket server();
+
+ abstract Socket client();
+
+ final void closeQuietly(Closeable closeable) {
+ if (closeable == null) {
+ return;
+ }
+ try {
+ closeable.close();
+ } catch (IOException ignored) {
+ }
+ }
+
+ abstract static class Factory {
+ abstract TestSocketPair createUnconnected() throws IOException;
+ }
+}
=====================================
src/test/java/jnr/unixsocket/UnixSocketChannelTest.java
=====================================
@@ -1,5 +1,10 @@
package jnr.unixsocket;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.CountDownLatch;
+
import org.junit.Test;
import org.junit.Assume;
@@ -48,4 +53,83 @@ public class UnixSocketChannelTest {
assertEquals("local socket path", ABSTRACT, ch.getLocalSocketAddress().path());
}
+ @Test
+ public void testInterruptRead() throws Exception {
+ Path socketPath = getTemporarySocketFileName();
+ startServer(socketPath);
+
+ int readTimeoutInMilliseconds = 5000;
+
+ UnixSocket socket = createClient(socketPath, readTimeoutInMilliseconds);
+ CountDownLatch readStartLatch = new CountDownLatch(1);
+ ReadFromSocketRunnable runnable = new ReadFromSocketRunnable(readStartLatch, socket);
+
+ Thread readThread = new Thread(runnable);
+
+ readThread.setDaemon(true);
+
+ long startTime = System.nanoTime();
+ readThread.start();
+ readStartLatch.await();
+ Thread.sleep(100); // Wait for the thread to call read()
+ socket.close();
+ readThread.join();
+ long stopTime = System.nanoTime();
+
+ long duration = stopTime - startTime;
+ long durationInMilliseconds = duration / 1_000_000;
+
+ assertTrue("read() was not interrupted by close() before read() timed out", durationInMilliseconds < readTimeoutInMilliseconds);
+ assertEquals("read() threw an exception", null, runnable.getThrownOnThread());
+ }
+
+ private Path getTemporarySocketFileName() throws IOException {
+ Path socketPath = Files.createTempFile("jnr-unixsocket-tests", ".sock");
+ Files.delete(socketPath);
+ socketPath.toFile().deleteOnExit();
+
+ return socketPath;
+ }
+
+ private void startServer(Path socketPath) throws IOException {
+ UnixServerSocketChannel serverChannel = UnixServerSocketChannel.open();
+ serverChannel.configureBlocking(false);
+ serverChannel.socket().bind(new UnixSocketAddress(socketPath.toFile()));
+ }
+
+ private UnixSocket createClient(Path socketPath, int readTimeoutInMilliseconds) throws IOException {
+ UnixSocketChannel clientChannel = UnixSocketChannel.open(new UnixSocketAddress(socketPath.toFile()));
+ UnixSocket socket = new UnixSocket(clientChannel);
+ socket.setSoTimeout(readTimeoutInMilliseconds);
+
+ return socket;
+ }
+
+ private class ReadFromSocketRunnable implements Runnable {
+ private CountDownLatch readStartLatch;
+ private UnixSocket socket;
+ private IOException thrownOnThread;
+
+ private ReadFromSocketRunnable(CountDownLatch readStartLatch, UnixSocket socket) {
+ this.readStartLatch = readStartLatch;
+ this.socket = socket;
+ }
+
+ @Override
+ public void run() {
+ try {
+ readStartLatch.countDown();
+ socket.getInputStream().read();
+ } catch (IOException e) {
+ // EBADF (bad file descriptor) is thrown when read() is interrupted
+ if (!e.getMessage().equals("Bad file descriptor")) {
+ thrownOnThread = e;
+ }
+ }
+ }
+
+ private IOException getThrownOnThread() {
+ return thrownOnThread;
+ }
+ }
}
=====================================
src/test/java/jnr/unixsocket/UnixSocketPair.java
=====================================
@@ -0,0 +1,76 @@
+package jnr.unixsocket;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.Socket;
+import java.util.UUID;
+
+class UnixSocketPair extends TestSocketPair {
+ static final Factory FACTORY = new Factory() {
+ @Override
+ TestSocketPair createUnconnected() throws IOException {
+ return new UnixSocketPair();
+ }
+ };
+
+ private final File file;
+ private final UnixSocketAddress address;
+
+ private UnixServerSocketChannel serverSocketChannel;
+ private UnixSocketChannel serverChannel;
+ private UnixSocketChannel clientChannel;
+
+ UnixSocketPair() throws IOException {
+ file = new File("/tmp/jnr-unixsocket-test" + UUID.randomUUID() + ".sock");
+ address = new UnixSocketAddress(file);
+ serverSocketChannel = UnixServerSocketChannel.open();
+ }
+
+ @Override
+ void serverBind() throws IOException {
+ serverSocketChannel.configureBlocking(true);
+ serverSocketChannel.socket().bind(address);
+ }
+
+ @Override
+ void clientConnect() throws IOException {
+ if (clientChannel != null) {
+ throw new IllegalStateException("already connected");
+ }
+
+ clientChannel = UnixSocketChannel.open();
+ clientChannel.connect(new UnixSocketAddress(file));
+ }
+
+ @Override
+ void serverAccept() throws IOException {
+ if (serverChannel != null) {
+ throw new IllegalStateException("already accepted");
+ }
+
+ serverChannel = serverSocketChannel.accept();
+ }
+
+ @Override
+ UnixSocketAddress socketAddress() {
+ return address;
+ }
+
+ @Override
+ Socket server() {
+ return serverChannel.socket();
+ }
+
+ @Override
+ Socket client() {
+ return clientChannel.socket();
+ }
+
+ @Override
+ public void close() throws IOException {
+ closeQuietly(serverSocketChannel);
+ closeQuietly(serverChannel);
+ closeQuietly(clientChannel);
+ file.delete();
+ }
+}
=====================================
src/test/java/jnr/unixsocket/example/UnixServer.java
=====================================
@@ -104,16 +104,19 @@ public class UnixServer {
public final boolean rxready() {
try {
ByteBuffer buf = ByteBuffer.allocate(1024);
- int n = channel.read(buf);
- UnixSocketAddress remote = channel.getRemoteSocketAddress();
- System.out.printf("Read in %d bytes from %s\n", n, remote);
+ int n;
- if (n > 0) {
- buf.flip();
- channel.write(buf);
- return true;
- } else if (n < 0) {
- return false;
+ while ((n = channel.read(buf)) > 0) {
+ UnixSocketAddress remote = channel.getRemoteSocketAddress();
+ System.out.printf("Read in %d bytes from %s%n", n, remote);
+
+ if (n > 0) {
+ buf.flip();
+ channel.write(buf);
+ buf.clear();
+ } else if (n < 0) {
+ return false;
+ }
}
} catch (IOException ex) {
View it on GitLab: https://salsa.debian.org/java-team/jnr-unixsocket/-/commit/7390f438699e831235f79ab04b812d96f2f24eba
--
View it on GitLab: https://salsa.debian.org/java-team/jnr-unixsocket/-/commit/7390f438699e831235f79ab04b812d96f2f24eba
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20231202/d95504ec/attachment.htm>
More information about the pkg-java-commits
mailing list