Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion hbase-http/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@
</dependency>
<dependency>
<groupId>org.apache.hbase.thirdparty</groupId>
<artifactId>hbase-shaded-jetty</artifactId>
<artifactId>hbase-shaded-jetty-12-plus-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hbase.thirdparty</groupId>
<artifactId>hbase-shaded-jetty-12-plus-ee8</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hbase.thirdparty</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import org.apache.yetus.audience.InterfaceAudience;
import org.apache.yetus.audience.InterfaceStability;

import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.DefaultServlet;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.DefaultServlet;

/**
* General servlet which is admin-authorized.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@
import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableMap;
import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.DefaultServlet;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.FilterHolder;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.FilterMapping;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.ServletContextHandler;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.ServletHolder;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.webapp.WebAppContext;
import org.apache.hbase.thirdparty.org.eclipse.jetty.http.HttpVersion;
import org.apache.hbase.thirdparty.org.eclipse.jetty.server.Handler;
import org.apache.hbase.thirdparty.org.eclipse.jetty.server.HttpConfiguration;
Expand All @@ -84,18 +90,10 @@
import org.apache.hbase.thirdparty.org.eclipse.jetty.server.SymlinkAllowedResourceAliasChecker;
import org.apache.hbase.thirdparty.org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.apache.hbase.thirdparty.org.eclipse.jetty.server.handler.ErrorHandler;
import org.apache.hbase.thirdparty.org.eclipse.jetty.server.handler.HandlerCollection;
import org.apache.hbase.thirdparty.org.eclipse.jetty.server.handler.RequestLogHandler;
import org.apache.hbase.thirdparty.org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.DefaultServlet;
import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.FilterHolder;
import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.FilterMapping;
import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.ServletContextHandler;
import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.ServletHolder;
import org.apache.hbase.thirdparty.org.eclipse.jetty.util.MultiException;
import org.apache.hbase.thirdparty.org.eclipse.jetty.util.ExceptionUtil;
import org.apache.hbase.thirdparty.org.eclipse.jetty.util.ssl.SslContextFactory;
import org.apache.hbase.thirdparty.org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.apache.hbase.thirdparty.org.eclipse.jetty.webapp.WebAppContext;
import org.apache.hbase.thirdparty.org.glassfish.jersey.server.ResourceConfig;
import org.apache.hbase.thirdparty.org.glassfish.jersey.servlet.ServletContainer;

Expand Down Expand Up @@ -654,23 +652,21 @@ private void initializeWebServer(String name, String hostName, Configuration con

Preconditions.checkNotNull(webAppContext);

HandlerCollection handlerCollection = new HandlerCollection();
Handler.Sequence handlers = new Handler.Sequence();

ContextHandlerCollection contexts = new ContextHandlerCollection();
RequestLog requestLog = HttpRequestLog.getRequestLog(name);

if (requestLog != null) {
RequestLogHandler requestLogHandler = new RequestLogHandler();
requestLogHandler.setRequestLog(requestLog);
handlerCollection.addHandler(requestLogHandler);
webServer.setRequestLog(requestLog);
}

final String appDir = getWebAppsPath(name);

handlerCollection.addHandler(contexts);
handlerCollection.addHandler(webAppContext);
handlers.addHandler(contexts);
handlers.addHandler(webAppContext);

webServer.setHandler(handlerCollection);
webServer.setHandler(handlers);

webAppContext.setAttribute(ADMINS_ACL, adminsAcl);

Expand Down Expand Up @@ -715,8 +711,9 @@ private void initializeWebServer(String name, String hostName, Configuration con
// Check if disable stack trace property is configured
if (!conf.getBoolean(HTTP_UI_SHOW_STACKTRACE_KEY, true)) {
// Disable stack traces for server errors in UI
webServer.setErrorHandler(new ErrorHandler());
webServer.getErrorHandler().setShowStacks(false);
ErrorHandler errorHandler = new ErrorHandler();
errorHandler.setShowStacks(false);
webServer.setErrorHandler(errorHandler);
// Disable stack traces for web app errors in UI
webAppContext.getErrorHandler().setShowStacks(false);
}
Expand Down Expand Up @@ -841,7 +838,8 @@ private void configureAliasChecks(ServletContextHandler context, boolean shouldS
if (context.getAliasChecks().stream().anyMatch(aliasCheckerClass::isInstance)) {
LOG.debug("{} is already part of alias check list", aliasCheckerClass.getName());
} else {
context.addAliasCheck(new SymlinkAllowedResourceAliasChecker(context));
context
.addAliasCheck(new SymlinkAllowedResourceAliasChecker(context.getCoreContextHandler()));
LOG.debug("{} added to the alias check list", aliasCheckerClass.getName());
}
LOG.info("Serving aliases allowed for /logs context");
Expand Down Expand Up @@ -1258,14 +1256,14 @@ public void start() throws IOException {
} catch (IOException ex) {
LOG.info("HttpServer.start() threw a non Bind IOException", ex);
throw ex;
} catch (MultiException ex) {
LOG.info("HttpServer.start() threw a MultiException", ex);
} catch (Exception ex) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So Jetty just throws Exception here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@NihalJain NihalJain Mar 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HBase uses Jetty's method to check and throw as below.

    /**
     * Throw a {@link Throwable} as a checked {@link Exception} if it
     * cannot be thrown as unchecked.
     * @param throwable The {@link Throwable} to throw or null.
     * @throws Error If the passed {@link Throwable} is an {@link Error}.
     * @throws Exception Otherwise, if the passed {@link Throwable} is not null.
     */
    public static void ifExceptionThrow(Throwable throwable)
        throws Error, Exception
    {
        if (throwable == null)
            return;
        if (throwable instanceof Error error)
            throw error;
        if (throwable instanceof Exception exception)
            throw exception;
        throw new RuntimeException(throwable);
    }

