YARN-5246. NMWebAppFilter web redirects drop query parameters. Contributed by Varun Vasudev.

This commit is contained in:
Junping Du 2016-06-19 17:44:54 -07:00
parent 0319d73c3b
commit d0162f2040
4 changed files with 103 additions and 24 deletions

View File

@ -23,6 +23,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -30,6 +31,7 @@
import org.apache.hadoop.classification.InterfaceAudience.Private;
import org.apache.hadoop.classification.InterfaceStability.Evolving;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.http.HtmlQuoting;
import org.apache.hadoop.http.HttpConfig.Policy;
import org.apache.hadoop.http.HttpServer2;
import org.apache.hadoop.net.NetUtils;
@ -42,6 +44,10 @@
import org.apache.hadoop.yarn.util.RMHAUtils;
import org.apache.hadoop.yarn.webapp.BadRequestException;
import org.apache.hadoop.yarn.webapp.NotFoundException;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import javax.servlet.http.HttpServletRequest;
@Private
@Evolving
@ -418,4 +424,49 @@ public static String getDefaultLogContentType() {
public static List<String> listSupportedLogContentType() {
return Arrays.asList("text", "octet-stream");
}
private static String getURLEncodedQueryString(HttpServletRequest request) {
String queryString = request.getQueryString();
if (queryString != null && !queryString.isEmpty()) {
String reqEncoding = request.getCharacterEncoding();
if (reqEncoding == null || reqEncoding.isEmpty()) {
reqEncoding = "ISO-8859-1";
}
Charset encoding = Charset.forName(reqEncoding);
List<NameValuePair> params = URLEncodedUtils.parse(queryString, encoding);
return URLEncodedUtils.format(params, encoding);
}
return null;
}
/**
* Get a HTML escaped uri with the query parameters of the request.
* @param request HttpServletRequest with the request details
* @return HTML escaped uri with the query paramters
*/
public static String getHtmlEscapedURIWithQueryString(
HttpServletRequest request) {
String urlEncodedQueryString = getURLEncodedQueryString(request);
if (urlEncodedQueryString != null) {
return HtmlQuoting.quoteHtmlChars(
request.getRequestURI() + "?" + urlEncodedQueryString);
}
return HtmlQuoting.quoteHtmlChars(request.getRequestURI());
}
/**
* Add the query params from a HttpServletRequest to the target uri passed.
* @param request HttpServletRequest with the request details
* @param targetUri the uri to which the query params must be added
* @return URL encoded string containing the targetUri + "?" + query string
*/
public static String appendQueryParams(HttpServletRequest request,
String targetUri) {
String ret = targetUri;
String urlEncodedQueryString = getURLEncodedQueryString(request);
if (urlEncodedQueryString != null) {
ret += "?" + urlEncodedQueryString;
}
return ret;
}
}

View File

