You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ne...@apache.org on 2017/03/14 15:40:36 UTC

[01/13] incubator-trafficcontrol git commit: [TC-179] Reverted changes to AbstractServiceUpdater and removed sleep from http dataserver.

Repository: incubator-trafficcontrol
Updated Branches:
  refs/heads/master 0a2298464 -> 839167710


[TC-179] Reverted changes to AbstractServiceUpdater and removed sleep from http dataserver.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/9b731864
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/9b731864
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/9b731864

Branch: refs/heads/master
Commit: 9b73186410a1aec29889dce6a8592dc2c7954e68
Parents: c42b0e9
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Fri Mar 10 14:05:14 2017 -0700
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:14 2017 -0600

----------------------------------------------------------------------
 .../traffic_router/core/loc/AbstractServiceUpdater.java       | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/9b731864/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java
index b35b7e6..4f63cc9 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java
@@ -91,15 +91,10 @@ public abstract class AbstractServiceUpdater {
 	};
 
 	public void init() {
-		if (dataBaseURL == null) {
-			LOGGER.warn("[" + getClass().getSimpleName() + "] Unable to fetch external resource; init() called with a null databaseUrl");
-			return;
-		}
-
 		final long pollingInterval = getPollingInterval();
 		final Date nextFetchDate = new Date(System.currentTimeMillis() + pollingInterval);
 		LOGGER.info("[" + getClass().getSimpleName() + "] Fetching external resource " + dataBaseURL + " at interval: " + pollingInterval + " : " + TimeUnit.MILLISECONDS + " next update occurrs at " + nextFetchDate);
-		scheduledService = executorService.scheduleWithFixedDelay(updater, 0, pollingInterval, TimeUnit.MILLISECONDS); // start immediately with pollingInterval fixed delay
+		scheduledService = executorService.scheduleWithFixedDelay(updater, pollingInterval, pollingInterval, TimeUnit.MILLISECONDS);
 	}
 
 	@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})


[05/13] incubator-trafficcontrol git commit: [TC-179] Remove unnecessary throws/catch declarations, as we're not relying on a JSON library for the response at this time.

Posted by ne...@apache.org.
[TC-179] Remove unnecessary throws/catch declarations, as we're not relying on a JSON library for the response at this time.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/30859245
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/30859245
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/30859245

Branch: refs/heads/master
Commit: 30859245b11ff4b1da7dd253879fe3231f150762
Parents: 4d50329
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Fri Mar 10 10:15:23 2017 -0700
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:14 2017 -0600

----------------------------------------------------------------------
 .../traffic_router/core/http/RouterFilter.java                | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/30859245/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
index 58c49bc..4b7ef57 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
@@ -23,7 +23,6 @@ import com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouter;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouterManager;
 import com.comcast.cdn.traffic_control.traffic_router.geolocation.GeolocationException;
 import org.apache.log4j.Logger;
-import org.apache.wicket.ajax.json.JSONException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.filter.OncePerRequestFilter;
 
@@ -98,7 +97,7 @@ public class RouterFilter extends OncePerRequestFilter {
 			httpAccessRecordBuilder.responseURL(null);
 			httpAccessRecordBuilder.rerr(e.getMessage());
 			throw e;
-		} catch (GeolocationException | JSONException e) {
+		} catch (final GeolocationException e) {
 			httpAccessRecordBuilder.responseCode(-1);
 			httpAccessRecordBuilder.responseURL(null);
 			httpAccessRecordBuilder.rerr(e.getMessage());
@@ -121,7 +120,7 @@ public class RouterFilter extends OncePerRequestFilter {
 		}
 	}
 
