|
@@ -32,6 +32,7 @@ import java.util.EnumSet;
|
|
|
import java.util.Enumeration;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
+import java.util.Set;
|
|
|
|
|
|
import javax.servlet.http.Cookie;
|
|
|
import javax.servlet.http.HttpServlet;
|
|
@@ -45,8 +46,6 @@ import org.apache.commons.httpclient.HttpMethod;
|
|
|
import org.apache.commons.httpclient.cookie.CookiePolicy;
|
|
|
import org.apache.commons.httpclient.methods.GetMethod;
|
|
|
import org.apache.commons.httpclient.params.HttpClientParams;
|
|
|
-import org.apache.commons.logging.Log;
|
|
|
-import org.apache.commons.logging.LogFactory;
|
|
|
import org.apache.hadoop.io.IOUtils;
|
|
|
import org.apache.hadoop.yarn.api.records.ApplicationId;
|
|
|
import org.apache.hadoop.yarn.api.records.ApplicationReport;
|
|
@@ -59,13 +58,20 @@ import org.apache.hadoop.yarn.util.TrackingUriPlugin;
|
|
|
import org.apache.hadoop.yarn.webapp.MimeType;
|
|
|
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet;
|
|
|
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
|
|
public class WebAppProxyServlet extends HttpServlet {
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
- private static final Log LOG = LogFactory.getLog(WebAppProxyServlet.class);
|
|
|
- private static final HashSet<String> passThroughHeaders =
|
|
|
- new HashSet<String>(Arrays.asList("User-Agent", "Accept", "Accept-Encoding",
|
|
|
- "Accept-Language", "Accept-Charset"));
|
|
|
+ private static final Logger LOG = LoggerFactory.getLogger(
|
|
|
+ WebAppProxyServlet.class);
|
|
|
+ private static final Set<String> passThroughHeaders =
|
|
|
+ new HashSet<>(Arrays.asList(
|
|
|
+ "User-Agent",
|
|
|
+ "Accept",
|
|
|
+ "Accept-Encoding",
|
|
|
+ "Accept-Language",
|
|
|
+ "Accept-Charset"));
|
|
|
|
|
|
public static final String PROXY_USER_COOKIE_NAME = "proxy-user";
|
|
|
|
|
@@ -83,15 +89,14 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
}
|
|
|
|
|
|
public HTML<WebAppProxyServlet._> html() {
|
|
|
- return new HTML<WebAppProxyServlet._>("html", null, EnumSet.of(EOpt.ENDTAG));
|
|
|
+ return new HTML<>("html", null, EnumSet.of(EOpt.ENDTAG));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Default constructor
|
|
|
*/
|
|
|
- public WebAppProxyServlet()
|
|
|
- {
|
|
|
+ public WebAppProxyServlet() {
|
|
|
super();
|
|
|
conf = new YarnConfiguration();
|
|
|
this.trackingUriPlugins =
|
|
@@ -109,12 +114,7 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
*/
|
|
|
private static void notFound(HttpServletResponse resp, String message)
|
|
|
throws IOException {
|
|
|
- resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
|
|
- resp.setContentType(MimeType.HTML);
|
|
|
- Page p = new Page(resp.getWriter());
|
|
|
- p.html().
|
|
|
- h1(message).
|
|
|
- _();
|
|
|
+ ProxyUtils.notFound(resp, message);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -133,7 +133,8 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
resp.setContentType(MimeType.HTML);
|
|
|
Page p = new Page(resp.getWriter());
|
|
|
p.html().
|
|
|
- h1("WARNING: The following page may not be safe!").h3().
|
|
|
+ h1("WARNING: The following page may not be safe!").
|
|
|
+ h3().
|
|
|
_("click ").a(link, "here").
|
|
|
_(" to continue to an Application Master web interface owned by ", user).
|
|
|
_().
|
|
@@ -163,7 +164,7 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
HostConfiguration config = new HostConfiguration();
|
|
|
InetAddress localAddress = InetAddress.getByName(proxyHost);
|
|
|
if (LOG.isDebugEnabled()) {
|
|
|
- LOG.debug("local InetAddress for proxy host: " + localAddress.toString());
|
|
|
+ LOG.debug("local InetAddress for proxy host: {}", localAddress);
|
|
|
}
|
|
|
config.setLocalAddress(localAddress);
|
|
|
HttpMethod method = new GetMethod(uri.getEscapedURI());
|
|
@@ -174,15 +175,17 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
String name = names.nextElement();
|
|
|
if(passThroughHeaders.contains(name)) {
|
|
|
String value = req.getHeader(name);
|
|
|
- LOG.debug("REQ HEADER: "+name+" : "+value);
|
|
|
+ if (LOG.isDebugEnabled()) {
|
|
|
+ LOG.debug("REQ HEADER: {} : {}", name, value);
|
|
|
+ }
|
|
|
method.setRequestHeader(name, value);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
String user = req.getRemoteUser();
|
|
|
- if(user != null && !user.isEmpty()) {
|
|
|
- method.setRequestHeader("Cookie",PROXY_USER_COOKIE_NAME+"="+
|
|
|
- URLEncoder.encode(user, "ASCII"));
|
|
|
+ if (user != null && !user.isEmpty()) {
|
|
|
+ method.setRequestHeader("Cookie",
|
|
|
+ PROXY_USER_COOKIE_NAME + "=" + URLEncoder.encode(user, "ASCII"));
|
|
|
}
|
|
|
OutputStream out = resp.getOutputStream();
|
|
|
try {
|
|
@@ -190,11 +193,11 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
for(Header header : method.getResponseHeaders()) {
|
|
|
resp.setHeader(header.getName(), header.getValue());
|
|
|
}
|
|
|
- if(c != null) {
|
|
|
+ if (c != null) {
|
|
|
resp.addCookie(c);
|
|
|
}
|
|
|
InputStream in = method.getResponseBodyAsStream();
|
|
|
- if(in != null) {
|
|
|
+ if (in != null) {
|
|
|
IOUtils.copyBytes(in, out, 4096, true);
|
|
|
}
|
|
|
} finally {
|
|
@@ -216,8 +219,7 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
private boolean isSecurityEnabled() {
|
|
|
Boolean b = (Boolean) getServletContext()
|
|
|
.getAttribute(WebAppProxy.IS_SECURITY_ENABLED_ATTRIBUTE);
|
|
|
- if(b != null) return b;
|
|
|
- return false;
|
|
|
+ return b != null ? b : false;
|
|
|
}
|
|
|
|
|
|
private ApplicationReport getApplicationReport(ApplicationId id)
|
|
@@ -238,15 +240,14 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
String userApprovedParamS =
|
|
|
req.getParameter(ProxyUriUtils.PROXY_APPROVAL_PARAM);
|
|
|
boolean userWasWarned = false;
|
|
|
- boolean userApproved =
|
|
|
- (userApprovedParamS != null && Boolean.valueOf(userApprovedParamS));
|
|
|
+ boolean userApproved = Boolean.valueOf(userApprovedParamS);
|
|
|
boolean securityEnabled = isSecurityEnabled();
|
|
|
final String remoteUser = req.getRemoteUser();
|
|
|
final String pathInfo = req.getPathInfo();
|
|
|
|
|
|
- String parts[] = pathInfo.split("/", 3);
|
|
|
+ String[] parts = pathInfo.split("/", 3);
|
|
|
if(parts.length < 2) {
|
|
|
- LOG.warn(remoteUser+" Gave an invalid proxy path "+pathInfo);
|
|
|
+ LOG.warn("{} gave an invalid proxy path {}", remoteUser, pathInfo);
|
|
|
notFound(resp, "Your path appears to be formatted incorrectly.");
|
|
|
return;
|
|
|
}
|
|
@@ -255,9 +256,9 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
String rest = parts.length > 2 ? parts[2] : "";
|
|
|
ApplicationId id = Apps.toAppID(appId);
|
|
|
if(id == null) {
|
|
|
- LOG.warn(req.getRemoteUser()+" Attempting to access "+appId+
|
|
|
- " that is invalid");
|
|
|
- notFound(resp, appId+" appears to be formatted incorrectly.");
|
|
|
+ LOG.warn("{} attempting to access {} that is invalid",
|
|
|
+ remoteUser, appId);
|
|
|
+ notFound(resp, appId + " appears to be formatted incorrectly.");
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -277,35 +278,34 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
|
|
|
boolean checkUser = securityEnabled && (!userWasWarned || !userApproved);
|
|
|
|
|
|
- ApplicationReport applicationReport = null;
|
|
|
+ ApplicationReport applicationReport;
|
|
|
try {
|
|
|
applicationReport = getApplicationReport(id);
|
|
|
} catch (ApplicationNotFoundException e) {
|
|
|
applicationReport = null;
|
|
|
}
|
|
|
if(applicationReport == null) {
|
|
|
- LOG.warn(req.getRemoteUser()+" Attempting to access "+id+
|
|
|
- " that was not found");
|
|
|
+ LOG.warn("{} attempting to access {} that was not found",
|
|
|
+ remoteUser, id);
|
|
|
|
|
|
URI toFetch =
|
|
|
ProxyUriUtils
|
|
|
.getUriFromTrackingPlugins(id, this.trackingUriPlugins);
|
|
|
- if (toFetch != null)
|
|
|
- {
|
|
|
- resp.sendRedirect(resp.encodeRedirectURL(toFetch.toString()));
|
|
|
+ if (toFetch != null) {
|
|
|
+ ProxyUtils.sendRedirect(req, resp, toFetch.toString());
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- notFound(resp, "Application "+appId+" could not be found, " +
|
|
|
- "please try the history server");
|
|
|
+ notFound(resp, "Application " + appId + " could not be found, " +
|
|
|
+ "please try the history server");
|
|
|
return;
|
|
|
}
|
|
|
String original = applicationReport.getOriginalTrackingUrl();
|
|
|
- URI trackingUri = null;
|
|
|
+ URI trackingUri;
|
|
|
// fallback to ResourceManager's app page if no tracking URI provided
|
|
|
if(original == null || original.equals("N/A")) {
|
|
|
- resp.sendRedirect(resp.encodeRedirectURL(
|
|
|
- StringHelper.pjoin(rmAppPageUrlBase, id.toString())));
|
|
|
+ ProxyUtils.sendRedirect(req, resp,
|
|
|
+ StringHelper.pjoin(rmAppPageUrlBase, id.toString()));
|
|
|
return;
|
|
|
} else {
|
|
|
if (ProxyUriUtils.getSchemeFromUrl(original).isEmpty()) {
|
|
@@ -318,8 +318,9 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
|
|
|
String runningUser = applicationReport.getUser();
|
|
|
if(checkUser && !runningUser.equals(remoteUser)) {
|
|
|
- LOG.info("Asking "+remoteUser+" if they want to connect to the " +
|
|
|
- "app master GUI of "+appId+" owned by "+runningUser);
|
|
|
+ LOG.info("Asking {} if they want to connect to the "
|
|
|
+ + "app master GUI of {} owned by {}",
|
|
|
+ remoteUser, appId, runningUser);
|
|
|
warnUserPage(resp, ProxyUriUtils.getPathAndQuery(id, rest,
|
|
|
req.getQueryString(), true), runningUser, id);
|
|
|
return;
|
|
@@ -329,29 +330,45 @@ public class WebAppProxyServlet extends HttpServlet {
|
|
|
StringHelper.ujoin(trackingUri.getPath(), rest), req.getQueryString(),
|
|
|
null);
|
|
|
|
|
|
- LOG.info(req.getRemoteUser()+" is accessing unchecked "+toFetch+
|
|
|
- " which is the app master GUI of "+appId+" owned by "+runningUser);
|
|
|
+ LOG.info("{} is accessing unchecked {}"
|
|
|
+ + " which is the app master GUI of {} owned by {}",
|
|
|
+ remoteUser, toFetch, appId, runningUser);
|
|
|
|
|
|
- switch(applicationReport.getYarnApplicationState()) {
|
|
|
- case KILLED:
|
|
|
- case FINISHED:
|
|
|
- case FAILED:
|
|
|
- resp.sendRedirect(resp.encodeRedirectURL(toFetch.toString()));
|
|
|
- return;
|
|
|
+ switch (applicationReport.getYarnApplicationState()) {
|
|
|
+ case KILLED:
|
|
|
+ case FINISHED:
|
|
|
+ case FAILED:
|
|
|
+ ProxyUtils.sendRedirect(req, resp, toFetch.toString());
|
|
|
+ return;
|
|
|
+ default:
|
|
|
+ // fall out of the switch
|
|
|
}
|
|
|
Cookie c = null;
|
|
|
- if(userWasWarned && userApproved) {
|
|
|
+ if (userWasWarned && userApproved) {
|
|
|
c = makeCheckCookie(id, true);
|
|
|
}
|
|
|
proxyLink(req, resp, toFetch, c, getProxyHost());
|
|
|
|
|
|
- } catch(URISyntaxException e) {
|
|
|
+ } catch(URISyntaxException | YarnException e) {
|
|
|
throw new IOException(e);
|
|
|
- } catch (YarnException e) {
|
|
|
- throw new IOException(e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * This method is used by Java object deserialization, to fill in the
|
|
|
+ * transient {@link #trackingUriPlugins} field.
|
|
|
+ * See {@link ObjectInputStream#defaultReadObject()}
|
|
|
+ * <p>
|
|
|
+ * <I>Do not remove</I>
|
|
|
+ * <p>
|
|
|
+ * Yarn isn't currently serializing this class, but findbugs
|
|
|
+ * complains in its absence.
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * @param input source
|
|
|
+ * @throws IOException IO failure
|
|
|
+ * @throws ClassNotFoundException classloader fun
|
|
|
+ */
|
|
|
private void readObject(ObjectInputStream input)
|
|
|
throws IOException, ClassNotFoundException {
|
|
|
input.defaultReadObject();
|