Refer https://github.com/jetty/jetty.project/blob/ccdbe1742eac511914869a53f0efc2075775d0b8/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/ExceptionUtil.java#L81

Older impl:

    /**
     * Throw a multiexception.
     * If this multi exception is empty then no action is taken. If it
     * contains a single exception that is thrown, otherwise the this
     * multi exception is thrown.
     *
     * @throws Exception the Error or Exception if nested is 1, or the MultiException itself if nested is more than 1.
     */
    public void ifExceptionThrow()
        throws Exception
    {
        if (nested == null)
            return;

        switch (nested.size())
        {
            case 0:
                break;
            case 1:
                Throwable th = nested.get(0);
                if (th instanceof Error)
                    throw (Error)th;
                if (th instanceof Exception)
                    throw (Exception)th;
                throw new MultiException(nested);
            default:
                throw new MultiException(nested);
        }
    }

Refer https://github.com/jetty/jetty.project/blob/e3fa9466633db6bf36e0eb0d17e3de166c788ede/jetty-util/src/main/java/org/eclipse/jetty/util/MultiException.java#L104

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should catch Error as well now? WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only value we add here is logging.
Maybe just catch, log and re-throw throwable ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or don't catch and rely on the other catch block ?

LOG.info("HttpServer.start() threw a Exception", ex);
throw ex;
}
// Make sure there is no handler failures.
Handler[] handlers = webServer.getHandlers();
for (int i = 0; i < handlers.length; i++) {
if (handlers[i].isFailed()) {
List<Handler> handlers = webServer.getHandlers();
for (Handler handler : handlers) {
if (handler.isFailed()) {
throw new IOException("Problem in starting http server. Server handlers failed");
}
}
Expand Down Expand Up @@ -1335,7 +1333,7 @@ void openListeners() throws Exception {
* stop the server
*/
public void stop() throws Exception {
MultiException exception = null;
ExceptionUtil.MultiException exception = null;
for (ListenerInfo li : listeners) {
if (!li.isManaged) {
continue;
Expand Down Expand Up @@ -1372,9 +1370,10 @@ public void stop() throws Exception {

}

private MultiException addMultiException(MultiException exception, Exception e) {
private ExceptionUtil.MultiException addMultiException(ExceptionUtil.MultiException exception,
Exception e) {
if (exception == null) {
exception = new MultiException();
exception = new ExceptionUtil.MultiException();
}
exception.add(e);
return exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
import org.apache.hadoop.conf.Configuration;
import org.apache.yetus.audience.InterfaceAudience;

import org.apache.hbase.thirdparty.org.eclipse.jetty.security.ConstraintMapping;
import org.apache.hbase.thirdparty.org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.FilterHolder;
import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.ServletContextHandler;
import org.apache.hbase.thirdparty.org.eclipse.jetty.util.security.Constraint;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.nested.ServletConstraint;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.security.ConstraintMapping;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.security.ConstraintSecurityHandler;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.FilterHolder;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.ServletContextHandler;

/**
* HttpServer utility.
Expand All @@ -43,7 +43,7 @@ public final class HttpServerUtil {
*/
public static void constrainHttpMethods(ServletContextHandler ctxHandler,
boolean allowOptionsMethod) {
Constraint c = new Constraint();
ServletConstraint c = new ServletConstraint();
c.setAuthenticate(true);

ConstraintMapping cmt = new ConstraintMapping();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import org.apache.yetus.audience.InterfaceAudience;

import org.apache.hbase.thirdparty.com.google.common.net.HostAndPort;
import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.ServletHolder;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.ServletHolder;

/**
* Create a Jetty embedded server to answer http requests. The primary goal is to serve up status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.hbase.thirdparty.org.eclipse.jetty.servlet.DefaultServlet;
import org.apache.hbase.thirdparty.org.eclipse.jetty.ee8.servlet.DefaultServlet;

/**
* Servlet to serve files generated by {@link ProfileServlet}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
import org.apache.hadoop.security.ssl.SSLFactory;
import org.apache.hadoop.util.HttpExceptionUtils;
import org.apache.hadoop.util.ServletUtil;
import org.apache.hadoop.util.Tool;
import org.apache.yetus.audience.InterfaceAudience;
Expand Down Expand Up @@ -267,7 +266,11 @@ private void process(String urlString) throws Exception {

HttpURLConnection connection = connect(url);

HttpExceptionUtils.validateResponse(connection, 200);
// We now use the validateResponse method of hbase to handle for HTML response,
// as with Jetty 12: getResponseMessage() returns "Precondition Failed" vs
// "Modification of logger protected.org.apache.hadoop.hbase.http.log.TestLogLevel is
// disallowed in configuration" in Jetty 9
LogLevelExceptionUtils.validateResponse(connection, 200);

// read from the servlet

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.http.log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.yetus.audience.InterfaceAudience;

/**
* HTTP utility class to help propagate server side exception in log level servlet to the client
* over HTTP (HTML payload) It parses HTTP client connections and recreates the exception.
*/
@InterfaceAudience.Private
public class LogLevelExceptionUtils {

private static void throwEx(Throwable ex) {
LogLevelExceptionUtils.<RuntimeException> throwException(ex);
}

@SuppressWarnings("unchecked")
private static <E extends Throwable> void throwException(Throwable ex) throws E {
throw (E) ex;
}

/**
* Validates the status of an <code>HttpURLConnection</code> against an expected HTTP status code.
* If the current status code is not the expected one it throws an exception with a detail message
* using Server side error messages if available.
* <p>
* <b>NOTE: This is an adapted version of the original method in HttpServerUtil.java of Hadoop,
* but we handle for HTML response.
* @param conn the <code>HttpURLConnection</code>.
* @param expectedStatus the expected HTTP status code.
* @throws IOException thrown if the current status code does not match the expected one.
*/
@SuppressWarnings("unchecked")
public static void validateResponse(HttpURLConnection conn, int expectedStatus)
throws IOException {
if (conn.getResponseCode() != expectedStatus) {
Exception toThrow = null;

try (InputStream es = conn.getErrorStream()) {
if (es != null) {
try (InputStreamReader isr = new InputStreamReader(es, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)) {
final String errorAsHtml = reader.lines().collect(Collectors.joining("\n"));

final String status = extractValue(errorAsHtml, "<th>STATUS:</th><td>(\\d+)</td>");
final String message = extractValue(errorAsHtml, "<th>MESSAGE:</th><td>([^<]+)</td>");
final String uri = extractValue(errorAsHtml, "<th>URI:</th><td>([^<]+)</td>");
final String exception = extractValue(errorAsHtml, "<title>([^<]+)</title>");

toThrow = new IOException(
String.format("HTTP status [%s], message [%s], URL [%s], exception [%s]", status,
message, uri, exception));
}
}
} catch (Exception ex) {
toThrow =
new IOException(String.format("HTTP status [%d], message [%s], URL [%s], exception [%s]",
conn.getResponseCode(), conn.getResponseMessage(), conn.getURL(), ex), ex);
}
if (toThrow != null) {
throwEx(toThrow);
}
}
}

private static String extractValue(String html, String regex) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(html);
if (matcher.find()) {
return matcher.group(1);
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ public void testRequestQuoterWithNotNull() {

@SuppressWarnings("unchecked")
private static Map<String, Object> parse(String jsonString) {
return (Map<String, Object>) JSON.parse(jsonString);
return (Map<String, Object>) new JSON().fromJSON(jsonString);
}

@Test
Expand Down Expand Up @@ -615,6 +615,9 @@ private HttpServer checkBindAddress(String host, int port, boolean findPort) thr
ServerConnector listener = server.getServerConnectors().get(0);

assertEquals(port, listener.getPort());
// We are doing this as otherwise testBindAddress fails, not sure how we were even starting
// server in jetty 9 without this call
server.start();
// verify hostname is what was given
server.openListeners();
assertEquals(host, server.getConnectorAddress(0).getHostName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public void testWriteJson() throws Exception {
Set<String> programSet = new HashSet<>();
programSet.add("programatically");
programSet.add("programmatically");
Object parsed = JSON.parse(json);
Object parsed = new JSON().fromJSON(json);
Object[] properties = ((Map<String, Object[]>) parsed).get("properties");
for (Object o : properties) {
Map<String, Object> propertyInfo = (Map<String, Object>) o;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,10 @@ private boolean validateCommand(String[] args) {
* @throws Exception if unable to create or start a Jetty server
*/
private HttpServer createServer(String protocol, boolean isSpnego) throws Exception {
HttpServer.Builder builder = new HttpServer.Builder().setName("..")
// Changed to "" as ".." moves it a steps back in path because the path is relative to the
// current working directory. throws "java.lang.IllegalArgumentException: Base Resource is not
// valid: hbase-http/target/test-classes/static" as it is not able to find the static folder.
HttpServer.Builder builder = new HttpServer.Builder().setName("")
.addEndpoint(new URI(protocol + "://localhost:0")).setFindPort(true).setConf(serverConf);
if (isSpnego) {
// Set up server Kerberos credentials.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public Response get(@PathParam(PATH) @DefaultValue("UNKNOWN_" + PATH) final Stri
final Map<String, Object> m = new TreeMap<>();
m.put(PATH, path);
m.put(OP, op);
final String js = JSON.toString(m);
final String js = new JSON().toJSON(m);
return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
}
}
Loading