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.provider.test;
18  
19  import static org.apache.commons.vfs2.VfsTestUtils.assertSameMessage;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertNull;
22  import static org.junit.jupiter.api.Assertions.fail;
23  
24  import org.apache.commons.vfs2.FileSystemException;
25  import org.apache.commons.vfs2.provider.GenericFileName;
26  import org.apache.commons.vfs2.provider.GenericURLFileNameParser;
27  import org.apache.commons.vfs2.provider.URLFileNameParser;
28  import org.junit.jupiter.api.Test;
29  
30  /**
31   * Some GenericFileName test cases.
32   */
33  public class GenericFileNameTest {
34  
35      /**
36       * Tests error handling in URI parser.
37       */
38      @Test
39      public void testBadlyFormedUri() throws Exception {
40          // Does not start with ftp://
41          testBadlyFormedUri("ftp:", "vfs.provider/missing-double-slashes.error");
42          testBadlyFormedUri("ftp:/", "vfs.provider/missing-double-slashes.error");
43          testBadlyFormedUri("ftp:a", "vfs.provider/missing-double-slashes.error");
44  
45          // Missing hostname
46          testBadlyFormedUri("ftp://", "vfs.provider/missing-hostname.error");
47          testBadlyFormedUri("ftp://:21/file", "vfs.provider/missing-hostname.error");
48          testBadlyFormedUri("ftp:///file", "vfs.provider/missing-hostname.error");
49  
50          // Empty port
51          testBadlyFormedUri("ftp://host:", "vfs.provider/missing-port.error");
52          testBadlyFormedUri("ftp://host:/file", "vfs.provider/missing-port.error");
53          testBadlyFormedUri("ftp://host:port/file", "vfs.provider/missing-port.error");
54  
55          // Missing absolute path
56          testBadlyFormedUri("ftp://host:90a", "vfs.provider/missing-hostname-path-sep.error");
57          testBadlyFormedUri("ftp://host?a", "vfs.provider/missing-hostname-path-sep.error");
58  
59          // TODO Improperly accepted malformed uris
60          // testBadlyFormedUri("ftp://host[a/file", "malformed uri");
61          // testBadlyFormedUri("ftp://host]a/file", "malformed uri");
62      }
63  
64      /**
65       * Tests that parsing a URI fails with the expected error.
66       */
67      private void testBadlyFormedUri(final String uri, final String errorMsg) {
68          try {
69              new URLFileNameParser(80).parseUri(null, null, uri);
70              fail();
71          } catch (final FileSystemException e) {
72              assertSameMessage(errorMsg, uri, e);
73          }
74      }
75  
76      @Test
77      public void testIPv6BadlyFormedUri() {
78          // address with opening bracket only
79          testBadlyFormedUri("ftp://[", "vfs.provider/unterminated-ipv6-hostname.error");
80  
81          // address with closing bracket only (ftp://]) actually currently parses ok, but it's not considered as IPv6 case by parser
82  
83          // address with unterminated host name
84          testBadlyFormedUri("ftp://[fe80::8b2:d61e:e5c:b333", "vfs.provider/unterminated-ipv6-hostname.error");
85  
86          // address without opening bracket (first ":" considered as port number separator in this case)
87          testBadlyFormedUri("ftp://fe80::8b2:d61e:e5c:b333]", "vfs.provider/missing-port.error");
88  
89          // empty address in brackets
90          testBadlyFormedUri("ftp://[]", "vfs.provider/missing-hostname.error");
91  
92          // double square brackets
93          // (first "]" considered as terminating bracket, path separator is expected instead of the second "]")
94          testBadlyFormedUri("ftp://[[fe80::8b2:d61e:e5c:b333]]", "vfs.provider/missing-hostname-path-sep.error");
95  
96          // two empty strings in brackets
97          testBadlyFormedUri("ftp://[][]", "vfs.provider/missing-hostname.error");
98  
99          // two non-empty strings in brackets
100         testBadlyFormedUri("ftp://[fe80::8b2:d61e:e5c:b333][fe80::8b2:d61e:e5c:b333]", "vfs.provider/missing-hostname-path-sep.error");
101     }
102 
103     @Test
104     public void testParseIPv6InvalidHostsTolerance() throws Exception {
105         // We don't strictly validate IPv6 host name, if it can be parsed out from URI
106         // Assuming, it'll just fail on connection stage
107 
108         final GenericURLFileNameParser urlParser = new GenericURLFileNameParser(21);
109 
110         // too few segments
111         GenericFileName name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[1:2e]:2222/test");
112         assertEquals("[1:2e]", name.getHostName());
113         assertEquals(2222, name.getPort());
114         assertEquals("ftp://[1:2e]:2222/test", name.getURI());
115 
116         // IPv4 address in square brackets
117         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[192.168.1.1]:2222/test");
118         assertEquals("[192.168.1.1]", name.getHostName());
119         assertEquals(2222, name.getPort());
120         assertEquals("ftp://[192.168.1.1]:2222/test", name.getURI());
121 
122         // too many segments
123         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[::7:6:5:4:3:2:1:0]:2222/test");
124         assertEquals("[::7:6:5:4:3:2:1:0]", name.getHostName());
125         assertEquals(2222, name.getPort());
126         assertEquals("ftp://[::7:6:5:4:3:2:1:0]:2222/test", name.getURI());
127 
128         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[3ffe:0:0:0:0:0:0:0:1]:2222/test");
129         assertEquals("[3ffe:0:0:0:0:0:0:0:1]", name.getHostName());
130         assertEquals(2222, name.getPort());
131         assertEquals("ftp://[3ffe:0:0:0:0:0:0:0:1]:2222/test", name.getURI());
132 
133         // segment exceeds 16 bits
134         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[3ffe::10000]:2222/test");
135         assertEquals("[3ffe::10000]", name.getHostName());
136         assertEquals(2222, name.getPort());
137         assertEquals("ftp://[3ffe::10000]:2222/test", name.getURI());
138 
139         // whitespace host
140         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[ ]:2222/test");
141         assertEquals("[ ]", name.getHostName());
142         assertEquals(2222, name.getPort());
143         assertEquals("ftp://[ ]:2222/test", name.getURI());
144 
145         // just some invalid sequences
146         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[:]:2222/test");
147         assertEquals("[:]", name.getHostName());
148         assertEquals(2222, name.getPort());
149         assertEquals("ftp://[:]:2222/test", name.getURI());
150 
151         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[:::]:2222/test");
152         assertEquals("[:::]", name.getHostName());
153         assertEquals(2222, name.getPort());
154         assertEquals("ftp://[:::]:2222/test", name.getURI());
155 
156         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[xyz]:2222/test");
157         assertEquals("[xyz]", name.getHostName());
158         assertEquals(2222, name.getPort());
159         assertEquals("ftp://[xyz]:2222/test", name.getURI());
160     }
161 
162     @Test
163     public void testParseIPv6Uri() throws Exception {
164         final GenericURLFileNameParser urlParser = new GenericURLFileNameParser(21);
165 
166         // basic case
167         GenericFileName name = (GenericFileName) urlParser.parseUri(
168                 null, null, "ftp://[fe80::3dd0:7f8e:57b7:34d5]:2222/test");
169         assertEquals("[fe80::3dd0:7f8e:57b7:34d5]", name.getHostName());
170         assertEquals(2222, name.getPort());
171         assertEquals("/test", name.getPath());
172         assertEquals("ftp://[fe80::3dd0:7f8e:57b7:34d5]:2222/", name.getRootURI());
173         assertEquals("ftp://[fe80::3dd0:7f8e:57b7:34d5]:2222/test", name.getURI());
174 
175         // full uri case
176         name = (GenericFileName) urlParser.parseUri(
177                 null, null, "http://user:password@[fe80::3dd0:7f8e:57b7:34d5]:2222/test?param1=value1&param2=value2#fragment");
178         assertEquals("[fe80::3dd0:7f8e:57b7:34d5]", name.getHostName());
179         assertEquals(2222, name.getPort());
180         assertEquals("/test", name.getPath());
181         assertEquals("http://user:password@[fe80::3dd0:7f8e:57b7:34d5]:2222/", name.getRootURI());
182         assertEquals(
183                 "http://user:password@[fe80::3dd0:7f8e:57b7:34d5]:2222/test?param1=value1&param2=value2#fragment",
184                 name.getURI());
185 
186         // no trailing zeroes case
187         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[2001:658:22a:cafe::]:2222/test");
188         assertEquals("[2001:658:22a:cafe::]", name.getHostName());
189         assertEquals(2222, name.getPort());
190         assertEquals("ftp://[2001:658:22a:cafe::]:2222/test", name.getURI());
191 
192         // the loopback address
193         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[::1]:2222/test");
194         assertEquals("[::1]", name.getHostName());
195         assertEquals(2222, name.getPort());
196         assertEquals("ftp://[::1]:2222/test", name.getURI());
197 
198         // the unspecified address
199         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[::]:2222/test");
200         assertEquals("[::]", name.getHostName());
201         assertEquals(2222, name.getPort());
202         assertEquals("ftp://[::]:2222/test", name.getURI());
203 
204         // form for a mixed environment of IPv4 and IPv6
205         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[0:0:0:0:0:0:13.1.68.3]:2222/test");
206         assertEquals("[0:0:0:0:0:0:13.1.68.3]", name.getHostName());
207         assertEquals(2222, name.getPort());
208         assertEquals("ftp://[0:0:0:0:0:0:13.1.68.3]:2222/test", name.getURI());
209 
210         // compressed form for a mixed environment of IPv4 and IPv6
211         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[::13.1.68.3]:2222/test");
212         assertEquals("[::13.1.68.3]", name.getHostName());
213         assertEquals(2222, name.getPort());
214         assertEquals("ftp://[::13.1.68.3]:2222/test", name.getURI());
215 
216         // compressed form for a mixed environment of IPv4 and IPv6
217         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[::FFFF:129.144.52.38]:2222/test");
218         assertEquals("[::ffff:129.144.52.38]", name.getHostName());
219         assertEquals(2222, name.getPort());
220         assertEquals("ftp://[::ffff:129.144.52.38]:2222/test", name.getURI());
221 
222         // a multicast address
223         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[FF01::101]:2222/test");
224         assertEquals("[ff01::101]", name.getHostName());
225         assertEquals(2222, name.getPort());
226         assertEquals("ftp://[ff01::101]:2222/test", name.getURI());
227 
228         // url without path
229         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[FF01::101]:2222");
230         assertEquals("[ff01::101]", name.getHostName());
231         assertEquals(2222, name.getPort());
232         assertEquals("ftp://[ff01::101]:2222/", name.getURI());
233 
234         // url without path and port
235         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[FF01::101]");
236         assertEquals("[ff01::101]", name.getHostName());
237         assertEquals(21, name.getPort());
238         assertEquals("ftp://[ff01::101]/", name.getURI());
239 
240         // address with scopeId
241         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[fe80::8b2:d61e:e5c:b333%15]");
242         assertEquals("[fe80::8b2:d61e:e5c:b333%15]", name.getHostName());
243         assertEquals(21, name.getPort());
244         assertEquals("ftp://[fe80::8b2:d61e:e5c:b333%15]/", name.getURI());
245 
246         // address with scopeId and escaped characters in the path
247         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://[fe80::8b2:d61e:e5c:b333%15]/tests%3A+test+1");
248         assertEquals("[fe80::8b2:d61e:e5c:b333%15]", name.getHostName());
249         assertEquals(21, name.getPort());
250         assertEquals("ftp://[fe80::8b2:d61e:e5c:b333%15]/tests:+test+1", name.getURI());
251     }
252 
253     /**
254      * Tests parsing a URI into its parts.
255      */
256     @Test
257     public void testParseUri() throws Exception {
258         final URLFileNameParser urlParser = new URLFileNameParser(21);
259         // Simple name
260         GenericFileName name = (GenericFileName) urlParser.parseUri(null, null, "ftp://hostname/file");
261         assertEquals("ftp", name.getScheme());
262         assertNull(name.getUserName());
263         assertNull(name.getPassword());
264         assertEquals("hostname", name.getHostName());
265         assertEquals(21, name.getPort());
266         assertEquals(name.getDefaultPort(), name.getPort());
267         assertEquals("/file", name.getPath());
268         assertEquals("ftp://hostname/", name.getRootURI());
269         assertEquals("ftp://hostname/file", name.getURI());
270 
271         // Name with port
272         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://hostname:9090/file");
273         assertEquals("ftp", name.getScheme());
274         assertNull(name.getUserName());
275         assertNull(name.getPassword());
276         assertEquals("hostname", name.getHostName());
277         assertEquals(9090, name.getPort());
278         assertEquals("/file", name.getPath());
279         assertEquals("ftp://hostname:9090/", name.getRootURI());
280         assertEquals("ftp://hostname:9090/file", name.getURI());
281 
282         // Name with no path
283         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://hostname");
284         assertEquals("ftp", name.getScheme());
285         assertNull(name.getUserName());
286         assertNull(name.getPassword());
287         assertEquals("hostname", name.getHostName());
288         assertEquals(21, name.getPort());
289         assertEquals("/", name.getPath());
290         assertEquals("ftp://hostname/", name.getRootURI());
291         assertEquals("ftp://hostname/", name.getURI());
292 
293         // Name with username
294         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://user@hostname/file");
295         assertEquals("ftp", name.getScheme());
296         assertEquals("user", name.getUserName());
297         assertNull(name.getPassword());
298         assertEquals("hostname", name.getHostName());
299         assertEquals(21, name.getPort());
300         assertEquals("/file", name.getPath());
301         assertEquals("ftp://user@hostname/", name.getRootURI());
302         assertEquals("ftp://user@hostname/file", name.getURI());
303 
304         // Name with username and password
305         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://user:password@hostname/file");
306         assertEquals("ftp", name.getScheme());
307         assertEquals("user", name.getUserName());
308         assertEquals("password", name.getPassword());
309         assertEquals("hostname", name.getHostName());
310         assertEquals(21, name.getPort());
311         assertEquals("/file", name.getPath());
312         assertEquals("ftp://user:password@hostname/", name.getRootURI());
313         assertEquals("ftp://user:password@hostname/file", name.getURI());
314 
315         // Encoded username and password: %75 -> 'u', %40 -> '@'
316         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://%75ser%3A:%40@hostname");
317         assertEquals("ftp", name.getScheme());
318         assertEquals("user:", name.getUserName());
319         assertEquals("@", name.getPassword());
320         assertEquals("hostname", name.getHostName());
321         assertEquals(21, name.getPort());
322         assertEquals("/", name.getPath());
323         // RFC 2396: The ':' character in a userinfo does not have to be escaped/percent-encoded, it is NOT RECOMMENDED for use.
324         // RFC 3986: The ':' character in a userinfo is deprecated.
325         // See also https://issues.apache.org/jira/browse/VFS-810
326         assertEquals("ftp://user::%40@hostname/", name.getRootURI());
327         assertEquals("ftp://user::%40@hostname/", name.getURI());
328 
329         // Hostname with unreserved uri symbols "-", ".", "_", "~"
330         // https://datatracker.ietf.org/doc/html/rfc3986#page-49
331         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://p0~p1_p2-p3.p4/file");
332         assertEquals("ftp", name.getScheme());
333         assertNull(name.getUserName());
334         assertNull(name.getPassword());
335         assertEquals("p0~p1_p2-p3.p4", name.getHostName());
336         assertEquals(21, name.getPort());
337         assertEquals("/file", name.getPath());
338         assertEquals("ftp://p0~p1_p2-p3.p4/", name.getRootURI());
339         assertEquals("ftp://p0~p1_p2-p3.p4/file", name.getURI());
340 
341         // Hostname with sub-delim uri symbols that are currently accepted with the hostname parser
342         // https://datatracker.ietf.org/doc/html/rfc3986#page-49
343         name = (GenericFileName) urlParser.parseUri(null, null, "ftp://p0!p1'p2(p3)*p4/file");
344         assertEquals("ftp", name.getScheme());
345         assertNull(name.getUserName());
346         assertNull(name.getPassword());
347         assertEquals("p0!p1'p2(p3)*p4", name.getHostName());
348         assertEquals(21, name.getPort());
349         assertEquals("/file", name.getPath());
350         assertEquals("ftp://p0!p1'p2(p3)*p4/", name.getRootURI());
351         assertEquals("ftp://p0!p1'p2(p3)*p4/file", name.getURI());
352 
353         // Hostnames with sub-delim uri symbols that are currently not accepted with the hostname parser
354         // (which looks wrong)
355         // https://datatracker.ietf.org/doc/html/rfc3986#page-49
356         // name = (GenericFileName) urlParser.parseUri(null, null, "ftp://p0$p1/file");
357         // name = (GenericFileName) urlParser.parseUri(null, null, "ftp://p0&p1/file");
358         // name = (GenericFileName) urlParser.parseUri(null, null, "ftp://p0+p1/file");
359         // name = (GenericFileName) urlParser.parseUri(null, null, "ftp://p0,p1/file");
360         // name = (GenericFileName) urlParser.parseUri(null, null, "ftp://p0;p1/file");
361         // name = (GenericFileName) urlParser.parseUri(null, null, "ftp://p0=p1/file");
362     }
363 }