@ -39,6 +39,9 @@
import org.junit.BeforeClass;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import javax.servlet.http.HttpServletRequest;
public class TestWebAppUtils {
private static final String RM1_NODE_ID = "rm1";
@ -176,6 +179,45 @@ protected Configuration provisionCredentialsForSSL() throws IOException,
return conf;
}
@Test
public void testAppendQueryParams() throws Exception {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
String targetUri = "/test/path";
Mockito.when(request.getCharacterEncoding()).thenReturn(null);
Map<String, String> paramResultMap = new HashMap<>();
paramResultMap.put("param1=x", targetUri + "?" + "param1=x");
paramResultMap
.put("param1=x&param2=y", targetUri + "?" + "param1=x&param2=y");
paramResultMap.put("param1=x&param2=y&param3=x+y",
targetUri + "?" + "param1=x&param2=y&param3=x+y");
for (Map.Entry<String, String> entry : paramResultMap.entrySet()) {
Mockito.when(request.getQueryString()).thenReturn(entry.getKey());
String uri = WebAppUtils.appendQueryParams(request, targetUri);
Assert.assertEquals(entry.getValue(), uri);
}
}
@Test
public void testGetHtmlEscapedURIWithQueryString() throws Exception {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
String targetUri = "/test/path";
Mockito.when(request.getCharacterEncoding()).thenReturn(null);
Mockito.when(request.getRequestURI()).thenReturn(targetUri);
Map<String, String> paramResultMap = new HashMap<>();
paramResultMap.put("param1=x", targetUri + "?" + "param1=x");
paramResultMap
.put("param1=x&param2=y", targetUri + "?" + "param1=x&amp;param2=y");
paramResultMap.put("param1=x&param2=y&param3=x+y",
targetUri + "?" + "param1=x&amp;param2=y&amp;param3=x+y");
for (Map.Entry<String, String> entry : paramResultMap.entrySet()) {
Mockito.when(request.getQueryString()).thenReturn(entry.getKey());
String uri = WebAppUtils.getHtmlEscapedURIWithQueryString(request);
Assert.assertEquals(entry.getValue(), uri);
}
}
public class TestBuilder extends HttpServer2.Builder {
public String keypass;
public String keystorePassword;

View File

@ -38,6 +38,7 @@
import org.apache.hadoop.yarn.webapp.Controller.RequestContext;
import com.google.inject.Injector;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
@Singleton
public class NMWebAppFilter extends GuiceContainer{
@ -58,8 +59,7 @@ public NMWebAppFilter(Injector injector, Context nmContext) {
public void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain) throws IOException,
ServletException {
String uri = HtmlQuoting.quoteHtmlChars(request.getRequestURI());
String redirectPath = containerLogPageRedirectPath(uri);
String redirectPath = containerLogPageRedirectPath(request);
if (redirectPath != null) {
String redirectMsg =
"Redirecting to log server" + " : " + redirectPath;
@ -72,7 +72,8 @@ public void doFilter(HttpServletRequest request,
super.doFilter(request, response, chain);
}
private String containerLogPageRedirectPath(String uri) {
private String containerLogPageRedirectPath(HttpServletRequest request) {
String uri = HtmlQuoting.quoteHtmlChars(request.getRequestURI());
String redirectPath = null;
if (!uri.contains("/ws/v1/node") && uri.contains("/containerlogs")) {
String[] parts = uri.split("/");
@ -105,7 +106,8 @@ private String containerLogPageRedirectPath(String uri) {
sb.append(containerIdStr);
sb.append("/");
sb.append(appOwner);
redirectPath = sb.toString();
redirectPath =
WebAppUtils.appendQueryParams(request, sb.toString());
} else {
injector.getInstance(RequestContext.class).set(
ContainerLogsPage.REDIRECT_URL, "false");

View File

@ -25,8 +25,6 @@
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Random;
import java.util.Set;
@ -50,8 +48,6 @@
import org.apache.hadoop.yarn.util.ConverterUtils;
import org.apache.hadoop.yarn.webapp.YarnWebParams;
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -116,22 +112,10 @@ public void doFilter(HttpServletRequest request,
htmlEscapedUri = "/";
}
String uriWithQueryString = htmlEscapedUri;
String htmlEscapedUriWithQueryString = htmlEscapedUri;
String queryString = request.getQueryString();
if (queryString != null && !queryString.isEmpty()) {
String reqEncoding = request.getCharacterEncoding();
if (reqEncoding == null || reqEncoding.isEmpty()) {
reqEncoding = "ISO-8859-1";
}
Charset encoding = Charset.forName(reqEncoding);
List<NameValuePair> params = URLEncodedUtils.parse(queryString, encoding);
String urlEncodedQueryString = URLEncodedUtils.format(params, encoding);
uriWithQueryString += "?" + urlEncodedQueryString;
htmlEscapedUriWithQueryString = HtmlQuoting.quoteHtmlChars(
request.getRequestURI() + "?" + urlEncodedQueryString);
}
String uriWithQueryString =
WebAppUtils.appendQueryParams(request, htmlEscapedUri);
String htmlEscapedUriWithQueryString =
WebAppUtils.getHtmlEscapedURIWithQueryString(request);
RMWebApp rmWebApp = injector.getInstance(RMWebApp.class);
rmWebApp.checkIfStandbyRM();