Skip to content

8336815: Several methods in java.net.Socket and ServerSocket do not specify behavior when already bound, connected or closed #20268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from

Conversation

jaikiran
Copy link
Member

@jaikiran jaikiran commented Jul 20, 2024

Can I please get a review of this change which updates the specification of a few methods in java.net.Socket and java.net.ServerSocket classes to specify the IOException that the implementation currently already throws?

This is merely a doc update and doesn't change the implementation.

Given that these exceptions can now be asserted, a new jtreg test has been introduced to verify the exceptions thrown from these APIs. The test passes. tier testing is currently in progress. I'll open a CSR shortly.


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change requires CSR request JDK-8336868 to be approved
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issues

  • JDK-8336815: Several methods in java.net.Socket and ServerSocket do not specify behavior when already bound, connected or closed (Bug - P4)
  • JDK-8336868: Several methods in java.net.Socket and ServerSocket do not specify behavior when already bound, connected or closed (CSR)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/20268/head:pull/20268
$ git checkout pull/20268

Update a local copy of the PR:
$ git checkout pull/20268
$ git pull https://git.openjdk.org/jdk.git pull/20268/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 20268

View PR using the GUI difftool:
$ git pr show -t 20268

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/20268.diff

Webrev

Link to Webrev Comment

…ify behavior when already bound, connected or closed
@jaikiran
Copy link
Member Author

/csr needed

@bridgekeeper
Copy link

bridgekeeper bot commented Jul 20, 2024

👋 Welcome back jpai! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Jul 20, 2024

@jaikiran This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8336815: Several methods in java.net.Socket and ServerSocket do not specify behavior when already bound, connected or closed

Reviewed-by: alanb

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been 43 new commits pushed to the master branch:

  • 05d88de: 8336098: G1: Refactor G1RebuildRSAndScrubTask
  • 5b4824c: 8336827: compiler/vectorization/TestFloat16VectorConvChain.java timeouts on ppc64 platforms after JDK-8335860
  • 3251eea: 8336499: Failure when creating non-CRT RSA private keys in SunPKCS11
  • 476d2ae: 8336831: Optimize StringConcatHelper.simpleConcat
  • 4c91d5c: 8322133: getParameterSpec(ECGenParameterSpec.class) on EC AlgorithmParameters does not return standard names
  • 2f2223d: 8336944: Shenandoah: Should only relativize stack chunks for successful evacuations
  • 8efcb40: 8335823: Update --release 23 symbol information for JDK 23 build 33
  • 8e1f17e: 8327054: DiagnosticCommand Compiler.perfmap does not log on output()
  • 0e555b5: 8204582: Extra spaces in jlink documentation make it incorrect.
  • a2a236f: 8335939: Hide element writing across the ClassFile API
  • ... and 33 more: https://git.openjdk.org/jdk/compare/c25c4896ad9ef031e3cddec493aef66ff87c48a7...master

As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

@openjdk openjdk bot added rfr Pull request is ready for review csr Pull request needs approved CSR before integration labels Jul 20, 2024
@openjdk
Copy link

openjdk bot commented Jul 20, 2024

@jaikiran has indicated that a compatibility and specification (CSR) request is needed for this pull request.

@jaikiran please create a CSR request for issue JDK-8336815 with the correct fix version. This pull request cannot be integrated until the CSR request is approved.

@openjdk
Copy link

openjdk bot commented Jul 20, 2024

@jaikiran The following label will be automatically applied to this pull request:

  • net

When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command.

@openjdk openjdk bot added the net net-dev@openjdk.org label Jul 20, 2024
@mlbridge
Copy link

mlbridge bot commented Jul 20, 2024

Webrevs

