001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.http; 019 020import java.io.IOException; 021import java.io.PrintWriter; 022import java.net.HttpURLConnection; 023import java.net.URI; 024import java.net.URL; 025import java.util.Arrays; 026import java.util.Enumeration; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Map; 030import java.util.SortedSet; 031import java.util.TreeSet; 032import java.util.concurrent.CountDownLatch; 033import java.util.concurrent.Executor; 034import java.util.concurrent.Executors; 035import javax.servlet.Filter; 036import javax.servlet.FilterChain; 037import javax.servlet.FilterConfig; 038import javax.servlet.ServletContext; 039import javax.servlet.ServletException; 040import javax.servlet.ServletRequest; 041import javax.servlet.ServletResponse; 042import javax.servlet.http.HttpServlet; 043import javax.servlet.http.HttpServletRequest; 044import javax.servlet.http.HttpServletRequestWrapper; 045import javax.servlet.http.HttpServletResponse; 046import org.apache.hadoop.conf.Configuration; 047import org.apache.hadoop.fs.CommonConfigurationKeys; 048import org.apache.hadoop.hbase.HBaseClassTestRule; 049import org.apache.hadoop.hbase.http.HttpServer.QuotingInputFilter.RequestQuoter; 050import org.apache.hadoop.hbase.http.resource.JerseyResource; 051import org.apache.hadoop.hbase.testclassification.MiscTests; 052import org.apache.hadoop.hbase.testclassification.SmallTests; 053import org.apache.hadoop.net.NetUtils; 054import org.apache.hadoop.security.Groups; 055import org.apache.hadoop.security.ShellBasedUnixGroupsMapping; 056import org.apache.hadoop.security.UserGroupInformation; 057import org.apache.hadoop.security.authorize.AccessControlList; 058import org.eclipse.jetty.server.ServerConnector; 059import org.eclipse.jetty.util.ajax.JSON; 060import org.junit.AfterClass; 061import org.junit.Assert; 062import org.junit.BeforeClass; 063import org.junit.ClassRule; 064import org.junit.Ignore; 065import org.junit.Test; 066import org.junit.experimental.categories.Category; 067import org.mockito.Mockito; 068import org.slf4j.Logger; 069import org.slf4j.LoggerFactory; 070 071@Category({MiscTests.class, SmallTests.class}) 072public class TestHttpServer extends HttpServerFunctionalTest { 073 074 @ClassRule 075 public static final HBaseClassTestRule CLASS_RULE = 076 HBaseClassTestRule.forClass(TestHttpServer.class); 077 078 private static final Logger LOG = LoggerFactory.getLogger(TestHttpServer.class); 079 private static HttpServer server; 080 private static URL baseUrl; 081 // jetty 9.4.x needs this many threads to start, even in the small. 082 static final int MAX_THREADS = 16; 083 084 @SuppressWarnings("serial") 085 public static class EchoMapServlet extends HttpServlet { 086 @Override 087 public void doGet(HttpServletRequest request, 088 HttpServletResponse response 089 ) throws ServletException, IOException { 090 PrintWriter out = response.getWriter(); 091 Map<String, String[]> params = request.getParameterMap(); 092 SortedSet<String> keys = new TreeSet<>(params.keySet()); 093 for(String key: keys) { 094 out.print(key); 095 out.print(':'); 096 String[] values = params.get(key); 097 if (values.length > 0) { 098 out.print(values[0]); 099 for(int i=1; i < values.length; ++i) { 100 out.print(','); 101 out.print(values[i]); 102 } 103 } 104 out.print('\n'); 105 } 106 out.close(); 107 } 108 } 109 110 @SuppressWarnings("serial") 111 public static class EchoServlet extends HttpServlet { 112 @Override 113 public void doGet(HttpServletRequest request, 114 HttpServletResponse response 115 ) throws ServletException, IOException { 116 PrintWriter out = response.getWriter(); 117 SortedSet<String> sortedKeys = new TreeSet<>(); 118 Enumeration<String> keys = request.getParameterNames(); 119 while(keys.hasMoreElements()) { 120 sortedKeys.add(keys.nextElement()); 121 } 122 for(String key: sortedKeys) { 123 out.print(key); 124 out.print(':'); 125 out.print(request.getParameter(key)); 126 out.print('\n'); 127 } 128 out.close(); 129 } 130 } 131 132 @SuppressWarnings("serial") 133 public static class LongHeaderServlet extends HttpServlet { 134 @Override 135 public void doGet(HttpServletRequest request, 136 HttpServletResponse response 137 ) throws ServletException, IOException { 138 Assert.assertEquals(63 * 1024, request.getHeader("longheader").length()); 139 response.setStatus(HttpServletResponse.SC_OK); 140 } 141 } 142 143 @SuppressWarnings("serial") 144 public static class HtmlContentServlet extends HttpServlet { 145 @Override 146 public void doGet(HttpServletRequest request, 147 HttpServletResponse response 148 ) throws ServletException, IOException { 149 response.setContentType("text/html"); 150 PrintWriter out = response.getWriter(); 151 out.print("hello world"); 152 out.close(); 153 } 154 } 155 156 @BeforeClass public static void setup() throws Exception { 157 Configuration conf = new Configuration(); 158 conf.setInt(HttpServer.HTTP_MAX_THREADS, MAX_THREADS); 159 server = createTestServer(conf); 160 server.addServlet("echo", "/echo", EchoServlet.class); 161 server.addServlet("echomap", "/echomap", EchoMapServlet.class); 162 server.addServlet("htmlcontent", "/htmlcontent", HtmlContentServlet.class); 163 server.addServlet("longheader", "/longheader", LongHeaderServlet.class); 164 server.addJerseyResourcePackage( 165 JerseyResource.class.getPackage().getName(), "/jersey/*"); 166 server.start(); 167 baseUrl = getServerURL(server); 168 LOG.info("HTTP server started: "+ baseUrl); 169 } 170 171 @AfterClass public static void cleanup() throws Exception { 172 server.stop(); 173 } 174 175 /** Test the maximum number of threads cannot be exceeded. */ 176 @Test public void testMaxThreads() throws Exception { 177 int clientThreads = MAX_THREADS * 10; 178 Executor executor = Executors.newFixedThreadPool(clientThreads); 179 // Run many clients to make server reach its maximum number of threads 180 final CountDownLatch ready = new CountDownLatch(clientThreads); 181 final CountDownLatch start = new CountDownLatch(1); 182 for (int i = 0; i < clientThreads; i++) { 183 executor.execute(new Runnable() { 184 @Override 185 public void run() { 186 ready.countDown(); 187 try { 188 start.await(); 189 assertEquals("a:b\nc:d\n", 190 readOutput(new URL(baseUrl, "/echo?a=b&c=d"))); 191 int serverThreads = server.webServer.getThreadPool().getThreads(); 192 assertTrue("More threads are started than expected, Server Threads count: " 193 + serverThreads, serverThreads <= MAX_THREADS); 194 System.out.println("Number of threads = " + serverThreads + 195 " which is less or equal than the max = " + MAX_THREADS); 196 } catch (Exception e) { 197 // do nothing 198 } 199 } 200 }); 201 } 202 // Start the client threads when they are all ready 203 ready.await(); 204 start.countDown(); 205 } 206 207 @Test public void testEcho() throws Exception { 208 assertEquals("a:b\nc:d\n", 209 readOutput(new URL(baseUrl, "/echo?a=b&c=d"))); 210 assertEquals("a:b\nc<:d\ne:>\n", 211 readOutput(new URL(baseUrl, "/echo?a=b&c<=d&e=>"))); 212 } 213 214 /** Test the echo map servlet that uses getParameterMap. */ 215 @Test public void testEchoMap() throws Exception { 216 assertEquals("a:b\nc:d\n", 217 readOutput(new URL(baseUrl, "/echomap?a=b&c=d"))); 218 assertEquals("a:b,>\nc<:d\n", 219 readOutput(new URL(baseUrl, "/echomap?a=b&c<=d&a=>"))); 220 } 221 222 /** 223 * Test that verifies headers can be up to 64K long. 224 * The test adds a 63K header leaving 1K for other headers. 225 * This is because the header buffer setting is for ALL headers, 226 * names and values included. */ 227 @Test public void testLongHeader() throws Exception { 228 URL url = new URL(baseUrl, "/longheader"); 229 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 230 StringBuilder sb = new StringBuilder(); 231 for (int i = 0 ; i < 63 * 1024; i++) { 232 sb.append("a"); 233 } 234 conn.setRequestProperty("longheader", sb.toString()); 235 assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode()); 236 } 237 238 @Test 239 public void testContentTypes() throws Exception { 240 // Static CSS files should have text/css 241 URL cssUrl = new URL(baseUrl, "/static/test.css"); 242 HttpURLConnection conn = (HttpURLConnection)cssUrl.openConnection(); 243 conn.connect(); 244 assertEquals(200, conn.getResponseCode()); 245 assertEquals("text/css", conn.getContentType()); 246 247 // Servlets should have text/plain with proper encoding by default 248 URL servletUrl = new URL(baseUrl, "/echo?a=b"); 249 conn = (HttpURLConnection)servletUrl.openConnection(); 250 conn.connect(); 251 assertEquals(200, conn.getResponseCode()); 252 assertEquals("text/plain;charset=utf-8", conn.getContentType()); 253 254 // We should ignore parameters for mime types - ie a parameter 255 // ending in .css should not change mime type 256 servletUrl = new URL(baseUrl, "/echo?a=b.css"); 257 conn = (HttpURLConnection)servletUrl.openConnection(); 258 conn.connect(); 259 assertEquals(200, conn.getResponseCode()); 260 assertEquals("text/plain;charset=utf-8", conn.getContentType()); 261 262 // Servlets that specify text/html should get that content type 263 servletUrl = new URL(baseUrl, "/htmlcontent"); 264 conn = (HttpURLConnection)servletUrl.openConnection(); 265 conn.connect(); 266 assertEquals(200, conn.getResponseCode()); 267 assertEquals("text/html;charset=utf-8", conn.getContentType()); 268 269 // JSPs should default to text/html with utf8 270 // JSPs do not work from unit tests 271 // servletUrl = new URL(baseUrl, "/testjsp.jsp"); 272 // conn = (HttpURLConnection)servletUrl.openConnection(); 273 // conn.connect(); 274 // assertEquals(200, conn.getResponseCode()); 275 // assertEquals("text/html; charset=utf-8", conn.getContentType()); 276 } 277 278 /** 279 * Dummy filter that mimics as an authentication filter. Obtains user identity 280 * from the request parameter user.name. Wraps around the request so that 281 * request.getRemoteUser() returns the user identity. 282 * 283 */ 284 public static class DummyServletFilter implements Filter { 285 @Override 286 public void destroy() { } 287 288 @Override 289 public void doFilter(ServletRequest request, ServletResponse response, 290 FilterChain filterChain) throws IOException, ServletException { 291 final String userName = request.getParameter("user.name"); 292 ServletRequest requestModified = 293 new HttpServletRequestWrapper((HttpServletRequest) request) { 294 @Override 295 public String getRemoteUser() { 296 return userName; 297 } 298 }; 299 filterChain.doFilter(requestModified, response); 300 } 301 302 @Override 303 public void init(FilterConfig arg0) throws ServletException { } 304 } 305 306 /** 307 * FilterInitializer that initialized the DummyFilter. 308 * 309 */ 310 public static class DummyFilterInitializer extends FilterInitializer { 311 public DummyFilterInitializer() { 312 } 313 314 @Override 315 public void initFilter(FilterContainer container, Configuration conf) { 316 container.addFilter("DummyFilter", DummyServletFilter.class.getName(), null); 317 } 318 } 319 320 /** 321 * Access a URL and get the corresponding return Http status code. The URL 322 * will be accessed as the passed user, by sending user.name request 323 * parameter. 324 * 325 * @param urlstring 326 * @param userName 327 * @return 328 * @throws IOException 329 */ 330 static int getHttpStatusCode(String urlstring, String userName) 331 throws IOException { 332 URL url = new URL(urlstring + "?user.name=" + userName); 333 System.out.println("Accessing " + url + " as user " + userName); 334 HttpURLConnection connection = (HttpURLConnection)url.openConnection(); 335 connection.connect(); 336 return connection.getResponseCode(); 337 } 338 339 /** 340 * Custom user->group mapping service. 341 */ 342 public static class MyGroupsProvider extends ShellBasedUnixGroupsMapping { 343 static Map<String, List<String>> mapping = new HashMap<>(); 344 345 static void clearMapping() { 346 mapping.clear(); 347 } 348 349 @Override 350 public List<String> getGroups(String user) throws IOException { 351 return mapping.get(user); 352 } 353 } 354 355 /** 356 * Verify the access for /logs, /stacks, /conf, /logLevel and /metrics 357 * servlets, when authentication filters are set, but authorization is not 358 * enabled. 359 * @throws Exception 360 */ 361 @Test 362 @Ignore 363 public void testDisabledAuthorizationOfDefaultServlets() throws Exception { 364 365 Configuration conf = new Configuration(); 366 367 // Authorization is disabled by default 368 conf.set(HttpServer.FILTER_INITIALIZERS_PROPERTY, 369 DummyFilterInitializer.class.getName()); 370 conf.set(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING, 371 MyGroupsProvider.class.getName()); 372 Groups.getUserToGroupsMappingService(conf); 373 MyGroupsProvider.clearMapping(); 374 MyGroupsProvider.mapping.put("userA", Arrays.asList("groupA")); 375 MyGroupsProvider.mapping.put("userB", Arrays.asList("groupB")); 376 377 HttpServer myServer = new HttpServer.Builder().setName("test") 378 .addEndpoint(new URI("http://localhost:0")).setFindPort(true).build(); 379 myServer.setAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE, conf); 380 myServer.start(); 381 String serverURL = "http://" + NetUtils.getHostPortString(myServer.getConnectorAddress(0)) + "/"; 382 for (String servlet : new String[] { "conf", "logs", "stacks", 383 "logLevel", "metrics" }) { 384 for (String user : new String[] { "userA", "userB" }) { 385 assertEquals(HttpURLConnection.HTTP_OK, getHttpStatusCode(serverURL 386 + servlet, user)); 387 } 388 } 389 myServer.stop(); 390 } 391 392 /** 393 * Verify the administrator access for /logs, /stacks, /conf, /logLevel and 394 * /metrics servlets. 395 * 396 * @throws Exception 397 */ 398 @Test 399 @Ignore 400 public void testAuthorizationOfDefaultServlets() throws Exception { 401 Configuration conf = new Configuration(); 402 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION, 403 true); 404 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_INSTRUMENTATION_REQUIRES_ADMIN, 405 true); 406 conf.set(HttpServer.FILTER_INITIALIZERS_PROPERTY, 407 DummyFilterInitializer.class.getName()); 408 409 conf.set(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING, 410 MyGroupsProvider.class.getName()); 411 Groups.getUserToGroupsMappingService(conf); 412 MyGroupsProvider.clearMapping(); 413 MyGroupsProvider.mapping.put("userA", Arrays.asList("groupA")); 414 MyGroupsProvider.mapping.put("userB", Arrays.asList("groupB")); 415 MyGroupsProvider.mapping.put("userC", Arrays.asList("groupC")); 416 MyGroupsProvider.mapping.put("userD", Arrays.asList("groupD")); 417 MyGroupsProvider.mapping.put("userE", Arrays.asList("groupE")); 418 419 HttpServer myServer = new HttpServer.Builder().setName("test") 420 .addEndpoint(new URI("http://localhost:0")).setFindPort(true).setConf(conf) 421 .setACL(new AccessControlList("userA,userB groupC,groupD")).build(); 422 myServer.setAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE, conf); 423 myServer.start(); 424 425 String serverURL = "http://" 426 + NetUtils.getHostPortString(myServer.getConnectorAddress(0)) + "/"; 427 for (String servlet : new String[] { "conf", "logs", "stacks", 428 "logLevel", "metrics" }) { 429 for (String user : new String[] { "userA", "userB", "userC", "userD" }) { 430 assertEquals(HttpURLConnection.HTTP_OK, getHttpStatusCode(serverURL 431 + servlet, user)); 432 } 433 assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, getHttpStatusCode( 434 serverURL + servlet, "userE")); 435 } 436 myServer.stop(); 437 } 438 439 @Test 440 public void testRequestQuoterWithNull() throws Exception { 441 HttpServletRequest request = Mockito.mock(HttpServletRequest.class); 442 Mockito.doReturn(null).when(request).getParameterValues("dummy"); 443 RequestQuoter requestQuoter = new RequestQuoter(request); 444 String[] parameterValues = requestQuoter.getParameterValues("dummy"); 445 Assert.assertEquals("It should return null " 446 + "when there are no values for the parameter", null, parameterValues); 447 } 448 449 @Test 450 public void testRequestQuoterWithNotNull() throws Exception { 451 HttpServletRequest request = Mockito.mock(HttpServletRequest.class); 452 String[] values = new String[] { "abc", "def" }; 453 Mockito.doReturn(values).when(request).getParameterValues("dummy"); 454 RequestQuoter requestQuoter = new RequestQuoter(request); 455 String[] parameterValues = requestQuoter.getParameterValues("dummy"); 456 Assert.assertTrue("It should return Parameter Values", Arrays.equals( 457 values, parameterValues)); 458 } 459 460 @SuppressWarnings("unchecked") 461 private static Map<String, Object> parse(String jsonString) { 462 return (Map<String, Object>)JSON.parse(jsonString); 463 } 464 465 @Test public void testJersey() throws Exception { 466 LOG.info("BEGIN testJersey()"); 467 final String js = readOutput(new URL(baseUrl, "/jersey/foo?op=bar")); 468 final Map<String, Object> m = parse(js); 469 LOG.info("m=" + m); 470 assertEquals("foo", m.get(JerseyResource.PATH)); 471 assertEquals("bar", m.get(JerseyResource.OP)); 472 LOG.info("END testJersey()"); 473 } 474 475 @Test 476 public void testHasAdministratorAccess() throws Exception { 477 Configuration conf = new Configuration(); 478 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION, false); 479 ServletContext context = Mockito.mock(ServletContext.class); 480 Mockito.when(context.getAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE)).thenReturn(conf); 481 Mockito.when(context.getAttribute(HttpServer.ADMINS_ACL)).thenReturn(null); 482 HttpServletRequest request = Mockito.mock(HttpServletRequest.class); 483 Mockito.when(request.getRemoteUser()).thenReturn(null); 484 HttpServletResponse response = Mockito.mock(HttpServletResponse.class); 485 486 //authorization OFF 487 Assert.assertTrue(HttpServer.hasAdministratorAccess(context, request, response)); 488 489 //authorization ON & user NULL 490 response = Mockito.mock(HttpServletResponse.class); 491 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION, true); 492 Assert.assertFalse(HttpServer.hasAdministratorAccess(context, request, response)); 493 Mockito.verify(response).sendError(Mockito.eq(HttpServletResponse.SC_UNAUTHORIZED), Mockito.anyString()); 494 495 //authorization ON & user NOT NULL & ACLs NULL 496 response = Mockito.mock(HttpServletResponse.class); 497 Mockito.when(request.getRemoteUser()).thenReturn("foo"); 498 Assert.assertTrue(HttpServer.hasAdministratorAccess(context, request, response)); 499 500 //authorization ON & user NOT NULL & ACLs NOT NULL & user not in ACLs 501 response = Mockito.mock(HttpServletResponse.class); 502 AccessControlList acls = Mockito.mock(AccessControlList.class); 503 Mockito.when(acls.isUserAllowed(Mockito.<UserGroupInformation>any())).thenReturn(false); 504 Mockito.when(context.getAttribute(HttpServer.ADMINS_ACL)).thenReturn(acls); 505 Assert.assertFalse(HttpServer.hasAdministratorAccess(context, request, response)); 506 Mockito.verify(response).sendError(Mockito.eq(HttpServletResponse.SC_UNAUTHORIZED), Mockito.anyString()); 507 508 //authorization ON & user NOT NULL & ACLs NOT NULL & user in in ACLs 509 response = Mockito.mock(HttpServletResponse.class); 510 Mockito.when(acls.isUserAllowed(Mockito.<UserGroupInformation>any())).thenReturn(true); 511 Mockito.when(context.getAttribute(HttpServer.ADMINS_ACL)).thenReturn(acls); 512 Assert.assertTrue(HttpServer.hasAdministratorAccess(context, request, response)); 513 514 } 515 516 @Test 517 public void testRequiresAuthorizationAccess() throws Exception { 518 Configuration conf = new Configuration(); 519 ServletContext context = Mockito.mock(ServletContext.class); 520 Mockito.when(context.getAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE)).thenReturn(conf); 521 HttpServletRequest request = Mockito.mock(HttpServletRequest.class); 522 HttpServletResponse response = Mockito.mock(HttpServletResponse.class); 523 524 //requires admin access to instrumentation, FALSE by default 525 Assert.assertTrue(HttpServer.isInstrumentationAccessAllowed(context, request, response)); 526 527 //requires admin access to instrumentation, TRUE 528 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_INSTRUMENTATION_REQUIRES_ADMIN, true); 529 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION, true); 530 AccessControlList acls = Mockito.mock(AccessControlList.class); 531 Mockito.when(acls.isUserAllowed(Mockito.<UserGroupInformation>any())).thenReturn(false); 532 Mockito.when(context.getAttribute(HttpServer.ADMINS_ACL)).thenReturn(acls); 533 Assert.assertFalse(HttpServer.isInstrumentationAccessAllowed(context, request, response)); 534 } 535 536 @Test public void testBindAddress() throws Exception { 537 checkBindAddress("localhost", 0, false).stop(); 538 // hang onto this one for a bit more testing 539 HttpServer myServer = checkBindAddress("localhost", 0, false); 540 HttpServer myServer2 = null; 541 try { 542 int port = myServer.getConnectorAddress(0).getPort(); 543 // it's already in use, true = expect a higher port 544 myServer2 = checkBindAddress("localhost", port, true); 545 // try to reuse the port 546 port = myServer2.getConnectorAddress(0).getPort(); 547 myServer2.stop(); 548 assertNull(myServer2.getConnectorAddress(0)); // not bound 549 myServer2.openListeners(); 550 assertEquals(port, myServer2.getConnectorAddress(0).getPort()); // expect same port 551 } finally { 552 myServer.stop(); 553 if (myServer2 != null) { 554 myServer2.stop(); 555 } 556 } 557 } 558 559 private HttpServer checkBindAddress(String host, int port, boolean findPort) 560 throws Exception { 561 HttpServer server = createServer(host, port); 562 try { 563 // not bound, ephemeral should return requested port (0 for ephemeral) 564 ServerConnector listener = server.getServerConnectors().get(0); 565 566 assertEquals(port, listener.getPort()); 567 // verify hostname is what was given 568 server.openListeners(); 569 assertEquals(host, server.getConnectorAddress(0).getHostName()); 570 571 int boundPort = server.getConnectorAddress(0).getPort(); 572 if (port == 0) { 573 assertTrue(boundPort != 0); // ephemeral should now return bound port 574 } else if (findPort) { 575 assertTrue(boundPort > port); 576 // allow a little wiggle room to prevent random test failures if 577 // some consecutive ports are already in use 578 assertTrue(boundPort - port < 8); 579 } 580 } catch (Exception e) { 581 server.stop(); 582 throw e; 583 } 584 return server; 585 } 586 587 @Test 588 public void testXFrameHeaderSameOrigin() throws Exception { 589 Configuration conf = new Configuration(); 590 conf.set("hbase.http.filter.xframeoptions.mode", "SAMEORIGIN"); 591 592 HttpServer myServer = new HttpServer.Builder().setName("test") 593 .addEndpoint(new URI("http://localhost:0")) 594 .setFindPort(true).setConf(conf).build(); 595 myServer.setAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE, conf); 596 myServer.addServlet("echo", "/echo", EchoServlet.class); 597 myServer.start(); 598 599 String serverURL = "http://" 600 + NetUtils.getHostPortString(myServer.getConnectorAddress(0)); 601 URL url = new URL(new URL(serverURL), "/echo?a=b&c=d"); 602 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 603 assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode()); 604 assertEquals("SAMEORIGIN", conn.getHeaderField("X-Frame-Options")); 605 myServer.stop(); 606 } 607 608 609 610 @Test 611 public void testNoCacheHeader() throws Exception { 612 URL url = new URL(baseUrl, "/echo?a=b&c=d"); 613 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 614 assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode()); 615 assertEquals("no-cache", conn.getHeaderField("Cache-Control")); 616 assertEquals("no-cache", conn.getHeaderField("Pragma")); 617 assertNotNull(conn.getHeaderField("Expires")); 618 assertNotNull(conn.getHeaderField("Date")); 619 assertEquals(conn.getHeaderField("Expires"), conn.getHeaderField("Date")); 620 assertEquals("DENY", conn.getHeaderField("X-Frame-Options")); 621 } 622}