diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyUtils.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyUtils.java
index 7d61f74..21169d8 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyUtils.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/ProxyUtils.java
@@ -25,6 +25,7 @@
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
+import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -40,6 +41,19 @@
public static final String E_HTTP_HTTPS_ONLY =
"This filter only works for HTTP/HTTPS";
public static final String LOCATION = "Location";
+ public static final String BODY_HAS_MOVED = "Content has moved ";
+ public static final String TITLE_APPLICATION_COMPLETED =
+ "Application has finished";
+ public static final String BODY_COMPLETED = "Logs available ";
+ /**
+ * There's no web UI. This may change
+ */
+ public static final String TITLE_NO_REGISTERED_WEB_UI =
+ "No Application Web UI";
+ public static final String BODY_NO_REGISTERED_WEB_UI =
+ "This application has no registered Web UI";
+ public static final String TITLE_UNSAFE_LINK =
+ "WARNING: The following page may not be safe!";
public static class _ implements Hamlet._ {
//Empty
@@ -54,7 +68,7 @@
return new HTML<>("html", null, EnumSet.of(EOpt.ENDTAG));
}
}
-
+
/**
* Handle redirects with a status code that can in future support verbs other
* than GET, thus supporting full REST functionality.
@@ -62,41 +76,76 @@
* The target URL is included in the redirect text returned
*
* At the end of this method, the output stream is closed.
- *
* @param request request (hence: the verb and any other information
* relevant to a redirect)
* @param response the response
* @param target the target URL -unencoded
+ * @param failureTarget true if target is a result of some failure
*
*/
public static void sendRedirect(HttpServletRequest request,
HttpServletResponse response,
String target)
throws IOException {
+ sendRedirect(request, response, target,
+ false, "Moved", BODY_HAS_MOVED);
+ }
+
+ /**
+ * Handle redirects with a status code that can in future support verbs other
+ * than GET, thus supporting full REST functionality.
+ *
+ * The target URL is included in the redirect text returned
+ *
+ * At the end of this method, the output stream is closed.
+ * @param request request (hence: the verb and any other information
+ * relevant to a redirect)
+ * @param response the response
+ * @param target the target URL -unencoded
+ * @param failureTarget true if target is a result of some failure
+ *
+ */
+ public static void sendRedirect(HttpServletRequest request,
+ HttpServletResponse response,
+ String target,
+ boolean failureTarget,
+ String title,
+ String text)
+ throws IOException {
+ String method = request.getMethod();
if (LOG.isDebugEnabled()) {
LOG.debug("Redirecting {} {} to {}",
- request.getMethod(),
+ method,
request.getRequestURI(),
target);
}
String location = response.encodeRedirectURL(target);
- response.setStatus(HttpServletResponse.SC_FOUND);
response.setHeader(LOCATION, location);
+ int status;
+ if (!failureTarget) {
+ status = statusCode(method);
+ } else {
+ // app is terminated. Here policy is
+ // simple 302 on GET/HEAD (to logs etc)
+ // and hard 404 failure of other verbs
+ status = isGetRequest(method) ? HttpServletResponse.SC_FOUND
+ : HttpServletResponse.SC_NOT_FOUND;
+ }
+ response.setStatus(status);
response.setContentType(MimeType.HTML);
PrintWriter writer = response.getWriter();
Page p = new Page(writer);
p.html()
- .head().title("Moved")._()
+ .head().title(title)._()
.body()
- .h1("Moved")
+ .h1(title)
.div()
- ._("Content has moved ")
+ ._(text)
.a(location, "here")._()
._()._();
writer.close();
}
-
/**
* Output 404 with appropriate message.
* @param resp the http response.
@@ -124,4 +173,84 @@ public static void rejectNonHttpRequests(ServletRequest req) throws
throw new ServletException(E_HTTP_HTTPS_ONLY);
}
}
+
+ /**
+ * Test for the method being an idempotent "GET" operation (i.e. GET or HEAD)
+ * @param method method
+ * @return true if the method is considered a GET/HEAD query.
+ */
+ public static boolean isGetRequest(String method) {
+ return "HEAD".equals(method) || "GET".equals(method);
+ }
+
+ /**
+ /**
+ * Test for the request being an idempotent "GET" operation (i.e. GET or HEAD)
+ * @param method method
+ * @param request incoming request
+ * @return true if the request is considered a GET/HEAD query.
+ */
+ public static boolean isGetRequest(HttpServletRequest request) {
+ return isGetRequest(request.getMethod());
+ }
+
+
+ /**
+ * Create the status code appropriat for a given method
+ * @param method method
+ * @return status code
+ */
+ public static int statusCode(String method) {
+ return isGetRequest(method) ?
+ HttpServletResponse.SC_FOUND
+ : HttpServletResponse.SC_TEMPORARY_REDIRECT;
+ }
+
+
+ /**
+ * Warn the user that the link may not be safe.
+ * For non GET/HEAD operations, this is escalated into a 401/Forbidden
+ * response.
+ * @param request originating request
+ * @param resp the http response
+ * @param link the link to point to
+ * @param user the user that owns the link.
+ * @throws IOException on any error.
+ */
+ public static void warnUser(
+ HttpServletRequest request,
+ HttpServletResponse resp,
+ String link, String user, Cookie checkCookie) throws IOException {
+ if (isGetRequest(request)) {
+ //Set the cookie when we warn which overrides the query parameter
+ //This is so that if a user passes in the approved query parameter without
+ //having first visited this page then this page will still be displayed
+ resp.addCookie(checkCookie);
+ resp.setContentType(MimeType.HTML);
+ Page p = new Page(resp.getWriter());
+ p.html().
+ h1(TITLE_UNSAFE_LINK).
+ h3().
+ _("click ").a(link, "here").
+ _(" to continue to an Application Master web interface owned by ",
+ user).
+ _().
+ _();
+ } else {
+ // its a URL belonging to a service other than the user, and an attempt
+ // is being made to manipulate it.
+ // reject the operation
+ resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ resp.setContentType(MimeType.HTML);
+ Page p = new Page(resp.getWriter());
+ String message = String.format(
+ "Forbidden to access URL %s of %s via %s request",
+ link,
+ user,
+ request.getMethod());
+ p.html().
+ h1(message).
+ _();
+ }
+ }
}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java
index 6c0391f..bb295ff 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java
@@ -21,14 +21,12 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.PrintWriter;
import java.io.ObjectInputStream;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.Arrays;
-import java.util.EnumSet;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
@@ -52,11 +50,10 @@
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.ApplicationNotFoundException;
import org.apache.hadoop.yarn.exceptions.YarnException;
+import static org.apache.hadoop.yarn.server.webproxy.ProxyUtils.*;
import org.apache.hadoop.yarn.util.Apps;
import org.apache.hadoop.yarn.util.StringHelper;
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;
@@ -75,24 +72,18 @@
public static final String PROXY_USER_COOKIE_NAME = "proxy-user";
+
+ /**
+ * This is the "unavailable" marker from the timeline store: {@value}.
+ * There's no direct import of the value, to avoid dependency ordering
+ * problems.
+ */
+ public static final String TIMELINE_STORE_UNAVAILABLE = "N/A";
+
private transient List trackingUriPlugins;
private final String rmAppPageUrlBase;
private transient YarnConfiguration conf;
- private static class _ implements Hamlet._ {
- //Empty
- }
-
- private static class Page extends Hamlet {
- Page(PrintWriter out) {
- super(out, 0, false);
- }
-
- public HTML html() {
- return new HTML<>("html", null, EnumSet.of(EOpt.ENDTAG));
- }
- }
-
/**
* Default constructor
*/
@@ -124,21 +115,14 @@ private static void notFound(HttpServletResponse resp, String message)
* @param user the user that owns the link.
* @throws IOException on any error.
*/
- private static void warnUserPage(HttpServletResponse resp, String link,
+ private static void warnUserPage(HttpServletRequest request,
+ HttpServletResponse resp, String link,
String user, ApplicationId id) throws IOException {
//Set the cookie when we warn which overrides the query parameter
//This is so that if a user passes in the approved query parameter without
//having first visited this page then this page will still be displayed
- resp.addCookie(makeCheckCookie(id, false));
- resp.setContentType(MimeType.HTML);
- Page p = new Page(resp.getWriter());
- p.html().
- 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).
- _().
- _();
+ Cookie checkCookie = makeCheckCookie(id, false);
+ warnUser(request, resp, link, user, checkCookie);
}
/**
@@ -232,7 +216,13 @@ private String getProxyHost() throws IOException {
return ((String) getServletContext()
.getAttribute(WebAppProxy.PROXY_HOST_ATTRIBUTE));
}
-
+
+ /**
+ * Handle HEAD/GET requests
+ * @param req request
+ * @param resp response
+ * @throws IOException
+ */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException{
@@ -303,9 +293,13 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
String original = applicationReport.getOriginalTrackingUrl();
URI trackingUri;
// fallback to ResourceManager's app page if no tracking URI provided
- if(original == null || original.equals("N/A")) {
+ // this may change if the app registers itself
+ if(original == null || original.equals(TIMELINE_STORE_UNAVAILABLE)) {
ProxyUtils.sendRedirect(req, resp,
- StringHelper.pjoin(rmAppPageUrlBase, id.toString()));
+ StringHelper.pjoin(rmAppPageUrlBase, id.toString()),
+ true,
+ TITLE_NO_REGISTERED_WEB_UI,
+ BODY_NO_REGISTERED_WEB_UI);
return;
} else {
if (ProxyUriUtils.getSchemeFromUrl(original).isEmpty()) {
@@ -321,8 +315,9 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
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);
+ warnUserPage(req, resp,
+ ProxyUriUtils.getPathAndQuery(id, rest, req.getQueryString(), true),
+ runningUser, id);
return;
}
URI toFetch = new URI(trackingUri.getScheme(),
@@ -338,7 +333,10 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
case KILLED:
case FINISHED:
case FAILED:
- ProxyUtils.sendRedirect(req, resp, toFetch.toString());
+ // app has completed; send an appropriate redirect
+ // tagged as a non-live endpoint
+ ProxyUtils.sendRedirect(req, resp, toFetch.toString(),
+ true, TITLE_APPLICATION_COMPLETED, BODY_COMPLETED);
return;
default:
// fall out of the switch
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/AppReportFetcherForTest.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/AppReportFetcherForTest.java
new file mode 100644
index 0000000..a956805
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/AppReportFetcherForTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.yarn.server.webproxy;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.CommonConfigurationKeys;
+import org.apache.hadoop.yarn.api.records.ApplicationId;
+import org.apache.hadoop.yarn.api.records.ApplicationReport;
+import org.apache.hadoop.yarn.api.records.YarnApplicationState;
+import org.apache.hadoop.yarn.api.records.impl.pb.ApplicationReportPBImpl;
+import org.apache.hadoop.yarn.exceptions.ApplicationNotFoundException;
+import org.apache.hadoop.yarn.exceptions.YarnException;
+
+class AppReportFetcherForTest extends AppReportFetcher {
+
+ public static final int REPORT_DEFAULT = 0;
+ public static final int REPORT_NULL = 1;
+ public static final int REPORT_USERNAME_WRONG_USER = 2;
+ public static final int REPORT_APP_KILLED = 3;
+ public static final int REPORT_APP_NOT_FOUND = 4;
+
+
+
+ public int answer = 0;
+ private final int originalPort;
+
+ public AppReportFetcherForTest(Configuration conf, int originalPort) {
+ super(conf);
+ this.originalPort = originalPort;
+ }
+
+ public ApplicationReport getApplicationReport(ApplicationId appId)
+ throws YarnException {
+ ApplicationReport result;
+
+ switch (this.answer) {
+ case REPORT_DEFAULT:
+ result = getDefaultApplicationReport(appId);
+ break;
+
+ case REPORT_NULL:
+ result = null;
+ break;
+
+ case REPORT_USERNAME_WRONG_USER:
+ result = getDefaultApplicationReport(appId);
+ result.setUser("user");
+ break;
+
+ case REPORT_APP_KILLED:
+ result = getDefaultApplicationReport(appId);
+ result.setYarnApplicationState(YarnApplicationState.KILLED);
+ break;
+
+ case REPORT_APP_NOT_FOUND:
+ throw new ApplicationNotFoundException("Application is not found");
+
+ default:
+ result = null;
+ }
+ return result;
+ }
+
+ public ApplicationReport getDefaultApplicationReport(ApplicationId appId) {
+ ApplicationReport result = new ApplicationReportPBImpl();
+ result.setApplicationId(appId);
+ result.setOriginalTrackingUrl("localhost:" +
+ originalPort + "/foo/bar");
+ result.setYarnApplicationState(YarnApplicationState.RUNNING);
+ result.setUser(CommonConfigurationKeys.DEFAULT_HADOOP_HTTP_STATIC_USER);
+ return result;
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/HttpServletResponseForTest.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/HttpServletResponseForTest.java
new file mode 100644
index 0000000..2d7d79d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/HttpServletResponseForTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.yarn.server.webproxy;
+
+import org.glassfish.grizzly.servlet.HttpServletResponseImpl;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Stub http servlet response for assertion checks
+ */
+public class HttpServletResponseForTest extends HttpServletResponseImpl {
+ public int status;
+ public String contentType;
+ private final Map headers = new HashMap<>(1);
+ public StringWriter body;
+
+ @Override
+ public String encodeRedirectURL(String url) {
+ return url;
+ }
+
+ @Override
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+ @Override
+ public void setContentType(String type) {
+ this.contentType = type;
+ }
+
+ @Override
+ public void setHeader(String name, String value) {
+ headers.put(name, value);
+ }
+
+ public String getHeader(String name) {
+ return headers.get(name);
+ }
+
+ @Override
+ public PrintWriter getWriter() throws IOException {
+ body = new StringWriter();
+ return new PrintWriter(body);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb =
+ new StringBuilder("HttpServletResponseForTest{");
+ sb.append("status=").append(status);
+ sb.append(", [");
+ for (Map.Entry entry : headers.entrySet()) {
+ sb.append(entry.getKey())
+ .append(":")
+ .append(entry.getValue());
+ sb.append("; ");
+ }
+ sb.append(" ] body=").append(getBodyAsString());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ public String getBodyAsString() {
+ return body.toString();
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyRedirection.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyRedirection.java
new file mode 100644
index 0000000..c27646b
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestProxyRedirection.java
@@ -0,0 +1,106 @@
+/*
+ * 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.yarn.server.webproxy;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+import static org.mockito.Mockito.when;
+import static org.apache.hadoop.yarn.server.webproxy.ProxyUtils.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class TestProxyRedirection extends Assert {
+
+ private static void assertStatusCode(int code, String method) {
+ int status = statusCode(method);
+ assertEquals("Wrong status code for " + method,
+ code, status);
+ }
+
+ @Test
+ public void testHead() throws Throwable {
+ assertStatusCode(HttpServletResponse.SC_FOUND, "HEAD");
+ }
+
+ @Test
+ public void testGet() throws Throwable {
+ assertStatusCode(HttpServletResponse.SC_FOUND, "GET");
+ }
+
+ @Test
+ public void testPut() throws Throwable {
+ assertStatusCode(HttpServletResponse.SC_TEMPORARY_REDIRECT, "PUT");
+ }
+
+ @Test
+ public void testPost() throws Throwable {
+ assertStatusCode(HttpServletResponse.SC_TEMPORARY_REDIRECT, "POST");
+ }
+
+ @Test
+ public void testDeletePost() throws Throwable {
+ assertStatusCode(HttpServletResponse.SC_TEMPORARY_REDIRECT, "DELETE");
+ }
+
+ @Test
+ public void testRedirectGet() throws Throwable {
+ HttpServletResponseForTest response = new HttpServletResponseForTest();
+
+ // request with HttpServletRequest
+ HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(request.getRemoteAddr()).thenReturn("remoteAddr");
+ Mockito.when(request.getRequestURI()).thenReturn("/requestURI");
+ Mockito.when(request.getMethod()).thenReturn("GET");
+
+ ProxyUtils.sendRedirect(request, response, "http://target");
+ // address "redirect" is not in host list
+ String resp = response.toString();
+ assertEquals(resp,
+ HttpServletResponse.SC_FOUND,
+ response.status);
+ String redirect = response.getHeader(ProxyUtils.LOCATION);
+ assertEquals(resp, "http://target", redirect);
+ assertTrue(resp, response.getBodyAsString().contains(BODY_HAS_MOVED));
+ }
+
+ @Test
+ public void testRedirectPOST() throws Throwable {
+ HttpServletResponseForTest response = new HttpServletResponseForTest();
+
+ // request with HttpServletRequest
+ HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ when(request.getRemoteAddr()).thenReturn("remoteAddr");
+ when(request.getRequestURI()).thenReturn("/requestURI");
+ when(request.getMethod()).thenReturn("POST");
+
+ sendRedirect(request, response, "http://target");
+ // address "redirect" is not in host list
+ String resp = response.toString();
+ assertEquals(resp,
+ HttpServletResponse.SC_TEMPORARY_REDIRECT,
+ response.status);
+ String redirect = response.getHeader(ProxyUtils.LOCATION);
+ assertEquals(resp, "http://target", redirect);
+ assertTrue(resp, response.getBodyAsString().contains(BODY_HAS_MOVED));
+ }
+
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestWebAppProxyServlet.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestWebAppProxyServlet.java
index e55c0adb..fb3a28f 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestWebAppProxyServlet.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestWebAppProxyServlet.java
@@ -40,19 +40,13 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.http.HttpServer2;
import org.apache.hadoop.security.authorize.AccessControlList;
import org.apache.hadoop.service.CompositeService;
import org.apache.hadoop.util.StringUtils;
-import org.apache.hadoop.yarn.api.records.ApplicationId;
-import org.apache.hadoop.yarn.api.records.ApplicationReport;
-import org.apache.hadoop.yarn.api.records.YarnApplicationState;
-import org.apache.hadoop.yarn.api.records.impl.pb.ApplicationReportPBImpl;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
-import org.apache.hadoop.yarn.exceptions.ApplicationNotFoundException;
-import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
+import static org.apache.hadoop.yarn.server.webproxy.AppReportFetcherForTest.*;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -148,8 +142,9 @@ public void testWebAppProxyServlet() throws Exception {
assertEquals(HttpURLConnection.HTTP_OK, proxyConn.getResponseCode());
assertTrue(isResponseCookiePresent(
proxyConn, "checked_application_0_0000", "true"));
+
// cannot found application 1: null
- appReportFetcher.answer = 1;
+ appReportFetcher.answer = REPORT_NULL;
proxyConn = (HttpURLConnection) url.openConnection();
proxyConn.setRequestProperty("Cookie", "checked_application_0_0000=true");
proxyConn.connect();
@@ -157,8 +152,9 @@ public void testWebAppProxyServlet() throws Exception {
proxyConn.getResponseCode());
assertFalse(isResponseCookiePresent(
proxyConn, "checked_application_0_0000", "true"));
+
// cannot found application 2: ApplicationNotFoundException
- appReportFetcher.answer = 4;
+ appReportFetcher.answer = REPORT_APP_NOT_FOUND;
proxyConn = (HttpURLConnection) url.openConnection();
proxyConn.setRequestProperty("Cookie", "checked_application_0_0000=true");
proxyConn.connect();
@@ -166,8 +162,9 @@ public void testWebAppProxyServlet() throws Exception {
proxyConn.getResponseCode());
assertFalse(isResponseCookiePresent(
proxyConn, "checked_application_0_0000", "true"));
+
// wrong user
- appReportFetcher.answer = 2;
+ appReportFetcher.answer = REPORT_USERNAME_WRONG_USER;
proxyConn = (HttpURLConnection) url.openConnection();
proxyConn.connect();
assertEquals(HttpURLConnection.HTTP_OK, proxyConn.getResponseCode());
@@ -175,8 +172,9 @@ public void testWebAppProxyServlet() throws Exception {
assertTrue(s
.contains("to continue to an Application Master web interface owned by"));
assertTrue(s.contains("WARNING: The following page may not be safe!"));
+
//case if task has a not running status
- appReportFetcher.answer = 3;
+ appReportFetcher.answer = REPORT_APP_KILLED;
proxyConn = (HttpURLConnection) url.openConnection();
proxyConn.setRequestProperty("Cookie", "checked_application_0_0000=true");
proxyConn.connect();
@@ -303,7 +301,7 @@ protected void serviceStart() throws Exception {
proxyServer.addServlet(ProxyUriUtils.PROXY_SERVLET_NAME,
ProxyUriUtils.PROXY_PATH_SPEC, WebAppProxyServlet.class);
- appReportFetcher = new AppReportFetcherForTest(conf);
+ appReportFetcher = new AppReportFetcherForTest(conf, originalPort);
proxyServer.setAttribute(FETCHER_ATTRIBUTE,
appReportFetcher );
proxyServer.setAttribute(IS_SECURITY_ENABLED_ATTRIBUTE, Boolean.TRUE);
@@ -320,42 +318,4 @@ protected void serviceStart() throws Exception {
}
- private class AppReportFetcherForTest extends AppReportFetcher {
-
- int answer = 0;
-
- public AppReportFetcherForTest(Configuration conf) {
- super(conf);
- }
-
- public ApplicationReport getApplicationReport(ApplicationId appId)
- throws YarnException {
- if (answer == 0) {
- return getDefaultApplicationReport(appId);
- } else if (answer == 1) {
- return null;
- } else if (answer == 2) {
- ApplicationReport result = getDefaultApplicationReport(appId);
- result.setUser("user");
- return result;
- } else if (answer == 3) {
- ApplicationReport result = getDefaultApplicationReport(appId);
- result.setYarnApplicationState(YarnApplicationState.KILLED);
- return result;
- } else if (answer == 4) {
- throw new ApplicationNotFoundException("Application is not found");
- }
- return null;
- }
-
- private ApplicationReport getDefaultApplicationReport(ApplicationId appId) {
- ApplicationReport result = new ApplicationReportPBImpl();
- result.setApplicationId(appId);
- result.setOriginalTrackingUrl("localhost:" + originalPort + "/foo/bar");
- result.setYarnApplicationState(YarnApplicationState.RUNNING);
- result.setUser(CommonConfigurationKeys.DEFAULT_HADOOP_HTTP_STATIC_USER);
- return result;
- }
-
- }
}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/amfilter/TestAmFilter.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/amfilter/TestAmFilter.java
index 4601c20..d19ec9f 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/amfilter/TestAmFilter.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/amfilter/TestAmFilter.java
@@ -19,8 +19,6 @@
package org.apache.hadoop.yarn.server.webproxy.amfilter;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -31,9 +29,9 @@
import static org.junit.Assert.*;
+import org.apache.hadoop.yarn.server.webproxy.HttpServletResponseForTest;
import org.apache.hadoop.yarn.server.webproxy.ProxyUtils;
import org.apache.hadoop.yarn.server.webproxy.WebAppProxyServlet;
-import org.glassfish.grizzly.servlet.HttpServletResponseImpl;
import org.junit.Test;
import org.mockito.Mockito;
@@ -55,7 +53,7 @@
protected Set getProxyAddresses() {
if (proxyAddresses == null) {
- proxyAddresses = new HashSet();
+ proxyAddresses = new HashSet<>();
}
proxyAddresses.add(proxyHost);
return proxyAddresses;
@@ -126,25 +124,11 @@ public void doFilter(ServletRequest servletRequest,
@Test(timeout = 1000)
@SuppressWarnings("deprecation")
public void testFilter() throws Exception {
- Map params = new HashMap();
- params.put(AmIpFilter.PROXY_HOST, proxyHost);
- params.put(AmIpFilter.PROXY_URI_BASE, proxyUri);
- FilterConfig config = new DummyFilterConfig(params);
- // dummy filter
- FilterChain chain = new FilterChain() {
- @Override
- public void doFilter(ServletRequest servletRequest,
- ServletResponse servletResponse) throws IOException, ServletException {
- doFilterRequest = servletRequest.getClass().getName();
- if (servletRequest instanceof AmIpServletRequestWrapper) {
- servletWrapper = (AmIpServletRequestWrapper) servletRequest;
- }
- }
- };
- AmIpFilter testFilter = new AmIpFilter();
- testFilter.init(config);
+ // dummy filter
+ FilterChain chain = createDummyFilterChain();
+ AmIpFilter testFilter = createTestFilter();
HttpServletResponseForTest response = new HttpServletResponseForTest();
// Test request should implements HttpServletRequest
@@ -159,13 +143,15 @@ public void doFilter(ServletRequest servletRequest,
// request with HttpServletRequest
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
- Mockito.when(request.getRemoteAddr()).thenReturn("redirect");
- Mockito.when(request.getRequestURI()).thenReturn("/redirect");
+ Mockito.when(request.getRemoteAddr()).thenReturn("remoteAddr");
+ Mockito.when(request.getRequestURI()).thenReturn("/requestURI");
+ Mockito.when(request.getMethod()).thenReturn("GET");
+ testFilter.doFilter(request, response, chain);
testFilter.doFilter(request, response, chain);
// address "redirect" is not in host list
- assertEquals(302, response.status);
+ assertEquals(response.toString(), 302, response.status);
String redirect = response.getHeader(ProxyUtils.LOCATION);
- assertEquals("http://bogus/redirect", redirect);
+ assertEquals(response.toString(), "http://bogus/requestURI", redirect);
// "127.0.0.1" contains in host list. Without cookie
Mockito.when(request.getRemoteAddr()).thenReturn("127.0.0.1");
testFilter.doFilter(request, response, chain);
@@ -189,53 +175,59 @@ public void doFilter(ServletRequest servletRequest,
}
- private class HttpServletResponseForTest extends HttpServletResponseImpl {
- String redirectLocation = "";
- int status;
- private String contentType;
- private final Map headers = new HashMap<>(1);
- private StringWriter body;
-
-
- public String getRedirect() {
- return redirectLocation;
- }
+ public FilterChain createDummyFilterChain() {
+ return new FilterChain() {
+ @Override
+ public void doFilter(ServletRequest servletRequest,
+ ServletResponse servletResponse) throws
+ IOException,
+ ServletException {
+ doFilterRequest = servletRequest.getClass().getName();
+ if (servletRequest instanceof AmIpServletRequestWrapper) {
+ servletWrapper = (AmIpServletRequestWrapper) servletRequest;
- @Override
- public void sendRedirect(String location) throws IOException {
- redirectLocation = location;
- }
+ }
+ }
+ };
+ }
- @Override
- public String encodeRedirectURL(String url) {
- return url;
- }
+ public AmIpFilter createTestFilter() throws ServletException {
+ Map params = new HashMap<>();
+ params.put(AmIpFilter.PROXY_HOST, proxyHost);
+ params.put(AmIpFilter.PROXY_URI_BASE, proxyUri);
+ FilterConfig config = new DummyFilterConfig(params);
+ AmIpFilter testFilter = new AmIpFilter();
+ testFilter.init(config);
+ return testFilter;
+ }
- @Override
- public void setStatus(int status) {
- this.status = status;
- }
- @Override
- public void setContentType(String type) {
- this.contentType = type;
- }
+ /**
+ * Test AmIpFilter
+ */
+ @Test(timeout = 1000)
+ @SuppressWarnings("deprecation")
+ public void testComplexVerbRedirect() throws Exception {
- @Override
- public void setHeader(String name, String value) {
- headers.put(name, value);
- }
+ // dummy filter
+ FilterChain chain = createDummyFilterChain();
+ AmIpFilter testFilter = createTestFilter();
- public String getHeader(String name) {
- return headers.get(name);
- }
+ HttpServletResponseForTest response = new HttpServletResponseForTest();
- @Override
- public PrintWriter getWriter() throws IOException {
- body = new StringWriter();
- return new PrintWriter(body);
- }
+ // request with HttpServletRequest
+ HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(request.getRemoteAddr()).thenReturn("remoteAddr");
+ Mockito.when(request.getRequestURI()).thenReturn("/requestURI");
+ Mockito.when(request.getMethod()).thenReturn("PUT");
+ testFilter.doFilter(request, response, chain);
+ testFilter.doFilter(request, response, chain);
+ // address "redirect" is not in host list
+ assertEquals(response.toString(),
+ HttpServletResponse.SC_TEMPORARY_REDIRECT,
+ response.status);
+ String redirect = response.getHeader(ProxyUtils.LOCATION);
+ assertEquals(response.toString(), "http://bogus/requestURI", redirect);
}
-
}