* throw the specified exceptions
* @run junit SocketBasicExceptionsTest
*/
public class SocketBasicExceptionsTest {
Copy link
Contributor

@AlanBateman AlanBateman Jul 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect these exceptions are already tested by existing tests for Socket and Server, can you check? Only asking because it looks a bit unusual to create a test for a small subset of the possible exceptions thrown by these classes.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had looked around in the test/jdk/java/net/Socket and test/jdk/java/net/ServerSocket tests to see if this is already tested. But I can't see anything that does this specific testing. I now decided to do a local change to the source to not throw the IOException when already bound/closed and reran the tests in these directories:

diff --git a/src/java.base/share/classes/java/net/ServerSocket.java b/src/java.base/share/classes/java/net/ServerSocket.java
index e1271fc9deb..3280f9edc4f 100644
--- a/src/java.base/share/classes/java/net/ServerSocket.java
+++ b/src/java.base/share/classes/java/net/ServerSocket.java
@@ -366,10 +366,10 @@ public void bind(SocketAddress endpoint) throws IOException {
      * @since 1.4
      */
     public void bind(SocketAddress endpoint, int backlog) throws IOException {
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        if (isBound())
-            throw new SocketException("Already bound");
+//        if (isClosed())
+//            throw new SocketException("Socket is closed");
+//        if (isBound())
+//            throw new SocketException("Already bound");
         if (endpoint == null)
             endpoint = new InetSocketAddress(0);
         if (!(endpoint instanceof InetSocketAddress epoint))
@@ -532,10 +532,10 @@ public SocketAddress getLocalSocketAddress() {
      * @see SecurityManager#checkAccept
      */
     public Socket accept() throws IOException {
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        if (!isBound())
-            throw new SocketException("Socket is not bound yet");
+//        if (isClosed())
+//            throw new SocketException("Socket is closed");
+//        if (!isBound())
+//            throw new SocketException("Socket is not bound yet");
         Socket s = new Socket((SocketImpl) null);
         implAccept(s);
         return s;
diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.base/share/classes/java/net/Socket.java
index 1809687d5c0..cddbeb54a5a 100644
--- a/src/java.base/share/classes/java/net/Socket.java
+++ b/src/java.base/share/classes/java/net/Socket.java
@@ -737,10 +737,10 @@ public void connect(SocketAddress endpoint, int timeout) throws IOException {
             throw new IllegalArgumentException("connect: timeout can't be negative");
 
         int s = state;
-        if (isClosed(s))
-            throw new SocketException("Socket is closed");
-        if (isConnected(s))
-            throw new SocketException("already connected");
+//        if (isClosed(s))
+//            throw new SocketException("Socket is closed");
+//        if (isConnected(s))
+//            throw new SocketException("already connected");
 
         if (!(endpoint instanceof InetSocketAddress epoint))
             throw new IllegalArgumentException("Unsupported address type");
@@ -795,10 +795,10 @@ public void connect(SocketAddress endpoint, int timeout) throws IOException {
      */
     public void bind(SocketAddress bindpoint) throws IOException {
         int s = state;
-        if (isClosed(s))
-            throw new SocketException("Socket is closed");
-        if (isBound(s))
-            throw new SocketException("Already bound");
+//        if (isClosed(s))
+//            throw new SocketException("Socket is closed");
+//        if (isBound(s))
+//            throw new SocketException("Already bound");
 
         if (bindpoint != null && (!(bindpoint instanceof InetSocketAddress)))
             throw new IllegalArgumentException("Unsupported address type");

All of them continue to pass. But I think that probably doesn't mean much because even the new test that I introduced in this PR passes, because deep within the calls, these IOException do get thrown.

I do agree that it's a bit odd to be testing this in a new test class. Maybe we rename it to BasicTests and then in future use this test class for additional similar basic testing of these APIs? I am even open to dropping this new test if it feels odd to introduce and unnecessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had looked around in the test/jdk/java/net/Socket and test/jdk/java/net/ServerSocket tests to see if this is already tested. But I can't see anything that does this specific testing. I now decided to do a local change to the source to not throw the IOException when already bound/closed and reran the tests in these directories:

If there are holes in the testing then we need to fill them but I'm not sure about introducing SocketBasicExceptionsTest to test a small subset of the possible exceptions is the best approach. Also the tests for ServerSocket are in a different directory. What would you think about a ClosedSocketTest and ClosedServerServerTest (or better name) in each directory to test that the methods throw as expected, it could test that close, isXXX, ... don't throw as expected. We can do the same for bind and Socket.connect.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've now introduced a ClosedSocketTest and a ClosedServerSocketTest in their relevant directories. Each of them invoke various operations on a closed Socket/ServerSocket instance and verify that they throw the expected exception or complete normally if expected to do so.

When writing this test, I noticed that a few other methods on ServerSocket and Socket also needed an update to their specification to match their current implementation. I have included those changes as well. Once we come to an agreement about these changes, I will update the title of the issue to make it clear that this covers more APIs than what the title currently says.

One related question is - some of these APIs, like the bind() are specified to throw an IOException when the implementation throws a SocketException (a subclass of IOException). Some other APIs within the same classes specify that they throw this exact SocketException. Should bind() and a few other APIs which currently specify IOException be updated to say SocketException to closely match their implementation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When writing this test, I noticed that a few other methods on ServerSocket and Socket also needed an update to their specification to match their current implementation.

Thanks, and doesn't surprise me that it's not explicitly specified in other methods. I think the spec change looks okay although I think it would be better to not repeat the "if" in each of the thrown, e.g. "if the bind operation fails, the socket is already bound, or the socket is closed". Up to you if you want to stick with what you have.

Reading the updates, some of the socket options have "an error in the underlying protocol, such as a TCP error" but none of these methods are doing I/O, they are just setting or reading socket options. They should say "if there is an error SO_RCVBUF" or whatever. It's not important here, it's just jumping out when reading these old descriptions.

You asked about specifying the more specific SocketException. These APIs are pluggable via SocketImpl so specifying a more specific exception could potentially invalidate existing implementations. I don't know if there are any other implementations in 2024 but I think changing anything here would require further thought on the compatibility impact.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've now introduced a ClosedSocketTest and a ClosedServerSocketTest in their relevant directories. Each of them invoke various operations on a closed Socket/ServerSocket instance and verify that they throw the expected exception or complete normally if expected to do so.

Thanks. My only comment is that using a MethodSource and SocketOp/ServerSocketOp in these tests looks a bit overkill. You may find it a bit simpler to just using assertThrow, something like assertThrows(SocketException.class, s::getReceiveBufferSize). I don't have a strong opinion, I just prefer simpler tests I think.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the PR to remove the usages of the parameterized test and instead inline the operations within the test itself. The tests continue to pass.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to not repeat the "if" in each of the thrown, e.g. "if the bind operation fails, the socket is already bound, or the socket is closed".

Done - I've removed the repeated "if" from the throws clause.

some of the socket options have "an error in the underlying protocol, such as a TCP error" but none of these methods are doing I/O, they are just setting or reading socket options. They should say "if there is an error SO_RCVBUF" or whatever. It's not important here, it's just jumping out when reading these old descriptions.

I decided to leave them as-is in this PR, given how many of those there are. I think we can decide to clean them up in a separate PR if needed.

You asked about specifying the more specific SocketException. These APIs are pluggable via SocketImpl so specifying a more specific exception could potentially invalidate existing implementations. I don't know if there are any other implementations in 2024 but I think changing anything here would require further thought on the compatibility impact.

Given that context, leaving them in their current form sounds reasonable to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated tests looks okay.

No action required but just to say that future work could expand the test for when socket is bound before closed or connected before closed, thus ensure the methods have the specified behavior for all possible states. For the method that don't throw then it would be possible to assert the return values.

@@ -518,7 +518,7 @@ public SocketAddress getLocalSocketAddress() {
* client socket implementation factory}, if one has been set.
*
* @throws IOException if an I/O error occurs when waiting for a
* connection.
* connection, or if the socket isn't bound or is already closed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you check the existing text as I think it uses "not bound" rather than "isn't bound".

Copy link
Contributor

@AlanBateman AlanBateman Jul 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also "already closed" might be a bit inconsistent with the existing API docs too, can you check as I think we use "is closed" in most places.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello Alan, I've now updated the PR to address this text.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I can review the CSR when it's ready.

Copy link
Contributor

@AlanBateman AlanBateman Jul 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated docs changes look good, thanks for taking the feedback through the iterations.

Copy link
Contributor

@AlanBateman AlanBateman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec updates and test look okay to me.

@jaikiran jaikiran changed the title 8336815: Socket.bind/connect and ServerSocket.bind/accept do not specify behavior when already bound, connected or closed 8336815: Several methods in java.net.Socket and ServerSocket do not specify behavior when already bound, connected or closed Jul 22, 2024
@jaikiran
Copy link
Member Author

Thank you Alan for the review and inputs on the spec and the test. The CSR is now ready for review https://bugs.openjdk.org/browse/JDK-8336868.

@openjdk openjdk bot added ready Pull request is ready to be integrated and removed csr Pull request needs approved CSR before integration labels Jul 23, 2024
@jaikiran
Copy link
Member Author

The CSR has been approved. Joe suggested a tiny change to add a note that many of these methods throw an exception when invoked on a closed socket. I've updated this PR to include that note. No other changes.

@openjdk openjdk bot removed the ready Pull request is ready to be integrated label Jul 24, 2024
@@ -736,6 +736,9 @@ private void ensureCompatible(SocketImpl si) throws IOException {
* <p> If this socket has an associated channel then the channel is closed
* as well.
*
* <p>Once closed, several of the methods defined by this class will throw
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing paragraphs use "

", probably want to keep it consistent to avoid mixing styes in the one method.

* use (i.e. can't be reconnected or rebound). A new socket needs to be
* created.
* use (i.e. can't be reconnected or rebound) and several of the methods defined
* by this class will throw an exception when invoked on the closed socket. A new
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "if invoked" might be better than "when invoked" here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and updated the PR to address both these places.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Jul 24, 2024
@jaikiran
Copy link
Member Author

/integrate

@openjdk
Copy link

openjdk bot commented Jul 24, 2024

Going to push as commit 04e8cb8.
Since your change was applied there have been 46 commits pushed to the master branch:

  • b2599f8: 8336316: JFR: Use SettingControl::getValue() instead of setValue() for ActiveSetting event
  • aa15b89: 8336485: jdk/jfr/jcmd/TestJcmdView.java RuntimeException: 'Invoked Concurrent' missing from stdout/stderr
  • 332df83: 8336927: Missing equals and hashCode in java.lang.classfile.Annotation
  • 05d88de: 8336098: G1: Refactor G1RebuildRSAndScrubTask
  • 5b4824c: 8336827: compiler/vectorization/TestFloat16VectorConvChain.java timeouts on ppc64 platforms after JDK-8335860
  • 3251eea: 8336499: Failure when creating non-CRT RSA private keys in SunPKCS11
  • 476d2ae: 8336831: Optimize StringConcatHelper.simpleConcat
  • 4c91d5c: 8322133: getParameterSpec(ECGenParameterSpec.class) on EC AlgorithmParameters does not return standard names
  • 2f2223d: 8336944: Shenandoah: Should only relativize stack chunks for successful evacuations
  • 8efcb40: 8335823: Update --release 23 symbol information for JDK 23 build 33
  • ... and 36 more: https://git.openjdk.org/jdk/compare/c25c4896ad9ef031e3cddec493aef66ff87c48a7...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Jul 24, 2024
@openjdk openjdk bot closed this Jul 24, 2024
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Jul 24, 2024
@openjdk
Copy link

openjdk bot commented Jul 24, 2024

@jaikiran Pushed as commit 04e8cb8.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

@jaikiran jaikiran deleted the 8336815 branch July 24, 2024 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integrated Pull request has been integrated net net-dev@openjdk.org
Development

Successfully merging this pull request may close these issues.

2 participants