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 org.apache.hadoop.net.NetUtils;
022import org.apache.hadoop.security.authorize.AccessControlList;
023import org.junit.Assert;
024import org.apache.hadoop.conf.Configuration;
025import org.apache.hadoop.hbase.http.HttpServer.Builder;
026
027import java.io.File;
028import java.io.IOException;
029import java.io.InputStream;
030import java.net.ServerSocket;
031import java.net.URI;
032import java.net.URL;
033import java.net.MalformedURLException;
034
035/**
036 * This is a base class for functional tests of the {@link HttpServer}.
037 * The methods are static for other classes to import statically.
038 */
039public class HttpServerFunctionalTest extends Assert {
040  /** JVM property for the webapp test dir : {@value} */
041  public static final String TEST_BUILD_WEBAPPS = "test.build.webapps";
042  /** expected location of the test.build.webapps dir: {@value} */
043  private static final String BUILD_WEBAPPS_DIR = "src/main/resources/hbase-webapps";
044
045  /** name of the test webapp: {@value} */
046  private static final String TEST = "test";
047
048  /**
049   * Create but do not start the test webapp server. The test webapp dir is
050   * prepared/checked in advance.
051   *
052   * @return the server instance
053   *
054   * @throws IOException if a problem occurs
055   * @throws AssertionError if a condition was not met
056   */
057  public static HttpServer createTestServer() throws IOException {
058    prepareTestWebapp();
059    return createServer(TEST);
060  }
061
062  /**
063   * Create but do not start the test webapp server. The test webapp dir is
064   * prepared/checked in advance.
065   * @param conf the server configuration to use
066   * @return the server instance
067   *
068   * @throws IOException if a problem occurs
069   * @throws AssertionError if a condition was not met
070   */
071  public static HttpServer createTestServer(Configuration conf)
072      throws IOException {
073    prepareTestWebapp();
074    return createServer(TEST, conf);
075  }
076
077  public static HttpServer createTestServer(Configuration conf, AccessControlList adminsAcl)
078      throws IOException {
079    prepareTestWebapp();
080    return createServer(TEST, conf, adminsAcl);
081  }
082
083  /**
084   * Create but do not start the test webapp server. The test webapp dir is
085   * prepared/checked in advance.
086   * @param conf the server configuration to use
087   * @return the server instance
088   *
089   * @throws IOException if a problem occurs
090   * @throws AssertionError if a condition was not met
091   */
092  public static HttpServer createTestServer(Configuration conf,
093      String[] pathSpecs) throws IOException {
094    prepareTestWebapp();
095    return createServer(TEST, conf, pathSpecs);
096  }
097
098  public static HttpServer createTestServerWithSecurity(Configuration conf) throws IOException {
099    prepareTestWebapp();
100    return localServerBuilder(TEST).setFindPort(true).setConf(conf).setSecurityEnabled(true)
101        // InfoServer normally sets these for us
102        .setUsernameConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_PRINCIPAL_KEY)
103        .setKeytabConfKey(HttpServer.HTTP_SPNEGO_AUTHENTICATION_KEYTAB_KEY)
104        .build();
105  }
106
107  /**
108   * Prepare the test webapp by creating the directory from the test properties
109   * fail if the directory cannot be created.
110   * @throws AssertionError if a condition was not met
111   */
112  protected static void prepareTestWebapp() {
113    String webapps = System.getProperty(TEST_BUILD_WEBAPPS, BUILD_WEBAPPS_DIR);
114    File testWebappDir = new File(webapps +
115        File.separatorChar + TEST);
116    try {
117    if (!testWebappDir.exists()) {
118      fail("Test webapp dir " + testWebappDir.getCanonicalPath() + " missing");
119    }
120    }
121    catch (IOException e) {
122    }
123  }
124
125  /**
126   * Create an HttpServer instance on the given address for the given webapp
127   * @param host to bind
128   * @param port to bind
129   * @return the server
130   * @throws IOException if it could not be created
131   */
132  public static HttpServer createServer(String host, int port)
133      throws IOException {
134    prepareTestWebapp();
135    return new HttpServer.Builder().setName(TEST)
136        .addEndpoint(URI.create("http://" + host + ":" + port))
137        .setFindPort(true).build();
138  }
139
140  /**
141   * Create an HttpServer instance for the given webapp
142   * @param webapp the webapp to work with
143   * @return the server
144   * @throws IOException if it could not be created
145   */
146  public static HttpServer createServer(String webapp) throws IOException {
147    return localServerBuilder(webapp).setFindPort(true).build();
148  }
149  /**
150   * Create an HttpServer instance for the given webapp
151   * @param webapp the webapp to work with
152   * @param conf the configuration to use for the server
153   * @return the server
154   * @throws IOException if it could not be created
155   */
156  public static HttpServer createServer(String webapp, Configuration conf)
157      throws IOException {
158    return localServerBuilder(webapp).setFindPort(true).setConf(conf).build();
159  }
160
161  public static HttpServer createServer(String webapp, Configuration conf, AccessControlList adminsAcl)
162      throws IOException {
163    return localServerBuilder(webapp).setFindPort(true).setConf(conf).setACL(adminsAcl).build();
164  }
165
166  private static Builder localServerBuilder(String webapp) {
167    return new HttpServer.Builder().setName(webapp).addEndpoint(
168        URI.create("http://localhost:0"));
169  }
170
171  /**
172   * Create an HttpServer instance for the given webapp
173   * @param webapp the webapp to work with
174   * @param conf the configuration to use for the server
175   * @param pathSpecs the paths specifications the server will service
176   * @return the server
177   * @throws IOException if it could not be created
178   */
179  public static HttpServer createServer(String webapp, Configuration conf,
180      String[] pathSpecs) throws IOException {
181    return localServerBuilder(webapp).setFindPort(true).setConf(conf).setPathSpec(pathSpecs).build();
182  }
183
184  /**
185   * Create and start a server with the test webapp
186   *
187   * @return the newly started server
188   *
189   * @throws IOException on any failure
190   * @throws AssertionError if a condition was not met
191   */
192  public static HttpServer createAndStartTestServer() throws IOException {
193    HttpServer server = createTestServer();
194    server.start();
195    return server;
196  }
197
198  /**
199   * If the server is non null, stop it
200   * @param server to stop
201   * @throws Exception on any failure
202   */
203  public static void stop(HttpServer server) throws Exception {
204    if (server != null) {
205      server.stop();
206    }
207  }
208
209  /**
210   * Pass in a server, return a URL bound to localhost and its port
211   * @param server server
212   * @return a URL bonded to the base of the server
213   * @throws MalformedURLException if the URL cannot be created.
214   */
215  public static URL getServerURL(HttpServer server)
216      throws MalformedURLException {
217    assertNotNull("No server", server);
218    return new URL("http://"
219        + 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));
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}