Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1d4c723

Browse files
author
Dmitriy Fingerman
committedMar 14, 2025·
HIVE-28775: HiveServer2: enabling HA Leader endpoint on a different port than WebUI
1 parent 38dc6b6 commit 1d4c723

File tree

11 files changed

+385
-150
lines changed

11 files changed

+385
-150
lines changed
 

‎common/src/java/org/apache/hadoop/hive/conf/HiveConf.java

+2
Original file line numberDiff line numberDiff line change
@@ -3979,6 +3979,8 @@ public static enum ConfVars {
39793979
HIVE_SERVER2_WEBUI_BIND_HOST("hive.server2.webui.host", "0.0.0.0", "The host address the HiveServer2 WebUI will listen on"),
39803980
HIVE_SERVER2_WEBUI_PORT("hive.server2.webui.port", 10002, "The port the HiveServer2 WebUI will listen on. This can be"
39813981
+ "set to 0 or a negative integer to disable the web UI"),
3982+
HIVE_SERVER2_HEALTH_HA_PORT("hive.server2.health.ha.port", 11002, "The port the HiveServer2 health-ha web app will listen on. This can be"
3983+
+ "set to 0 or a negative integer to disable HS2 health-ha checking"),
39823984
HIVE_SERVER2_WEBUI_MAX_THREADS("hive.server2.webui.max.threads", 50, "The max HiveServer2 WebUI threads"),
39833985
HIVE_SERVER2_WEBUI_USE_SSL("hive.server2.webui.use.ssl", false,
39843986
"Set this to true for using SSL encryption for HiveServer2 WebUI."),

‎common/src/java/org/apache/hive/http/HttpServer.java

+123-61
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,17 @@
8282
import org.eclipse.jetty.security.ConstraintMapping;
8383
import org.eclipse.jetty.security.ConstraintSecurityHandler;
8484
import org.eclipse.jetty.security.LoginService;
85-
import org.eclipse.jetty.server.Connector;
8685
import org.eclipse.jetty.server.Handler;
8786
import org.eclipse.jetty.server.HttpConfiguration;
8887
import org.eclipse.jetty.server.HttpConnectionFactory;
8988
import org.eclipse.jetty.server.LowResourceMonitor;
89+
import org.eclipse.jetty.server.Request;
9090
import org.eclipse.jetty.server.Server;
91+
import org.eclipse.jetty.server.handler.AbstractHandler;
9192
import org.eclipse.jetty.server.handler.ContextHandler.Context;
9293
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
9394
import org.eclipse.jetty.server.ServerConnector;
95+
import org.eclipse.jetty.server.handler.HandlerCollection;
9496
import org.eclipse.jetty.servlet.DefaultServlet;
9597
import org.eclipse.jetty.servlet.FilterHolder;
9698
import org.eclipse.jetty.servlet.FilterMapping;
@@ -138,6 +140,8 @@ public class HttpServer {
138140
private String appDir;
139141
private WebAppContext webAppContext;
140142
private Server webServer;
143+
private QueuedThreadPool threadPool;
144+
private PortHandlerWrapper portHandlerWrapper;
141145

142146
/**
143147
* Create a status server on the given port.
@@ -179,6 +183,7 @@ public static class Builder {
179183
new LinkedList<Pair<String, Class<? extends HttpServlet>>>();
180184
private boolean disableDirListing = false;
181185
private final Map<String, Pair<String, Filter>> globalFilters = new LinkedHashMap<>();
186+
private String contextPath;
182187

183188
public Builder(String name) {
184189
Preconditions.checkArgument(name != null && !name.isEmpty(), "Name must be specified");
@@ -189,6 +194,10 @@ public HttpServer build() throws IOException {
189194
return new HttpServer(this);
190195
}
191196

197+
public void setContextPath(String contextPath) {
198+
this.contextPath = contextPath;
199+
}
200+
192201
public Builder setConf(HiveConf origConf) {
193202
this.conf = new HiveConf(origConf);
194203
origConf.stripHiddenConfigurations(conf);
@@ -489,13 +498,13 @@ static boolean userHasAdministratorAccess(ServletContext servletContext,
489498
/**
490499
* Create the web context for the application of specified name
491500
*/
492-
WebAppContext createWebAppContext(Builder b) {
501+
WebAppContext createWebAppContext(Builder b) throws FileNotFoundException {
493502
WebAppContext ctx = new WebAppContext();
494503
setContextAttributes(ctx.getServletContext(), b.contextAttrs);
495504
ctx.getServletContext().getSessionCookieConfig().setHttpOnly(true);
496505
ctx.setDisplayName(b.name);
497-
ctx.setContextPath("/");
498-
ctx.setWar(appDir + "/" + b.name);
506+
ctx.setContextPath(b.contextPath == null ? "/" : b.contextPath);
507+
ctx.setWar(getWebAppsPath(b.name) + "/" + b.name);
499508
return ctx;
500509
}
501510

@@ -519,8 +528,9 @@ void setupSpnegoFilter(Builder b, ServletContextHandler ctx) throws IOException
519528
/**
520529
* Setup cross-origin requests (CORS) filter.
521530
* @param b - builder
531+
* @param webAppContext - webAppContext
522532
*/
523-
private void setupCORSFilter(Builder b) {
533+
private void setupCORSFilter(Builder b, WebAppContext webAppContext) {
524534
FilterHolder holder = new FilterHolder();
525535
holder.setClassName(CrossOriginFilter.class.getName());
526536
Map<String, String> params = new HashMap<>();
@@ -533,10 +543,62 @@ private void setupCORSFilter(Builder b) {
533543
handler.addFilterWithMapping(holder, "/*", FilterMapping.ALL);
534544
}
535545

546+
public void addWebApp(Builder b) throws IOException {
547+
WebAppContext webAppContext = createWebAppContext(b);
548+
ContextHandlerCollection portHandler = new ContextHandlerCollection();
549+
ServerConnector connector = addWebApp(b, webAppContext, portHandler);
550+
portHandlerWrapper.addHandler(connector, portHandler);
551+
}
552+
553+
private ServerConnector addWebApp(Builder b, WebAppContext webAppContext, ContextHandlerCollection portHandler)
554+
throws IOException {
555+
556+
if (b.useSPNEGO) {
557+
// Secure the web server with kerberos
558+
setupSpnegoFilter(b, webAppContext);
559+
}
560+
561+
if (b.enableCORS) {
562+
setupCORSFilter(b, webAppContext);
563+
}
564+
565+
Map<String, String> xFrameParams = setHeaders();
566+
if (b.xFrameEnabled) {
567+
setupXframeFilter(b, xFrameParams, webAppContext);
568+
}
569+
570+
if (b.disableDirListing) {
571+
disableDirectoryListingOnServlet(webAppContext);
572+
}
573+
574+
ServerConnector connector = createChannelConnector(threadPool.getQueueSize(), b);
575+
webServer.addConnector(connector);
576+
577+
RewriteHandler rwHandler = new RewriteHandler();
578+
rwHandler.setRewriteRequestURI(true);
579+
rwHandler.setRewritePathInfo(false);
580+
581+
RewriteRegexRule rootRule = new RewriteRegexRule();
582+
rootRule.setRegex("^/$");
583+
rootRule.setReplacement(b.contextRootRewriteTarget);
584+
rootRule.setTerminating(true);
585+
586+
rwHandler.addRule(rootRule);
587+
rwHandler.setHandler(webAppContext);
588+
589+
portHandler.addHandler(rwHandler);
590+
591+
for (Pair<String, Class<? extends HttpServlet>> p : b.servlets) {
592+
addServlet(p.getFirst(), "/" + p.getFirst(), p.getSecond(), webAppContext);
593+
}
594+
595+
return connector;
596+
}
597+
536598
/**
537599
* Create a channel connector for "http/https" requests
538600
*/
539-
Connector createChannelConnector(int queueSize, Builder b) {
601+
ServerConnector createChannelConnector(int queueSize, Builder b) {
540602
ServerConnector connector;
541603

542604
final HttpConfiguration conf = new HttpConfiguration();
@@ -604,7 +666,7 @@ void setContextAttributes(Context ctx, Map<String, Object> contextAttrs) {
604666

605667
private void createWebServer(final Builder b) throws IOException {
606668
// Create the thread pool for the web server to handle HTTP requests
607-
QueuedThreadPool threadPool = new QueuedThreadPool();
669+
threadPool = new QueuedThreadPool();
608670
if (b.maxThreads > 0) {
609671
threadPool.setMaxThreads(b.maxThreads);
610672
}
@@ -615,59 +677,29 @@ private void createWebServer(final Builder b) throws IOException {
615677
this.appDir = getWebAppsPath(b.name);
616678
this.webAppContext = createWebAppContext(b);
617679

618-
if (b.useSPNEGO) {
619-
// Secure the web server with kerberos
620-
setupSpnegoFilter(b, webAppContext);
621-
}
622-
623-
if (b.enableCORS) {
624-
setupCORSFilter(b);
625-
}
626-
627-
Map<String, String> xFrameParams = setHeaders();
628-
if (b.xFrameEnabled) {
629-
setupXframeFilter(b,xFrameParams);
630-
}
631-
632-
if (b.disableDirListing) {
633-
disableDirectoryListingOnServlet(webAppContext);
634-
}
635-
636-
initializeWebServer(b, threadPool.getMaxThreads());
680+
initializeWebServer(b);
637681
}
638682

639-
private void initializeWebServer(final Builder b, int queueSize) throws IOException {
683+
private void initializeWebServer(final Builder b) throws IOException {
640684
// Set handling for low resource conditions.
641685
final LowResourceMonitor low = new LowResourceMonitor(webServer);
642686
low.setLowResourcesIdleTimeout(10000);
643687
webServer.addBean(low);
644688

645-
Connector connector = createChannelConnector(queueSize, b);
646-
webServer.addConnector(connector);
647-
648-
RewriteHandler rwHandler = new RewriteHandler();
649-
rwHandler.setRewriteRequestURI(true);
650-
rwHandler.setRewritePathInfo(false);
651-
652-
RewriteRegexRule rootRule = new RewriteRegexRule();
653-
rootRule.setRegex("^/$");
654-
rootRule.setReplacement(b.contextRootRewriteTarget);
655-
rootRule.setTerminating(true);
656-
657-
rwHandler.addRule(rootRule);
658-
rwHandler.setHandler(webAppContext);
659-
660-
// Configure web application contexts for the web server
661-
ContextHandlerCollection contexts = new ContextHandlerCollection();
662-
contexts.addHandler(rwHandler);
663-
webServer.setHandler(contexts);
664-
689+
// Configure the global context handler collection for the web server
690+
portHandlerWrapper = new PortHandlerWrapper();
691+
webServer.setHandler(portHandlerWrapper);
692+
693+
ContextHandlerCollection portHandler = new ContextHandlerCollection();
694+
portHandler.addHandler(webAppContext);
695+
ServerConnector connector = addWebApp(b, webAppContext, portHandler);
696+
697+
portHandlerWrapper.addHandler(connector, portHandler);
665698

666699
if (b.usePAM) {
667-
setupPam(b, contexts);
700+
setupPam(b, portHandlerWrapper);
668701
}
669702

670-
671703
addServlet("jmx", "/jmx", JMXJsonServlet.class);
672704
addServlet("conf", "/conf", ConfServlet.class);
673705
addServlet("stacks", "/stacks", StackServlet.class);
@@ -679,8 +711,7 @@ private void initializeWebServer(final Builder b, int queueSize) throws IOExcept
679711
if (Files.notExists(tmpDir)) {
680712
Files.createDirectories(tmpDir);
681713
}
682-
ServletContextHandler genCtx =
683-
new ServletContextHandler(contexts, "/prof-output");
714+
ServletContextHandler genCtx = new ServletContextHandler(portHandler, "/prof-output");
684715
setContextAttributes(genCtx.getServletContext(), b.contextAttrs);
685716
genCtx.addServlet(ProfileOutputServlet.class, "/*");
686717
genCtx.setResourceBase(tmpDir.toAbsolutePath().toString());
@@ -689,23 +720,17 @@ private void initializeWebServer(final Builder b, int queueSize) throws IOExcept
689720
LOG.info("ASYNC_PROFILER_HOME env or -Dasync.profiler.home not specified. Disabling /prof endpoint..");
690721
}
691722

692-
for (Pair<String, Class<? extends HttpServlet>> p : b.servlets) {
693-
addServlet(p.getFirst(), "/" + p.getFirst(), p.getSecond());
694-
}
695-
696723
b.globalFilters.forEach((k, v) -> addFilter(k, v.getFirst(), v.getSecond(), webAppContext.getServletHandler()));
697724

698-
ServletContextHandler staticCtx =
699-
new ServletContextHandler(contexts, "/static");
725+
ServletContextHandler staticCtx = new ServletContextHandler(portHandler, "/static");
700726
staticCtx.setResourceBase(appDir + "/static");
701727
staticCtx.addServlet(DefaultServlet.class, "/*");
702728
staticCtx.setDisplayName("static");
703729
disableDirectoryListingOnServlet(staticCtx);
704730

705731
String logDir = getLogDir(b.conf);
706732
if (logDir != null) {
707-
ServletContextHandler logCtx =
708-
new ServletContextHandler(contexts, "/logs");
733+
ServletContextHandler logCtx = new ServletContextHandler(portHandler, "/logs");
709734
setContextAttributes(logCtx.getServletContext(), b.contextAttrs);
710735
if(b.useSPNEGO) {
711736
setupSpnegoFilter(b,logCtx);
@@ -716,7 +741,7 @@ private void initializeWebServer(final Builder b, int queueSize) throws IOExcept
716741
}
717742

718743
// Define the global filers for each servlet context except the staticCtx(css style).
719-
Optional<Handler[]> handlers = Optional.ofNullable(contexts.getHandlers());
744+
Optional<Handler[]> handlers = Optional.ofNullable(portHandler.getHandlers());
720745
handlers.ifPresent(hs -> Arrays.stream(hs)
721746
.filter(h -> h instanceof ServletContextHandler && !"static".equals(((ServletContextHandler) h).getDisplayName()))
722747
.forEach(h -> b.globalFilters.forEach((k, v) ->
@@ -748,7 +773,7 @@ private Map<String, String> getDefaultHeaders() {
748773
return headers;
749774
}
750775

751-
private void setupXframeFilter(Builder b, Map<String, String> params) {
776+
private void setupXframeFilter(Builder b, Map<String, String> params, WebAppContext webAppContext) {
752777
FilterHolder holder = new FilterHolder();
753778
holder.setClassName(QuotingInputFilter.class.getName());
754779
holder.setInitParameters(params);
@@ -811,6 +836,15 @@ public void addServlet(String name, String pathSpec,
811836
webAppContext.addServlet(holder, pathSpec);
812837
}
813838

839+
private void addServlet(String name, String pathSpec, Class<? extends HttpServlet> clazz,
840+
WebAppContext webAppContext) {
841+
ServletHolder holder = new ServletHolder(clazz);
842+
if (name != null) {
843+
holder.setName(name);
844+
}
845+
webAppContext.addServlet(holder, pathSpec);
846+
}
847+
814848
public void addServlet(String name, String pathSpec, ServletHolder holder) {
815849
if (name != null) {
816850
holder.setName(name);
@@ -1026,4 +1060,32 @@ private void initHttpHeaderMap() {
10261060
}
10271061
}
10281062

1063+
class PortHandlerWrapper extends ContextHandlerCollection {
1064+
1065+
private final Map<ServerConnector, HandlerCollection> connectorToHandlerMap = new HashMap<>();
1066+
1067+
public PortHandlerWrapper() {
1068+
}
1069+
1070+
public void addHandler(ServerConnector connector, HandlerCollection handler) {
1071+
connectorToHandlerMap.put(connector, handler);
1072+
addHandler(handler);
1073+
}
1074+
1075+
@Override
1076+
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
1077+
// Determine the connector (port) the request came through
1078+
int port = request.getServerPort();
1079+
1080+
// Find the handler for the corresponding port
1081+
Handler handler = connectorToHandlerMap.entrySet().stream()
1082+
.filter(entry -> entry.getKey().getPort() == port)
1083+
.map(Map.Entry::getValue)
1084+
.findFirst()
1085+
.orElseThrow(() -> new IllegalStateException("No handler found for port " + port));
1086+
1087+
// Delegate the request to the handler
1088+
handler.handle(target, baseRequest, request, response);
1089+
}
1090+
}
10291091
}

‎data/conf/hive-site.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@
358358

359359
<property>
360360
<name>hive.server2.webui.max.threads</name>
361-
<value>4</value>
361+
<value>6</value>
362362
</property>
363363

364364
<property>

‎data/conf/mr/hive-site.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@
354354

355355
<property>
356356
<name>hive.server2.webui.max.threads</name>
357-
<value>4</value>
357+
<value>6</value>
358358
</property>
359359

360360
<property>

‎itests/hive-unit/src/test/java/org/apache/hive/jdbc/TestActivePassiveHA.java

+101
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,72 @@ private static void setHAConfigs(Configuration conf) {
139139
conf.setInt(ConfVars.HIVE_ZOOKEEPER_CONNECTION_MAX_RETRIES.varname, 1);
140140
}
141141

142+
@Test(timeout = 60000)
143+
public void testHealthCheckHA() throws Exception {
144+
String instanceId1 = UUID.randomUUID().toString();
145+
miniHS2_1.start(getConfOverlay(instanceId1));
146+
147+
String leaderURL = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
148+
String healthCheckURL = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
149+
150+
String leaderURLHealthCheckPort = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/leader";
151+
String healthCheckURLWebUIPort = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/health-ha/leader";
152+
153+
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
154+
assertEquals(true, miniHS2_1.isLeader());
155+
156+
assertEquals("true", sendGet(leaderURL));
157+
assertEquals("true", sendGet(healthCheckURL));
158+
159+
assertEquals("Not Found", sendGet(leaderURLHealthCheckPort));
160+
assertEquals("Not Found", sendGet(healthCheckURLWebUIPort));
161+
}
162+
163+
@Test(timeout = 60000)
164+
public void testHealthCheckHAAuth() throws Exception {
165+
hiveConf1.setBoolVar(ConfVars.HIVE_SERVER2_WEBUI_ENABLE_CORS, true);
166+
setPamConfs(hiveConf1);
167+
PamAuthenticator pamAuthenticator = new TestHS2HttpServerPam.TestPamAuthenticator(hiveConf1);
168+
String instanceId1 = UUID.randomUUID().toString();
169+
miniHS2_1.setPamAuthenticator(pamAuthenticator);
170+
miniHS2_1.start(getSecureConfOverlay(instanceId1));
171+
172+
String leaderURL = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
173+
String healthCheckURL = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
174+
175+
String leaderURLHealthCheckPort = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/leader";
176+
String healthCheckURLWebUIPort = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/health-ha/leader";
177+
178+
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
179+
assertEquals(true, miniHS2_1.isLeader());
180+
181+
assertEquals("true", sendGet(leaderURL, true, true));
182+
assertEquals("true", sendGet(healthCheckURL, true, true));
183+
184+
try {
185+
sendGet(leaderURLHealthCheckPort, true, true);
186+
} catch (AssertionError e) {
187+
assertTrue(e.getMessage().contains("Not Found"));
188+
} catch (Exception e) {
189+
fail("Expected AssertionError");
190+
}
191+
192+
assertEquals("Not Found", sendGet(healthCheckURLWebUIPort, true, true));
193+
assertEquals("Method Not Allowed", sendDelete(healthCheckURL, true, true));
194+
assertEquals("Method Not Allowed", sendDelete(healthCheckURLWebUIPort, true, true));
195+
196+
try {
197+
sendDelete(leaderURLHealthCheckPort, true, true);
198+
} catch (AssertionError e) {
199+
assertTrue(e.getMessage().contains("Not Found"));
200+
} catch (Exception e) {
201+
fail("Expected AssertionError");
202+
}
203+
204+
String resp = sendDelete(leaderURL, true, true);
205+
assertTrue(resp.contains("Failover successful!"));
206+
}
207+
142208
@Test(timeout = 60000)
143209
public void testActivePassiveHA() throws Exception {
144210
String instanceId1 = UUID.randomUUID().toString();
@@ -150,10 +216,14 @@ public void testActivePassiveHA() throws Exception {
150216
assertEquals(true, miniHS2_1.isLeader());
151217
String url = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
152218
assertEquals("true", sendGet(url));
219+
String healthCheckURL = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
220+
assertEquals("true", sendGet(healthCheckURL));
153221

154222
assertEquals(false, miniHS2_2.isLeader());
155223
url = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
156224
assertEquals("false", sendGet(url));
225+
healthCheckURL = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
226+
assertEquals("false", sendGet(healthCheckURL));
157227

158228
url = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/peers";
159229
String resp = sendGet(url);
@@ -192,6 +262,8 @@ public void testActivePassiveHA() throws Exception {
192262
assertEquals(true, miniHS2_2.isLeader());
193263
url = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
194264
assertEquals("true", sendGet(url));
265+
healthCheckURL = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
266+
assertEquals("true", sendGet(healthCheckURL));
195267

196268
while (client.getAll().size() != 1) {
197269
Thread.sleep(100);
@@ -233,6 +305,8 @@ public void testActivePassiveHA() throws Exception {
233305
assertEquals(false, miniHS2_1.isLeader());
234306
url = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
235307
assertEquals("false", sendGet(url));
308+
healthCheckURL = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
309+
assertEquals("false", sendGet(healthCheckURL));
236310

237311
while (client.getAll().size() != 2) {
238312
Thread.sleep(100);
@@ -282,10 +356,14 @@ public void testConnectionActivePassiveHAServiceDiscovery() throws Exception {
282356
assertEquals(true, miniHS2_1.isLeader());
283357
String url = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
284358
assertEquals("true", sendGet(url));
359+
String healthCheckURL = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
360+
assertEquals("true", sendGet(healthCheckURL));
285361

286362
assertEquals(false, miniHS2_2.isLeader());
287363
url = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
288364
assertEquals("false", sendGet(url));
365+
healthCheckURL = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
366+
assertEquals("false", sendGet(healthCheckURL));
289367

290368
// miniHS2_1 will be leader
291369
String zkConnectString = zkServer.getConnectString();
@@ -347,11 +425,14 @@ public void testManualFailover() throws Exception {
347425
miniHS2_2.start(confOverlay);
348426
String url1 = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
349427
String url2 = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
428+
String healthCheckURL1 = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
429+
String healthCheckURL2 = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
350430

351431
// when we start miniHS2_1 will be leader (sequential start)
352432
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
353433
assertEquals(true, miniHS2_1.isLeader());
354434
assertEquals("true", sendGet(url1, true, true));
435+
assertEquals("true", sendGet(healthCheckURL1, true, true));
355436

356437
// trigger failover on miniHS2_1
357438
String resp = sendDelete(url1, true, true);
@@ -361,11 +442,13 @@ public void testManualFailover() throws Exception {
361442
assertEquals(true, miniHS2_1.getNotLeaderTestFuture().get());
362443
assertEquals(false, miniHS2_1.isLeader());
363444
assertEquals("false", sendGet(url1, true, true));
445+
assertEquals("false", sendGet(healthCheckURL1, true, true));
364446

365447
// make sure miniHS2_2 is the new leader
366448
assertEquals(true, miniHS2_2.getIsLeaderTestFuture().get());
367449
assertEquals(true, miniHS2_2.isLeader());
368450
assertEquals("true", sendGet(url2, true, true));
451+
assertEquals("true", sendGet(healthCheckURL2, true, true));
369452

370453
// send failover request again to miniHS2_1 and get a failure
371454
resp = sendDelete(url1, true, true);
@@ -379,8 +462,10 @@ public void testManualFailover() throws Exception {
379462
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
380463
assertEquals(true, miniHS2_1.isLeader());
381464
assertEquals("true", sendGet(url1, true, true));
465+
assertEquals("true", sendGet(healthCheckURL1, true, true));
382466
assertEquals(true, miniHS2_2.getNotLeaderTestFuture().get());
383467
assertEquals("false", sendGet(url2, true, true));
468+
assertEquals("false", sendGet(healthCheckURL2, true, true));
384469
assertEquals(false, miniHS2_2.isLeader());
385470
} finally {
386471
resetFailoverConfs();
@@ -404,10 +489,12 @@ public void testManualFailoverUnauthorized() throws Exception {
404489
miniHS2_2.start(confOverlay);
405490

406491
String url1 = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
492+
String healthCheckURL1 = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
407493
// when we start miniHS2_1 will be leader (sequential start)
408494
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
409495
assertEquals(true, miniHS2_1.isLeader());
410496
assertEquals("true", sendGet(url1, true));
497+
assertEquals("true", sendGet(healthCheckURL1, true));
411498

412499
// trigger failover on miniHS2_1 without authorization header
413500
assertTrue(sendDelete(url1, false).contains("Unauthorized"));
@@ -439,6 +526,7 @@ public void testNoConnectionOnPassive() throws Exception {
439526
miniHS2_2.setPamAuthenticator(pamAuthenticator2);
440527
miniHS2_2.start(confOverlay);
441528
String url1 = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
529+
String healthCheckURL1 = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
442530
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
443531
assertEquals(true, miniHS2_1.isLeader());
444532

@@ -454,6 +542,9 @@ public void testNoConnectionOnPassive() throws Exception {
454542
Thread.sleep(100);
455543
}
456544

545+
resp = sendDelete(healthCheckURL1, true);
546+
assertTrue(resp, resp.contains("Method Not Allowed"));
547+
457548
assertEquals(true, miniHS2_2.getIsLeaderTestFuture().get());
458549
assertEquals(true, miniHS2_2.isLeader());
459550

@@ -496,6 +587,8 @@ public void testClientConnectionsOnFailover() throws Exception {
496587
miniHS2_2.start(confOverlay);
497588
String url1 = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
498589
String url2 = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_WEBUI_PORT.varname) + "/leader";
590+
String healthCheckUrl1 = "http://localhost:" + hiveConf1.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
591+
String healthCheckUrl2 = "http://localhost:" + hiveConf2.get(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT.varname) + "/health-ha/leader";
499592
String zkJdbcUrl = miniHS2_1.getJdbcURL();
500593
String zkConnectString = zkServer.getConnectString();
501594
assertTrue(zkJdbcUrl.contains(zkConnectString));
@@ -504,6 +597,7 @@ public void testClientConnectionsOnFailover() throws Exception {
504597
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
505598
assertEquals(true, miniHS2_1.isLeader());
506599
assertEquals("true", sendGet(url1, true));
600+
assertEquals("true", sendGet(healthCheckUrl1, true));
507601

508602
// before failover, check if we are getting connection from miniHS2_1
509603
String hs2_1_directUrl = "jdbc:hive2://" + miniHS2_1.getHost() + ":" + miniHS2_1.getBinaryPort() +
@@ -523,15 +617,20 @@ public void testClientConnectionsOnFailover() throws Exception {
523617
Thread.sleep(100);
524618
}
525619

620+
resp = sendDelete(healthCheckUrl1, true);
621+
assertTrue(resp.contains("Method Not Allowed"));
622+
526623
// make sure miniHS2_1 is not leader
527624
assertEquals(true, miniHS2_1.getNotLeaderTestFuture().get());
528625
assertEquals(false, miniHS2_1.isLeader());
529626
assertEquals("false", sendGet(url1, true));
627+
assertEquals("false", sendGet(healthCheckUrl1, true));
530628

531629
// make sure miniHS2_2 is the new leader
532630
assertEquals(true, miniHS2_2.getIsLeaderTestFuture().get());
533631
assertEquals(true, miniHS2_2.isLeader());
534632
assertEquals("true", sendGet(url2, true));
633+
assertEquals("true", sendGet(healthCheckUrl2, true));
535634

536635
// when we make a new connection we should get it from miniHS2_2 this time
537636
String hs2_2_directUrl = "jdbc:hive2://" + miniHS2_2.getHost() + ":" + miniHS2_2.getHttpPort() +
@@ -555,8 +654,10 @@ public void testClientConnectionsOnFailover() throws Exception {
555654
assertEquals(true, miniHS2_1.getIsLeaderTestFuture().get());
556655
assertEquals(true, miniHS2_1.isLeader());
557656
assertEquals("true", sendGet(url1, true));
657+
assertEquals("true", sendGet(healthCheckUrl1, true));
558658
assertEquals(true, miniHS2_2.getNotLeaderTestFuture().get());
559659
assertEquals("false", sendGet(url2, true));
660+
assertEquals("false", sendGet(healthCheckUrl2, true));
560661
assertEquals(false, miniHS2_2.isLeader());
561662
// make sure miniHS2_2 closes all its connections
562663
while (miniHS2_2.getOpenSessionsCount() != 0) {

‎itests/util/src/main/java/org/apache/hive/jdbc/miniHS2/AbstractHiveService.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,17 @@ public abstract class AbstractHiveService {
3636
private int binaryPort;
3737
private int httpPort;
3838
private int webPort;
39+
private int healthHAPort;
3940
private boolean startedHiveService = false;
4041
private List<String> addedProperties = new ArrayList<String>();
4142

42-
public AbstractHiveService(HiveConf hiveConf, String hostname, int binaryPort, int httpPort, int webPort) {
43+
public AbstractHiveService(HiveConf hiveConf, String hostname, int binaryPort, int httpPort, int webPort, int healthHAPort) {
4344
this.hiveConf = hiveConf;
4445
this.hostname = hostname;
4546
this.binaryPort = binaryPort;
4647
this.httpPort = httpPort;
4748
this.webPort = webPort;
49+
this.healthHAPort = healthHAPort;
4850
}
4951

5052
/**
@@ -142,6 +144,10 @@ public int getWebPort() {
142144
return webPort;
143145
}
144146

147+
public int getHealthHAPort() {
148+
return healthHAPort;
149+
}
150+
145151
public boolean isStarted() {
146152
return startedHiveService;
147153
}

‎itests/util/src/main/java/org/apache/hive/jdbc/miniHS2/MiniHS2.java

+3
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@ private MiniHS2(HiveConf hiveConf, MiniClusterType miniClusterType, boolean useM
292292
(usePortsFromConf ? hiveConf.getIntVar(HiveConf.ConfVars.HIVE_SERVER2_THRIFT_HTTP_PORT) : MetaStoreTestUtils
293293
.findFreePort()),
294294
(usePortsFromConf ? hiveConf.getIntVar(ConfVars.HIVE_SERVER2_WEBUI_PORT) : MetaStoreTestUtils
295+
.findFreePort()),
296+
(usePortsFromConf ? hiveConf.getIntVar(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT) : MetaStoreTestUtils
295297
.findFreePort()));
296298
hiveConf.setLongVar(ConfVars.HIVE_SERVER2_MAX_START_ATTEMPTS, 3l);
297299
hiveConf.setTimeVar(ConfVars.HIVE_SERVER2_SLEEP_INTERVAL_BETWEEN_START_ATTEMPTS, 10,
@@ -379,6 +381,7 @@ private MiniHS2(HiveConf hiveConf, MiniClusterType miniClusterType, boolean useM
379381
hiveConf.setIntVar(ConfVars.HIVE_SERVER2_THRIFT_PORT, getBinaryPort());
380382
hiveConf.setIntVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_PORT, getHttpPort());
381383
hiveConf.setIntVar(ConfVars.HIVE_SERVER2_WEBUI_PORT, getWebPort());
384+
hiveConf.setIntVar(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT, getHealthHAPort());
382385

383386
Path scratchDir = new Path(baseFsDir, "scratch");
384387
// Create root scratchdir with write all, so that user impersonation has no issues.

‎service/pom.xml

+5-1
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,11 @@
443443
<taskdef classname="org.apache.jasper.JspC" name="jspcompiler" classpathref="maven.compile.classpath"/>
444444
<mkdir dir="${build.webapps}/hiveserver2/WEB-INF"/>
445445
<jspcompiler uriroot="${src.webapps}/hiveserver2" outputdir="${generated.sources}/java" package="org.apache.hive.generated.hiveserver2" webxml="${build.webapps}/hiveserver2/WEB-INF/web.xml"/>
446-
</target>
446+
<mkdir dir="${build.webapps}/leader/WEB-INF"/>
447+
<copy todir="${build.webapps}/health-ha">
448+
<fileset dir="${src.webapps}/health-ha"/>
449+
</copy>
450+
</target>
447451
</configuration>
448452
<goals>
449453
<goal>run</goal>

‎service/src/java/org/apache/hive/service/server/HiveServer2.java

+101-85
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
import org.apache.hive.service.cli.thrift.ThriftBinaryCLIService;
120120
import org.apache.hive.service.cli.thrift.ThriftCLIService;
121121
import org.apache.hive.service.cli.thrift.ThriftHttpCLIService;
122+
import org.apache.hive.service.servlet.HS2HealthHAStatus;
122123
import org.apache.hive.service.servlet.HS2LeadershipStatus;
123124
import org.apache.hive.service.servlet.HS2Peers;
124125
import org.apache.hive.service.servlet.LDAPAuthenticationFilter;
@@ -402,90 +403,9 @@ public synchronized void init(HiveConf hiveConf) {
402403
// Set the default JspFactory to avoid NPE while opening the home page
403404
JspFactory.setDefaultFactory(new org.apache.jasper.runtime.JspFactoryImpl());
404405
}
405-
HttpServer.Builder builder = new HttpServer.Builder("hiveserver2");
406-
builder.setPort(webUIPort).setConf(hiveConf);
407-
builder.setHost(webHost);
408-
builder.setMaxThreads(
409-
hiveConf.getIntVar(ConfVars.HIVE_SERVER2_WEBUI_MAX_THREADS));
410-
builder.setAdmins(hiveConf.getVar(ConfVars.USERS_IN_ADMIN_ROLE));
411-
// SessionManager is initialized
412-
builder.setContextAttribute("hive.sm",
413-
cliService.getSessionManager());
414-
hiveConf.set("startcode",
415-
String.valueOf(System.currentTimeMillis()));
416-
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_SSL)) {
417-
String keyStorePath = hiveConf.getVar(
418-
ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PATH);
419-
if (StringUtils.isBlank(keyStorePath)) {
420-
throw new IllegalArgumentException(
421-
ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PATH.varname
422-
+ " Not configured for SSL connection");
423-
}
424-
builder.setKeyStorePassword(ShimLoader.getHadoopShims().getPassword(
425-
hiveConf, ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PASSWORD.varname));
426-
builder.setKeyStorePath(keyStorePath);
427-
builder.setKeyStoreType(hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_TYPE));
428-
builder.setKeyManagerFactoryAlgorithm(
429-
hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYMANAGERFACTORY_ALGORITHM));
430-
builder.setExcludeCiphersuites(
431-
hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_SSL_EXCLUDE_CIPHERSUITES));
432-
builder.setUseSSL(true);
433-
}
434-
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_SPNEGO)) {
435-
String spnegoPrincipal = hiveConf.getVar(
436-
ConfVars.HIVE_SERVER2_WEBUI_SPNEGO_PRINCIPAL);
437-
String spnegoKeytab = hiveConf.getVar(
438-
ConfVars.HIVE_SERVER2_WEBUI_SPNEGO_KEYTAB);
439-
if (StringUtils.isBlank(spnegoPrincipal) || StringUtils.isBlank(spnegoKeytab)) {
440-
throw new IllegalArgumentException(
441-
ConfVars.HIVE_SERVER2_WEBUI_SPNEGO_PRINCIPAL.varname
442-
+ "/" + ConfVars.HIVE_SERVER2_WEBUI_SPNEGO_KEYTAB.varname
443-
+ " Not configured for SPNEGO authentication");
444-
}
445-
builder.setSPNEGOPrincipal(spnegoPrincipal);
446-
builder.setSPNEGOKeytab(spnegoKeytab);
447-
builder.setUseSPNEGO(true);
448-
}
449-
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_ENABLE_CORS)) {
450-
builder.setEnableCORS(true);
451-
String allowedOrigins = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_ORIGINS);
452-
String allowedMethods = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_METHODS);
453-
String allowedHeaders = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_HEADERS);
454-
if (StringUtils.isBlank(allowedOrigins) || StringUtils.isBlank(allowedMethods) || StringUtils.isBlank(allowedHeaders)) {
455-
throw new IllegalArgumentException("CORS enabled. But " +
456-
ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_ORIGINS.varname + "/" +
457-
ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_METHODS.varname + "/" +
458-
ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_HEADERS.varname + "/" +
459-
" is not configured");
460-
}
461-
builder.setAllowedOrigins(allowedOrigins);
462-
builder.setAllowedMethods(allowedMethods);
463-
builder.setAllowedHeaders(allowedHeaders);
464-
LOG.info("CORS enabled - allowed-origins: {} allowed-methods: {} allowed-headers: {}", allowedOrigins,
465-
allowedMethods, allowedHeaders);
466-
}
467-
if(hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_XFRAME_ENABLED)){
468-
builder.configureXFrame(true).setXFrameOption(hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_XFRAME_VALUE));
469-
}
470-
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_PAM)) {
471-
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_SSL)) {
472-
String hiveServer2PamServices = hiveConf.getVar(ConfVars.HIVE_SERVER2_PAM_SERVICES);
473-
if (hiveServer2PamServices == null || hiveServer2PamServices.isEmpty()) {
474-
throw new IllegalArgumentException(ConfVars.HIVE_SERVER2_PAM_SERVICES.varname + " are not configured.");
475-
}
476-
builder.setPAMAuthenticator(pamAuthenticator == null ? new PamAuthenticator(hiveConf) : pamAuthenticator);
477-
builder.setUsePAM(true);
478-
} else if (hiveConf.getBoolVar(ConfVars.HIVE_IN_TEST)) {
479-
builder.setPAMAuthenticator(pamAuthenticator == null ? new PamAuthenticator(hiveConf) : pamAuthenticator);
480-
builder.setUsePAM(true);
481-
} else {
482-
throw new IllegalArgumentException(ConfVars.HIVE_SERVER2_WEBUI_USE_SSL.varname + " has false value. It is recommended to set to true when PAM is used.");
483-
}
484-
}
406+
HttpServer.Builder builder = initBuilder(webHost, webUIPort, "hiveserver2", "/", hiveConf);
485407
if (serviceDiscovery && activePassiveHA) {
486-
builder.setContextAttribute("hs2.isLeader", isLeader);
487-
builder.setContextAttribute("hs2.failover.callback", new FailoverHandlerCallback(hs2HARegistry));
488-
builder.setContextAttribute("hiveconf", hiveConf);
408+
addHAContextAttributes(builder, hiveConf);
489409
builder.addServlet("leader", HS2LeadershipStatus.class);
490410
builder.addServlet("peers", HS2Peers.class);
491411
}
@@ -505,10 +425,11 @@ public synchronized void init(HiveConf hiveConf) {
505425
if (ldapAuthService != null) {
506426
webServer.addServlet("login", "/login", new ServletHolder(new LoginServlet(ldapAuthService)));
507427
}
428+
initHealthHA(webServer, hiveConf);
508429
}
509430
}
510-
} catch (IOException ie) {
511-
throw new ServiceException(ie);
431+
} catch (Exception e) {
432+
throw new ServiceException(e);
512433
}
513434

514435
long otelExporterFrequency =
@@ -534,6 +455,101 @@ public synchronized void init(HiveConf hiveConf) {
534455
// Extra time for releasing the resources if timeout sets to 0
535456
ShutdownHookManager.addGracefulShutDownHook(() -> graceful_stop(), timeout == 0 ? 30 : timeout);
536457
}
458+
459+
private void addHAContextAttributes(HttpServer.Builder builder, HiveConf hiveConf) {
460+
builder.setContextAttribute("hs2.isLeader", isLeader);
461+
builder.setContextAttribute("hs2.failover.callback", new FailoverHandlerCallback(hs2HARegistry));
462+
builder.setContextAttribute("hiveconf", hiveConf);
463+
}
464+
465+
private HttpServer.Builder initBuilder(String webHost, int port, String name, String contextPath, HiveConf hiveConf) throws IOException {
466+
HttpServer.Builder builder = new HttpServer.Builder(name);
467+
builder.setPort(port).setConf(hiveConf);
468+
builder.setHost(webHost);
469+
builder.setContextPath(contextPath);
470+
builder.setMaxThreads(hiveConf.getIntVar(ConfVars.HIVE_SERVER2_WEBUI_MAX_THREADS));
471+
builder.setAdmins(hiveConf.getVar(ConfVars.USERS_IN_ADMIN_ROLE));
472+
// SessionManager is initialized
473+
builder.setContextAttribute("hive.sm", cliService.getSessionManager());
474+
hiveConf.set("startcode", String.valueOf(System.currentTimeMillis()));
475+
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_SSL)) {
476+
String keyStorePath = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PATH);
477+
if (StringUtils.isBlank(keyStorePath)) {
478+
throw new IllegalArgumentException(ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PATH.varname
479+
+ " Not configured for SSL connection");
480+
}
481+
builder.setKeyStorePassword(ShimLoader.getHadoopShims().getPassword(
482+
hiveConf, ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PASSWORD.varname));
483+
builder.setKeyStorePath(keyStorePath);
484+
builder.setKeyStoreType(hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_TYPE));
485+
builder.setKeyManagerFactoryAlgorithm(
486+
hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYMANAGERFACTORY_ALGORITHM));
487+
builder.setExcludeCiphersuites(hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_SSL_EXCLUDE_CIPHERSUITES));
488+
builder.setUseSSL(true);
489+
}
490+
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_SPNEGO)) {
491+
String spnegoPrincipal = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_SPNEGO_PRINCIPAL);
492+
String spnegoKeytab = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_SPNEGO_KEYTAB);
493+
if (StringUtils.isBlank(spnegoPrincipal) || StringUtils.isBlank(spnegoKeytab)) {
494+
throw new IllegalArgumentException(
495+
ConfVars.HIVE_SERVER2_WEBUI_SPNEGO_PRINCIPAL.varname
496+
+ "/" + ConfVars.HIVE_SERVER2_WEBUI_SPNEGO_KEYTAB.varname
497+
+ " Not configured for SPNEGO authentication");
498+
}
499+
builder.setSPNEGOPrincipal(spnegoPrincipal);
500+
builder.setSPNEGOKeytab(spnegoKeytab);
501+
builder.setUseSPNEGO(true);
502+
}
503+
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_ENABLE_CORS)) {
504+
builder.setEnableCORS(true);
505+
String allowedOrigins = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_ORIGINS);
506+
String allowedMethods = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_METHODS);
507+
String allowedHeaders = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_HEADERS);
508+
if (StringUtils.isBlank(allowedOrigins) || StringUtils.isBlank(allowedMethods) || StringUtils.isBlank(allowedHeaders)) {
509+
throw new IllegalArgumentException("CORS enabled. But " +
510+
ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_ORIGINS.varname + "/" +
511+
ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_METHODS.varname + "/" +
512+
ConfVars.HIVE_SERVER2_WEBUI_CORS_ALLOWED_HEADERS.varname + "/" +
513+
" is not configured");
514+
}
515+
builder.setAllowedOrigins(allowedOrigins);
516+
builder.setAllowedMethods(allowedMethods);
517+
builder.setAllowedHeaders(allowedHeaders);
518+
LOG.info("CORS enabled - allowed-origins: {} allowed-methods: {} allowed-headers: {}", allowedOrigins,
519+
allowedMethods, allowedHeaders);
520+
}
521+
if(hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_XFRAME_ENABLED)){
522+
builder.configureXFrame(true).setXFrameOption(hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_XFRAME_VALUE));
523+
}
524+
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_PAM)) {
525+
if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_SSL)) {
526+
String hiveServer2PamServices = hiveConf.getVar(ConfVars.HIVE_SERVER2_PAM_SERVICES);
527+
if (hiveServer2PamServices == null || hiveServer2PamServices.isEmpty()) {
528+
throw new IllegalArgumentException(ConfVars.HIVE_SERVER2_PAM_SERVICES.varname + " are not configured.");
529+
}
530+
builder.setPAMAuthenticator(pamAuthenticator == null ? new PamAuthenticator(hiveConf) : pamAuthenticator);
531+
builder.setUsePAM(true);
532+
} else if (hiveConf.getBoolVar(ConfVars.HIVE_IN_TEST)) {
533+
builder.setPAMAuthenticator(pamAuthenticator == null ? new PamAuthenticator(hiveConf) : pamAuthenticator);
534+
builder.setUsePAM(true);
535+
} else {
536+
throw new IllegalArgumentException(ConfVars.HIVE_SERVER2_WEBUI_USE_SSL.varname + " has false value. It is recommended to set to true when PAM is used.");
537+
}
538+
}
539+
540+
return builder;
541+
}
542+
543+
private void initHealthHA(HttpServer webServer, HiveConf hiveConf) throws Exception {
544+
if (serviceDiscovery && activePassiveHA) {
545+
String webHost = hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_BIND_HOST);
546+
int healthCheckPort = hiveConf.getIntVar(ConfVars.HIVE_SERVER2_HEALTH_HA_PORT);
547+
HttpServer.Builder builder = initBuilder(webHost, healthCheckPort, "health-ha", "/health-ha", hiveConf);
548+
addHAContextAttributes(builder, hiveConf);
549+
builder.addServlet("leader", HS2HealthHAStatus.class);
550+
webServer.addWebApp(builder);
551+
}
552+
}
537553

538554
private void logCompactionParameters(HiveConf hiveConf) {
539555
LOG.info("Compaction HS2 parameters:");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hive.service.servlet;
20+
21+
import javax.servlet.http.HttpServletRequest;
22+
import javax.servlet.http.HttpServletResponse;
23+
import java.io.IOException;
24+
25+
public class HS2HealthHAStatus extends HS2LeadershipStatus {
26+
27+
@Override
28+
public void doDelete(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
29+
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "DELETE method is not allowed");
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
5+
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
6+
version="3.1"
7+
metadata-complete="false">
8+
9+
</web-app>
10+

0 commit comments

Comments
 (0)
Please sign in to comment.