diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index 2352df2cc17..febbf631af1 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -86,6 +86,7 @@ import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; import org.apache.hadoop.hbase.exceptions.DeserializationException; import org.apache.hadoop.hbase.executor.ExecutorType; +import org.apache.hadoop.hbase.http.InfoServer; import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils; import org.apache.hadoop.hbase.ipc.RpcServer; import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException; @@ -177,6 +178,7 @@ import org.apache.zookeeper.KeeperException; import org.mortbay.jetty.Connector; import org.mortbay.jetty.nio.SelectChannelConnector; import org.mortbay.jetty.servlet.Context; +import org.mortbay.jetty.servlet.ServletHolder; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps; @@ -203,7 +205,6 @@ import com.google.protobuf.Service; public class HMaster extends HRegionServer implements MasterServices, Server { private static final Log LOG = LogFactory.getLog(HMaster.class.getName()); - /** * Protection against zombie master. Started once Master accepts active responsibility and * starts taking over responsibilities. Allows a finite time window before giving up ownership. @@ -368,14 +369,42 @@ public class HMaster extends HRegionServer implements MasterServices, Server { private org.mortbay.jetty.Server masterJettyServer; public static class RedirectServlet extends HttpServlet { - private static final long serialVersionUID = 2894774810058302472L; - private static int regionServerInfoPort; + private static final long serialVersionUID = 2894774810058302473L; + private final int regionServerInfoPort; + private final String regionServerHostname; + + /** + * @param infoServer that we're trying to send all requests to + * @param hostname may be null. if given, will be used for redirects instead of host from client. + */ + public RedirectServlet(InfoServer infoServer, String hostname) { + regionServerInfoPort = infoServer.getPort(); + regionServerHostname = hostname; + } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String redirectHost = regionServerHostname; + if(redirectHost == null) { + redirectHost = request.getServerName(); + if(!Addressing.isLocalAddress(InetAddress.getByName(redirectHost))) { + LOG.warn("Couldn't resolve '" + redirectHost + "' as an address local to this node and '" + + MASTER_HOSTNAME_KEY + "' is not set; client will get a HTTP 400 response. If " + + "your HBase deployment relies on client accessible names that the region server process " + + "can't resolve locally, then you should set the previously mentioned configuration variable " + + "to an appropriate hostname."); + // no sending client provided input back to the client, so the goal host is just in the logs. + response.sendError(400, "Request was to a host that I can't resolve for any of the network interfaces on " + + "this node. If this is due to an intermediary such as an HTTP load balancer or other proxy, your HBase " + + "administrator can set '" + MASTER_HOSTNAME_KEY + "' to point to the correct hostname."); + return; + } + } + // TODO this scheme should come from looking at the scheme registered in the infoserver's http server for the + // host and port we're using, but it's buried way too deep to do that ATM. String redirectUrl = request.getScheme() + "://" - + request.getServerName() + ":" + regionServerInfoPort + + redirectHost + ":" + regionServerInfoPort + request.getRequestURI(); response.sendRedirect(redirectUrl); } @@ -481,13 +510,16 @@ public class HMaster extends HRegionServer implements MasterServices, Server { if (!conf.getBoolean("hbase.master.infoserver.redirect", true)) { return -1; } - int infoPort = conf.getInt("hbase.master.info.port.orig", + final int infoPort = conf.getInt("hbase.master.info.port.orig", HConstants.DEFAULT_MASTER_INFOPORT); // -1 is for disabling info server, so no redirecting if (infoPort < 0 || infoServer == null) { return -1; } - String addr = conf.get("hbase.master.info.bindAddress", "0.0.0.0"); + if(infoPort == infoServer.getPort()) { + return infoPort; + } + final String addr = conf.get("hbase.master.info.bindAddress", "0.0.0.0"); if (!Addressing.isLocalAddress(InetAddress.getByName(addr))) { String msg = "Failed to start redirecting jetty server. Address " + addr @@ -497,18 +529,22 @@ public class HMaster extends HRegionServer implements MasterServices, Server { throw new IOException(msg); } - RedirectServlet.regionServerInfoPort = infoServer.getPort(); - if(RedirectServlet.regionServerInfoPort == infoPort) { - return infoPort; - } + // TODO I'm pretty sure we could just add another binding to the InfoServer run by + // the RegionServer and have it run the RedirectServlet instead of standing up + // a second entire stack here. masterJettyServer = new org.mortbay.jetty.Server(); Connector connector = new SelectChannelConnector(); connector.setHost(addr); connector.setPort(infoPort); masterJettyServer.addConnector(connector); masterJettyServer.setStopAtShutdown(true); + + final String redirectHostname = shouldUseThisHostnameInstead() ? useThisHostnameInstead : null; + + final RedirectServlet redirect = new RedirectServlet(infoServer, redirectHostname); Context context = new Context(masterJettyServer, "/", Context.NO_SESSIONS); - context.addServlet(RedirectServlet.class, "/*"); + context.addServlet(new ServletHolder(redirect), "/*"); + try { masterJettyServer.start(); } catch (Exception e) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index b2b57601fa5..187fde0f7aa 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -420,14 +420,15 @@ public class HRegionServer extends HasThread implements /* * hostname specified by hostname config */ - private String useThisHostnameInstead; + protected String useThisHostnameInstead; // key to the config parameter of server hostname // the specification of server hostname is optional. The hostname should be resolvable from // both master and region server + @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG) final static String RS_HOSTNAME_KEY = "hbase.regionserver.hostname"; - - final static String MASTER_HOSTNAME_KEY = "hbase.master.hostname"; + @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG) + protected final static String MASTER_HOSTNAME_KEY = "hbase.master.hostname"; /** * This servers startcode. diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/TestInfoServers.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/TestInfoServers.java index ae5d4dfb148..ba4dfa3fec3 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/TestInfoServers.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/TestInfoServers.java @@ -65,7 +65,8 @@ public class TestInfoServers { } /** - * @throws Exception + * Ensure when we go to top level index pages that we get redirected to an info-server specific status + * page. */ @Test public void testInfoServersRedirect() throws Exception {