1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.hdfs.web; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertNotNull; 23 import static org.junit.Assert.assertTrue; 24 25 import java.io.File; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.net.HttpURLConnection; 29 import java.net.URI; 30 import java.net.URISyntaxException; 31 import java.net.URL; 32 import java.net.URLConnection; 33 34 import org.apache.hadoop.conf.Configuration; 35 import org.apache.hadoop.fs.BlockLocation; 36 import org.apache.hadoop.fs.FSDataInputStream; 37 import org.apache.hadoop.fs.FSDataOutputStream; 38 import org.apache.hadoop.fs.FileStatus; 39 import org.apache.hadoop.fs.FileSystem; 40 import org.apache.hadoop.fs.FileUtil; 41 import org.apache.hadoop.fs.Path; 42 import org.apache.hadoop.hdfs.DFSConfigKeys; 43 import org.apache.hadoop.hdfs.MiniDFSCluster; 44 import org.apache.hadoop.hdfs.server.datanode.DataNode; 45 import org.apache.hadoop.hdfs.server.datanode.DataNodeTestUtils; 46 import org.apache.hadoop.hdfs.server.protocol.DatanodeRegistration; 47 import org.apache.hadoop.security.ssl.KeyStoreTestUtil; 48 import org.apache.hadoop.util.ServletUtil; 49 import org.junit.After; 50 import org.junit.AfterClass; 51 import org.junit.Before; 52 import org.junit.BeforeClass; 53 import org.junit.Test; 54 55 public class TestHftpFileSystem { 56 private static final String BASEDIR = System.getProperty("test.build.dir", 57 "target/test-dir") + "/" + TestHftpFileSystem.class.getSimpleName(); 58 private static String keystoresDir; 59 private static String sslConfDir; 60 private static Configuration config = null; 61 private static MiniDFSCluster cluster = null; 62 private static String blockPoolId = null; 63 private static String hftpUri = null; 64 private FileSystem hdfs = null; 65 private HftpFileSystem hftpFs = null; 66 67 private static final Path[] TEST_PATHS = new Path[] { 68 // URI does not encode, Request#getPathInfo returns /foo 69 new Path("/foo;bar"), 70 71 // URI does not encode, Request#getPathInfo returns verbatim 72 new Path("/foo+"), new Path("/foo+bar/foo+bar"), 73 new Path("/foo=bar/foo=bar"), new Path("/foo,bar/foo,bar"), 74 new Path("/foo@bar/foo@bar"), new Path("/foo&bar/foo&bar"), 75 new Path("/foo$bar/foo$bar"), new Path("/foo_bar/foo_bar"), 76 new Path("/foo~bar/foo~bar"), new Path("/foo.bar/foo.bar"), 77 new Path("/foo../bar/foo../bar"), new Path("/foo.../bar/foo.../bar"), 78 new Path("/foo'bar/foo'bar"), 79 new Path("/foo#bar/foo#bar"), 80 new Path("/foo!bar/foo!bar"), 81 // HDFS file names may not contain ":" 82 83 // URI percent encodes, Request#getPathInfo decodes 84 new Path("/foo bar/foo bar"), new Path("/foo?bar/foo?bar"), 85 new Path("/foo\">bar/foo\">bar"), }; 86 87 @BeforeClass setUp()88 public static void setUp() throws Exception { 89 config = new Configuration(); 90 cluster = new MiniDFSCluster.Builder(config).numDataNodes(2).build(); 91 blockPoolId = cluster.getNamesystem().getBlockPoolId(); 92 hftpUri = "hftp://" 93 + config.get(DFSConfigKeys.DFS_NAMENODE_HTTP_ADDRESS_KEY); 94 File base = new File(BASEDIR); 95 FileUtil.fullyDelete(base); 96 base.mkdirs(); 97 keystoresDir = new File(BASEDIR).getAbsolutePath(); 98 sslConfDir = KeyStoreTestUtil.getClasspathDir(TestHftpFileSystem.class); 99 100 KeyStoreTestUtil.setupSSLConfig(keystoresDir, sslConfDir, config, false); 101 } 102 103 @AfterClass tearDown()104 public static void tearDown() throws Exception { 105 if (cluster != null) { 106 cluster.shutdown(); 107 } 108 FileUtil.fullyDelete(new File(BASEDIR)); 109 KeyStoreTestUtil.cleanupSSLConfig(keystoresDir, sslConfDir); 110 } 111 112 @Before initFileSystems()113 public void initFileSystems() throws IOException { 114 hdfs = cluster.getFileSystem(); 115 hftpFs = (HftpFileSystem) new Path(hftpUri).getFileSystem(config); 116 // clear out the namespace 117 for (FileStatus stat : hdfs.listStatus(new Path("/"))) { 118 hdfs.delete(stat.getPath(), true); 119 } 120 } 121 122 @After resetFileSystems()123 public void resetFileSystems() throws IOException { 124 FileSystem.closeAll(); 125 } 126 127 /** 128 * Test file creation and access with file names that need encoding. 129 */ 130 @Test testFileNameEncoding()131 public void testFileNameEncoding() throws IOException, URISyntaxException { 132 for (Path p : TEST_PATHS) { 133 // Create and access the path (data and streamFile servlets) 134 FSDataOutputStream out = hdfs.create(p, true); 135 out.writeBytes("0123456789"); 136 out.close(); 137 FSDataInputStream in = hftpFs.open(p); 138 assertEquals('0', in.read()); 139 in.close(); 140 141 // Check the file status matches the path. Hftp returns a FileStatus 142 // with the entire URI, extract the path part. 143 assertEquals(p, new Path(hftpFs.getFileStatus(p).getPath().toUri() 144 .getPath())); 145 146 // Test list status (listPath servlet) 147 assertEquals(1, hftpFs.listStatus(p).length); 148 149 // Test content summary (contentSummary servlet) 150 assertNotNull("No content summary", hftpFs.getContentSummary(p)); 151 152 // Test checksums (fileChecksum and getFileChecksum servlets) 153 assertNotNull("No file checksum", hftpFs.getFileChecksum(p)); 154 } 155 } 156 testDataNodeRedirect(Path path)157 private void testDataNodeRedirect(Path path) throws IOException { 158 // Create the file 159 if (hdfs.exists(path)) { 160 hdfs.delete(path, true); 161 } 162 FSDataOutputStream out = hdfs.create(path, (short) 1); 163 out.writeBytes("0123456789"); 164 out.close(); 165 166 // Get the path's block location so we can determine 167 // if we were redirected to the right DN. 168 BlockLocation[] locations = hdfs.getFileBlockLocations(path, 0, 10); 169 String xferAddr = locations[0].getNames()[0]; 170 171 // Connect to the NN to get redirected 172 URL u = hftpFs.getNamenodeURL( 173 "/data" + ServletUtil.encodePath(path.toUri().getPath()), 174 "ugi=userx,groupy"); 175 HttpURLConnection conn = (HttpURLConnection) u.openConnection(); 176 HttpURLConnection.setFollowRedirects(true); 177 conn.connect(); 178 conn.getInputStream(); 179 180 boolean checked = false; 181 // Find the datanode that has the block according to locations 182 // and check that the URL was redirected to this DN's info port 183 for (DataNode node : cluster.getDataNodes()) { 184 DatanodeRegistration dnR = DataNodeTestUtils.getDNRegistrationForBP(node, 185 blockPoolId); 186 if (dnR.getXferAddr().equals(xferAddr)) { 187 checked = true; 188 assertEquals(dnR.getInfoPort(), conn.getURL().getPort()); 189 } 190 } 191 assertTrue("The test never checked that location of " 192 + "the block and hftp desitnation are the same", checked); 193 } 194 195 /** 196 * Test that clients are redirected to the appropriate DN. 197 */ 198 @Test testDataNodeRedirect()199 public void testDataNodeRedirect() throws IOException { 200 for (Path p : TEST_PATHS) { 201 testDataNodeRedirect(p); 202 } 203 } 204 205 /** 206 * Tests getPos() functionality. 207 */ 208 @Test testGetPos()209 public void testGetPos() throws IOException { 210 final Path testFile = new Path("/testfile+1"); 211 // Write a test file. 212 FSDataOutputStream out = hdfs.create(testFile, true); 213 out.writeBytes("0123456789"); 214 out.close(); 215 216 FSDataInputStream in = hftpFs.open(testFile); 217 218 // Test read(). 219 for (int i = 0; i < 5; ++i) { 220 assertEquals(i, in.getPos()); 221 in.read(); 222 } 223 224 // Test read(b, off, len). 225 assertEquals(5, in.getPos()); 226 byte[] buffer = new byte[10]; 227 assertEquals(2, in.read(buffer, 0, 2)); 228 assertEquals(7, in.getPos()); 229 230 // Test read(b). 231 int bytesRead = in.read(buffer); 232 assertEquals(7 + bytesRead, in.getPos()); 233 234 // Test EOF. 235 for (int i = 0; i < 100; ++i) { 236 in.read(); 237 } 238 assertEquals(10, in.getPos()); 239 in.close(); 240 } 241 242 /** 243 * Tests seek(). 244 */ 245 @Test testSeek()246 public void testSeek() throws IOException { 247 final Path testFile = new Path("/testfile+1"); 248 FSDataOutputStream out = hdfs.create(testFile, true); 249 out.writeBytes("0123456789"); 250 out.close(); 251 FSDataInputStream in = hftpFs.open(testFile); 252 in.seek(7); 253 assertEquals('7', in.read()); 254 in.close(); 255 } 256 257 @Test testReadClosedStream()258 public void testReadClosedStream() throws IOException { 259 final Path testFile = new Path("/testfile+2"); 260 FSDataOutputStream os = hdfs.create(testFile, true); 261 os.writeBytes("0123456789"); 262 os.close(); 263 264 // ByteRangeInputStream delays opens until reads. Make sure it doesn't 265 // open a closed stream that has never been opened 266 FSDataInputStream in = hftpFs.open(testFile); 267 in.close(); 268 checkClosedStream(in); 269 checkClosedStream(in.getWrappedStream()); 270 271 // force the stream to connect and then close it 272 in = hftpFs.open(testFile); 273 int ch = in.read(); 274 assertEquals('0', ch); 275 in.close(); 276 checkClosedStream(in); 277 checkClosedStream(in.getWrappedStream()); 278 279 // make sure seeking doesn't automagically reopen the stream 280 in.seek(4); 281 checkClosedStream(in); 282 checkClosedStream(in.getWrappedStream()); 283 } 284 checkClosedStream(InputStream is)285 private void checkClosedStream(InputStream is) { 286 IOException ioe = null; 287 try { 288 is.read(); 289 } catch (IOException e) { 290 ioe = e; 291 } 292 assertNotNull("No exception on closed read", ioe); 293 assertEquals("Stream closed", ioe.getMessage()); 294 } 295 296 @Test testHftpDefaultPorts()297 public void testHftpDefaultPorts() throws IOException { 298 Configuration conf = new Configuration(); 299 URI uri = URI.create("hftp://localhost"); 300 HftpFileSystem fs = (HftpFileSystem) FileSystem.get(uri, conf); 301 302 assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT, 303 fs.getDefaultPort()); 304 305 assertEquals(uri, fs.getUri()); 306 307 // HFTP uses http to get the token so canonical service name should 308 // return the http port. 309 assertEquals("127.0.0.1:" + DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT, 310 fs.getCanonicalServiceName()); 311 } 312 313 @Test testHftpCustomDefaultPorts()314 public void testHftpCustomDefaultPorts() throws IOException { 315 Configuration conf = new Configuration(); 316 conf.setInt(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_KEY, 123); 317 318 URI uri = URI.create("hftp://localhost"); 319 HftpFileSystem fs = (HftpFileSystem) FileSystem.get(uri, conf); 320 321 assertEquals(123, fs.getDefaultPort()); 322 323 assertEquals(uri, fs.getUri()); 324 325 // HFTP uses http to get the token so canonical service name should 326 // return the http port. 327 assertEquals("127.0.0.1:123", fs.getCanonicalServiceName()); 328 } 329 330 @Test testHftpCustomUriPortWithDefaultPorts()331 public void testHftpCustomUriPortWithDefaultPorts() throws IOException { 332 Configuration conf = new Configuration(); 333 URI uri = URI.create("hftp://localhost:123"); 334 HftpFileSystem fs = (HftpFileSystem) FileSystem.get(uri, conf); 335 336 assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT, 337 fs.getDefaultPort()); 338 339 assertEquals(uri, fs.getUri()); 340 assertEquals("127.0.0.1:123", fs.getCanonicalServiceName()); 341 } 342 343 @Test testHftpCustomUriPortWithCustomDefaultPorts()344 public void testHftpCustomUriPortWithCustomDefaultPorts() throws IOException { 345 Configuration conf = new Configuration(); 346 conf.setInt(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_KEY, 123); 347 348 URI uri = URI.create("hftp://localhost:789"); 349 HftpFileSystem fs = (HftpFileSystem) FileSystem.get(uri, conf); 350 351 assertEquals(123, fs.getDefaultPort()); 352 353 assertEquals(uri, fs.getUri()); 354 assertEquals("127.0.0.1:789", fs.getCanonicalServiceName()); 355 } 356 357 @Test testTimeout()358 public void testTimeout() throws IOException { 359 Configuration conf = new Configuration(); 360 URI uri = URI.create("hftp://localhost"); 361 HftpFileSystem fs = (HftpFileSystem) FileSystem.get(uri, conf); 362 URLConnection conn = fs.connectionFactory.openConnection(new URL( 363 "http://localhost")); 364 assertEquals(URLConnectionFactory.DEFAULT_SOCKET_TIMEOUT, 365 conn.getConnectTimeout()); 366 assertEquals(URLConnectionFactory.DEFAULT_SOCKET_TIMEOUT, 367 conn.getReadTimeout()); 368 } 369 370 // / 371 372 @Test testHsftpDefaultPorts()373 public void testHsftpDefaultPorts() throws IOException { 374 Configuration conf = new Configuration(); 375 URI uri = URI.create("hsftp://localhost"); 376 HsftpFileSystem fs = (HsftpFileSystem) FileSystem.get(uri, conf); 377 378 assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_DEFAULT, 379 fs.getDefaultPort()); 380 381 assertEquals(uri, fs.getUri()); 382 assertEquals("127.0.0.1:" + DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_DEFAULT, 383 fs.getCanonicalServiceName()); 384 } 385 386 @Test testHsftpCustomDefaultPorts()387 public void testHsftpCustomDefaultPorts() throws IOException { 388 Configuration conf = new Configuration(); 389 conf.setInt(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_KEY, 123); 390 conf.setInt(DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_KEY, 456); 391 392 URI uri = URI.create("hsftp://localhost"); 393 HsftpFileSystem fs = (HsftpFileSystem) FileSystem.get(uri, conf); 394 395 assertEquals(456, fs.getDefaultPort()); 396 397 assertEquals(uri, fs.getUri()); 398 assertEquals("127.0.0.1:456", fs.getCanonicalServiceName()); 399 } 400 401 @Test testHsftpCustomUriPortWithDefaultPorts()402 public void testHsftpCustomUriPortWithDefaultPorts() throws IOException { 403 Configuration conf = new Configuration(); 404 URI uri = URI.create("hsftp://localhost:123"); 405 HsftpFileSystem fs = (HsftpFileSystem) FileSystem.get(uri, conf); 406 407 assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_DEFAULT, 408 fs.getDefaultPort()); 409 410 assertEquals(uri, fs.getUri()); 411 assertEquals("127.0.0.1:123", fs.getCanonicalServiceName()); 412 } 413 414 @Test testHsftpCustomUriPortWithCustomDefaultPorts()415 public void testHsftpCustomUriPortWithCustomDefaultPorts() throws IOException { 416 Configuration conf = new Configuration(); 417 conf.setInt(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_KEY, 123); 418 conf.setInt(DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_KEY, 456); 419 420 URI uri = URI.create("hsftp://localhost:789"); 421 HsftpFileSystem fs = (HsftpFileSystem) FileSystem.get(uri, conf); 422 423 assertEquals(456, fs.getDefaultPort()); 424 425 assertEquals(uri, fs.getUri()); 426 assertEquals("127.0.0.1:789", fs.getCanonicalServiceName()); 427 } 428 } 429