-	private void setMultiResponse(final HTTPRouteResult routeResult, final HttpServletRequest httpServletRequest, final HttpServletResponse response, final HTTPAccessRecord.Builder httpAccessRecordBuilder) throws IOException, JSONException {
+	private void setMultiResponse(final HTTPRouteResult routeResult, final HttpServletRequest httpServletRequest, final HttpServletResponse response, final HTTPAccessRecord.Builder httpAccessRecordBuilder) throws IOException {
 		for (final DeliveryService deliveryService : routeResult.getDeliveryServices()) {
 			final Map<String, String> responseHeaders = deliveryService.getResponseHeaders();
 
@@ -149,7 +148,7 @@ public class RouterFilter extends OncePerRequestFilter {
 		}
 	}
 
-	private void setSingleResponse(final HTTPRouteResult routeResult, final HttpServletRequest httpServletRequest, final HttpServletResponse response, final HTTPAccessRecord.Builder httpAccessRecordBuilder) throws IOException, JSONException {
+	private void setSingleResponse(final HTTPRouteResult routeResult, final HttpServletRequest httpServletRequest, final HttpServletResponse response, final HTTPAccessRecord.Builder httpAccessRecordBuilder) throws IOException {
 		final String redirect = httpServletRequest.getParameter(REDIRECT_QUERY_PARAM);
 		final String format = httpServletRequest.getParameter("format");
 		final URL location = routeResult.getUrl();


[04/13] incubator-trafficcontrol git commit: [TC-179] Add a sleep step to starting the fake data server.

Posted by ne...@apache.org.
[TC-179] Add a sleep step to starting the fake data server.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/c42b0e94
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/c42b0e94
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/c42b0e94

Branch: refs/heads/master
Commit: c42b0e9459ee36aed5389841cbca1e8345569d92
Parents: 76e176d
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Fri Mar 10 13:07:59 2017 -0700
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:14 2017 -0600

----------------------------------------------------------------------
 .../traffic_router/core/external/HttpDataServer.java          | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/c42b0e94/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java
index 3929a7a..e321c9a 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java
@@ -53,11 +53,14 @@ public class HttpDataServer implements HttpHandler {
 //		}
 //	}
 
-	public void start(int port) throws IOException {
+	public void start(int port) throws IOException, InterruptedException {
+		int s = 30;
 		httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port),10);
 		httpServer.createContext("/", this);
 		httpServer.start();
-		System.out.println(">>>>>>>>>>>>> Started Fake Http Data Server at " + port);
+		System.out.println(">>>>>>>>>>>>> Started Fake Http Data Server at " + port + "; sleeping for " + s + "s");
+
+		Thread.sleep(s * 1000L);
 	}
 
 	public void stop() {


[11/13] incubator-trafficcontrol git commit: [TC-179] Fix to not send JSON body data on HEAD requests.

Posted by ne...@apache.org.
[TC-179] Fix to not send JSON body data on HEAD requests.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/78e8f944
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/78e8f944
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/78e8f944

Branch: refs/heads/master
Commit: 78e8f9447801f653177b4124dc2681e06eb920ee
Parents: dc5bb7b
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Mon Mar 13 15:22:46 2017 -0600
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:15 2017 -0600

----------------------------------------------------------------------
 .../traffic_router/core/http/RouterFilter.java  | 27 ++++++----
 .../core/external/RouterTest.java               | 53 ++++++++++++++++++--
 .../core/external/SteeringTest.java             | 52 +++++++++++++++++++
 3 files changed, 119 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/78e8f944/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
index 6a1e3a8..efdbad9 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
@@ -40,6 +40,7 @@ import java.util.Set;
 public class RouterFilter extends OncePerRequestFilter {
 	private static final Logger ACCESS = Logger.getLogger("com.comcast.cdn.traffic_control.traffic_router.core.access");
 	public static final String REDIRECT_QUERY_PARAM = "trred";
+	private static final String HEAD = "HEAD";
 
 	@Autowired
 	private TrafficRouterManager trafficRouterManager;
@@ -133,9 +134,11 @@ public class RouterFilter extends OncePerRequestFilter {
 
 		final String redirect = httpServletRequest.getParameter(REDIRECT_QUERY_PARAM);
 
-		response.setContentType("application/json");
-		response.getWriter().println(routeResult.toMultiLocationJSONString());
-		httpAccessRecordBuilder.responseURLs(routeResult.getUrls());
+		if (!HEAD.equals(httpServletRequest.getMethod())) {
+			response.setContentType("application/json");
+			response.getWriter().println(routeResult.toMultiLocationJSONString());
+			httpAccessRecordBuilder.responseURLs(routeResult.getUrls());
+		}
 
 		// don't actually parse the boolean value; trred would always be false unless the query param is "true"
 		if ("false".equalsIgnoreCase(redirect)) {
@@ -164,15 +167,21 @@ public class RouterFilter extends OncePerRequestFilter {
 		}
 
 		if ("false".equalsIgnoreCase(redirect)) {
-			response.setContentType("application/json");
-			response.getWriter().println(routeResult.toMultiLocationJSONString());
+			if (!HEAD.equals(httpServletRequest.getMethod())) {
+				response.setContentType("application/json");
+				response.getWriter().println(routeResult.toMultiLocationJSONString());
+				httpAccessRecordBuilder.responseURLs(routeResult.getUrls());
+			}
+
 			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK);
-			httpAccessRecordBuilder.responseURLs(routeResult.getUrls());
 		} else if ("json".equals(format)) {
-			response.setContentType("application/json");
-			response.getWriter().println(routeResult.toLocationJSONString());
+			if (!HEAD.equals(httpServletRequest.getMethod())) {
+				response.setContentType("application/json");
+				response.getWriter().println(routeResult.toLocationJSONString());
+				httpAccessRecordBuilder.responseURL(location);
+			}
+
 			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK);
-			httpAccessRecordBuilder.responseURL(location);
 		} else {
 			response.sendRedirect(location.toString());
 			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_MOVED_TEMPORARILY);

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/78e8f944/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
index bf13e4a..df29d89 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
@@ -20,8 +20,10 @@ import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.http.Header;
+import org.apache.http.HttpEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
 import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
@@ -55,14 +57,14 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.isIn;
 import static org.hamcrest.Matchers.isOneOf;
+import static org.hamcrest.Matchers.nullValue;
 import static org.hamcrest.Matchers.startsWith;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
 @Category(ExternalTest.class)
@@ -70,7 +72,7 @@ import static org.junit.Assert.fail;
 public class RouterTest {
 	private CloseableHttpClient httpClient;
 	private final String cdnDomain = ".thecdn.example.com";
-	
+
 	private String deliveryServiceId;
 	private String deliveryServiceDomain;
 	private List<String> validLocations = new ArrayList<>();
@@ -171,6 +173,7 @@ public class RouterTest {
 				int port = cacheNode.get("port").asInt();
 				String portText = (port == 80) ? "" : ":" + port;
 				validLocations.add("http://" + cacheId + "." + deliveryServiceDomain + portText + "/stuff?fakeClientIpAddress=12.34.56.78");
+				validLocations.add("http://" + cacheId + "." + deliveryServiceDomain + portText + "/stuff?fakeClientIpAddress=12.34.56.78&format=json");
 			}
 
 			if (cacheNode.get("deliveryServices").has(httpsOnlyId)) {
@@ -456,7 +459,7 @@ public class RouterTest {
 		} catch (SSLHandshakeException e) {
 			// Expected, this means we're doing the right thing
 		}
-		
+
 		// Pretend someone did a cr-config snapshot that would have updated the location to be different
 		HttpPost httpPost = new HttpPost("http://localhost:" + testHttpPort + "/crconfig-2");
 		httpClient.execute(httpPost).close();
@@ -556,6 +559,48 @@ public class RouterTest {
 		}
 	}
 
+	@Test
+	public void itDoesUseLocationFormatResponse() throws IOException, InterruptedException {
+		HttpGet httpGet = new HttpGet("http://localhost:" + routerHttpPort + "/stuff?fakeClientIpAddress=12.34.56.78&format=json");
+		httpGet.addHeader("Host", "tr." + deliveryServiceId + ".bar");
+		CloseableHttpResponse response = null;
+
+		try {
+			response = httpClient.execute(httpGet);
+			assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
+
+			HttpEntity entity = response.getEntity();
+			ObjectMapper objectMapper = new ObjectMapper(new JsonFactory());
+
+			assertThat(entity.getContent(), not(nullValue()));
+			System.out.println(entity.getContent());
+
+			JsonNode json = objectMapper.readTree(entity.getContent());
+			System.out.println(json);
+
+			assertThat(json.has("location"), equalTo(true));
+			assertThat(json.get("location").asText(), isIn(validLocations));
+			assertThat(json.get("location").asText(), Matchers.startsWith("http://"));
+		} finally {
+			if (response != null) response.close();
+		}
+	}
+
+	@Test
+	public void itDoesNotUseLocationFormatResponseForHead() throws IOException, InterruptedException {
+		HttpHead httpHead = new HttpHead("http://localhost:" + routerHttpPort + "/stuff?fakeClientIpAddress=12.34.56.78&format=json");
+		httpHead.addHeader("Host", "tr." + deliveryServiceId + ".bar");
+		CloseableHttpResponse response = null;
+
+		try {
+			response = httpClient.execute(httpHead);
+			assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
+			assertThat("Failed getting null body for HEAD request", response.getEntity(), nullValue());
+		} finally {
+			if (response != null) response.close();
+		}
+	}
+
 	// This is a workaround to get HttpClient to do the equivalent of
 	// curl -v --resolve 'tr.https-only-test.thecdn.cdnlab.example.com:8443:127.0.0.1' https://tr.https-only-test.thecdn.example.com:8443/foo.json
 	class ClientSslSocketFactory extends SSLConnectionSocketFactory {

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/78e8f944/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java
index 31e6479..7e0e49e 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.http.HttpEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClientBuilder;
@@ -456,4 +457,55 @@ public class SteeringTest {
 			}
 		}
 	}
+
+	@Test
+	public void itUsesNoMultiLocationFormatResponseWithout302WithHead() throws Exception {
+		final List<String> paths = new ArrayList<String>();
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=false");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=FALSE");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=FalsE");
+
+		for (final String path : paths) {
+			HttpHead httpHead = new HttpHead("http://localhost:" + routerHttpPort + path);
+			httpHead.addHeader("Host", "tr.client-steering-test-1.thecdn.example.com");
+
+			CloseableHttpResponse response = null;
+
+			try {
+				response = httpClient.execute(httpHead);
+				assertThat("Failed getting 200 for request " + httpHead.getFirstHeader("Host").getValue(), response.getStatusLine().getStatusCode(), equalTo(200));
+				assertThat("Failed getting null body for HEAD request", response.getEntity(), nullValue());
+			} finally {
+				if (response != null) { response.close(); }
+			}
+		}
+	}
+
+	@Test
+	public void itUsesNoMultiLocationFormatResponseWithHead() throws Exception {
+		final List<String> paths = new ArrayList<String>();
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=true");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=TRUE");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=TruE");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=T");
+
+		for (final String path : paths) {
+			HttpHead httpHead = new HttpHead("http://localhost:" + routerHttpPort + path);
+			httpHead.addHeader("Host", "tr.client-steering-test-1.thecdn.example.com");
+
+			CloseableHttpResponse response = null;
+
+			try {
+				response = httpClient.execute(httpHead);
+				String location = ".client-steering-target-2.thecdn.example.com:8090" + path;
+
+				assertThat("Failed getting 302 for request " + httpHead.getFirstHeader("Host").getValue(), response.getStatusLine().getStatusCode(), equalTo(302));
+				assertThat(response.getFirstHeader("Location").getValue(), endsWith(location));
+				assertThat("Failed getting null body for HEAD request", response.getEntity(), nullValue());
+			} finally {
+				if (response != null) { response.close(); }
+			}
+		}
+	}
 }


[02/13] incubator-trafficcontrol git commit: [TC-179] Fix for an NPE on caused by a null delivery service when sending traffic to a bypass destination, and slight formatting change in HTTPRouteResult.

Posted by ne...@apache.org.
[TC-179] Fix for an NPE on caused by a null delivery service when sending traffic to a bypass destination, and slight formatting change in HTTPRouteResult.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/bd5b48e6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/bd5b48e6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/bd5b48e6

Branch: refs/heads/master
Commit: bd5b48e661692c5b0b6a9dfb26abaed4a2655eb4
Parents: 4190b7d
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Thu Mar 9 11:21:13 2017 -0700
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:14 2017 -0600

----------------------------------------------------------------------
 .../traffic_router/core/http/RouterFilter.java      | 16 ++++++++++------
 .../traffic_router/core/router/HTTPRouteResult.java |  4 ++--
 2 files changed, 12 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/bd5b48e6/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
index fc1b742..58c49bc 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
@@ -152,27 +152,31 @@ public class RouterFilter extends OncePerRequestFilter {
 	private void setSingleResponse(final HTTPRouteResult routeResult, final HttpServletRequest httpServletRequest, final HttpServletResponse response, final HTTPAccessRecord.Builder httpAccessRecordBuilder) throws IOException, JSONException {
 		final String redirect = httpServletRequest.getParameter(REDIRECT_QUERY_PARAM);
 		final String format = httpServletRequest.getParameter("format");
-		final DeliveryService deliveryService = routeResult.getDeliveryService();
 		final URL location = routeResult.getUrl();
-		final Map<String, String> responseHeaders = deliveryService.getResponseHeaders();
 
-		for (final String key : responseHeaders.keySet()) {
-			response.addHeader(key, responseHeaders.get(key));
-		}
+		if (routeResult.getDeliveryService() != null) {
+			final DeliveryService deliveryService = routeResult.getDeliveryService();
+			final Map<String, String> responseHeaders = deliveryService.getResponseHeaders();
 
-		httpAccessRecordBuilder.responseURL(location);
+			for (final String key : responseHeaders.keySet()) {
+				response.addHeader(key, responseHeaders.get(key));
+			}
+		}
 
 		if ("false".equalsIgnoreCase(redirect)) {
 			response.setContentType("application/json");
 			response.getWriter().println(routeResult.toMultiLocationJSONString());
 			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK);
+			httpAccessRecordBuilder.responseURLs(routeResult.getUrls());
 		} else if ("json".equals(format)) {
 			response.setContentType("application/json");
 			response.getWriter().println(routeResult.toLocationJSONString());
 			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK);
+			httpAccessRecordBuilder.responseURL(location);
 		} else {
 			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_MOVED_TEMPORARILY);
 			response.sendRedirect(location.toString());
+			httpAccessRecordBuilder.responseURL(location);
 		}
 	}
 

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/bd5b48e6/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
index 1b3d756..4ad828e 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
@@ -48,7 +48,7 @@ public class HTTPRouteResult implements RouteResult {
 	}
 
 	public URL getUrl() {
-		return (!urls.isEmpty()) ? urls.get(0) : null;
+		return !urls.isEmpty() ? urls.get(0) : null;
 	}
 
 	public void setUrl(final URL url) {
@@ -65,7 +65,7 @@ public class HTTPRouteResult implements RouteResult {
 	}
 
 	public DeliveryService getDeliveryService() {
-		return (!deliveryServices.isEmpty()) ? deliveryServices.get(0) : null;
+		return !deliveryServices.isEmpty() ? deliveryServices.get(0) : null;
 	}
 
 	public void setDeliveryService(final DeliveryService deliveryService) {


[09/13] incubator-trafficcontrol git commit: [TC-179] Remove commented code and remove default value for result details to prevent unwanted log spam.

Posted by ne...@apache.org.
[TC-179] Remove commented code and remove default value for result details to prevent unwanted log spam.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/91284c6d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/91284c6d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/91284c6d

Branch: refs/heads/master
Commit: 91284c6d2ad54cc74363408b445b5eace03538bb
Parents: 78e8f94
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Tue Mar 14 09:11:43 2017 -0600
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:15 2017 -0600

----------------------------------------------------------------------
 .../core/router/HTTPRouteResult.java            | 24 --------------------
 .../traffic_router/core/router/StatTracker.java |  2 +-
 2 files changed, 1 insertion(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/91284c6d/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
index 4ad828e..b2ae848 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
@@ -81,34 +81,10 @@ public class HTTPRouteResult implements RouteResult {
 		this.responseCode = rc;
 	}
 
-	/*
-	public JSONObject toJSON() throws JSONException {
-		final JSONObject jo = new JSONObject();
-		jo.put("location", getUrl().toString());
-
-		return jo;
-	}
-	*/
-
 	public String toLocationJSONString() {
 		return "{\"location\": \"" + getUrl().toString() + "\" }";
 	}
 
-	/*
-	public JSONObject toJSON() throws JSONException {
-		final JSONObject jo = new JSONObject();
-		final JSONArray locations = new JSONArray();
-
-		for (final URL url : urls) {
-			locations.put(url);
-		}
-
-		jo.put("locations", locations);
-
-		return jo;
-	}
-	*/
-
 	public String toMultiLocationJSONString() {
 		final StringJoiner joiner = new StringJoiner("\",\"");
 

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/91284c6d/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java
index 5988a81..5e8ed71 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java
@@ -118,7 +118,7 @@ public class StatTracker {
 		RouteType routeType;
 		String fqdn;
 		ResultType result = ResultType.ERROR;
-		ResultDetails resultDetails = ResultDetails.NO_DETAILS;
+		ResultDetails resultDetails;
 		Geolocation resultLocation;
 
 		Geolocation clientGeolocation; // the GEO info always retrieved from GEO DB, not from Cache Location


[10/13] incubator-trafficcontrol git commit: [TC-179] Fix for NPE on DS miss.

Posted by ne...@apache.org.
[TC-179] Fix for NPE on DS miss.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/dc5bb7b8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/dc5bb7b8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/dc5bb7b8

Branch: refs/heads/master
Commit: dc5bb7b8c1b4822ba81643ef4da0e2254cce306b
Parents: cc29ee4
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Fri Mar 10 21:34:51 2017 -0700
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:15 2017 -0600

----------------------------------------------------------------------
 .../traffic_control/traffic_router/core/http/RouterFilter.java   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/dc5bb7b8/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
index a2bff66..6a1e3a8 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
@@ -85,7 +85,7 @@ public class RouterFilter extends OncePerRequestFilter {
 			final TrafficRouter trafficRouter = trafficRouterManager.getTrafficRouter();
 			routeResult = trafficRouter.route(request, track);
 
-			if (routeResult == null || routeResult.getUrls().isEmpty()) {
+			if (routeResult == null || routeResult.getUrl() == null) {
 				setErrorResponseCode(response, httpAccessRecordBuilder, routeResult);
 			} else if (routeResult.isMultiRouteRequest()) {
 				setMultiResponse(routeResult, httpServletRequest, response, httpAccessRecordBuilder);
@@ -174,8 +174,8 @@ public class RouterFilter extends OncePerRequestFilter {
 			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK);
 			httpAccessRecordBuilder.responseURL(location);
 		} else {
-			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_MOVED_TEMPORARILY);
 			response.sendRedirect(location.toString());
+			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_MOVED_TEMPORARILY);
 			httpAccessRecordBuilder.responseURL(location);
 		}
 	}


[07/13] incubator-trafficcontrol git commit: [TC-179] Add missing result details to access log and slight fixes elsewhere.

Posted by ne...@apache.org.
[TC-179] Add missing result details to access log and slight fixes elsewhere.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/76e176dd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/76e176dd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/76e176dd

Branch: refs/heads/master
Commit: 76e176dd6d6f3b27403029f0d7dc69effc9d0299
Parents: 3085924
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Fri Mar 10 12:03:28 2017 -0700
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:14 2017 -0600

----------------------------------------------------------------------
 .../traffic_router/core/dns/ZoneManager.java           |  1 +
 .../traffic_router/core/http/RouterFilter.java         |  1 +
 .../traffic_router/core/router/TrafficRouter.java      | 13 ++++++++++---
 3 files changed, 12 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/76e176dd/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/ZoneManager.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/ZoneManager.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/ZoneManager.java
index 504375c..1713bd5 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/ZoneManager.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/ZoneManager.java
@@ -734,6 +734,7 @@ public class ZoneManager extends Resolver {
 			LOGGER.error(e.getMessage(), e);
 		} finally {
 			builder.resultType(track.getResult());
+			builder.resultDetails(track.getResultDetails());
 			builder.resultLocation(track.getResultLocation());
 			statTracker.saveTrack(track);
 		}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/76e176dd/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
index 4b7ef57..a2bff66 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
@@ -111,6 +111,7 @@ public class RouterFilter extends OncePerRequestFilter {
 			final Map<String,String> accessRequestHeaders = new HttpAccessRequestHeaders().makeMap(httpServletRequest, requestHeaders);
 
 			final HTTPAccessRecord access = httpAccessRecordBuilder.resultType(track.getResult())
+				.resultDetails(track.getResultDetails())
 				.resultLocation(track.getResultLocation())
 				.requestHeaders(accessRequestHeaders)
 				.regionalGeoResult(track.getRegionalGeoResult())

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/76e176dd/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
index 2eccbd6..bcde94b 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
@@ -442,8 +442,15 @@ public class TrafficRouter {
 
 	public HTTPRouteResult multiRoute(final HTTPRequest request, final Track track) throws MalformedURLException, GeolocationException {
 		final DeliveryService entryDeliveryService = cacheRegister.getDeliveryService(request, true);
+
+		if (isTlsMismatch(request, entryDeliveryService)) {
+			track.setResult(ResultType.ERROR);
+			track.setResultDetails(ResultDetails.DS_TLS_MISMATCH);
+			return null;
+		}
+
 		final HTTPRouteResult routeResult = new HTTPRouteResult(true);
-		final List<DeliveryService> deliveryServices = getDeliveryServices(request, track);
+		final List<DeliveryService> deliveryServices = getDeliveryServices(request, track, entryDeliveryService);
 
 		if (deliveryServices == null) {
 			return null;
@@ -526,8 +533,8 @@ public class TrafficRouter {
 		return routeResult;
 	}
 
-	private List<DeliveryService> getDeliveryServices(final HTTPRequest request, final Track track) {
-		final List<DeliveryService> deliveryServices = consistentHashMultiDeliveryService(cacheRegister.getDeliveryService(request, true), request.getPath());
+	private List<DeliveryService> getDeliveryServices(final HTTPRequest request, final Track track, final DeliveryService entryDeliveryService) {
+		final List<DeliveryService> deliveryServices = consistentHashMultiDeliveryService(entryDeliveryService, request.getPath());
 
 		if (deliveryServices == null || deliveryServices.isEmpty()) {
 			track.setResult(ResultType.DS_MISS);


[06/13] incubator-trafficcontrol git commit: [TC-179] Add log4j.jar to list of files to symlink into /opt/tomcat/lib to ensure it's on Tomcat's CLASSPATH.

Posted by ne...@apache.org.
[TC-179] Add log4j.jar to list of files to symlink into /opt/tomcat/lib to ensure it's on Tomcat's CLASSPATH.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/4d503290
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/4d503290
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/4d503290

Branch: refs/heads/master
Commit: 4d5032908aabbd88d411c0bba0355be58f7dc6a8
Parents: bd5b48e
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Fri Mar 10 10:02:47 2017 -0700
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:14 2017 -0600

----------------------------------------------------------------------
 traffic_router/core/src/main/scripts/postinstall.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4d503290/traffic_router/core/src/main/scripts/postinstall.sh
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/scripts/postinstall.sh b/traffic_router/core/src/main/scripts/postinstall.sh
index 5cee661..f1b9fc2 100644
--- a/traffic_router/core/src/main/scripts/postinstall.sh
+++ b/traffic_router/core/src/main/scripts/postinstall.sh
@@ -16,7 +16,7 @@
 chkconfig --add tomcat
 chkconfig tomcat on
 
-for JAR in traffic_router_connector.jar traffic_router_shared.jar jackson-core.jar jackson-annotations.jar jackson-databind.jar; do
+for JAR in traffic_router_connector.jar traffic_router_shared.jar jackson-core.jar jackson-annotations.jar jackson-databind.jar log4j.jar; do
     if [ ! -e /opt/tomcat/lib/$JAR ]; then
         echo "Creating symbolic link from /opt/traffic_router/lib/$JAR to /opt/tomcat/lib"
         /bin/ln -s /opt/traffic_router/lib/$JAR /opt/tomcat/lib/$JAR


[13/13] incubator-trafficcontrol git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol

Posted by ne...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/83916771
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/83916771
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/83916771

Branch: refs/heads/master
Commit: 83916771059b4497c95e49b68862f3422f6b5cb5
Parents: 7800b2a 0a22984
Author: Dave Neuman <ne...@apache.org>
Authored: Tue Mar 14 09:40:14 2017 -0600
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:40:14 2017 -0600

----------------------------------------------------------------------
 traffic_ops/app/lib/API/Deliveryservice.pm | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)
----------------------------------------------------------------------



[08/13] incubator-trafficcontrol git commit: [TC-179] Reverted changes to HttpDataServer.

Posted by ne...@apache.org.
[TC-179] Reverted changes to HttpDataServer.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/cc29ee4f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/cc29ee4f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/cc29ee4f

Branch: refs/heads/master
Commit: cc29ee4f6011b4fd2dab95d09af74174e402efea
Parents: 9b73186
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Fri Mar 10 14:55:59 2017 -0700
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:15 2017 -0600

----------------------------------------------------------------------
 .../traffic_router/core/external/HttpDataServer.java          | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/cc29ee4f/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java
index e321c9a..3929a7a 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/HttpDataServer.java
@@ -53,14 +53,11 @@ public class HttpDataServer implements HttpHandler {
 //		}
 //	}
 
-	public void start(int port) throws IOException, InterruptedException {
-		int s = 30;
+	public void start(int port) throws IOException {
 		httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port),10);
 		httpServer.createContext("/", this);
 		httpServer.start();
-		System.out.println(">>>>>>>>>>>>> Started Fake Http Data Server at " + port + "; sleeping for " + s + "s");
-
-		Thread.sleep(s * 1000L);
+		System.out.println(">>>>>>>>>>>>> Started Fake Http Data Server at " + port);
 	}
 
 	public void stop() {


[12/13] incubator-trafficcontrol git commit: This closes #348

Posted by ne...@apache.org.
This closes #348


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/7800b2a3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/7800b2a3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/7800b2a3

Branch: refs/heads/master
Commit: 7800b2a3b408efa967a1a5d9dec67687ce747329
Parents: 91284c6
Author: Dave Neuman <ne...@apache.org>
Authored: Tue Mar 14 09:40:01 2017 -0600
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:40:01 2017 -0600

----------------------------------------------------------------------

----------------------------------------------------------------------



[03/13] incubator-trafficcontrol git commit: [TC-179] Adds support for client steering/mult-location responses in Traffic Router, plus fixed a few bugs in the process.

Posted by ne...@apache.org.
[TC-179] Adds support for client steering/mult-location responses in Traffic Router, plus fixed a few bugs in the process.


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/4190b7d2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/4190b7d2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/4190b7d2

Branch: refs/heads/master
Commit: 4190b7d27dc81d538a442a94f8e276d3b39dfbb4
Parents: 271b8b9
Author: Jeff Elsloo <je...@cable.comcast.com>
Authored: Wed Mar 8 15:21:20 2017 -0700
Committer: Dave Neuman <ne...@apache.org>
Committed: Tue Mar 14 09:39:14 2017 -0600

----------------------------------------------------------------------
 .../core/cache/CacheRegister.java               |   2 +-
 .../traffic_router/core/ds/Steering.java        |  10 +
 .../core/hash/ConsistentHasher.java             |  11 +-
 .../core/hash/DefaultHashable.java              |   4 +-
 .../core/http/HTTPAccessEventBuilder.java       |   6 +-
 .../core/http/HTTPAccessRecord.java             |  17 +
 .../traffic_router/core/http/RouterFilter.java  | 104 ++++--
 .../core/loc/AbstractServiceUpdater.java        |   7 +-
 .../core/router/HTTPRouteResult.java            |  98 +++++-
 .../traffic_router/core/router/StatTracker.java |   4 +-
 .../core/router/TrafficRouter.java              | 139 +++++++-
 .../core/external/RouterTest.java               |  18 +-
 .../core/external/SteeringTest.java             |  80 +++++
 .../core/http/HTTPAccessEventBuilderTest.java   |  40 ++-
 .../TrafficRouterHTTPRoutingMissesTest.java     |   1 +
 .../core/router/TrafficRouterTest.java          |   1 +
 .../resources/internal/api/1.2/steering.json    |  28 ++
 .../src/test/resources/publish/CrConfig.json    | 328 +++++++++++++++++++
 .../core/src/test/resources/publish/CrStates    |  24 ++
 19 files changed, 844 insertions(+), 78 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/cache/CacheRegister.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/cache/CacheRegister.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/cache/CacheRegister.java
index cf13778..c7626e2 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/cache/CacheRegister.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/cache/CacheRegister.java
@@ -102,8 +102,8 @@ public class CacheRegister implements CacheLocationManager {
 	 * @return the DeliveryService that matches the request
 	 */
 	public DeliveryService getDeliveryService(final Request request, final boolean isHttp) {
-
 		TreeSet<DeliveryServiceMatcher> matchers = dnsServiceMatchers;
+
 		if (isHttp) {
 			matchers = httpServiceMatchers;
 		}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/ds/Steering.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/ds/Steering.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/ds/Steering.java
index d42a9d8..841df23 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/ds/Steering.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/ds/Steering.java
@@ -24,6 +24,8 @@ public class Steering {
 	@JsonProperty
 	private String deliveryService;
 	@JsonProperty
+	private boolean clientSteering;
+	@JsonProperty
 	private List<SteeringTarget> targets = new ArrayList<SteeringTarget>();
 	@JsonProperty
 	private List<SteeringFilter> filters = new ArrayList<SteeringFilter>();
@@ -44,6 +46,14 @@ public class Steering {
 		this.deliveryService = id;
 	}
 
+	public boolean isClientSteering() {
+		return clientSteering;
+	}
+
+	public void setClientSteering(final boolean clientSteering) {
+		this.clientSteering = clientSteering;
+	}
+
 	public List<SteeringFilter> getFilters() {
 		return filters;
 	}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/ConsistentHasher.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/ConsistentHasher.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/ConsistentHasher.java
index ff1347e..4dd71d0 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/ConsistentHasher.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/ConsistentHasher.java
@@ -33,23 +33,26 @@ public class ConsistentHasher {
 		return selectHashables(hashables, dispersion, s).get(0);
 	}
 
-	public <T extends Hashable> List<T> selectHashables(final List<T> hashables, final Dispersion dispersion, final String s) {
+	public <T extends Hashable> List<T> selectHashables(final List<T> hashables, final String s) {
+		return selectHashables(hashables, null, s);
+	}
 
+	public <T extends Hashable> List<T> selectHashables(final List<T> hashables, final Dispersion dispersion, final String s) {
 		final SortedMap<Double, T> sortedHashables = sortHashables(hashables, s);
 		final List<T> selectedHashables = new ArrayList<T>();
 
 		for (final T hashable : sortedHashables.values()) {
-			if (selectedHashables.size() >= dispersion.getLimit()) {
+			if (dispersion != null && selectedHashables.size() >= dispersion.getLimit()) {
 				break;
 			}
 
 			selectedHashables.add(hashable);
 		}
-		if (dispersion.isShuffled()) {
+		if (dispersion != null && dispersion.isShuffled()) {
 			Collections.shuffle(selectedHashables);
 		}
 
-		return (dispersion.getLimit() <= selectedHashables.size()) ? selectedHashables.subList(0, dispersion.getLimit()) : selectedHashables;
+		return (dispersion != null && dispersion.getLimit() <= selectedHashables.size()) ? selectedHashables.subList(0, dispersion.getLimit()) : selectedHashables;
 	}
 
 	private <T extends Hashable> SortedMap<Double, T> sortHashables(final List<T> hashables, final String s) {

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/DefaultHashable.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/DefaultHashable.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/DefaultHashable.java
index 2dfe264..a79a323 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/DefaultHashable.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/hash/DefaultHashable.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.TreeSet;
 
 public class DefaultHashable implements Hashable {
+	private static final int DEFAULT_HASH_COUNT = 1000;
 	private Double[] hashes;
 
 	@Override
@@ -31,8 +32,9 @@ public class DefaultHashable implements Hashable {
 	public DefaultHashable generateHashes(final String hashId, final int hashCount) {
 		final TreeSet<Double> hashSet = new TreeSet<Double>();
 		final MD5HashFunction hashFunction = new MD5HashFunction();
+		final int count = (hashCount > 0) ? hashCount : DEFAULT_HASH_COUNT;
 
-		for (int i = 0; i < hashCount; i++) {
+		for (int i = 0; i < count; i++) {
 			hashSet.add(hashFunction.hash(hashId + "--" + i));
 		}
 

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilder.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilder.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilder.java
index 96803c7..0609e75 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilder.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilder.java
@@ -102,7 +102,6 @@ public class HTTPAccessEventBuilder {
             rloc = decimalFormat.format(resultLocation.getLatitude()) + "," + decimalFormat.format(resultLocation.getLongitude());
         }
 
-
         final String xMmClientIpHeader = httpServletRequest.getHeader(HTTPRequest.X_MM_CLIENT_IP);
         final String fakeIpParameter = httpServletRequest.getParameter(HTTPRequest.FAKE_IP);
 
@@ -141,9 +140,12 @@ public class HTTPAccessEventBuilder {
             stringBuilder.append(" pssc=").append(pssc).append(" ttms=").append(String.format("%.03f",ttms));
         }
 
-        final String respurl = " rurl=\"" + formatObject(httpAccessRecord.getResponseURL()) + "\" ";
+        final String respurl = " rurl=\"" + formatObject(httpAccessRecord.getResponseURL()) + "\"";
         stringBuilder.append(respurl);
 
+        final String respurls = " rurls=\"" + formatObject(httpAccessRecord.getResponseURLs()) + "\" ";
+        stringBuilder.append(respurls);
+
         stringBuilder.append(formatRequestHeaders(httpAccessRecord.getRequestHeaders()));
         return stringBuilder.toString();
     }

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessRecord.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessRecord.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessRecord.java
index 38d0924..e5375b6 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessRecord.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessRecord.java
@@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletRequest;
 import java.net.URL;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 // Using Josh Bloch Builder pattern so suppress these warnings.
@@ -37,6 +38,7 @@ public class HTTPAccessRecord {
     private final long requestNanoTime;
     private final HttpServletRequest request;
     private final URL responseURL;
+    private final List<URL> responseURLs;
     private final int responseCode;
     private final ResultType resultType;
     private final String rerr;
@@ -61,6 +63,10 @@ public class HTTPAccessRecord {
         return responseURL;
     }
 
+    public List<URL> getResponseURLs() {
+        return responseURLs;
+    }
+
     public ResultType getResultType() {
         return resultType;
     }
@@ -94,6 +100,7 @@ public class HTTPAccessRecord {
         private final HttpServletRequest request;
         private int responseCode = -1;
         private URL responseURL;
+        private List<URL> responseURLs;
         private ResultType resultType;
         private String rerr;
         private ResultDetails resultDetails;
@@ -118,6 +125,7 @@ public class HTTPAccessRecord {
             this.requestDate = httpAccessRecord.requestDate;
             this.request = httpAccessRecord.request;
             this.responseURL = httpAccessRecord.responseURL;
+            this.responseURLs = httpAccessRecord.responseURLs;
             this.responseCode = httpAccessRecord.responseCode;
             this.requestNanoTime = httpAccessRecord.requestNanoTime;
         }
@@ -132,6 +140,11 @@ public class HTTPAccessRecord {
             return this;
         }
 
+        public Builder responseURLs(final List<URL> responseURLs) {
+            this.responseURLs = responseURLs;
+            return this;
+        }
+
         public Builder resultType(final ResultType resultType) {
             this.resultType = resultType;
             return this;
@@ -172,6 +185,7 @@ public class HTTPAccessRecord {
         request = builder.request;
         responseCode = builder.responseCode;
         responseURL = builder.responseURL;
+        responseURLs = builder.responseURLs;
         resultType = builder.resultType;
         rerr = builder.rerr;
         resultDetails = builder.resultDetails;
@@ -193,6 +207,7 @@ public class HTTPAccessRecord {
         if (requestDate != null ? !requestDate.equals(that.requestDate) : that.requestDate != null) return false;
         if (request != null ? !request.equals(that.request) : that.request != null) return false;
         if (responseURL != null ? !responseURL.equals(that.responseURL) : that.responseURL != null) return false;
+        if (responseURLs != null ? !responseURLs.equals(that.responseURLs) : that.responseURLs != null) return false;
         if (resultType != that.resultType) return false;
         if (rerr != null ? !rerr.equals(that.rerr) : that.rerr != null) return false;
         if (resultDetails != that.resultDetails) return false;
@@ -210,6 +225,7 @@ public class HTTPAccessRecord {
         result = 31 * result + (int) (requestNanoTime ^ (requestNanoTime >>> 32));
         result = 31 * result + (request != null ? request.hashCode() : 0);
         result = 31 * result + (responseURL != null ? responseURL.hashCode() : 0);
+        result = 31 * result + (responseURLs != null ? responseURLs.hashCode() : 0);
         result = 31 * result + responseCode;
         result = 31 * result + (resultType != null ? resultType.hashCode() : 0);
         result = 31 * result + (rerr != null ? rerr.hashCode() : 0);
@@ -226,6 +242,7 @@ public class HTTPAccessRecord {
                 "requestDate=" + requestDate +
                 ", request=" + request +
                 ", responseURL=" + responseURL +
+                ", responseURLs=" + responseURLs +
                 ", responseCode=" + responseCode +
                 ", resultType=" + resultType +
                 ", rerr='" + rerr + '\'' +

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
index 9a09907..fc1b742 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/http/RouterFilter.java
@@ -23,6 +23,7 @@ import com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouter;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouterManager;
 import com.comcast.cdn.traffic_control.traffic_router.geolocation.GeolocationException;
 import org.apache.log4j.Logger;
+import org.apache.wicket.ajax.json.JSONException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.filter.OncePerRequestFilter;
 
@@ -39,6 +40,7 @@ import java.util.Set;
 
 public class RouterFilter extends OncePerRequestFilter {
 	private static final Logger ACCESS = Logger.getLogger("com.comcast.cdn.traffic_control.traffic_router.core.access");
+	public static final String REDIRECT_QUERY_PARAM = "trred";
 
 	@Autowired
 	private TrafficRouterManager trafficRouterManager;
@@ -77,51 +79,34 @@ public class RouterFilter extends OncePerRequestFilter {
 
 	private void writeHttpResponse(final HttpServletResponse response, final HttpServletRequest httpServletRequest,
 	                               final HTTPRequest request, final StatTracker.Track track, final HTTPAccessRecord httpAccessRecord) throws IOException {
-		final String format = httpServletRequest.getParameter("format");
 		final HTTPAccessRecord.Builder httpAccessRecordBuilder = new HTTPAccessRecord.Builder(httpAccessRecord);
-		DeliveryService deliveryService = null;
+		HTTPRouteResult routeResult = null;
+
 		try {
 			final TrafficRouter trafficRouter = trafficRouterManager.getTrafficRouter();
-			final HTTPRouteResult routeResult = trafficRouter.route(request, track);
+			routeResult = trafficRouter.route(request, track);
 
-			if (routeResult != null) {
-				deliveryService = routeResult.getDeliveryService();
-			}
-
-			if (routeResult == null || routeResult.getUrl() == null) {
+			if (routeResult == null || routeResult.getUrls().isEmpty()) {
 				setErrorResponseCode(response, httpAccessRecordBuilder, routeResult);
+			} else if (routeResult.isMultiRouteRequest()) {
+				setMultiResponse(routeResult, httpServletRequest, response, httpAccessRecordBuilder);
 			} else {
-				final URL location = routeResult.getUrl();
-				final Map<String, String> responseHeaders = deliveryService.getResponseHeaders();
-
-				for (final String key : responseHeaders.keySet()) {
-					response.addHeader(key, responseHeaders.get(key));
-				}
-
-				httpAccessRecordBuilder.responseURL(location);
-
-				if("json".equals(format)) {
-					response.setContentType("application/json"); // "text/plain"
-					response.getWriter().println("{\"location\": \""+location.toString()+"\" }");
-					httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK);
-				} else {
-					httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_MOVED_TEMPORARILY);
-					response.sendRedirect(location.toString());
-				}
+				setSingleResponse(routeResult, httpServletRequest, response, httpAccessRecordBuilder);
 			}
 		} catch (final IOException e) {
 			httpAccessRecordBuilder.responseCode(-1);
 			httpAccessRecordBuilder.responseURL(null);
 			httpAccessRecordBuilder.rerr(e.getMessage());
 			throw e;
-		} catch (GeolocationException e) {
+		} catch (GeolocationException | JSONException e) {
 			httpAccessRecordBuilder.responseCode(-1);
 			httpAccessRecordBuilder.responseURL(null);
 			httpAccessRecordBuilder.rerr(e.getMessage());
 		} finally {
 			final Set<String> requestHeaders = trafficRouterManager.getTrafficRouter().getRequestHeaders();
-			if (deliveryService != null) {
-				requestHeaders.addAll(deliveryService.getRequestHeaders());
+
+			if (routeResult != null && routeResult.getRequestHeaders() != null) {
+				requestHeaders.addAll(routeResult.getRequestHeaders());
 			}
 
 			final Map<String,String> accessRequestHeaders = new HttpAccessRequestHeaders().makeMap(httpServletRequest, requestHeaders);
@@ -136,12 +121,67 @@ public class RouterFilter extends OncePerRequestFilter {
 		}
 	}
 
+	private void setMultiResponse(final HTTPRouteResult routeResult, final HttpServletRequest httpServletRequest, final HttpServletResponse response, final HTTPAccessRecord.Builder httpAccessRecordBuilder) throws IOException, JSONException {
+		for (final DeliveryService deliveryService : routeResult.getDeliveryServices()) {
+			final Map<String, String> responseHeaders = deliveryService.getResponseHeaders();
+
+			for (final String key : responseHeaders.keySet()) {
+				// if two DSs append the same header, the last one wins; no way around it unless we enforce unique response headers between subordinate DSs
+				response.addHeader(key, responseHeaders.get(key));
+			}
+		}
+
+		final String redirect = httpServletRequest.getParameter(REDIRECT_QUERY_PARAM);
+
+		response.setContentType("application/json");
+		response.getWriter().println(routeResult.toMultiLocationJSONString());
+		httpAccessRecordBuilder.responseURLs(routeResult.getUrls());
+
+		// don't actually parse the boolean value; trred would always be false unless the query param is "true"
+		if ("false".equalsIgnoreCase(redirect)) {
+			response.setStatus(HttpServletResponse.SC_OK);
+			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK);
+		} else {
+			response.setHeader("Location", routeResult.getUrl().toString());
+			response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
+			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_MOVED_TEMPORARILY);
+			httpAccessRecordBuilder.responseURL(routeResult.getUrl());
+		}
+	}
+
+	private void setSingleResponse(final HTTPRouteResult routeResult, final HttpServletRequest httpServletRequest, final HttpServletResponse response, final HTTPAccessRecord.Builder httpAccessRecordBuilder) throws IOException, JSONException {
+		final String redirect = httpServletRequest.getParameter(REDIRECT_QUERY_PARAM);
+		final String format = httpServletRequest.getParameter("format");
+		final DeliveryService deliveryService = routeResult.getDeliveryService();
+		final URL location = routeResult.getUrl();
+		final Map<String, String> responseHeaders = deliveryService.getResponseHeaders();
+
+		for (final String key : responseHeaders.keySet()) {
+			response.addHeader(key, responseHeaders.get(key));
+		}
+
+		httpAccessRecordBuilder.responseURL(location);
+
+		if ("false".equalsIgnoreCase(redirect)) {
+			response.setContentType("application/json");
+			response.getWriter().println(routeResult.toMultiLocationJSONString());
+			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK);
+		} else if ("json".equals(format)) {
+			response.setContentType("application/json");
+			response.getWriter().println(routeResult.toLocationJSONString());
+			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK);
+		} else {
+			httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_MOVED_TEMPORARILY);
+			response.sendRedirect(location.toString());
+		}
+	}
+
 	private void setErrorResponseCode(final HttpServletResponse response,
-	                                  final HTTPAccessRecord.Builder httpAccessRecordBuilder, final HTTPRouteResult routeResult) throws IOException {
+	                                  final HTTPAccessRecord.Builder httpAccessRecordBuilder, final HTTPRouteResult result) throws IOException {
 
-		if (routeResult != null && routeResult.getResponseCode() > 0) {
-			httpAccessRecordBuilder.responseCode(routeResult.getResponseCode());
-			response.sendError(routeResult.getResponseCode());
+		if (result != null && result.getResponseCode() > 0) {
+			httpAccessRecordBuilder.responseCode(result.getResponseCode());
+			response.sendError(result.getResponseCode());
 			return;
 		}
 

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java
index 4f63cc9..b35b7e6 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/loc/AbstractServiceUpdater.java
@@ -91,10 +91,15 @@ public abstract class AbstractServiceUpdater {
 	};
 
 	public void init() {
+		if (dataBaseURL == null) {
+			LOGGER.warn("[" + getClass().getSimpleName() + "] Unable to fetch external resource; init() called with a null databaseUrl");
+			return;
+		}
+
 		final long pollingInterval = getPollingInterval();
 		final Date nextFetchDate = new Date(System.currentTimeMillis() + pollingInterval);
 		LOGGER.info("[" + getClass().getSimpleName() + "] Fetching external resource " + dataBaseURL + " at interval: " + pollingInterval + " : " + TimeUnit.MILLISECONDS + " next update occurrs at " + nextFetchDate);
-		scheduledService = executorService.scheduleWithFixedDelay(updater, pollingInterval, pollingInterval, TimeUnit.MILLISECONDS);
+		scheduledService = executorService.scheduleWithFixedDelay(updater, 0, pollingInterval, TimeUnit.MILLISECONDS); // start immediately with pollingInterval fixed delay
 	}
 
 	@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
index 2c7e87f..1b3d756 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/HTTPRouteResult.java
@@ -16,25 +16,61 @@
 package com.comcast.cdn.traffic_control.traffic_router.core.router;
 
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.StringJoiner;
 
 import com.comcast.cdn.traffic_control.traffic_router.core.ds.DeliveryService;
 
 public class HTTPRouteResult implements RouteResult {
-	private URL url;
+	final private List<URL> urls = new ArrayList<URL>();
+	final private List<DeliveryService> deliveryServices = new ArrayList<DeliveryService>();
+	final private boolean multiRouteRequest;
 	private int responseCode;
-	private DeliveryService deliveryService;
+
+	public HTTPRouteResult(final boolean multiRouteRequest) {
+		this.multiRouteRequest = multiRouteRequest;
+	}
 
 	@Override
 	public Object getResult() {
-		return getUrl();
+		return getUrls();
+	}
+
+	public List<URL> getUrls() {
+		return urls;
+	}
+
+	public void addUrl(final URL url) {
+		urls.add(url);
 	}
 
 	public URL getUrl() {
-		return url;
+		return (!urls.isEmpty()) ? urls.get(0) : null;
 	}
 
 	public void setUrl(final URL url) {
-		this.url = url;
+		urls.clear();
+		urls.add(url);
+	}
+
+	public List<DeliveryService> getDeliveryServices() {
+		return deliveryServices;
+	}
+
+	public void addDeliveryService(final DeliveryService deliveryService) {
+		deliveryServices.add(deliveryService);
+	}
+
+	public DeliveryService getDeliveryService() {
+		return (!deliveryServices.isEmpty()) ? deliveryServices.get(0) : null;
+	}
+
+	public void setDeliveryService(final DeliveryService deliveryService) {
+		deliveryServices.clear();
+		deliveryServices.add(deliveryService);
 	}
 
 	public int getResponseCode() {
@@ -45,11 +81,55 @@ public class HTTPRouteResult implements RouteResult {
 		this.responseCode = rc;
 	}
 
-	public DeliveryService getDeliveryService() {
-		return deliveryService;
+	/*
+	public JSONObject toJSON() throws JSONException {
+		final JSONObject jo = new JSONObject();
+		jo.put("location", getUrl().toString());
+
+		return jo;
 	}
+	*/
 
-	public void setDeliveryService(final DeliveryService deliveryService) {
-		this.deliveryService = deliveryService;
+	public String toLocationJSONString() {
+		return "{\"location\": \"" + getUrl().toString() + "\" }";
+	}
+
+	/*
+	public JSONObject toJSON() throws JSONException {
+		final JSONObject jo = new JSONObject();
+		final JSONArray locations = new JSONArray();
+
+		for (final URL url : urls) {
+			locations.put(url);
+		}
+
+		jo.put("locations", locations);
+
+		return jo;
+	}
+	*/
+
+	public String toMultiLocationJSONString() {
+		final StringJoiner joiner = new StringJoiner("\",\"");
+
+		for (final URL url : urls) {
+			joiner.add(url.toString());
+		}
+
+		return "{\"locations\":[\"" + joiner.toString() + "\"]}";
+	}
+
+	public Set<String> getRequestHeaders() {
+		final Set<String> requestHeaders = new HashSet<String>();
+
+		for (final DeliveryService deliveryService : getDeliveryServices()) {
+			requestHeaders.addAll(deliveryService.getRequestHeaders());
+		}
+
+		return requestHeaders;
+	}
+
+	public boolean isMultiRouteRequest() {
+		return multiRouteRequest;
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java
index 53c67aa..5988a81 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/StatTracker.java
@@ -110,7 +110,7 @@ public class StatTracker {
 		}
 
 		public enum ResultDetails {
-			NO_DETAILS, DS_NOT_FOUND, DS_NO_BYPASS, DS_BYPASS, DS_CZ_ONLY, DS_CLIENT_GEO_UNSUPPORTED, GEO_NO_CACHE_FOUND,
+			NO_DETAILS, DS_NOT_FOUND, DS_TLS_MISMATCH, DS_NO_BYPASS, DS_BYPASS, DS_CZ_ONLY, DS_CLIENT_GEO_UNSUPPORTED, GEO_NO_CACHE_FOUND,
 			REGIONAL_GEO_NO_RULE, REGIONAL_GEO_ALTERNATE_WITHOUT_CACHE, REGIONAL_GEO_ALTERNATE_WITH_CACHE
 		}
 
@@ -120,7 +120,7 @@ public class StatTracker {
 		ResultType result = ResultType.ERROR;
 		ResultDetails resultDetails = ResultDetails.NO_DETAILS;
 		Geolocation resultLocation;
-		
+
 		Geolocation clientGeolocation; // the GEO info always retrieved from GEO DB, not from Cache Location
 		boolean isClientGeolocationQueried;
 

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
index 021fd48..2eccbd6 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
@@ -290,7 +290,7 @@ public class TrafficRouter {
 		}
 
 		final List<Cache> caches = getCachesByGeo(deliveryService, clientLocation, track);
-		
+
 		if (caches == null || caches.isEmpty()) {
 			track.setResultDetails(ResultDetails.GEO_NO_CACHE_FOUND);
 		}
@@ -322,6 +322,7 @@ public class TrafficRouter {
 
 		if (caches != null) {
 			track.setResult(ResultType.CZ);
+			track.setClientGeolocation(cacheLocation.getGeolocation());
 			result.setAddresses(inetRecordsFromCaches(ds, caches, request));
 			return result;
 		}
@@ -439,24 +440,65 @@ public class TrafficRouter {
 		return caches;
 	}
 
+	public HTTPRouteResult multiRoute(final HTTPRequest request, final Track track) throws MalformedURLException, GeolocationException {
+		final DeliveryService entryDeliveryService = cacheRegister.getDeliveryService(request, true);
+		final HTTPRouteResult routeResult = new HTTPRouteResult(true);
+		final List<DeliveryService> deliveryServices = getDeliveryServices(request, track);
+
+		if (deliveryServices == null) {
+			return null;
+		}
+
+		for (final DeliveryService deliveryService : deliveryServices) {
+			if (deliveryService.isRegionalGeoEnabled()) {
+				LOGGER.error("Regional Geo Blocking is not supported with multi-route delivery services.. skipping " + entryDeliveryService.getId() + "/" + deliveryService.getId());
+				continue;
+			}
+
+			if (deliveryService.isAvailable()) {
+				final List<Cache> caches = selectCaches(request, deliveryService, track);
+				routeResult.addDeliveryService(deliveryService);
+
+				if (caches != null && !caches.isEmpty()) {
+					final Cache cache = consistentHasher.selectHashable(caches, deliveryService.getDispersion(), request.getPath());
+					routeResult.addUrl(new URL(deliveryService.createURIString(request, cache)));
+				}
+			}
+		}
+
+		if (routeResult.getUrls().isEmpty()) {
+			routeResult.addUrl(entryDeliveryService.getFailureHttpResponse(request, track));
+		}
+
+		return routeResult;
+	}
+
 	public HTTPRouteResult route(final HTTPRequest request, final Track track) throws MalformedURLException, GeolocationException {
 		track.setRouteType(RouteType.HTTP, request.getHostname());
 
+		if (isMultiRouteRequest(request)) {
+			return multiRoute(request, track);
+		} else {
+			return singleRoute(request, track);
+		}
+	}
+
+	public HTTPRouteResult singleRoute(final HTTPRequest request, final Track track) throws MalformedURLException, GeolocationException {
 		final DeliveryService deliveryService = getDeliveryService(request, track);
 
 		if (deliveryService == null) {
 			return null;
 		}
 
-		final HTTPRouteResult routeResult = new HTTPRouteResult();
-
-		routeResult.setDeliveryService(deliveryService);
+		final HTTPRouteResult routeResult = new HTTPRouteResult(false);
 
 		if (!deliveryService.isAvailable()) {
 			routeResult.setUrl(deliveryService.getFailureHttpResponse(request, track));
 			return routeResult;
 		}
 
+		routeResult.setDeliveryService(deliveryService);
+
 		final List<Cache> caches = selectCaches(request, deliveryService, track);
 
 		if (caches == null || caches.isEmpty()) {
@@ -470,6 +512,7 @@ public class TrafficRouter {
 			routeResult.setUrl(deliveryService.getFailureHttpResponse(request, track));
 			return routeResult;
 		}
+
 		final Cache cache = consistentHasher.selectHashable(caches, deliveryService.getDispersion(), request.getPath());
 
 		if (deliveryService.isRegionalGeoEnabled()) {
@@ -479,9 +522,30 @@ public class TrafficRouter {
 
 		final String uriString = deliveryService.createURIString(request, cache);
 		routeResult.setUrl(new URL(uriString));
+
 		return routeResult;
 	}
 
+	private List<DeliveryService> getDeliveryServices(final HTTPRequest request, final Track track) {
+		final List<DeliveryService> deliveryServices = consistentHashMultiDeliveryService(cacheRegister.getDeliveryService(request, true), request.getPath());
+
+		if (deliveryServices == null || deliveryServices.isEmpty()) {
+			track.setResult(ResultType.DS_MISS);
+			track.setResultDetails(ResultDetails.DS_NOT_FOUND);
+			return null;
+		}
+
+		for (final DeliveryService deliveryService : deliveryServices) {
+			if (isTlsMismatch(request, deliveryService)) {
+				track.setResult(ResultType.ERROR);
+				track.setResultDetails(ResultDetails.DS_TLS_MISMATCH);
+				return null;
+			}
+		}
+
+		return deliveryServices;
+	}
+
 	private DeliveryService getDeliveryService(final HTTPRequest request, final Track track) {
 		final String xtcSteeringOption = request.getHeaderValue(XTC_STEERING_OPTION);
 		final DeliveryService deliveryService = consistentHashDeliveryService(cacheRegister.getDeliveryService(request, true), request.getPath(), xtcSteeringOption);
@@ -492,18 +556,25 @@ public class TrafficRouter {
 			return null;
 		}
 
-		if (request.isSecure() && !deliveryService.isSslEnabled()) {
+		if (isTlsMismatch(request, deliveryService)) {
 			track.setResult(ResultType.ERROR);
-			track.setResultDetails(ResultDetails.DS_NOT_FOUND);
+			track.setResultDetails(ResultDetails.DS_TLS_MISMATCH);
 			return null;
 		}
 
+		return deliveryService;
+	}
+
+	private boolean isTlsMismatch(final HTTPRequest request, final DeliveryService deliveryService) {
+		if (request.isSecure() && !deliveryService.isSslEnabled()) {
+			return true;
+		}
+
 		if (!request.isSecure() && !deliveryService.isAcceptHttp()) {
-			track.setResult(ResultType.ERROR);
-			track.setResultDetails(ResultDetails.DS_NOT_FOUND);
-			return null;
+			return true;
 		}
-		return deliveryService;
+
+		return false;
 	}
 
 	protected NetworkNode getNetworkNode(final String ip) {
@@ -522,9 +593,10 @@ public class TrafficRouter {
 			return null;
 		}
 
+		final DeliveryService deliveryService = cacheRegister.getDeliveryService(deliveryServiceId);
 		CacheLocation cacheLocation = networkNode.getCacheLocation();
 
-		if (cacheLocation != null) {
+		if (cacheLocation != null && !getSupportingCaches(cacheLocation.getCaches(), deliveryService).isEmpty()) {
 			return cacheLocation;
 		}
 
@@ -534,7 +606,6 @@ public class TrafficRouter {
 
 		// find CacheLocation
 		cacheLocation = getCacheRegister().getCacheLocationById(networkNode.getLoc());
-		final DeliveryService deliveryService = cacheRegister.getDeliveryService(deliveryServiceId);
 		if (cacheLocation != null && !getSupportingCaches(cacheLocation.getCaches(), deliveryService).isEmpty()) {
 			networkNode.setCacheLocation(cacheLocation);
 			return cacheLocation;
@@ -598,12 +669,52 @@ public class TrafficRouter {
 		return consistentHashDeliveryService(cacheRegister.getDeliveryService(deliveryServiceId), requestPath, "");
 	}
 
+	private boolean isSteeringDeliveryService(final DeliveryService deliveryService) {
+		return deliveryService != null && steeringRegistry.has(deliveryService.getId());
+	}
+
+	private boolean isMultiRouteRequest(final HTTPRequest request) {
+		final DeliveryService deliveryService = cacheRegister.getDeliveryService(request, true);
+
+		if (deliveryService == null || !isSteeringDeliveryService(deliveryService)) {
+			return false;
+		}
+
+		return steeringRegistry.get(deliveryService.getId()).isClientSteering();
+	}
+
+	public List<DeliveryService> consistentHashMultiDeliveryService(final DeliveryService deliveryService, final String requestPath) {
+		if (deliveryService == null) {
+			return null;
+		}
+
+		final List<DeliveryService> deliveryServices = new ArrayList<DeliveryService>();
+
+		if (!isSteeringDeliveryService(deliveryService)) {
+			deliveryServices.add(deliveryService);
+			return deliveryServices;
+		}
+
+		final Steering steering = steeringRegistry.get(deliveryService.getId());
+		final List<SteeringTarget> steeringTargets = consistentHasher.selectHashables(steering.getTargets(), requestPath);
+
+		for (final SteeringTarget steeringTarget : steeringTargets) {
+			final DeliveryService target = cacheRegister.getDeliveryService(steeringTarget.getDeliveryService());
+
+			if (target != null) {
+				deliveryServices.add(target);
+			}
+		}
+
+		return deliveryServices;
+	}
+
 	public DeliveryService consistentHashDeliveryService(final DeliveryService deliveryService, final String requestPath, final String xtcSteeringOption) {
 		if (deliveryService == null) {
 			return null;
 		}
 
-		if (!steeringRegistry.has(deliveryService.getId())) {
+		if (!isSteeringDeliveryService(deliveryService)) {
 			return deliveryService;
 		}
 
@@ -644,7 +755,7 @@ public class TrafficRouter {
 		final List<CacheLocation> orderedLocations = orderCacheLocations(cacheLocations, clientLocation);
 
 		for (final CacheLocation cacheLocation : orderedLocations) {
-			if (! getSupportingCaches(cacheLocation.getCaches(), deliveryService).isEmpty()) {
+			if (!getSupportingCaches(cacheLocation.getCaches(), deliveryService).isEmpty()) {
 				return cacheLocation;
 			}
 		}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
index 0e504ae..bf13e4a 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/RouterTest.java
@@ -50,8 +50,10 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.security.KeyStore;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -109,9 +111,15 @@ public class RouterTest {
 			fail("Could not find file '" + resourcePath + "' needed for test from the current classpath as a resource!");
 		}
 
-		JsonNode steeringNode = objectMapper.readTree(inputStream).get("response").get(0);
+		Set<String> steeringDeliveryServices = new HashSet<String>();
+		JsonNode steeringData = objectMapper.readTree(inputStream).get("response");
+		Iterator<JsonNode> elements = steeringData.elements();
 
-		String steeringDeliveryServiceId = steeringNode.get("deliveryService").asText();
+		while (elements.hasNext()) {
+			JsonNode ds = elements.next();
+			String dsId = ds.get("deliveryService").asText();
+			steeringDeliveryServices.add(dsId);
+		}
 
 		resourcePath = "publish/CrConfig.json";
 		inputStream = getClass().getClassLoader().getResourceAsStream(resourcePath);
@@ -127,7 +135,7 @@ public class RouterTest {
 		while (deliveryServices.hasNext()) {
 			String dsId = deliveryServices.next();
 
-			if (dsId.equals(steeringDeliveryServiceId)) {
+			if (steeringDeliveryServices.contains(dsId)) {
 				continue;
 			}
 
@@ -222,7 +230,9 @@ public class RouterTest {
 
 	@After
 	public void after() throws IOException {
-	 	httpClient.close();
+		if (httpClient != null) {
+			httpClient.close();
+		}
 	}
 
 	@Test

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java
index c5240d4..31e6479 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/external/SteeringTest.java
@@ -15,10 +15,13 @@
 
 package com.comcast.cdn.traffic_control.traffic_router.core.external;
 
+import com.comcast.cdn.traffic_control.traffic_router.core.http.RouterFilter;
 import com.comcast.cdn.traffic_control.traffic_router.core.util.ExternalTest;
 import com.fasterxml.jackson.core.JsonFactory;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.http.HttpEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
@@ -376,4 +379,81 @@ public class SteeringTest {
 
 		return stringBuilder.toString();
 	}
+
+	@Test
+	public void itUsesMultiLocationFormatResponse() throws Exception {
+		final List<String> paths = new ArrayList<String>();
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=true");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=TRUE");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=TruE");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=T");
+
+		for (final String path : paths) {
+			HttpGet httpGet = new HttpGet("http://localhost:" + routerHttpPort + path);
+			httpGet.addHeader("Host", "tr.client-steering-test-1.thecdn.example.com");
+
+			CloseableHttpResponse response = null;
+
+			try {
+				response = httpClient.execute(httpGet);
+				String location1 = ".client-steering-target-2.thecdn.example.com:8090" + path;
+				String location2 = ".client-steering-target-1.thecdn.example.com:8090" + path;
+
+				assertThat("Failed getting 302 for request " + httpGet.getFirstHeader("Host").getValue(), response.getStatusLine().getStatusCode(), equalTo(302));
+				assertThat(response.getFirstHeader("Location").getValue(), endsWith(location1));
+
+				HttpEntity entity = response.getEntity();
+				ObjectMapper objectMapper = new ObjectMapper(new JsonFactory());
+
+				assertThat(entity.getContent(), not(nullValue()));
+
+				JsonNode json = objectMapper.readTree(entity.getContent());
+
+				assertThat(json.has("locations"), equalTo(true));
+				assertThat(json.get("locations").size(), equalTo(2));
+				assertThat(json.get("locations").get(0).asText(), equalTo(response.getFirstHeader("Location").getValue()));
+				assertThat(json.get("locations").get(1).asText(), endsWith(location2));
+			} finally {
+				if (response != null) { response.close(); }
+			}
+		}
+	}
+
+	@Test
+	public void itUsesMultiLocationFormatResponseWithout302() throws Exception {
+		final List<String> paths = new ArrayList<String>();
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=false");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=FALSE");
+		paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&" + RouterFilter.REDIRECT_QUERY_PARAM + "=FalsE");
+
+		for (final String path : paths) {
+			HttpGet httpGet = new HttpGet("http://localhost:" + routerHttpPort + path);
+			httpGet.addHeader("Host", "tr.client-steering-test-1.thecdn.example.com");
+
+			CloseableHttpResponse response = null;
+
+			try {
+				response = httpClient.execute(httpGet);
+				String location1 = ".client-steering-target-2.thecdn.example.com:8090" + path;
+				String location2 = ".client-steering-target-1.thecdn.example.com:8090" + path;
+
+				assertThat("Failed getting 200 for request " + httpGet.getFirstHeader("Host").getValue(), response.getStatusLine().getStatusCode(), equalTo(200));
+
+				HttpEntity entity = response.getEntity();
+				ObjectMapper objectMapper = new ObjectMapper(new JsonFactory());
+
+				assertThat(entity.getContent(), not(nullValue()));
+
+				JsonNode json = objectMapper.readTree(entity.getContent());
+
+				assertThat(json.has("locations"), equalTo(true));
+				assertThat(json.get("locations").size(), equalTo(2));
+				assertThat(json.get("locations").get(0).asText(), endsWith(location1));
+				assertThat(json.get("locations").get(1).asText(), endsWith(location2));
+			} finally {
+				if (response != null) { response.close(); }
+			}
+		}
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilderTest.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilderTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilderTest.java
index 80e119f..32d5e48 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilderTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/http/HTTPAccessEventBuilderTest.java
@@ -28,8 +28,10 @@ import org.powermock.modules.junit4.PowerMockRunner;
 
 import javax.servlet.http.HttpServletRequest;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -74,7 +76,7 @@ public class HTTPAccessEventBuilderTest {
 
         String httpAccessEvent = HTTPAccessEventBuilder.create(httpAccessRecord);
 
-        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=- rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" rurl=\"-\" rh=\"-\""));
+        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=- rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" rurl=\"-\" rurls=\"-\" rh=\"-\""));
     }
 
     @Test
@@ -91,7 +93,29 @@ public class HTTPAccessEventBuilderTest {
         HTTPAccessRecord httpAccessRecord = builder.resultType(ResultType.CZ).build();
         String httpAccessEvent = HTTPAccessEventBuilder.create(httpAccessRecord);
 
-        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=CZ rloc=\"39.75,-104.99\" rdtl=- rerr=\"-\" rgb=\"-\" pssc=302 ttms=125.000 rurl=\"http://example.com/hereitis/index.html?foo=bar\" rh=\"-\""));
+        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=CZ rloc=\"39.75,-104.99\" rdtl=- rerr=\"-\" rgb=\"-\" pssc=302 ttms=125.000 rurl=\"http://example.com/hereitis/index.html?foo=bar\" rurls=\"-\" rh=\"-\""));
+    }
+
+    @Test
+    public void itAddsMuiltiResponseData() throws Exception {
+        when(System.nanoTime()).thenReturn(100111001L, 225111001L);
+
+        List<URL> urls = new ArrayList<URL>();
+        urls.add(new URL("http://example.com/hereitis/index.html?foo=bar"));
+        urls.add(new URL("http://example.com/thereitis/index.html?boo=baz"));
+
+        StatTracker.Track track = new StatTracker.Track();
+        HTTPAccessRecord.Builder builder = new HTTPAccessRecord.Builder(new Date(144140633999L), request)
+            .resultType(track.getResult())
+            .resultLocation(new Geolocation(39.7528,-104.9997))
+            .responseCode(302)
+            .responseURL(new URL("http://example.com/hereitis/index.html?foo=bar"))
+            .responseURLs(urls);
+
+        HTTPAccessRecord httpAccessRecord = builder.resultType(ResultType.CZ).build();
+        String httpAccessEvent = HTTPAccessEventBuilder.create(httpAccessRecord);
+
+        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=CZ rloc=\"39.75,-104.99\" rdtl=- rerr=\"-\" rgb=\"-\" pssc=302 ttms=125.000 rurl=\"http://example.com/hereitis/index.html?foo=bar\" rurls=\"[http://example.com/hereitis/index.html?foo=bar, http://example.com/thereitis/index.html?boo=baz]\" rh=\"-\""));
     }
 
     @Test
@@ -111,7 +135,7 @@ public class HTTPAccessEventBuilderTest {
         HTTPAccessRecord httpAccessRecord = builder.build();
         String httpAccessEvent = HTTPAccessEventBuilder.create(httpAccessRecord);
 
-        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=ERROR rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" pssc=302 ttms=0.124 rurl=\"http://example.com/hereitis/index.html?foo=bar\" rh=\"-\""));
+        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=ERROR rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" pssc=302 ttms=0.124 rurl=\"http://example.com/hereitis/index.html?foo=bar\" rurls=\"-\" rh=\"-\""));
     }
 
 
@@ -133,7 +157,7 @@ public class HTTPAccessEventBuilderTest {
         HTTPAccessRecord httpAccessRecord = builder.build();
         String httpAccessEvent = HTTPAccessEventBuilder.create(httpAccessRecord);
 
-        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=ERROR rloc=\"-\" rdtl=- rerr=\"RuntimeException: you're doing it wrong\" rgb=\"-\" pssc=302 ttms=0.456 rurl=\"http://example.com/hereitis/index.html?foo=bar\" rh=\"-\""));
+        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=ERROR rloc=\"-\" rdtl=- rerr=\"RuntimeException: you're doing it wrong\" rgb=\"-\" pssc=302 ttms=0.456 rurl=\"http://example.com/hereitis/index.html?foo=bar\" rurls=\"-\" rh=\"-\""));
     }
     
     @Test
@@ -152,7 +176,7 @@ public class HTTPAccessEventBuilderTest {
         HTTPAccessRecord httpAccessRecord = builder.build();
         String httpAccessEvent = HTTPAccessEventBuilder.create(httpAccessRecord);
 
-        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=MISS rloc=\"-\" rdtl=DS_NO_BYPASS rerr=\"-\" rgb=\"-\" pssc=503 ttms=0.789 rurl=\"-\" rh=\"-\""));
+        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.7.6 rhi=- url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=MISS rloc=\"-\" rdtl=DS_NO_BYPASS rerr=\"-\" rgb=\"-\" pssc=503 ttms=0.789 rurl=\"-\" rurls=\"-\" rh=\"-\""));
     }
 
     @Test
@@ -188,7 +212,7 @@ public class HTTPAccessEventBuilderTest {
         HTTPAccessRecord httpAccessRecord = new HTTPAccessRecord.Builder(new Date(144140678000L), request).build();
         String httpAccessEvent = HTTPAccessEventBuilder.create(httpAccessRecord);
 
-        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.100.100 rhi=12.34.56.78 url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=- rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" rurl=\"-\" rh=\"-\""));
+        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.100.100 rhi=12.34.56.78 url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=- rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" rurl=\"-\" rurls=\"-\" rh=\"-\""));
     }
 
     @Test
@@ -198,7 +222,7 @@ public class HTTPAccessEventBuilderTest {
         HTTPAccessRecord httpAccessRecord = new HTTPAccessRecord.Builder(new Date(144140678000L), request).build();
         String httpAccessEvent = HTTPAccessEventBuilder.create(httpAccessRecord);
 
-        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.123.123 rhi=192.168.7.6 url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=- rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" rurl=\"-\" rh=\"-\""));
+        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.123.123 rhi=192.168.7.6 url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=- rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" rurl=\"-\" rurls=\"-\" rh=\"-\""));
     }
 
     @Test
@@ -209,6 +233,6 @@ public class HTTPAccessEventBuilderTest {
         HTTPAccessRecord httpAccessRecord = new HTTPAccessRecord.Builder(new Date(144140678000L), request).build();
         String httpAccessEvent = HTTPAccessEventBuilder.create(httpAccessRecord);
 
-        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.100.100 rhi=192.168.7.6 url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=- rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" rurl=\"-\" rh=\"-\""));
+        assertThat(httpAccessEvent, equalTo("144140678.000 qtype=HTTP chi=192.168.100.100 rhi=192.168.7.6 url=\"http://example.com/index.html?foo=bar\" cqhm=GET cqhv=HTTP/1.1 rtype=- rloc=\"-\" rdtl=- rerr=\"-\" rgb=\"-\" rurl=\"-\" rurls=\"-\" rh=\"-\""));
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterHTTPRoutingMissesTest.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterHTTPRoutingMissesTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterHTTPRoutingMissesTest.java
index 2ca2394..97146af 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterHTTPRoutingMissesTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterHTTPRoutingMissesTest.java
@@ -43,6 +43,7 @@ public class TrafficRouterHTTPRoutingMissesTest {
         track = spy(StatTracker.getTrack());
         setInternalState(trafficRouter, "cacheRegister", cacheRegister);
         doCallRealMethod().when(trafficRouter).route(request, track);
+        doCallRealMethod().when(trafficRouter).singleRoute(request, track);
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterTest.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterTest.java
index 345db17..c888720 100644
--- a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterTest.java
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouterTest.java
@@ -92,6 +92,7 @@ public class TrafficRouterTest {
 
         when(trafficRouter.route(any(DNSRequest.class), any(Track.class))).thenCallRealMethod();
         when(trafficRouter.route(any(HTTPRequest.class), any(Track.class))).thenCallRealMethod();
+        when(trafficRouter.singleRoute(any(HTTPRequest.class), any(Track.class))).thenCallRealMethod();
         when(trafficRouter.selectDeliveryService(any(Request.class), anyBoolean())).thenReturn(deliveryService);
         when(trafficRouter.consistentHashDeliveryService(any(DeliveryService.class), anyString(), anyString())).thenCallRealMethod();
     }

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/test/resources/internal/api/1.2/steering.json
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/resources/internal/api/1.2/steering.json b/traffic_router/core/src/test/resources/internal/api/1.2/steering.json
index 03a0c73..65182ae 100644
--- a/traffic_router/core/src/test/resources/internal/api/1.2/steering.json
+++ b/traffic_router/core/src/test/resources/internal/api/1.2/steering.json
@@ -29,6 +29,34 @@
           "weight": "45000"
         }
       ]
+    },
+    {
+      "deliveryService": "client-steering-test-1",
+      "targets": [
+        {
+          "deliveryService": "client-steering-target-1",
+          "weight": "5000"
+        },
+        {
+          "deliveryService": "client-steering-target-2",
+          "weight": "5000"
+        }
+      ],
+      "clientSteering": true
+    },
+    {
+      "deliveryService": "client-steering-test-2",
+      "targets": [
+        {
+          "deliveryService": "client-steering-target-3",
+          "weight": "5000"
+        },
+        {
+          "deliveryService": "client-steering-target-4",
+          "weight": "5000"
+        }
+      ],
+      "clientSteering": true
     }
   ]
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/test/resources/publish/CrConfig.json
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/resources/publish/CrConfig.json b/traffic_router/core/src/test/resources/publish/CrConfig.json
index 03af8da..1cd42c0 100644
--- a/traffic_router/core/src/test/resources/publish/CrConfig.json
+++ b/traffic_router/core/src/test/resources/publish/CrConfig.json
@@ -138,6 +138,10 @@
       "interfaceName": "bond0",
       "deliveryServices": {
         "steering-test-2": ["edge-cache-020.steering-test-2.thecdn.example.com"],
+        "client-steering-target-1": ["edge-cache-020.client-steering-target-1.thecdn.example.com"],
+        "client-steering-target-2": ["edge-cache-020.client-steering-target-2.thecdn.example.com"],
+        "client-steering-target-3": ["edge-cache-020.client-steering-target-3.thecdn.example.com"],
+        "client-steering-target-4": ["edge-cache-020.client-steering-target-4.thecdn.example.com"],
         "steering-target-4": ["edge-cache-020.steering-target-4.thecdn.example.com"],
         "steering-target-3": ["edge-cache-020.steering-target-3.thecdn.example.com"]
       }
@@ -157,6 +161,10 @@
       "interfaceName": "bond0",
       "deliveryServices": {
         "steering-test-2": ["edge-cache-021.steering-test-2.thecdn.example.com"],
+        "client-steering-target-1": ["edge-cache-021.client-steering-target-1.thecdn.example.com"],
+        "client-steering-target-2": ["edge-cache-021.client-steering-target-2.thecdn.example.com"],
+        "client-steering-target-3": ["edge-cache-021.client-steering-target-3.thecdn.example.com"],
+        "client-steering-target-4": ["edge-cache-021.client-steering-target-4.thecdn.example.com"],
         "steering-target-4": ["edge-cache-021.steering-target-4.thecdn.example.com"],
         "steering-target-3": ["edge-cache-021.steering-target-3.thecdn.example.com"]
       }
@@ -176,6 +184,10 @@
       "interfaceName": "bond0",
       "deliveryServices": {
         "steering-test-2": ["edge-cache-022.steering-test-2.thecdn.example.com"],
+        "client-steering-target-1": ["edge-cache-022.client-steering-target-1.thecdn.example.com"],
+        "client-steering-target-2": ["edge-cache-022.client-steering-target-2.thecdn.example.com"],
+        "client-steering-target-3": ["edge-cache-022.client-steering-target-3.thecdn.example.com"],
+        "client-steering-target-4": ["edge-cache-022.client-steering-target-4.thecdn.example.com"],
         "steering-target-4": ["edge-cache-022.steering-target-4.thecdn.example.com"],
         "steering-target-3": ["edge-cache-022.steering-target-3.thecdn.example.com"]
       }
@@ -201,6 +213,10 @@
           "edge-cache-030.steering-target-1.thecdn.example.com",
           "edge-cache-030.steering-target-1-b-b.thecdn.example.com"
         ],
+        "client-steering-target-1": ["edge-cache-030.client-steering-target-1.thecdn.example.com"],
+        "client-steering-target-2": ["edge-cache-030.client-steering-target-2.thecdn.example.com"],
+        "client-steering-target-3": ["edge-cache-030.client-steering-target-3.thecdn.example.com"],
+        "client-steering-target-4": ["edge-cache-030.client-steering-target-4.thecdn.example.com"],
         "steering-target-4": ["edge-cache-030.steering-target-4.thecdn.example.com"],
         "steering-target-3": ["edge-cache-030.steering-target-3.thecdn.example.com"]
       }
@@ -226,6 +242,10 @@
           "edge-cache-031.steering-target-1.thecdn.example.com",
           "edge-cache-031.steering-target-1-b-b.thecdn.example.com"
         ],
+        "client-steering-target-1": ["edge-cache-031.client-steering-target-1.thecdn.example.com"],
+        "client-steering-target-2": ["edge-cache-031.client-steering-target-2.thecdn.example.com"],
+        "client-steering-target-3": ["edge-cache-031.client-steering-target-3.thecdn.example.com"],
+        "client-steering-target-4": ["edge-cache-031.client-steering-target-4.thecdn.example.com"],
         "steering-target-4": ["edge-cache-031.steering-target-4.thecdn.example.com"],
         "steering-target-3": ["edge-cache-031.steering-target-3.thecdn.example.com"]
       }
@@ -251,6 +271,10 @@
           "edge-cache-032.steering-target-1.thecdn.example.com",
           "edge-cache-032.steering-target-1-b-b.thecdn.example.com"
         ],
+        "client-steering-target-1": ["edge-cache-032.client-steering-target-1.thecdn.example.com"],
+        "client-steering-target-2": ["edge-cache-032.client-steering-target-2.thecdn.example.com"],
+        "client-steering-target-3": ["edge-cache-032.client-steering-target-3.thecdn.example.com"],
+        "client-steering-target-4": ["edge-cache-032.client-steering-target-4.thecdn.example.com"],
         "steering-target-4": ["edge-cache-032.steering-target-4.thecdn.example.com"],
         "steering-target-3": ["edge-cache-032.steering-target-3.thecdn.example.com"]
       }
@@ -280,6 +304,10 @@
           "edge-cache-040.steering-target-1.thecdn.example.com",
           "edge-cache-040.steering-target-1-b-b.thecdn.example.com"
         ],
+        "client-steering-target-1": ["edge-cache-040.client-steering-target-1.thecdn.example.com"],
+        "client-steering-target-2": ["edge-cache-040.client-steering-target-2.thecdn.example.com"],
+        "client-steering-target-3": ["edge-cache-040.client-steering-target-3.thecdn.example.com"],
+        "client-steering-target-4": ["edge-cache-040.client-steering-target-4.thecdn.example.com"],
         "steering-target-4": ["edge-cache-040.steering-target-4.thecdn.example.com"],
         "steering-target-3": ["edge-cache-040.steering-target-3.thecdn.example.com"]
       }
@@ -309,6 +337,10 @@
           "edge-cache-041.steering-target-1.thecdn.example.com",
           "edge-cache-041.steering-target-1-b-b.thecdn.example.com"
         ],
+        "client-steering-target-1": ["edge-cache-041.client-steering-target-1.thecdn.example.com"],
+        "client-steering-target-2": ["edge-cache-041.client-steering-target-2.thecdn.example.com"],
+        "client-steering-target-3": ["edge-cache-041.client-steering-target-3.thecdn.example.com"],
+        "client-steering-target-4": ["edge-cache-041.client-steering-target-4.thecdn.example.com"],
         "steering-target-4": ["edge-cache-041.steering-target-4.thecdn.example.com"],
         "steering-target-3": ["edge-cache-041.steering-target-3.thecdn.example.com"]
       }
@@ -338,6 +370,10 @@
           "edge-cache-042.steering-target-1.thecdn.example.com",
           "edge-cache-042.steering-target-1-b-b.thecdn.example.com"
         ],
+        "client-steering-target-1": ["edge-cache-042.client-steering-target-1.thecdn.example.com"],
+        "client-steering-target-2": ["edge-cache-042.client-steering-target-2.thecdn.example.com"],
+        "client-steering-target-3": ["edge-cache-042.client-steering-target-3.thecdn.example.com"],
+        "client-steering-target-4": ["edge-cache-042.client-steering-target-4.thecdn.example.com"],
         "steering-target-4": ["edge-cache-042.steering-target-4.thecdn.example.com"],
         "steering-target-3": ["edge-cache-042.steering-target-3.thecdn.example.com"]
       }
@@ -530,6 +566,298 @@
       },
       "geolocationProvider": "maxmindGeolocationService"
     },
+    "client-steering-test-1": {
+      "sslEnabled": "false",
+      "protocol": {
+        "acceptHttp" : "true",
+        "acceptHttps" : "false",
+        "redirectToHttps" : "false"
+      },
+      "requestHeaders": ["X-MoneyTrace"],
+      "domains": ["client-steering-test-1.thecdn.example.com"],
+      "missLocation": {
+        "long": "-87.627778",
+        "lat": "41.881944"
+      },
+      "ttls": {
+        "AAAA": "3600",
+        "SOA": "7200",
+        "A": "3600",
+        "NS": "3600"
+      },
+      "ttl": "3600",
+      "ip6RoutingEnabled": "true",
+      "bypassDestination": {"HTTP": {
+        "port": "80",
+        "fqdn": "client-steering-test-1.overflowcdn.net"
+      }},
+      "dispersion": {
+        "limit": 1,
+        "shuffled": "true"
+      },
+      "coverageZoneOnly": "false",
+      "regionalGeoBlocking": "false",
+      "matchsets": [{
+        "protocol": "HTTP",
+        "matchlist": [{
+          "regex": ".*\\.client-steering-test-1\\..*",
+          "match-type": "HOST"
+        }]
+      }],
+      "soa": {
+        "expire": "604800",
+        "minimum": "30",
+        "admin": "operations@thecdn.example.com",
+        "retry": "7200",
+        "refresh": "28800"
+      },
+      "geolocationProvider": "maxmindGeolocationService",
+      "staticDnsEntries": []
+    },
+    "client-steering-test-2": {
+      "sslEnabled": "false",
+      "protocol": {
+        "acceptHttp" : "true",
+        "acceptHttps" : "false",
+        "redirectToHttps" : "false"
+      },
+      "requestHeaders": ["X-MoneyTrace"],
+      "domains": ["client-steering-test-2.thecdn.example.com"],
+      "missLocation": {
+        "long": "-87.627778",
+        "lat": "41.881944"
+      },
+      "ttls": {
+        "AAAA": "3600",
+        "SOA": "7200",
+        "A": "3600",
+        "NS": "3600"
+      },
+      "ttl": "3600",
+      "ip6RoutingEnabled": "true",
+      "bypassDestination": {"HTTP": {
+        "port": "80",
+        "fqdn": "client-steering-test-2.overflowcdn.net"
+      }},
+      "dispersion": {
+        "limit": 1,
+        "shuffled": "true"
+      },
+      "coverageZoneOnly": "false",
+      "regionalGeoBlocking": "false",
+      "matchsets": [{
+        "protocol": "HTTP",
+        "matchlist": [{
+          "regex": ".*\\.client-steering-test-2\\..*",
+          "match-type": "HOST"
+        }]
+      }],
+      "soa": {
+        "expire": "604800",
+        "minimum": "30",
+        "admin": "operations@thecdn.example.com",
+        "retry": "7200",
+        "refresh": "28800"
+      },
+      "geolocationProvider": "maxmindGeolocationService",
+      "staticDnsEntries": []
+    },
+    "client-steering-target-1": {
+      "sslEnabled": "false",
+      "protocol": {
+        "acceptHttp" : "true",
+        "acceptHttps" : "false",
+        "redirectToHttps" : "false"
+      },
+      "requestHeaders": [
+        "User-Agent",
+        "MyHeader",
+        "Date"
+      ],
+      "dispersion": {
+        "limit": 1,
+        "shuffled": "true"
+      },
+      "domains": ["client-steering-target-1.thecdn.example.com"],
+      "coverageZoneOnly": "false",
+      "matchsets": [
+        {
+          "protocol": "HTTP",
+          "matchlist": [{
+            "regex": ".*\\.client-steering-target-1\\..*",
+            "match-type": "HOST"
+          }]
+        }
+      ],
+      "regionalGeoBlocking": "false",
+      "ttls": {
+        "AAAA": "3600",
+        "SOA": "7200",
+        "A": "3600",
+        "NS": "3600"
+      },
+      "missLocation": {
+        "long": "-87.627778",
+        "lat": "41.881944"
+      },
+      "soa": {
+        "expire": "604800",
+        "minimum": "3 0",
+        "admin": "operations@thecdn.example.com",
+        "retry": "7200",
+        "refresh": "28800"
+      },
+      "geolocationProvider": "maxmindGeolocationService",
+      "ttl": "3600",
+      "ip6RoutingEnabled": "false"
+    },
+    "client-steering-target-2": {
+      "sslEnabled": "false",
+      "protocol": {
+        "acceptHttp" : "true",
+        "acceptHttps" : "false",
+        "redirectToHttps" : "false"
+      },
+      "requestHeaders": [
+        "User-Agent",
+        "MyHeader",
+        "Date"
+      ],
+      "dispersion": {
+        "limit": 1,
+        "shuffled": "true"
+      },
+      "domains": ["client-steering-target-2.thecdn.example.com"],
+      "coverageZoneOnly": "false",
+      "matchsets": [
+        {
+          "protocol": "HTTP",
+          "matchlist": [{
+            "regex": ".*\\.client-steering-target-2\\..*",
+            "match-type": "HOST"
+          }]
+        }
+      ],
+      "regionalGeoBlocking": "false",
+      "ttls": {
+        "AAAA": "3600",
+        "SOA": "7200",
+        "A": "3600",
+        "NS": "3600"
+      },
+      "missLocation": {
+        "long": "-87.627778",
+        "lat": "41.881944"
+      },
+      "soa": {
+        "expire": "604800",
+        "minimum": "3 0",
+        "admin": "operations@thecdn.example.com",
+        "retry": "7200",
+        "refresh": "28800"
+      },
+      "geolocationProvider": "maxmindGeolocationService",
+      "ttl": "3600",
+      "ip6RoutingEnabled": "false"
+    },
+    "client-steering-target-3": {
+      "sslEnabled": "false",
+      "protocol": {
+        "acceptHttp" : "true",
+        "acceptHttps" : "false",
+        "redirectToHttps" : "false"
+      },
+      "requestHeaders": [
+        "User-Agent",
+        "MyHeader",
+        "Date"
+      ],
+      "dispersion": {
+        "limit": 1,
+        "shuffled": "true"
+      },
+      "domains": ["client-steering-target-3.thecdn.example.com"],
+      "coverageZoneOnly": "false",
+      "matchsets": [
+        {
+          "protocol": "HTTP",
+          "matchlist": [{
+            "regex": ".*\\.client-steering-target-3\\..*",
+            "match-type": "HOST"
+          }]
+        }
+      ],
+      "regionalGeoBlocking": "false",
+      "ttls": {
+        "AAAA": "3600",
+        "SOA": "7200",
+        "A": "3600",
+        "NS": "3600"
+      },
+      "missLocation": {
+        "long": "-87.627778",
+        "lat": "41.881944"
+      },
+      "soa": {
+        "expire": "604800",
+        "minimum": "3 0",
+        "admin": "operations@thecdn.example.com",
+        "retry": "7200",
+        "refresh": "28800"
+      },
+      "geolocationProvider": "maxmindGeolocationService",
+      "ttl": "3600",
+      "ip6RoutingEnabled": "false"
+    },
+    "client-steering-target-4": {
+      "sslEnabled": "false",
+      "protocol": {
+        "acceptHttp" : "true",
+        "acceptHttps" : "false",
+        "redirectToHttps" : "false"
+      },
+      "requestHeaders": [
+        "User-Agent",
+        "MyHeader",
+        "Date"
+      ],
+      "dispersion": {
+        "limit": 1,
+        "shuffled": "true"
+      },
+      "domains": ["client-steering-target-4.thecdn.example.com"],
+      "coverageZoneOnly": "false",
+      "matchsets": [
+        {
+          "protocol": "HTTP",
+          "matchlist": [{
+            "regex": ".*\\.client-steering-target-4\\..*",
+            "match-type": "HOST"
+          }]
+        }
+      ],
+      "regionalGeoBlocking": "false",
+      "ttls": {
+        "AAAA": "3600",
+        "SOA": "7200",
+        "A": "3600",
+        "NS": "3600"
+      },
+      "missLocation": {
+        "long": "-87.627778",
+        "lat": "41.881944"
+      },
+      "soa": {
+        "expire": "604800",
+        "minimum": "3 0",
+        "admin": "operations@thecdn.example.com",
+        "retry": "7200",
+        "refresh": "28800"
+      },
+      "geolocationProvider": "maxmindGeolocationService",
+      "ttl": "3600",
+      "ip6RoutingEnabled": "false"
+    },
     "steering-test-1": {
       "sslEnabled": "false",
       "protocol": {

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/4190b7d2/traffic_router/core/src/test/resources/publish/CrStates
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/resources/publish/CrStates b/traffic_router/core/src/test/resources/publish/CrStates
index 64294e0..679c596 100644
--- a/traffic_router/core/src/test/resources/publish/CrStates
+++ b/traffic_router/core/src/test/resources/publish/CrStates
@@ -145,6 +145,30 @@
     "test-dns": {
       "disabledLocations": [],
       "isAvailable": true
+    },
+    "client-steering-test-1": {
+      "disabledLocations": [],
+      "isAvailable": true
+    },
+    "client-steering-test-2": {
+      "disabledLocations": [],
+      "isAvailable": true
+    },
+    "client-steering-target-1": {
+      "disabledLocations": [],
+      "isAvailable": true
+    },
+    "client-steering-target-2": {
+      "disabledLocations": [],
+      "isAvailable": true
+    },
+    "client-steering-target-3": {
+      "disabledLocations": [],
+      "isAvailable": true
+    },
+    "client-steering-target-4": {
+      "disabledLocations": [],
+      "isAvailable": true
     }
   }
 }