HBASE-26921 Rewrite the counting cells part in TestMultiVersions (#4316)
[hbase.git] / hbase-http / src / test / java / org / apache / hadoop / hbase / http / TestHttpServer.java
blob0d51870e0a44836f0b532c5c57eb988bf0418e32
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org.apache.hadoop.hbase.http;
20 import static org.hamcrest.Matchers.greaterThan;
21 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.PrintWriter;
26 import java.net.HttpURLConnection;
27 import java.net.URI;
28 import java.net.URL;
29 import java.nio.CharBuffer;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.Enumeration;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.SortedSet;
37 import java.util.TreeSet;
38 import java.util.concurrent.CountDownLatch;
39 import java.util.concurrent.Executor;
40 import java.util.concurrent.Executors;
41 import javax.servlet.Filter;
42 import javax.servlet.FilterChain;
43 import javax.servlet.FilterConfig;
44 import javax.servlet.ServletContext;
45 import javax.servlet.ServletException;
46 import javax.servlet.ServletRequest;
47 import javax.servlet.ServletResponse;
48 import javax.servlet.http.HttpServlet;
49 import javax.servlet.http.HttpServletRequest;
50 import javax.servlet.http.HttpServletRequestWrapper;
51 import javax.servlet.http.HttpServletResponse;
52 import org.apache.hadoop.conf.Configuration;
53 import org.apache.hadoop.fs.CommonConfigurationKeys;
54 import org.apache.hadoop.hbase.HBaseClassTestRule;
55 import org.apache.hadoop.hbase.http.HttpServer.QuotingInputFilter.RequestQuoter;
56 import org.apache.hadoop.hbase.http.resource.JerseyResource;
57 import org.apache.hadoop.hbase.testclassification.MiscTests;
58 import org.apache.hadoop.hbase.testclassification.SmallTests;
59 import org.apache.hadoop.net.NetUtils;
60 import org.apache.hadoop.security.Groups;
61 import org.apache.hadoop.security.ShellBasedUnixGroupsMapping;
62 import org.apache.hadoop.security.UserGroupInformation;
63 import org.apache.hadoop.security.authorize.AccessControlList;
64 import org.apache.http.HttpEntity;
65 import org.apache.http.HttpHeaders;
66 import org.apache.http.client.methods.CloseableHttpResponse;
67 import org.apache.http.client.methods.HttpGet;
68 import org.apache.http.impl.client.CloseableHttpClient;
69 import org.apache.http.impl.client.HttpClients;
70 import org.hamcrest.MatcherAssert;
71 import org.junit.AfterClass;
72 import org.junit.Assert;
73 import org.junit.BeforeClass;
74 import org.junit.ClassRule;
75 import org.junit.Ignore;
76 import org.junit.Test;
77 import org.junit.experimental.categories.Category;
78 import org.mockito.Mockito;
79 import org.slf4j.Logger;
80 import org.slf4j.LoggerFactory;
81 import org.apache.hbase.thirdparty.org.eclipse.jetty.server.ServerConnector;
82 import org.apache.hbase.thirdparty.org.eclipse.jetty.util.ajax.JSON;
84 @Category({MiscTests.class, SmallTests.class})
85 public class TestHttpServer extends HttpServerFunctionalTest {
86 @ClassRule
87 public static final HBaseClassTestRule CLASS_RULE =
88 HBaseClassTestRule.forClass(TestHttpServer.class);
90 private static final Logger LOG = LoggerFactory.getLogger(TestHttpServer.class);
91 private static HttpServer server;
92 private static URL baseUrl;
93 // jetty 9.4.x needs this many threads to start, even in the small.
94 static final int MAX_THREADS = 16;
96 @SuppressWarnings("serial")
97 public static class EchoMapServlet extends HttpServlet {
98 @Override
99 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
100 PrintWriter out = response.getWriter();
101 Map<String, String[]> params = request.getParameterMap();
102 SortedSet<String> keys = new TreeSet<>(params.keySet());
103 for(String key: keys) {
104 out.print(key);
105 out.print(':');
106 String[] values = params.get(key);
107 if (values.length > 0) {
108 out.print(values[0]);
109 for(int i=1; i < values.length; ++i) {
110 out.print(',');
111 out.print(values[i]);
114 out.print('\n');
116 out.close();
120 @SuppressWarnings("serial")
121 public static class EchoServlet extends HttpServlet {
122 @Override
123 public void doGet(HttpServletRequest request, HttpServletResponse response)
124 throws IOException {
125 PrintWriter out = response.getWriter();
126 SortedSet<String> sortedKeys = new TreeSet<>();
127 Enumeration<String> keys = request.getParameterNames();
128 while(keys.hasMoreElements()) {
129 sortedKeys.add(keys.nextElement());
131 for(String key: sortedKeys) {
132 out.print(key);
133 out.print(':');
134 out.print(request.getParameter(key));
135 out.print('\n');
137 out.close();
141 @SuppressWarnings("serial")
142 public static class LongHeaderServlet extends HttpServlet {
143 @Override
144 public void doGet(HttpServletRequest request, HttpServletResponse response) {
145 Assert.assertEquals(63 * 1024, request.getHeader("longheader").length());
146 response.setStatus(HttpServletResponse.SC_OK);
150 @SuppressWarnings("serial")
151 public static class HtmlContentServlet extends HttpServlet {
152 @Override
153 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
154 response.setContentType("text/html");
155 PrintWriter out = response.getWriter();
156 out.print("hello world");
157 out.close();
161 @BeforeClass public static void setup() throws Exception {
162 Configuration conf = new Configuration();
163 conf.setInt(HttpServer.HTTP_MAX_THREADS, MAX_THREADS);
164 server = createTestServer(conf);
165 server.addUnprivilegedServlet("echo", "/echo", EchoServlet.class);
166 server.addUnprivilegedServlet("echomap", "/echomap", EchoMapServlet.class);
167 server.addUnprivilegedServlet("htmlcontent", "/htmlcontent", HtmlContentServlet.class);
168 server.addUnprivilegedServlet("longheader", "/longheader", LongHeaderServlet.class);
169 server.addJerseyResourcePackage(
170 JerseyResource.class.getPackage().getName(), "/jersey/*");
171 server.start();
172 baseUrl = getServerURL(server);
173 LOG.info("HTTP server started: "+ baseUrl);
176 @AfterClass public static void cleanup() throws Exception {
177 server.stop();
181 * Test the maximum number of threads cannot be exceeded.
183 @Test
184 public void testMaxThreads() throws Exception {
185 int clientThreads = MAX_THREADS * 10;
186 Executor executor = Executors.newFixedThreadPool(clientThreads);
187 // Run many clients to make server reach its maximum number of threads
188 final CountDownLatch ready = new CountDownLatch(clientThreads);
189 final CountDownLatch start = new CountDownLatch(1);
190 for (int i = 0; i < clientThreads; i++) {
191 executor.execute(() -> {
192 ready.countDown();
193 try {
194 start.await();
195 assertEquals("a:b\nc:d\n",
196 readOutput(new URL(baseUrl, "/echo?a=b&c=d")));
197 int serverThreads = server.webServer.getThreadPool().getThreads();
198 assertTrue("More threads are started than expected, Server Threads count: "
199 + serverThreads, serverThreads <= MAX_THREADS);
200 LOG.info("Number of threads = " + serverThreads +
201 " which is less or equal than the max = " + MAX_THREADS);
202 } catch (Exception e) {
203 // do nothing
207 // Start the client threads when they are all ready
208 ready.await();
209 start.countDown();
212 @Test public void testEcho() throws Exception {
213 assertEquals("a:b\nc:d\n",
214 readOutput(new URL(baseUrl, "/echo?a=b&c=d")));
215 assertEquals("a:b\nc&lt;:d\ne:&gt;\n",
216 readOutput(new URL(baseUrl, "/echo?a=b&c<=d&e=>")));
219 /** Test the echo map servlet that uses getParameterMap. */
220 @Test public void testEchoMap() throws Exception {
221 assertEquals("a:b\nc:d\n",
222 readOutput(new URL(baseUrl, "/echomap?a=b&c=d")));
223 assertEquals("a:b,&gt;\nc&lt;:d\n",
224 readOutput(new URL(baseUrl, "/echomap?a=b&c<=d&a=>")));
228 * Test that verifies headers can be up to 64K long.
229 * The test adds a 63K header leaving 1K for other headers.
230 * This is because the header buffer setting is for ALL headers,
231 * names and values included. */
232 @Test public void testLongHeader() throws Exception {
233 URL url = new URL(baseUrl, "/longheader");
234 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
235 StringBuilder sb = new StringBuilder();
236 for (int i = 0 ; i < 63 * 1024; i++) {
237 sb.append("a");
239 conn.setRequestProperty("longheader", sb.toString());
240 assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
243 @Test
244 public void testContentTypes() throws Exception {
245 // Static CSS files should have text/css
246 URL cssUrl = new URL(baseUrl, "/static/test.css");
247 HttpURLConnection conn = (HttpURLConnection)cssUrl.openConnection();
248 conn.connect();
249 assertEquals(200, conn.getResponseCode());
250 assertEquals("text/css", conn.getContentType());
252 // Servlets should have text/plain with proper encoding by default
253 URL servletUrl = new URL(baseUrl, "/echo?a=b");
254 conn = (HttpURLConnection)servletUrl.openConnection();
255 conn.connect();
256 assertEquals(200, conn.getResponseCode());
257 assertEquals("text/plain;charset=utf-8", conn.getContentType());
259 // We should ignore parameters for mime types - ie a parameter
260 // ending in .css should not change mime type
261 servletUrl = new URL(baseUrl, "/echo?a=b.css");
262 conn = (HttpURLConnection)servletUrl.openConnection();
263 conn.connect();
264 assertEquals(200, conn.getResponseCode());
265 assertEquals("text/plain;charset=utf-8", conn.getContentType());
267 // Servlets that specify text/html should get that content type
268 servletUrl = new URL(baseUrl, "/htmlcontent");
269 conn = (HttpURLConnection)servletUrl.openConnection();
270 conn.connect();
271 assertEquals(200, conn.getResponseCode());
272 assertEquals("text/html;charset=utf-8", conn.getContentType());
274 // JSPs should default to text/html with utf8
275 // JSPs do not work from unit tests
276 // servletUrl = new URL(baseUrl, "/testjsp.jsp");
277 // conn = (HttpURLConnection)servletUrl.openConnection();
278 // conn.connect();
279 // assertEquals(200, conn.getResponseCode());
280 // assertEquals("text/html; charset=utf-8", conn.getContentType());
283 @Test
284 public void testNegotiatesEncodingGzip() throws IOException {
285 final InputStream stream = ClassLoader.getSystemResourceAsStream("webapps/static/test.css");
286 assertNotNull(stream);
287 final String sourceContent = readFully(stream);
289 try (final CloseableHttpClient client = HttpClients.createMinimal()) {
290 final HttpGet request = new HttpGet(new URL(baseUrl, "/static/test.css").toString());
292 request.setHeader(HttpHeaders.ACCEPT_ENCODING, null);
293 final long unencodedContentLength;
294 try (final CloseableHttpResponse response = client.execute(request)) {
295 final HttpEntity entity = response.getEntity();
296 assertNotNull(entity);
297 assertNull(entity.getContentEncoding());
298 unencodedContentLength = entity.getContentLength();
299 MatcherAssert.assertThat(unencodedContentLength, greaterThan(0L));
300 final String unencodedEntityBody = readFully(entity.getContent());
301 assertEquals(sourceContent, unencodedEntityBody);
304 request.setHeader(HttpHeaders.ACCEPT_ENCODING, "gzip");
305 final long encodedContentLength;
306 try (final CloseableHttpResponse response = client.execute(request)) {
307 final HttpEntity entity = response.getEntity();
308 assertNotNull(entity);
309 assertNotNull(entity.getContentEncoding());
310 assertEquals("gzip", entity.getContentEncoding().getValue());
311 encodedContentLength = entity.getContentLength();
312 MatcherAssert.assertThat(encodedContentLength, greaterThan(0L));
313 final String encodedEntityBody = readFully(entity.getContent());
314 // the encoding/decoding process, as implemented in this specific combination of dependency
315 // versions, does not perfectly preserve trailing whitespace. thus, `trim()`.
316 assertEquals(sourceContent.trim(), encodedEntityBody.trim());
318 MatcherAssert.assertThat(unencodedContentLength, greaterThan(encodedContentLength));
322 private static String readFully(final InputStream input) throws IOException {
323 // TODO: when the time comes, delete me and replace with a JDK11 IO helper API.
324 try (final BufferedReader reader = new BufferedReader(new InputStreamReader(input))) {
325 final StringBuilder sb = new StringBuilder();
326 final CharBuffer buffer = CharBuffer.allocate(1024 * 2);
327 while (reader.read(buffer) > 0) {
328 sb.append(buffer);
329 buffer.clear();
331 return sb.toString();
332 } finally {
333 input.close();
338 * Dummy filter that mimics as an authentication filter. Obtains user identity
339 * from the request parameter user.name. Wraps around the request so that
340 * request.getRemoteUser() returns the user identity.
343 public static class DummyServletFilter implements Filter {
344 @Override
345 public void destroy() { }
347 @Override
348 public void doFilter(ServletRequest request, ServletResponse response,
349 FilterChain filterChain) throws IOException, ServletException {
350 final String userName = request.getParameter("user.name");
351 ServletRequest requestModified =
352 new HttpServletRequestWrapper((HttpServletRequest) request) {
353 @Override
354 public String getRemoteUser() {
355 return userName;
358 filterChain.doFilter(requestModified, response);
361 @Override
362 public void init(FilterConfig arg0) { }
366 * FilterInitializer that initialized the DummyFilter.
369 public static class DummyFilterInitializer extends FilterInitializer {
370 public DummyFilterInitializer() {
373 @Override
374 public void initFilter(FilterContainer container, Configuration conf) {
375 container.addFilter("DummyFilter", DummyServletFilter.class.getName(), null);
380 * Access a URL and get the corresponding return Http status code. The URL
381 * will be accessed as the passed user, by sending user.name request
382 * parameter.
384 * @param urlstring The url to access
385 * @param userName The user to perform access as
386 * @return The HTTP response code
387 * @throws IOException if there is a problem communicating with the server
389 private static int getHttpStatusCode(String urlstring, String userName) throws IOException {
390 URL url = new URL(urlstring + "?user.name=" + userName);
391 System.out.println("Accessing " + url + " as user " + userName);
392 HttpURLConnection connection = (HttpURLConnection)url.openConnection();
393 connection.connect();
394 return connection.getResponseCode();
398 * Custom user->group mapping service.
400 public static class MyGroupsProvider extends ShellBasedUnixGroupsMapping {
401 static Map<String, List<String>> mapping = new HashMap<>();
403 static void clearMapping() {
404 mapping.clear();
407 @Override
408 public List<String> getGroups(String user) {
409 return mapping.get(user);
414 * Verify the access for /logs, /stacks, /conf, /logLevel and /metrics
415 * servlets, when authentication filters are set, but authorization is not
416 * enabled.
418 @Test
419 @Ignore
420 public void testDisabledAuthorizationOfDefaultServlets() throws Exception {
421 Configuration conf = new Configuration();
423 // Authorization is disabled by default
424 conf.set(HttpServer.FILTER_INITIALIZERS_PROPERTY,
425 DummyFilterInitializer.class.getName());
426 conf.set(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING,
427 MyGroupsProvider.class.getName());
428 Groups.getUserToGroupsMappingService(conf);
429 MyGroupsProvider.clearMapping();
430 MyGroupsProvider.mapping.put("userA", Collections.singletonList("groupA"));
431 MyGroupsProvider.mapping.put("userB", Collections.singletonList("groupB"));
433 HttpServer myServer = new HttpServer.Builder().setName("test")
434 .addEndpoint(new URI("http://localhost:0")).setFindPort(true).build();
435 myServer.setAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE, conf);
436 myServer.start();
437 String serverURL = "http://" + NetUtils.getHostPortString(myServer.getConnectorAddress(0)) + "/";
438 for (String servlet : new String[] { "conf", "logs", "stacks", "logLevel", "metrics" }) {
439 for (String user : new String[] { "userA", "userB" }) {
440 assertEquals(HttpURLConnection.HTTP_OK, getHttpStatusCode(serverURL
441 + servlet, user));
444 myServer.stop();
448 * Verify the administrator access for /logs, /stacks, /conf, /logLevel and
449 * /metrics servlets.
451 @Test
452 @Ignore
453 public void testAuthorizationOfDefaultServlets() throws Exception {
454 Configuration conf = new Configuration();
455 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION,
456 true);
457 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_INSTRUMENTATION_REQUIRES_ADMIN,
458 true);
459 conf.set(HttpServer.FILTER_INITIALIZERS_PROPERTY,
460 DummyFilterInitializer.class.getName());
462 conf.set(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING,
463 MyGroupsProvider.class.getName());
464 Groups.getUserToGroupsMappingService(conf);
465 MyGroupsProvider.clearMapping();
466 MyGroupsProvider.mapping.put("userA", Collections.singletonList("groupA"));
467 MyGroupsProvider.mapping.put("userB", Collections.singletonList("groupB"));
468 MyGroupsProvider.mapping.put("userC", Collections.singletonList("groupC"));
469 MyGroupsProvider.mapping.put("userD", Collections.singletonList("groupD"));
470 MyGroupsProvider.mapping.put("userE", Collections.singletonList("groupE"));
472 HttpServer myServer = new HttpServer.Builder().setName("test")
473 .addEndpoint(new URI("http://localhost:0")).setFindPort(true).setConf(conf)
474 .setACL(new AccessControlList("userA,userB groupC,groupD")).build();
475 myServer.setAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE, conf);
476 myServer.start();
478 String serverURL = "http://"
479 + NetUtils.getHostPortString(myServer.getConnectorAddress(0)) + "/";
480 for (String servlet : new String[] { "conf", "logs", "stacks", "logLevel", "metrics" }) {
481 for (String user : new String[] { "userA", "userB", "userC", "userD" }) {
482 assertEquals(HttpURLConnection.HTTP_OK, getHttpStatusCode(serverURL
483 + servlet, user));
485 assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED, getHttpStatusCode(
486 serverURL + servlet, "userE"));
488 myServer.stop();
491 @Test
492 public void testRequestQuoterWithNull() {
493 HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
494 Mockito.doReturn(null).when(request).getParameterValues("dummy");
495 RequestQuoter requestQuoter = new RequestQuoter(request);
496 String[] parameterValues = requestQuoter.getParameterValues("dummy");
497 Assert.assertNull("It should return null "
498 + "when there are no values for the parameter", parameterValues);
501 @Test
502 public void testRequestQuoterWithNotNull() {
503 HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
504 String[] values = new String[] { "abc", "def" };
505 Mockito.doReturn(values).when(request).getParameterValues("dummy");
506 RequestQuoter requestQuoter = new RequestQuoter(request);
507 String[] parameterValues = requestQuoter.getParameterValues("dummy");
508 Assert.assertTrue("It should return Parameter Values", Arrays.equals(
509 values, parameterValues));
512 @SuppressWarnings("unchecked")
513 private static Map<String, Object> parse(String jsonString) {
514 return (Map<String, Object>)JSON.parse(jsonString);
517 @Test public void testJersey() throws Exception {
518 LOG.info("BEGIN testJersey()");
519 final String js = readOutput(new URL(baseUrl, "/jersey/foo?op=bar"));
520 final Map<String, Object> m = parse(js);
521 LOG.info("m=" + m);
522 assertEquals("foo", m.get(JerseyResource.PATH));
523 assertEquals("bar", m.get(JerseyResource.OP));
524 LOG.info("END testJersey()");
527 @Test
528 public void testHasAdministratorAccess() throws Exception {
529 Configuration conf = new Configuration();
530 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION, false);
531 ServletContext context = Mockito.mock(ServletContext.class);
532 Mockito.when(context.getAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE)).thenReturn(conf);
533 Mockito.when(context.getAttribute(HttpServer.ADMINS_ACL)).thenReturn(null);
534 HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
535 Mockito.when(request.getRemoteUser()).thenReturn(null);
536 HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
538 //authorization OFF
539 Assert.assertTrue(HttpServer.hasAdministratorAccess(context, request, response));
541 //authorization ON & user NULL
542 response = Mockito.mock(HttpServletResponse.class);
543 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION, true);
544 Assert.assertFalse(HttpServer.hasAdministratorAccess(context, request, response));
545 Mockito.verify(response).sendError(Mockito.eq(HttpServletResponse.SC_UNAUTHORIZED),
546 Mockito.anyString());
548 //authorization ON & user NOT NULL & ACLs NULL
549 response = Mockito.mock(HttpServletResponse.class);
550 Mockito.when(request.getRemoteUser()).thenReturn("foo");
551 Assert.assertTrue(HttpServer.hasAdministratorAccess(context, request, response));
553 //authorization ON & user NOT NULL & ACLs NOT NULL & user not in ACLs
554 response = Mockito.mock(HttpServletResponse.class);
555 AccessControlList acls = Mockito.mock(AccessControlList.class);
556 Mockito.when(acls.isUserAllowed(Mockito.<UserGroupInformation>any())).thenReturn(false);
557 Mockito.when(context.getAttribute(HttpServer.ADMINS_ACL)).thenReturn(acls);
558 Assert.assertFalse(HttpServer.hasAdministratorAccess(context, request, response));
559 Mockito.verify(response).sendError(Mockito.eq(HttpServletResponse.SC_FORBIDDEN),
560 Mockito.anyString());
562 //authorization ON & user NOT NULL & ACLs NOT NULL & user in in ACLs
563 response = Mockito.mock(HttpServletResponse.class);
564 Mockito.when(acls.isUserAllowed(Mockito.<UserGroupInformation>any())).thenReturn(true);
565 Mockito.when(context.getAttribute(HttpServer.ADMINS_ACL)).thenReturn(acls);
566 Assert.assertTrue(HttpServer.hasAdministratorAccess(context, request, response));
570 @Test
571 public void testRequiresAuthorizationAccess() throws Exception {
572 Configuration conf = new Configuration();
573 ServletContext context = Mockito.mock(ServletContext.class);
574 Mockito.when(context.getAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE)).thenReturn(conf);
575 HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
576 HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
578 //requires admin access to instrumentation, FALSE by default
579 Assert.assertTrue(HttpServer.isInstrumentationAccessAllowed(context, request, response));
581 //requires admin access to instrumentation, TRUE
582 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_INSTRUMENTATION_REQUIRES_ADMIN, true);
583 conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION, true);
584 AccessControlList acls = Mockito.mock(AccessControlList.class);
585 Mockito.when(acls.isUserAllowed(Mockito.<UserGroupInformation>any())).thenReturn(false);
586 Mockito.when(context.getAttribute(HttpServer.ADMINS_ACL)).thenReturn(acls);
587 Assert.assertFalse(HttpServer.isInstrumentationAccessAllowed(context, request, response));
590 @Test
591 public void testBindAddress() throws Exception {
592 checkBindAddress("localhost", 0, false).stop();
593 // hang onto this one for a bit more testing
594 HttpServer myServer = checkBindAddress("localhost", 0, false);
595 HttpServer myServer2 = null;
596 try {
597 int port = myServer.getConnectorAddress(0).getPort();
598 // it's already in use, true = expect a higher port
599 myServer2 = checkBindAddress("localhost", port, true);
600 // try to reuse the port
601 port = myServer2.getConnectorAddress(0).getPort();
602 myServer2.stop();
603 assertNull(myServer2.getConnectorAddress(0)); // not bound
604 myServer2.openListeners();
605 assertEquals(port, myServer2.getConnectorAddress(0).getPort()); // expect same port
606 } finally {
607 myServer.stop();
608 if (myServer2 != null) {
609 myServer2.stop();
614 private HttpServer checkBindAddress(String host, int port, boolean findPort)
615 throws Exception {
616 HttpServer server = createServer(host, port);
617 try {
618 // not bound, ephemeral should return requested port (0 for ephemeral)
619 ServerConnector listener = server.getServerConnectors().get(0);
621 assertEquals(port, listener.getPort());
622 // verify hostname is what was given
623 server.openListeners();
624 assertEquals(host, server.getConnectorAddress(0).getHostName());
626 int boundPort = server.getConnectorAddress(0).getPort();
627 if (port == 0) {
628 assertTrue(boundPort != 0); // ephemeral should now return bound port
629 } else if (findPort) {
630 assertTrue(boundPort > port);
631 // allow a little wiggle room to prevent random test failures if
632 // some consecutive ports are already in use
633 assertTrue(boundPort - port < 8);
635 } catch (Exception e) {
636 server.stop();
637 throw e;
639 return server;
642 @Test
643 public void testXFrameHeaderSameOrigin() throws Exception {
644 Configuration conf = new Configuration();
645 conf.set("hbase.http.filter.xframeoptions.mode", "SAMEORIGIN");
647 HttpServer myServer = new HttpServer.Builder().setName("test")
648 .addEndpoint(new URI("http://localhost:0"))
649 .setFindPort(true).setConf(conf).build();
650 myServer.setAttribute(HttpServer.CONF_CONTEXT_ATTRIBUTE, conf);
651 myServer.addUnprivilegedServlet("echo", "/echo", EchoServlet.class);
652 myServer.start();
654 String serverURL = "http://"
655 + NetUtils.getHostPortString(myServer.getConnectorAddress(0));
656 URL url = new URL(new URL(serverURL), "/echo?a=b&c=d");
657 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
658 assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
659 assertEquals("SAMEORIGIN", conn.getHeaderField("X-Frame-Options"));
660 myServer.stop();
663 @Test
664 public void testNoCacheHeader() throws Exception {
665 URL url = new URL(baseUrl, "/echo?a=b&c=d");
666 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
667 assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
668 assertEquals("no-cache", conn.getHeaderField("Cache-Control"));
669 assertEquals("no-cache", conn.getHeaderField("Pragma"));
670 assertNotNull(conn.getHeaderField("Expires"));
671 assertNotNull(conn.getHeaderField("Date"));
672 assertEquals(conn.getHeaderField("Expires"), conn.getHeaderField("Date"));
673 assertEquals("DENY", conn.getHeaderField("X-Frame-Options"));
676 @Test
677 public void testHttpMethods() throws Exception {
678 // HTTP TRACE method should be disabled for security
679 // See https://www.owasp.org/index.php/Cross_Site_Tracing
680 URL url = new URL(baseUrl, "/echo?a=b");
681 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
682 conn.setRequestMethod("TRACE");
683 conn.connect();
684 assertEquals(HttpURLConnection.HTTP_FORBIDDEN, conn.getResponseCode());