View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2;
18  
19  import java.net.Inet6Address;
20  import java.net.InetAddress;
21  import java.net.NetworkInterface;
22  import java.net.SocketException;
23  import java.time.Duration;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.Enumeration;
27  import java.util.List;
28  
29  import org.apache.commons.lang3.StringUtils;
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.junit.Test;
33  
34  public class IPv6LocalConnectionTests extends AbstractProviderTestCase {
35  
36      private static final String LOOPBACK = "0:0:0:0:0:0:0:1";
37      private static final Log log = LogFactory.getLog(IPv6LocalConnectionTests.class);
38  
39      private static List<String> getLocalIPv6Addresses() throws SocketException {
40          final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
41          final List<String> result = new ArrayList<>();
42          for (final NetworkInterface networkInterface : Collections.list(networkInterfaces)) {
43              if (!networkInterface.isUp() || networkInterface.isLoopback()
44              // utun refers to VPN network interface, we don't expect this connection to work
45                      || networkInterface.getName().startsWith("utun")) {
46                  continue;
47              }
48              for (final InetAddress inetAddress : Collections.list(networkInterface.getInetAddresses())) {
49                  if (inetAddress instanceof Inet6Address && !inetAddress.isLoopbackAddress() && !inetAddress.isMulticastAddress()) {
50                      result.add(StringUtils.substringBefore(inetAddress.getHostAddress(), "%"));
51                  }
52              }
53          }
54          // Add loopback as backstop, this is needed for some IPv6 setups on Windows.
55          if (!result.contains(LOOPBACK)) {
56              result.add(LOOPBACK);
57          }
58          return result;
59      }
60  
61      @Override
62      protected Capability[] getRequiredCapabilities() {
63          return new Capability[] { Capability.URI, Capability.READ_CONTENT };
64      }
65  
66      @Override
67      protected void runTest() throws Throwable {
68          final List<String> localIPv6Addresses = getLocalIPv6Addresses();
69          if (localIPv6Addresses.isEmpty()) {
70              log.info("Local machine must have IPv6 address to run this test");
71              return;
72          }
73          super.runTest();
74      }
75  
76      private FileSystemOptions setupConnectionTimeoutHints(final FileSystem fileSystem) {
77          // Unfortunately there is no common way to set up timeouts for every protocol
78          // So, we use this hacky approach to make this class generic and formally independent of protocols implementations
79  
80          final FileSystemOptions result = (FileSystemOptions) fileSystem.getFileSystemOptions().clone();
81  
82          final Duration timeout = Duration.ofSeconds(5);
83  
84          result.setOption(fileSystem.getClass(), "org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder.CONNECT_TIMEOUT", timeout);
85          result.setOption(fileSystem.getClass(), "org.apache.commons.vfs2.provider.sftp.SftpFileSystemConfigBuilder.TIMEOUT", timeout);
86  
87          result.setOption(fileSystem.getClass(), "http.connection.timeout", timeout);
88          result.setOption(fileSystem.getClass(), "http.socket.timeout", timeout);
89  
90          // This actually doesn't affect FtpFileProvider now, but it looks like an issue
91          // This would work, if FtpClientFactory call client.setConnectTimeout() with CONNECT_TIMEOUT value
92          result.setOption(fileSystem.getClass(), "org.apache.commons.vfs2.provider.ftp.FtpFileSystemConfigBuilder.CONNECT_TIMEOUT", timeout);
93          result.setOption(fileSystem.getClass(), "org.apache.commons.vfs2.provider.ftp.FtpFileSystemConfigBuilder.SO_TIMEOUT", timeout);
94  
95          return result;
96      }
97  
98      @Test
99      public void testConnectIPv6UrlLocal() throws Exception {
100         final List<String> localIPv6Addresses = getLocalIPv6Addresses();
101         boolean connected = false;
102         for (final String ipv6Address : localIPv6Addresses) {
103             final String ipv6Url = StringUtils.replace(getReadFolder().getURL().toString(), "localhost", "[" + ipv6Address + "]");
104             try {
105                 final FileSystem fileSystem = getFileSystem();
106 
107                 final FileObject readFolderObject = getManager().resolveFile(ipv6Url, setupConnectionTimeoutHints(fileSystem));
108                 connected = connected || readFolderObject.resolveFile("file1.txt").getContent().getByteArray() != null;
109             } catch (final FileSystemException e) {
110                 // We don't care, if some of the discovered IPv6 addresses don't work.
111                 // We just need a single one to work for testing the functionality end-to-end.
112                 log.warn("Failed to connect to some of the local IPv6 network addresses", e);
113             }
114         }
115         assertTrue("None of the discovered local IPv6 network addresses has responded for connection: " + localIPv6Addresses, connected);
116     }
117 
118 }