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 static org.junit.Assert.assertNotNull; 021import static org.junit.Assert.fail; 022 023import java.io.BufferedReader; 024import java.io.File; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.InputStreamReader; 028import java.net.MalformedURLException; 029import java.net.ServerSocket; 030import java.net.URI; 031import java.net.URL; 032import java.net.URLConnection; 033import java.nio.charset.StandardCharsets; 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.net.NetUtils; 036import org.apache.hadoop.security.authorize.AccessControlList; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040/** 041 * This is a base class for functional tests of the {@link HttpServer}. The methods are static for 042 * other classes to import statically. 043 */ 044public class HttpServerFunctionalTest { 045 private static final Logger LOG = LoggerFactory.getLogger(HttpServerFunctionalTest.class); 046 /** JVM property for the webapp test dir : {@value} */ 047 public static final String TEST_BUILD_WEBAPPS = "test.build.webapps"; 048 /** expected location of the test.build.webapps dir: {@value} */ 049 private static final String BUILD_WEBAPPS_DIR = "src/test/resources/webapps"; 050 051 /** name of the test webapp: {@value} */ 052 private static final String TEST = "test"; 053 054 /** 055 * Create but do not start the test webapp server. The test webapp dir is prepared/checked in 056 * advance. 057 * @return the server instance 058 * @throws IOException if a problem occurs 059 * @throws AssertionError if a condition was not met 060 */ 061 public static HttpServer createTestServer() throws IOException { 062 prepareTestWebapp(); 063 return createServer(TEST); 064 } 065 066 /** 067 * Create but do not start the test webapp server. The test webapp dir is prepared/checked in 068 * advance. 069 * @param conf the server configuration to use 070 * @return the server instance 071 * @throws IOException if a problem occurs 072 * @throws AssertionError if a condition was not met 073 */ 074 public static HttpServer createTestServer(Configuration conf) throws IOException { 075 prepareTestWebapp(); 076 return createServer(TEST, conf); 077 } 078 079 public static HttpServer createTestServer(Configuration conf, AccessControlList adminsAcl) 080 throws IOException { 081 prepareTestWebapp(); 082 return createServer(TEST, conf, adminsAcl); 083 } 084 085 /** 086 * Create but do not start the test webapp server. The test webapp dir is prepared/checked in 087 * advance. 088 * @param conf the server configuration to use 089 * @return the server instance 090 * @throws IOException if a problem occurs 091 * @throws AssertionError if a condition was not met 092 */ 093 public static HttpServer createTestServer(Configuration conf, String[] pathSpecs) 094 throws IOException { 095 prepareTestWebapp(); 096 return createServer(TEST, conf, pathSpecs); 097 } 098 099 public static HttpServer createTestServerWithSecurity(Configuration conf) throws IOException { 100 prepareTestWebapp(); 101 return localServerBuilder(TEST).setFindPort(true).setConf(conf).setSecurityEnabled(true) 102 // InfoServer normally sets these for us 103 .setUsernameConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_KEY) 104 .setKeytabConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_KEYTAB_KEY).build(); 105 } 106 107 public static HttpServer createTestServerWithSecurityAndAcl(Configuration conf, 108 AccessControlList acl) throws IOException { 109 prepareTestWebapp(); 110 return localServerBuilder(TEST).setFindPort(true).setConf(conf).setSecurityEnabled(true) 111 // InfoServer normally sets these for us 112 .setUsernameConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_KEY) 113 .setKeytabConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_KEYTAB_KEY).setSecurityEnabled(true) 114 .setACL(acl).build(); 115 } 116 117 /** 118 * Prepare the test webapp by creating the directory from the test properties fail if the 119 * directory cannot be created. 120 * @throws IOException if an error occurred 121 * @throws AssertionError if a condition was not met 122 */ 123 protected static void prepareTestWebapp() throws IOException { 124 String webapps = System.getProperty(TEST_BUILD_WEBAPPS, BUILD_WEBAPPS_DIR); 125 File testWebappDir = new File(webapps + File.separatorChar + TEST); 126 if (!testWebappDir.exists()) { 127 fail("Test webapp dir " + testWebappDir.getCanonicalPath() + " missing"); 128 } 129 } 130 131 /** 132 * Create an HttpServer instance on the given address for the given webapp 133 * @param host to bind 134 * @param port to bind 135 * @return the server 136 * @throws IOException if it could not be created 137 */ 138 public static HttpServer createServer(String host, int port) throws IOException { 139 prepareTestWebapp(); 140 return new HttpServer.Builder().setName(TEST) 141 .addEndpoint(URI.create("http://" + host + ":" + port)).setFindPort(true).build(); 142 } 143 144 /** 145 * Create an HttpServer instance for the given webapp 146 * @param webapp the webapp to work with 147 * @return the server 148 * @throws IOException if it could not be created 149 */ 150 public static HttpServer createServer(String webapp) throws IOException { 151 return localServerBuilder(webapp).setFindPort(true).build(); 152 } 153 154 /** 155 * Create an HttpServer instance for the given webapp 156 * @param webapp the webapp to work with 157 * @param conf the configuration to use for the server 158 * @return the server 159 * @throws IOException if it could not be created 160 */ 161 public static HttpServer createServer(String webapp, Configuration conf) throws IOException { 162 return localServerBuilder(webapp).setFindPort(true).setConf(conf).build(); 163 } 164 165 public static HttpServer createServer(String webapp, Configuration conf, 166 AccessControlList adminsAcl) throws IOException { 167 return localServerBuilder(webapp).setFindPort(true).setConf(conf).setACL(adminsAcl).build(); 168 } 169 170 private static HttpServer.Builder localServerBuilder(String webapp) { 171 return new HttpServer.Builder().setName(webapp).addEndpoint(URI.create("http://localhost:0")); 172 } 173 174 /** 175 * Create an HttpServer instance for the given webapp 176 * @param webapp the webapp to work with 177 * @param conf the configuration to use for the server 178 * @param pathSpecs the paths specifications the server will service 179 * @return the server 180 * @throws IOException if it could not be created 181 */ 182 public static HttpServer createServer(String webapp, Configuration conf, String[] pathSpecs) 183 throws IOException { 184 return localServerBuilder(webapp).setFindPort(true).setConf(conf).setPathSpec(pathSpecs) 185 .build(); 186 } 187 188 /** 189 * Create and start a server with the test webapp 190 * @return the newly started server 191 * @throws IOException on any failure 192 * @throws AssertionError if a condition was not met 193 */ 194 public static HttpServer createAndStartTestServer() throws IOException { 195 HttpServer server = createTestServer(); 196 server.start(); 197 return server; 198 } 199 200 /** 201 * If the server is non null, stop it 202 * @param server to stop 203 * @throws Exception on any failure 204 */ 205 public static void stop(HttpServer server) throws Exception { 206 if (server != null) { 207 server.stop(); 208 } 209 } 210 211 /** 212 * Pass in a server, return a URL bound to localhost and its port 213 * @param server server 214 * @return a URL bonded to the base of the server 215 * @throws MalformedURLException if the URL cannot be created. 216 */ 217 public static URL getServerURL(HttpServer server) throws MalformedURLException { 218 assertNotNull("No server", server); 219 return new URL("http://" + NetUtils.getHostPortString(server.getConnectorAddress(0))); 220 } 221 222 /** 223 * Read in the content from a URL 224 * @param url URL To read 225 * @return the text from the output 226 * @throws IOException if something went wrong 227 */ 228 protected static String readOutput(URL url) throws IOException { 229 StringBuilder out = new StringBuilder(); 230 InputStream in = url.openConnection().getInputStream(); 231 byte[] buffer = new byte[64 * 1024]; 232 int len = in.read(buffer); 233 while (len > 0) { 234 out.append(new String(buffer, 0, len, StandardCharsets.UTF_8)); 235 len = in.read(buffer); 236 } 237 return out.toString(); 238 } 239 240 /** 241 * Recursively deletes a {@link File}. 242 */ 243 protected static void deleteRecursively(File d) { 244 if (d.isDirectory()) { 245 for (String name : d.list()) { 246 File child = new File(d, name); 247 if (child.isFile()) { 248 child.delete(); 249 } else { 250 deleteRecursively(child); 251 } 252 } 253 } 254 d.delete(); 255 } 256 257 /** 258 * Picks a free port on the host by binding a Socket to '0'. 259 */ 260 protected static int getFreePort() throws IOException { 261 ServerSocket s = new ServerSocket(0); 262 try { 263 s.setReuseAddress(true); 264 int port = s.getLocalPort(); 265 return port; 266 } finally { 267 if (null != s) { 268 s.close(); 269 } 270 } 271 } 272 273 /** 274 * access a url, ignoring some IOException such as the page does not exist 275 */ 276 public static void access(String urlstring) throws IOException { 277 URL url = new URL(urlstring); 278 279 URLConnection connection = url.openConnection(); 280 connection.connect(); 281 282 try (BufferedReader in = new BufferedReader( 283 new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { 284 for (; in.readLine() != null;) { 285 continue; 286 } 287 } catch (IOException ioe) { 288 LOG.info("Got exception: ", ioe); 289 } 290 } 291 292}