|
1 | 1 | /* |
2 | | - * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | 4 | * |
5 | 5 | * This code is free software; you can redistribute it and/or modify it |
|
25 | 25 | * @bug 6548464 |
26 | 26 | * @summary SocketChannel.open(SocketAddress) leaks file descriptor if |
27 | 27 | * connection cannot be established |
| 28 | + * @requires vm.flagless |
28 | 29 | * @build OpenLeak |
29 | | - * @run main/othervm -Djava.security.manager=allow OpenLeak |
| 30 | + * @run junit/othervm OpenLeak |
30 | 31 | */ |
31 | 32 |
|
| 33 | +import java.io.IOException; |
| 34 | +import java.net.ConnectException; |
32 | 35 | import java.net.InetAddress; |
33 | 36 | import java.net.InetSocketAddress; |
| 37 | +import java.net.SocketAddress; |
34 | 38 | import java.nio.channels.SocketChannel; |
| 39 | +import java.nio.channels.UnresolvedAddressException; |
| 40 | +import java.util.ArrayList; |
| 41 | +import java.util.List; |
| 42 | +import java.util.Locale; |
| 43 | + |
| 44 | +import org.junit.jupiter.params.ParameterizedTest; |
| 45 | +import org.junit.jupiter.params.provider.MethodSource; |
| 46 | + |
| 47 | +import static org.junit.jupiter.api.Assertions.assertThrows; |
| 48 | + |
35 | 49 |
|
36 | 50 | public class OpenLeak { |
37 | 51 |
|
38 | | - public static void main(String[] args) throws Exception { |
39 | | - InetAddress lh = InetAddress.getLocalHost(); |
40 | | - InetSocketAddress isa = new InetSocketAddress(lh, 12345); |
| 52 | + static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.ROOT); |
| 53 | + static final boolean IS_WINDOWS_2016 = OS_NAME.contains("windows") && OS_NAME.contains("2016"); |
| 54 | + |
| 55 | + // On Windows Server 2016 trying to connect to port 47 consumes the |
| 56 | + // whole connect timeout - which makes the test fail in timeout. |
| 57 | + // We skip this part of the test on Windows Server 2016 |
| 58 | + static final boolean TEST_WITH_RESERVED_PORT = !IS_WINDOWS_2016; |
| 59 | + |
| 60 | + private static final int MAX_LOOP = 250000; |
41 | 61 |
|
42 | | - System.setSecurityManager( new SecurityManager() ); |
43 | | - for (int i=0; i<100000; i++) { |
44 | | - try { |
45 | | - SocketChannel.open(isa); |
46 | | - throw new RuntimeException("This should not happen"); |
47 | | - } catch (SecurityException x) { } |
| 62 | + |
| 63 | + // Try to find a suitable port to provoke a "Connection Refused" |
| 64 | + // error. |
| 65 | + private static InetSocketAddress findSuitableRefusedAddress(InetSocketAddress isa) |
| 66 | + throws IOException { |
| 67 | + if (!TEST_WITH_RESERVED_PORT) return null; |
| 68 | + var addr = isa.getAddress(); |
| 69 | + try (SocketChannel sc1 = SocketChannel.open(isa)) { |
| 70 | + // If we manage to connect, let's try to use some other |
| 71 | + // port. |
| 72 | + // port 51 is reserved too - there should be nothing there... |
| 73 | + isa = new InetSocketAddress(addr, 51); |
| 74 | + try (SocketChannel sc2 = SocketChannel.open(isa)) { |
| 75 | + } |
| 76 | + // OK, last attempt... |
| 77 | + // port 61 is reserved too - there should be nothing there... |
| 78 | + isa = new InetSocketAddress(addr, 61); |
| 79 | + try (SocketChannel sc3 = SocketChannel.open(isa)) { |
| 80 | + } |
| 81 | + System.err.println("Could not find a suitable port"); |
| 82 | + return null; |
| 83 | + } catch (ConnectException x) { |
48 | 84 | } |
| 85 | + return isa; |
| 86 | + } |
| 87 | + |
| 88 | + private static InetSocketAddress createUnresolved(InetSocketAddress isa, InetSocketAddress def) { |
| 89 | + var sa = isa == null ? def : isa; |
| 90 | + return InetSocketAddress.createUnresolved(sa.getHostString(), sa.getPort()); |
| 91 | + } |
49 | 92 |
|
| 93 | + |
| 94 | + // Builds a list of test cases |
| 95 | + static List<Object[]> testCases() throws Exception { |
| 96 | + InetAddress lo = InetAddress.getLoopbackAddress(); |
| 97 | + |
| 98 | + // Try to find a suitable port that will cause a |
| 99 | + // Connection Refused exception |
| 100 | + // port 47 is reserved - there should be nothing there... |
| 101 | + InetSocketAddress def = new InetSocketAddress(lo, 47); |
| 102 | + InetSocketAddress isa = findSuitableRefusedAddress(def); |
| 103 | + InetSocketAddress sa = createUnresolved(isa, def); |
| 104 | + |
| 105 | + final List<Object[]> cases = new ArrayList<>(); |
| 106 | + cases.add(new Object[]{sa, UnresolvedAddressException.class}); |
| 107 | + if (isa != null) { |
| 108 | + cases.add(new Object[]{isa, ConnectException.class}); |
| 109 | + } |
| 110 | + return cases; |
| 111 | + } |
| 112 | + |
| 113 | + @ParameterizedTest |
| 114 | + @MethodSource("testCases") |
| 115 | + public void test(SocketAddress sa, Class<? extends Throwable> expectedException) throws Exception { |
| 116 | + System.err.printf("%nExpecting %s for %s%n", expectedException, sa); |
| 117 | + |
| 118 | + int i = 0; |
| 119 | + try { |
| 120 | + for (i = 0; i < MAX_LOOP; i++) { |
| 121 | + Throwable x = |
| 122 | + assertThrows(expectedException, () -> SocketChannel.open(sa)); |
| 123 | + if (i < 5 || i >= MAX_LOOP - 5) { |
| 124 | + // print a message for the first five and last 5 exceptions |
| 125 | + System.err.println(x); |
| 126 | + } |
| 127 | + } |
| 128 | + } catch (Throwable t) { |
| 129 | + System.err.println("Failed at " + i + " with " + t); |
| 130 | + throw t; |
| 131 | + } |
50 | 132 | } |
51 | 133 |
|
52 | 134 | } |
0 commit comments