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