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
Original file line number Diff line number Diff line change
Expand Up @@ -1882,6 +1882,19 @@ public static boolean isAclEnabled(Configuration conf) {
public static final long DEFAULT_RM_APPLICATION_MONITOR_INTERVAL_MS =
3000;

// If the proxy connection time enabled.
public static final String RM_PROXY_TIMEOUT_ENABLED =
RM_PREFIX + "proxy.timeout.enabled";

public static final boolean DEFALUT_RM_PROXY_TIMEOUT_ENABLED =
true;

public static final String RM_PROXY_CONNECTION_TIMEOUT =
RM_PREFIX + "proxy.connection.timeout";

public static final int DEFAULT_RM_PROXY_CONNECTION_TIMEOUT =
60000;

/**
* Interval of time the linux container executor should try cleaning up
* cgroups entry when cleaning up a container. This is required due to what
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2024,6 +2024,18 @@
<value/>
</property>

<property>
<description>Enable the web proxy connection timeout, default is enabled.</description>
<name>yarn.resourcemanager.proxy.timeout.enabled</name>
<value>true</value>
</property>

<property>
<description>The web proxy connection timeout.</description>
<name>yarn.resourcemanager.proxy.connection.timeout</name>
<value>60000</value>
</property>

<!-- Applications' Configuration -->

<property>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -123,6 +123,9 @@ public HTML<WebAppProxyServlet._> html() {
}
}

protected void setConf(YarnConfiguration conf){
this.conf = conf;
}
/**
* Default constructor
*/
Expand Down Expand Up @@ -189,24 +192,38 @@ private static void warnUserPage(HttpServletResponse resp, String link,
* @param method the http method
* @throws IOException on any error.
*/
private static void proxyLink(final HttpServletRequest req,
private void proxyLink(final HttpServletRequest req,
final HttpServletResponse resp, final URI link, final Cookie c,
final String proxyHost, final HTTP method) throws IOException {
DefaultHttpClient client = new DefaultHttpClient();
client
.getParams()
.setParameter(ClientPNames.COOKIE_POLICY,
CookiePolicy.BROWSER_COMPATIBILITY)
.setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();

boolean connectionTimeoutEnabled =
conf.getBoolean(YarnConfiguration.RM_PROXY_TIMEOUT_ENABLED,
YarnConfiguration.DEFALUT_RM_PROXY_TIMEOUT_ENABLED);
int connectionTimeout =
conf.getInt(YarnConfiguration.RM_PROXY_CONNECTION_TIMEOUT,
YarnConfiguration.DEFAULT_RM_PROXY_CONNECTION_TIMEOUT);

// Make sure we send the request from the proxy address in the config
// since that is what the AM filter checks against. IP aliasing or
// similar could cause issues otherwise.
InetAddress localAddress = InetAddress.getByName(proxyHost);
if (LOG.isDebugEnabled()) {
LOG.debug("local InetAddress for proxy host: {}", localAddress);
}
client.getParams()
.setParameter(ConnRoutePNames.LOCAL_ADDRESS, localAddress);
httpClientBuilder.setDefaultRequestConfig(
connectionTimeoutEnabled ?
RequestConfig.custom()
.setCircularRedirectsAllowed(true)
.setLocalAddress(localAddress)
.setConnectionRequestTimeout(connectionTimeout)
.setSocketTimeout(connectionTimeout)
.setConnectTimeout(connectionTimeout)
.build() :
RequestConfig.custom()
.setCircularRedirectsAllowed(true)
.setLocalAddress(localAddress)
.build());

HttpRequestBase base = null;
if (method.equals(HTTP.GET)) {
Expand Down Expand Up @@ -248,6 +265,7 @@ private static void proxyLink(final HttpServletRequest req,
PROXY_USER_COOKIE_NAME + "=" + URLEncoder.encode(user, "ASCII"));
}
OutputStream out = resp.getOutputStream();
HttpClient client = httpClientBuilder.build();
try {
HttpResponse httpResp = client.execute(base);
resp.setStatus(httpResp.getStatusLine().getStatusCode());
Expand Down Expand Up @@ -571,7 +589,6 @@ private FetchedAppReport getFetchedAppReport(ApplicationId id)
* again... If this method returns true, there was a redirect, and
* it was handled by redirecting the current request to an error page.
*
* @param path the part of the request path after the app id
* @param id the app id
* @param req the request object
* @param resp the response object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
Expand All @@ -32,10 +34,14 @@
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.SocketTimeoutException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -90,6 +96,7 @@ public static void start() throws Exception {
context.setContextPath("/foo");
server.setHandler(context);
context.addServlet(new ServletHolder(TestServlet.class), "/bar");
context.addServlet(new ServletHolder(TimeOutTestServlet.class), "/timeout");
server.getConnectors()[0].setHost("localhost");
server.start();
originalPort = server.getConnectors()[0].getLocalPort();
Expand Down Expand Up @@ -137,6 +144,29 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
}
}

@SuppressWarnings("serial")
public static class TimeOutTestServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
LOG.warn("doGet() interrupted", e);
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
resp.setStatus(HttpServletResponse.SC_OK);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setStatus(HttpServletResponse.SC_OK);
}
}

@Test(timeout=5000)
public void testWebAppProxyServlet() throws Exception {
configuration.set(YarnConfiguration.PROXY_ADDRESS, "localhost:9090");
Expand Down Expand Up @@ -248,6 +278,45 @@ public void testWebAppProxyServlet() throws Exception {
}
}

@Test(expected = SocketTimeoutException.class)
public void testWebAppProxyConnectionTimeout()
throws IOException, ServletException{
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getMethod()).thenReturn("GET");
when(request.getRemoteUser()).thenReturn("dr.who");
when(request.getPathInfo()).thenReturn("/application_00_0");
when(request.getHeaderNames()).thenReturn(Collections.emptyEnumeration());

HttpServletResponse response = mock(HttpServletResponse.class);
when(response.getOutputStream()).thenReturn(null);

WebAppProxyServlet servlet = new WebAppProxyServlet();
YarnConfiguration conf = new YarnConfiguration();
conf.setBoolean(YarnConfiguration.RM_PROXY_TIMEOUT_ENABLED,
true);
conf.setInt(YarnConfiguration.RM_PROXY_CONNECTION_TIMEOUT,
1000);

servlet.setConf(conf);

ServletConfig config = mock(ServletConfig.class);
ServletContext context = mock(ServletContext.class);
when(config.getServletContext()).thenReturn(context);

AppReportFetcherForTest appReportFetcher =
new AppReportFetcherForTest(new YarnConfiguration());

when(config.getServletContext()
.getAttribute(WebAppProxy.FETCHER_ATTRIBUTE))
.thenReturn(appReportFetcher);

appReportFetcher.answer = 7;

servlet.init(config);
servlet.doGet(request, response);

}

@Test(timeout=5000)
public void testAppReportForEmptyTrackingUrl() throws Exception {
configuration.set(YarnConfiguration.PROXY_ADDRESS, "localhost:9090");
Expand Down Expand Up @@ -330,12 +399,13 @@ public void testWebAppProxyPassThroughHeaders() throws Exception {
assertEquals(proxyConn.getRequestProperties().size(), 4);
proxyConn.connect();
assertEquals(HttpURLConnection.HTTP_OK, proxyConn.getResponseCode());
// Verify if number of headers received by end server is 8.
// Eight headers include Accept, Host, Connection, User-Agent, Cookie,
// Origin, Access-Control-Request-Method and
// Verify if number of headers received by end server is 9.
// This should match WebAppProxyServlet#PASS_THROUGH_HEADERS.
// Nine headers include Accept, Host, Connection, User-Agent, Cookie,
// Origin, Access-Control-Request-Method, Accept-Encoding, and
// Access-Control-Request-Headers. Pls note that Unknown-Header is dropped
// by proxy as it is not in the list of allowed headers.
assertEquals(numberOfHeaders, 8);
assertEquals(numberOfHeaders, 9);
assertFalse(hasUnknownHeader);
} finally {
proxy.close();
Expand Down Expand Up @@ -553,6 +623,12 @@ public FetchedAppReport getApplicationReport(ApplicationId appId)
return result;
} else if (answer == 6) {
return getDefaultApplicationReport(appId, false);
} else if (answer == 7) {
// test connection timeout
FetchedAppReport result = getDefaultApplicationReport(appId);
result.getApplicationReport().setOriginalTrackingUrl("localhost:"
+ originalPort + "/foo/timeout?a=b#main");
return result;
}
return null;
}
Expand Down