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/main/resources/hbase-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}