From bf6a0f0c32ade64951e23b2496d4829c2f8d046c Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Sun, 27 Oct 2019 22:11:32 -0500 Subject: [PATCH 01/18] Call setHeader() + setStatus() on the response instead of sendRedirect() to leave the response uncommitted --- .../traffic_router/core/http/RouterFilter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/RouterFilter.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/RouterFilter.java index bed96b269d..b535e88692 100644 --- a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/RouterFilter.java +++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/RouterFilter.java @@ -25,6 +25,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; @@ -154,7 +155,7 @@ private void setMultiResponse(final HTTPRouteResult routeResult, final HttpServl response.setStatus(HttpServletResponse.SC_OK); httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK); } else { - response.setHeader("Location", routeResult.getUrl().toString()); + response.setHeader(HttpHeaders.LOCATION, routeResult.getUrl().toString()); response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_MOVED_TEMPORARILY); httpAccessRecordBuilder.responseURL(routeResult.getUrl()); @@ -192,7 +193,8 @@ private void setSingleResponse(final HTTPRouteResult routeResult, final HttpServ httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK); } else { - response.sendRedirect(location.toString()); + response.setHeader(HttpHeaders.LOCATION, location.toString()); + response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_MOVED_TEMPORARILY); httpAccessRecordBuilder.responseURL(location); } From d3cf82afd156d0874f073b6a141f977c819eae0d Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Sun, 3 Nov 2019 21:36:12 -0600 Subject: [PATCH 02/18] Add a filter that sets Content-Length header for all responses --- .../core/http/BufferedResponseFilter.java | 61 ++++++ .../webapp/WEB-INF/applicationContext.xml | 3 + .../core/src/main/webapp/WEB-INF/web.xml | 14 ++ .../core/external/BufferedResponseTest.java | 176 ++++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java create mode 100644 traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java new file mode 100644 index 0000000000..e0612b8596 --- /dev/null +++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java @@ -0,0 +1,61 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.traffic_control.traffic_router.core.http; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.reflect.Field; + +public class BufferedResponseFilter extends OncePerRequestFilter { + public static final Logger LOGGER = LogManager.getLogger(BufferedResponseFilter.class); + + public void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException { + final ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); + + chain.doFilter(request, responseWrapper); + + if (responseWrapper.getContentSize() == 0) { + int contentLength = 0; + + try { + final Field contentLengthField = responseWrapper.getClass().getDeclaredField("contentLength"); + contentLengthField.setAccessible(true); + final Object contentLengthObject = contentLengthField.get(responseWrapper); + /* E.g., a HEAD request whose corresponding GET request has a non-empty body */ + if (contentLengthObject != null) { + contentLength = (Integer) contentLengthObject; + } + } catch (ReflectiveOperationException e) { + LOGGER.error("Encountered a " + e.getClass() + "exception in " + this.getClass() + ": " + e.getMessage()); + } finally { + response.setContentLength(contentLength); + } + } else { + /* When the content size is greater than 0, copyBodyToResponse() + * will set Content-Length. + */ + responseWrapper.copyBodyToResponse(); + } + } +} diff --git a/traffic_router/core/src/main/webapp/WEB-INF/applicationContext.xml b/traffic_router/core/src/main/webapp/WEB-INF/applicationContext.xml index 8b0c21fb1e..40bc7166b2 100644 --- a/traffic_router/core/src/main/webapp/WEB-INF/applicationContext.xml +++ b/traffic_router/core/src/main/webapp/WEB-INF/applicationContext.xml @@ -351,6 +351,9 @@ + + + /crossdomain.xml /clientaccesspolicy.xml diff --git a/traffic_router/core/src/main/webapp/WEB-INF/web.xml b/traffic_router/core/src/main/webapp/WEB-INF/web.xml index 99e3b5dffc..5586d7b72c 100644 --- a/traffic_router/core/src/main/webapp/WEB-INF/web.xml +++ b/traffic_router/core/src/main/webapp/WEB-INF/web.xml @@ -35,6 +35,20 @@ 1 + + bufferedResponseFilter + org.springframework.web.filter.DelegatingFilterProxy + + targetBeanName + bufferedResponseFilter + + + + + bufferedResponseFilter + /* + + routerFilter org.springframework.web.filter.DelegatingFilterProxy diff --git a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java new file mode 100644 index 0000000000..f172f2156e --- /dev/null +++ b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java @@ -0,0 +1,176 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.traffic_control.traffic_router.core.external; + +import org.apache.traffic_control.traffic_router.core.http.RouterFilter; +import org.apache.traffic_control.traffic_router.core.util.ExternalTest; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.catalina.LifecycleException; +import org.apache.http.Header; +import org.apache.http.client.config.RequestConfig; +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.HttpRequestBase; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +@Category(ExternalTest.class) +public class BufferedResponseTest { + final private String routerHttpPort = System.getProperty("routerHttpPort", "8888"); + private CloseableHttpClient httpClient; + + @Before + public void before() throws LifecycleException { + httpClient = HttpClientBuilder.create().build(); + } + + @After + public void after() throws Exception { + if (httpClient != null) httpClient.close(); + } + + @Test + public void itSetsContentLengthHeaderFor404() throws IOException { + final String encodedUrl = URLEncoder.encode("http://trafficrouter01.somedeliveryservice.somecdn.domain.foo/stuff", "utf-8"); + final HttpGet httpGet = new HttpGet("http://localhost:3333/crs/deliveryservices?url=" + encodedUrl); + CloseableHttpResponse response = null; + + try { + response = httpClient.execute(httpGet); + assertThat(response.getStatusLine().getStatusCode(), equalTo(404)); + assertThat(response.getFirstHeader("Transfer-Encoding"), nullValue()); + assertThat(response.getFirstHeader("Content-Length"), notNullValue()); + } finally { + if (response != null) response.close(); + } + } + + @Test + public void itSetsTheSameContentLengthForHeadAndGet() throws IOException { + final List paths = new ArrayList<>(); + paths.add("http://localhost:3333/crs/stats"); + paths.add("http://localhost:3333/crs/locations/caches"); + paths.add("http://localhost:3333/crs/consistenthash/deliveryservice?deliveryServiceId=csd-target-1&requestPath=/"); + + for (final String path : paths) { + final List requests = new ArrayList<>(); + requests.add(new HttpHead(path)); + requests.add(new HttpGet(path)); + + final List contentLengths = new ArrayList<>(); + + for (final HttpRequestBase request : requests) { + CloseableHttpResponse response = null; + + try { + response = httpClient.execute(request); + final Header contentLengthHeader = response.getFirstHeader("Content-Length"); + + assertThat(response.getFirstHeader("Transfer-Encoding"), nullValue()); + assertThat(contentLengthHeader, notNullValue()); + contentLengths.add(Integer.parseInt(contentLengthHeader.getValue())); + } finally { + if (response != null) response.close(); + } + } + + assertThat(contentLengths.size(), equalTo(2)); + assertThat("Expected HEAD and GET requests for " + path + "to have the same Content-Length", contentLengths.get(0), equalTo(contentLengths.get(1))); + } + } + + @Test + public void itSetsAnAccurateContentLengthForGet() throws IOException { + final List paths = new ArrayList<>(); + paths.add("http://localhost:3333/crs/stats"); + paths.add("http://localhost:3333/crs/locations/caches"); + paths.add("http://localhost:3333/crs/consistenthash/deliveryservice?deliveryServiceId=csd-target-1&requestPath=/"); + + for (final String path : paths) { + CloseableHttpResponse response = null; + + try { + final HttpGet httpGet = new HttpGet(path); + response = httpClient.execute(httpGet); + + final ObjectMapper objectMapper = new ObjectMapper(new JsonFactory()); + final String json = EntityUtils.toString(response.getEntity()); + final Header contentLengthHeader = response.getFirstHeader("Content-Length"); + + /* If the content length is too low and cuts off the response + * body, objectMapper.readTree(json) will likely throw a + * JsonProcessingException. + */ + objectMapper.readTree(json); + assertThat(response.getFirstHeader("Transfer-Encoding"), nullValue()); + assertThat(contentLengthHeader, notNullValue()); + assertThat(Integer.parseInt(contentLengthHeader.getValue()), equalTo(json.length())); + } finally { + if (response != null) response.close(); + } + } + } + + @Test + public void itSetsContentLengthHeaderForDeliveryServiceSteering() throws IOException { + final String testHostName = "tr.client-steering-test-1.thecdn.example.com"; + final RequestConfig config = RequestConfig.custom().setRedirectsEnabled(false).build(); + final List paths = new ArrayList<>(); + paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78"); + paths.add("/qwerytuiop/asdfghjkl?fakeClientIpAddress=12.34.56.78&format=json"); + 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 + "=true"); + + + for (final String path : paths) { + final List requests = new ArrayList<>(); + requests.add(new HttpHead("http://localhost:" + routerHttpPort + path)); + requests.add(new HttpGet("http://localhost:" + routerHttpPort + path)); + + for (final HttpRequestBase request : requests) { + CloseableHttpResponse response = null; + request.setConfig(config); + request.addHeader("Host", testHostName); + + try { + response = httpClient.execute(request); + + assertThat(response.getFirstHeader("Transfer-Encoding"), nullValue()); + assertThat(response.getFirstHeader("Content-Length"), notNullValue()); + } finally { + if (response != null) response.close(); + } + } + } + } +} From 7d54b4af12834f3c134ed278b754384ff449f6fb Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Sun, 3 Nov 2019 21:41:05 -0600 Subject: [PATCH 03/18] Use same program flow for GET and HEAD requests --- .../core/http/RouterFilter.java | 27 +++++++------------ .../webapp/WEB-INF/applicationContext.xml | 2 +- .../core/external/BufferedResponseTest.java | 9 ++++++- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/RouterFilter.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/RouterFilter.java index b535e88692..58b6615183 100644 --- a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/RouterFilter.java +++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/RouterFilter.java @@ -42,7 +42,6 @@ public class RouterFilter extends OncePerRequestFilter { private static final Logger ACCESS = LogManager.getLogger("org.apache.traffic_control.traffic_router.core.access"); public static final String REDIRECT_QUERY_PARAM = "trred"; - private static final String HEAD = "HEAD"; @Autowired private TrafficRouterManager trafficRouterManager; @@ -144,11 +143,9 @@ private void setMultiResponse(final HTTPRouteResult routeResult, final HttpServl final String redirect = httpServletRequest.getParameter(REDIRECT_QUERY_PARAM); - if (!HEAD.equals(httpServletRequest.getMethod())) { - response.setContentType("application/json"); - response.getWriter().println(routeResult.toMultiLocationJSONString()); - httpAccessRecordBuilder.responseURLs(routeResult.getUrls()); - } + 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)) { @@ -177,20 +174,14 @@ private void setSingleResponse(final HTTPRouteResult routeResult, final HttpServ } if ("false".equalsIgnoreCase(redirect)) { - if (!HEAD.equals(httpServletRequest.getMethod())) { - response.setContentType("application/json"); - response.getWriter().println(routeResult.toMultiLocationJSONString()); - httpAccessRecordBuilder.responseURLs(routeResult.getUrls()); - } - + response.setContentType("application/json"); + response.getWriter().println(routeResult.toMultiLocationJSONString()); + httpAccessRecordBuilder.responseURLs(routeResult.getUrls()); httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK); } else if ("json".equals(format)) { - if (!HEAD.equals(httpServletRequest.getMethod())) { - response.setContentType("application/json"); - response.getWriter().println(routeResult.toLocationJSONString()); - httpAccessRecordBuilder.responseURL(location); - } - + response.setContentType("application/json"); + response.getWriter().println(routeResult.toLocationJSONString()); + httpAccessRecordBuilder.responseURL(location); httpAccessRecordBuilder.responseCode(HttpServletResponse.SC_OK); } else { response.setHeader(HttpHeaders.LOCATION, location.toString()); diff --git a/traffic_router/core/src/main/webapp/WEB-INF/applicationContext.xml b/traffic_router/core/src/main/webapp/WEB-INF/applicationContext.xml index 40bc7166b2..f478724088 100644 --- a/traffic_router/core/src/main/webapp/WEB-INF/applicationContext.xml +++ b/traffic_router/core/src/main/webapp/WEB-INF/applicationContext.xml @@ -351,7 +351,7 @@ - + diff --git a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java index f172f2156e..d0d3aee815 100644 --- a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java +++ b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java @@ -157,6 +157,8 @@ public void itSetsContentLengthHeaderForDeliveryServiceSteering() throws IOExcep requests.add(new HttpHead("http://localhost:" + routerHttpPort + path)); requests.add(new HttpGet("http://localhost:" + routerHttpPort + path)); + final List contentLengths = new ArrayList<>(); + for (final HttpRequestBase request : requests) { CloseableHttpResponse response = null; request.setConfig(config); @@ -164,13 +166,18 @@ public void itSetsContentLengthHeaderForDeliveryServiceSteering() throws IOExcep try { response = httpClient.execute(request); + final Header contentLengthHeader = response.getFirstHeader("Content-Length"); assertThat(response.getFirstHeader("Transfer-Encoding"), nullValue()); - assertThat(response.getFirstHeader("Content-Length"), notNullValue()); + assertThat(contentLengthHeader, notNullValue()); + contentLengths.add(Integer.parseInt(contentLengthHeader.getValue())); } finally { if (response != null) response.close(); } } + + assertThat(contentLengths.size(), equalTo(2)); + assertThat("Expected HEAD and GET requests for " + testHostName + path + "to have the same Content-Length", contentLengths.get(0), equalTo(contentLengths.get(1))); } } } From db5a076abf5201995b0d7c8f6fde70f28ba0e356 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Sun, 3 Nov 2019 21:54:01 -0600 Subject: [PATCH 04/18] Allow HEAD requests for LocationController endpoints --- .../api/controllers/LocationController.java | 12 +++--- .../core/external/LocationsTest.java | 39 +++++++++++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/api/controllers/LocationController.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/api/controllers/LocationController.java index b96eb44b95..fedd98d534 100644 --- a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/api/controllers/LocationController.java +++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/api/controllers/LocationController.java @@ -35,7 +35,7 @@ public class LocationController { @Autowired private DataExporter dataExporter; - @RequestMapping(value = "/{locID}/caches", method = RequestMethod.GET) + @RequestMapping(value = "/{locID}/caches", method = {RequestMethod.GET, RequestMethod.HEAD}) public @ResponseBody Map> getCaches(@PathVariable("locID") final String locId) { final Map> map = new HashMap>(); @@ -43,17 +43,17 @@ Map> getCaches(@PathVariable("locID") final String locI return map; } - @RequestMapping(value = "", method = RequestMethod.GET) + @RequestMapping(value = "", method = {RequestMethod.GET, RequestMethod.HEAD}) public @ResponseBody - Map> getLocations() { - final Map> locations = new HashMap>(); + Map> getLocations() { + final Map> locations = new HashMap>(); locations.put("locations", dataExporter.getLocations()); return locations; } - @RequestMapping(value = "/caches", method = RequestMethod.GET) + @RequestMapping(value = "/caches", method = {RequestMethod.GET, RequestMethod.HEAD}) public @ResponseBody - Map>> getCaches() { + Map>> getCaches() { final Map>> map = new HashMap>>(); final Map> innerMap = new HashMap>(); diff --git a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/LocationsTest.java b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/LocationsTest.java index a9f48d9fe7..b144d964ae 100644 --- a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/LocationsTest.java +++ b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/LocationsTest.java @@ -22,6 +22,7 @@ import org.apache.catalina.LifecycleException; 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.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; @@ -30,10 +31,14 @@ import org.junit.Test; import org.junit.experimental.categories.Category; +import java.util.ArrayList; +import java.util.List; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.AnyOf.anyOf; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.fail; @Category(ExternalTest.class) public class LocationsTest { @@ -139,4 +144,38 @@ public void itGetsCachesForALocation() throws Exception { if (response != null) response.close(); } } + + @Test + public void itHandlesHeadRequests() throws Exception { + final List paths = new ArrayList(); + paths.add("http://localhost:3333/crs/locations"); + paths.add("http://localhost:3333/crs/locations/caches"); + + CloseableHttpResponse response = null; + + try { + final HttpGet httpGet = new HttpGet("http://localhost:3333/crs/locations"); + response = closeableHttpClient.execute(httpGet); + + ObjectMapper objectMapper = new ObjectMapper(new JsonFactory()); + JsonNode jsonNode = objectMapper.readTree(EntityUtils.toString(response.getEntity())); + + String location = jsonNode.get("locations").get(0).asText(); + paths.add("http://localhost:3333/crs/locations/" + location + "/caches"); + } catch (Exception e) { + fail(e.getMessage()); + } finally { + if (response != null) response.close(); + } + + for (final String path : paths) { + final HttpHead httpHead = new HttpHead(path); + try { + response = closeableHttpClient.execute(httpHead); + assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); + } finally { + if (response != null) response.close(); + } + } + } } From 7d35aa49c3b91c821c1ec32b6c94edc581fabeb4 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Sun, 3 Nov 2019 23:40:47 -0600 Subject: [PATCH 05/18] Update documentation --- .../traffic_router/traffic_router_api.rst | 165 +++++++++--------- 1 file changed, 85 insertions(+), 80 deletions(-) diff --git a/docs/source/development/traffic_router/traffic_router_api.rst b/docs/source/development/traffic_router/traffic_router_api.rst index fc6e4cdc8a..c26930c4b3 100644 --- a/docs/source/development/traffic_router/traffic_router_api.rst +++ b/docs/source/development/traffic_router/traffic_router_api.rst @@ -70,24 +70,24 @@ Request Structure GET /crs/stats HTTP/1.1 Host: trafficrouter.infra.ciab.test - User-Agent: curl/7.47.0 + User-Agent: curl/7.52.1 Accept: */* Response Structure ------------------ .. code-block:: http - :caption: Response Example + :caption: Response Example (JSON expanded) - HTTP/1.1 200 OK + HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 - Transfer-Encoding: chunked - Date: Tue, 15 Jan 2019 21:02:09 GMT + Content-Length: 1213 + Date: Sun, 03 Nov 2019 05:57:17 GMT { "app": { - "buildTimestamp": "2019-01-10", + "buildTimestamp": "2019-11-03", "name": "traffic_router", "deploy-dir": "/opt/traffic_router", - "git-revision": "437e9df81", + "git-revision": "3ebc920b7", "version": "3.0.0" }, "stats": { @@ -95,7 +95,7 @@ Response Structure "httpMap": { "video.demo1.mycdn.ciab.test": { "czCount": 0, - "geoCount": 0, + "geoCount": 1, "deepCzCount": 0, "missCount": 0, "dsrCount": 0, @@ -107,29 +107,29 @@ Response Structure } }, "totalDnsCount": 0, - "totalHttpCount": 1, - "totalDsMissCount": 0, - "appStartTime": 1547584831677, - "averageDnsTime": 0, - "averageHttpTime": 1547584863270, + "totalHttpCount": 2, + "totalDsMissCount": 1, + "appStartTime": 1572759663457, + "averageHttpTime": 786379849939, "updateTracker": { - "lastHttpsCertificatesCheck": 1547586068932, - "lastGeolocationDatabaseUpdaterUpdate": 1547584858917, - "lastCacheStateCheck": 1547586128932, - "lastCacheStateChange": 1547584867102, - "lastNetworkUpdaterUpdate": 1547584857484, - "lastHttpsCertificatesUpdate": 1547586071079, - "lastSteeringWatcherUpdate": 1547584923514, - "lastConfigCheck": 1547586127344, - "lastConfigChange": 1547584863406, - "lastNetworkUpdaterCheck": 1547584857465, - "lastGeolocationDatabaseUpdaterCheck": 1547584858906, - "lastFederationsWatcherUpdate": 1547584863433, - "lastHttpsCertificatesFetchSuccess": 1547586069070, - "lastSteeringWatcherCheck": 1547586124630, - "lastFederationsWatcherCheck": 1547586124584, - "lastHttpsCertificatesFetchAttempt": 1547586068932 - } + "lastHttpsCertificatesCheck": 1572760605162, + "lastGeolocationDatabaseUpdaterUpdate": 1572759695064, + "lastCacheStateCheck": 1572760637365, + "lastCacheStateChange": 1572759703763, + "lastNetworkUpdaterUpdate": 1572759694009, + "lastHttpsCertificatesUpdate": 1572760607280, + "lastSteeringWatcherUpdate": 1572759760062, + "lastConfigCheck": 1572760633918, + "lastConfigChange": 1572759699962, + "lastNetworkUpdaterCheck": 1572759693998, + "lastGeolocationDatabaseUpdaterCheck": 1572759695056, + "lastFederationsWatcherUpdate": 1572759699998, + "lastHttpsCertificatesFetchSuccess": 1572760605275, + "lastSteeringWatcherCheck": 1572760601104, + "lastFederationsWatcherCheck": 1572760601085, + "lastHttpsCertificatesFetchAttempt": 1572760605162 + }, + "averageDnsTime": 0 }} .. _tr-api-crs-stats-ip-ip: @@ -153,19 +153,19 @@ Request Structure GET /crs/stats/ip/255.255.255.255 HTTP/1.1 Host: trafficrouter.infra.ciab.test - User-Agent: curl/7.47.0 + User-Agent: curl/7.52.1 Accept: */* Response Structure ------------------ .. code-block:: http - :caption: Response Example + :caption: Response Example (JSON expanded) - HTTP/1.1 200 OK + HTTP/1.1 200 Content-Disposition: inline;filename=f.txt Content-Type: application/json;charset=UTF-8 - Transfer-Encoding: chunked - Date: Tue, 15 Jan 2019 21:06:09 GMT + Content-Length: 131 + Date: Sun, 03 Nov 2019 06:06:26 GMT { "locationByGeo": { "city": "Woodridge", @@ -194,7 +194,7 @@ Request Structure GET /crs/locations HTTP/1.1 Host: trafficrouter.infra.ciab.test - User-Agent: curl/7.47.0 + User-Agent: curl/7.52.1 Accept: */* Response Structure @@ -202,12 +202,12 @@ Response Structure :locations: An array of strings that are the :ref:`Names of Cache Groups ` to which this Traffic Router is capable of routing client traffic .. code-block:: http - :caption: Response Example + :caption: Response Example (JSON expanded) - HTTP/1.1 200 OK + HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 - Transfer-Encoding: chunked - Date: Tue, 15 Jan 2019 21:12:17 GMT + Content-Length: 35 + Date: Sun, 03 Nov 2019 06:12:41 GMT { "locations": [ "CDN_in_a_Box_Edge" @@ -226,18 +226,18 @@ Request Structure GET /crs/locations/caches HTTP/1.1 Host: trafficrouter.infra.ciab.test - User-Agent: curl/7.47.0 + User-Agent: curl/7.52.1 Accept: */* Response Structure ------------------ .. code-block:: http - :caption: Response Example + :caption: Response Example (JSON expanded) - HTTP/1.1 200 OK + HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 - Transfer-Encoding: chunked - Date: Tue, 15 Jan 2019 21:15:53 GMT + Content-Length: 278 + Date: Sun, 03 Nov 2019 06:21:06 GMT { "locations": { "CDN_in_a_Box_Edge": [ @@ -245,8 +245,8 @@ Response Structure "cacheId": "edge", "fqdn": "edge.infra.ciab.test", "ipAddresses": [ - "172.16.239.100", - "fc01:9400:1000:8:0:0:0:100" + "172.16.239.4", + "fc01:9400:1000:8:0:0:0:4" ], "port": 0, "adminStatus": null, @@ -282,26 +282,26 @@ Request Structure GET /crs/locations/CDN_in_a_Box_Edge/caches HTTP/1.1 Host: trafficrouter.infra.ciab.test - User-Agent: curl/7.47.0 + User-Agent: curl/7.52.1 Accept: */* Response Structure ------------------ .. code-block:: http - :caption: Response Example + :caption: Response Example (JSON expanded) - HTTP/1.1 200 OK + HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 - Transfer-Encoding: chunked - Date: Tue, 15 Jan 2019 21:18:25 GMT + Content-Length: 253 + Date: Sun, 03 Nov 2019 06:23:20 GMT { "caches": [ { "cacheId": "edge", "fqdn": "edge.infra.ciab.test", "ipAddresses": [ - "172.16.239.100", - "fc01:9400:1000:8:0:0:0:100" + "172.16.239.4", + "fc01:9400:1000:8:0:0:0:4" ], "port": 0, "adminStatus": null, @@ -409,18 +409,18 @@ Request Structure GET /crs/consistenthash/deliveryservice?deliveryServiceId=demo1&requestPath=/ HTTP/1.1 Host: trafficrouter.infra.ciab.test - User-Agent: curl/7.47.0 + User-Agent: curl/7.52.1 Accept: */* Response Structure ------------------ .. code-block:: http - :caption: Response Example + :caption: Response Example (JSON expanded) - HTTP/1.1 200 OK + HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 - Transfer-Encoding: chunked - Date: Tue, 15 Jan 2019 21:40:51 GMT + Content-Length: 828 + Date: Sun, 03 Nov 2019 06:26:30 GMT { "id": "demo1", "coverageZoneOnly": false, @@ -429,8 +429,8 @@ Response Structure "geoRedirectUrlType": "INVALID_URL", "routingName": "video", "missLocation": { - "latitude": 42, - "longitude": -88, + "latitude": 42.0, + "longitude": -88.0, "postalCode": null, "city": null, "countryCode": null, @@ -458,6 +458,13 @@ Response Structure "sslEnabled": true, "acceptHttp": true, "deepCache": "NEVER", + "consistentHashRegex": "", + "consistentHashQueryParams": [ + "abc", + "zyx", + "xxx", + "pdq" + ], "dns": false, "locationLimit": 0, "maxDnsIps": 0, @@ -552,23 +559,22 @@ Request Structure GET /crs/consistenthash/patternbased/regex?regex=%2F.*%3F%28%2F.*%3F%2F%29.*%3F%28%5C.m3u8%29&requestPath=%2Ftext1234%2Fname%2Fasset.m3u8 HTTP/1.1 Host: localhost:3333 - User-Agent: curl/7.54.0 + User-Agent: curl/7.52.1 Accept: */* Response Structure ------------------ .. code-block:: http - :caption: Response Example + :caption: Response Example (JSON expanded) - HTTP/1.1 200 OK + HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 - Transfer-Encoding: chunked - Date: Fri, 15 Feb 2019 22:06:53 GMT + Content-Length: 137 + Date: Sun, 03 Nov 2019 06:36:28 GMT - { - "resultingPathToConsistentHash":"/name/.m3u8", - "consistentHashRegex":"/.*?(/.*?/).*?(\\.m3u8)", - "requestPath":"/text1234/name/asset.m3u8" + { "resultingPathToConsistentHash": "/name/.m3u8", + "consistentHashRegex": "/.*?(/.*?/).*?(\\.m3u8)", + "requestPath": "/text1234/name/asset.m3u8" } .. _tr-api-crs-consistenthash-patternbased-deliveryservice: @@ -592,25 +598,24 @@ Request Structure .. code-block:: http :caption: Request Example - GET /crs/consistenthash/patternbased/deliveryservice?deliveryServiceId=asdf&requestPath=%2Fsometext1234%2Fstream_name%2Fasset_name.m3u8 HTTP/1.1 + GET /crs/consistenthash/patternbased/deliveryservice?deliveryServiceId=demo1&requestPath=%2Fsometext1234%2Fstream_name%2Fasset_name.m3u8 HTTP/1.1 Host: localhost:3333 - User-Agent: curl/7.54.0 + User-Agent: curl/7.52.1 Accept: */* Response Structure ------------------ .. code-block:: http - :caption: Response Example + :caption: Response Example (JSON expanded) - HTTP/1.1 200 OK + HTTP/1.1 200 Content-Type: application/json;charset=UTF-8 - Transfer-Encoding: chunked - Date: Fri, 15 Feb 2019 22:12:38 GMT + Content-Length: 163 + Date: Sun, 03 Nov 2019 06:45:07 GMT - { - "resultingPathToConsistentHash":"/sometext1234/stream_name/asset_name.m3u8", - "deliveryServiceId":"asdf", - "requestPath":"/sometext1234/stream_name/asset_name.m3u8" + { "resultingPathToConsistentHash": "/sometext1234/stream_name/asset_name.m3u8", + "deliveryServiceId": "demo1", + "requestPath": "/sometext1234/stream_name/asset_name.m3u8" } .. _tr-api-crs-consistenthash-cache-coveragezone-steering: From 0c7a32bdfbe31d46c19b61a0c025454dbc81701d Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Mon, 4 Nov 2019 14:25:40 -0600 Subject: [PATCH 06/18] Re-add HTTP reason-phrases in documentation --- .../traffic_router/traffic_router_api.rst | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/source/development/traffic_router/traffic_router_api.rst b/docs/source/development/traffic_router/traffic_router_api.rst index c26930c4b3..f2c4648db3 100644 --- a/docs/source/development/traffic_router/traffic_router_api.rst +++ b/docs/source/development/traffic_router/traffic_router_api.rst @@ -78,16 +78,16 @@ Response Structure .. code-block:: http :caption: Response Example (JSON expanded) - HTTP/1.1 200 + HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 - Content-Length: 1213 - Date: Sun, 03 Nov 2019 05:57:17 GMT + Content-Length: 1214 + Date: Mon, 04 Nov 2019 19:48:04 GMT { "app": { - "buildTimestamp": "2019-11-03", + "buildTimestamp": "2019-11-04", "name": "traffic_router", "deploy-dir": "/opt/traffic_router", - "git-revision": "3ebc920b7", + "git-revision": "eabc2b82e", "version": "3.0.0" }, "stats": { @@ -95,7 +95,7 @@ Response Structure "httpMap": { "video.demo1.mycdn.ciab.test": { "czCount": 0, - "geoCount": 1, + "geoCount": 0, "deepCzCount": 0, "missCount": 0, "dsrCount": 0, @@ -107,29 +107,29 @@ Response Structure } }, "totalDnsCount": 0, - "totalHttpCount": 2, - "totalDsMissCount": 1, - "appStartTime": 1572759663457, - "averageHttpTime": 786379849939, + "totalHttpCount": 1, + "totalDsMissCount": 0, + "appStartTime": 1572895915703, + "averageDnsTime": 0, + "averageHttpTime": 1572895947202, "updateTracker": { - "lastHttpsCertificatesCheck": 1572760605162, - "lastGeolocationDatabaseUpdaterUpdate": 1572759695064, - "lastCacheStateCheck": 1572760637365, - "lastCacheStateChange": 1572759703763, - "lastNetworkUpdaterUpdate": 1572759694009, - "lastHttpsCertificatesUpdate": 1572760607280, - "lastSteeringWatcherUpdate": 1572759760062, - "lastConfigCheck": 1572760633918, - "lastConfigChange": 1572759699962, - "lastNetworkUpdaterCheck": 1572759693998, - "lastGeolocationDatabaseUpdaterCheck": 1572759695056, - "lastFederationsWatcherUpdate": 1572759699998, - "lastHttpsCertificatesFetchSuccess": 1572760605275, - "lastSteeringWatcherCheck": 1572760601104, - "lastFederationsWatcherCheck": 1572760601085, - "lastHttpsCertificatesFetchAttempt": 1572760605162 - }, - "averageDnsTime": 0 + "lastHttpsCertificatesCheck": 1572896852436, + "lastGeolocationDatabaseUpdaterUpdate": 1572895942543, + "lastCacheStateCheck": 1572896884465, + "lastCacheStateChange": 1572895951089, + "lastNetworkUpdaterUpdate": 1572895941407, + "lastHttpsCertificatesUpdate": 1572896854512, + "lastSteeringWatcherUpdate": 1572896007369, + "lastConfigCheck": 1572896881213, + "lastConfigChange": 1572895947297, + "lastNetworkUpdaterCheck": 1572895941392, + "lastGeolocationDatabaseUpdaterCheck": 1572895942533, + "lastFederationsWatcherUpdate": 1572895947336, + "lastHttpsCertificatesFetchSuccess": 1572896852506, + "lastSteeringWatcherCheck": 1572896848090, + "lastFederationsWatcherCheck": 1572896848067, + "lastHttpsCertificatesFetchAttempt": 1572896852436 + } }} .. _tr-api-crs-stats-ip-ip: @@ -161,11 +161,11 @@ Response Structure .. code-block:: http :caption: Response Example (JSON expanded) - HTTP/1.1 200 + HTTP/1.1 200 OK Content-Disposition: inline;filename=f.txt Content-Type: application/json;charset=UTF-8 Content-Length: 131 - Date: Sun, 03 Nov 2019 06:06:26 GMT + Date: Mon, 04 Nov 2019 19:48:04 GMT { "locationByGeo": { "city": "Woodridge", @@ -204,10 +204,10 @@ Response Structure .. code-block:: http :caption: Response Example (JSON expanded) - HTTP/1.1 200 + HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Content-Length: 35 - Date: Sun, 03 Nov 2019 06:12:41 GMT + Date: Mon, 04 Nov 2019 19:48:04 GMT { "locations": [ "CDN_in_a_Box_Edge" @@ -234,10 +234,10 @@ Response Structure .. code-block:: http :caption: Response Example (JSON expanded) - HTTP/1.1 200 + HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Content-Length: 278 - Date: Sun, 03 Nov 2019 06:21:06 GMT + Date: Mon, 04 Nov 2019 19:48:04 GMT { "locations": { "CDN_in_a_Box_Edge": [ @@ -290,10 +290,10 @@ Response Structure .. code-block:: http :caption: Response Example (JSON expanded) - HTTP/1.1 200 + HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Content-Length: 253 - Date: Sun, 03 Nov 2019 06:23:20 GMT + Date: Mon, 04 Nov 2019 19:48:04 GMT { "caches": [ { @@ -417,10 +417,10 @@ Response Structure .. code-block:: http :caption: Response Example (JSON expanded) - HTTP/1.1 200 + HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Content-Length: 828 - Date: Sun, 03 Nov 2019 06:26:30 GMT + Date: Mon, 04 Nov 2019 19:48:04 GMT { "id": "demo1", "coverageZoneOnly": false, @@ -567,10 +567,10 @@ Response Structure .. code-block:: http :caption: Response Example (JSON expanded) - HTTP/1.1 200 + HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Content-Length: 137 - Date: Sun, 03 Nov 2019 06:36:28 GMT + Date: Mon, 04 Nov 2019 19:48:04 GMT { "resultingPathToConsistentHash": "/name/.m3u8", "consistentHashRegex": "/.*?(/.*?/).*?(\\.m3u8)", @@ -608,10 +608,10 @@ Response Structure .. code-block:: http :caption: Response Example (JSON expanded) - HTTP/1.1 200 + HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Content-Length: 163 - Date: Sun, 03 Nov 2019 06:45:07 GMT + Date: Mon, 04 Nov 2019 19:48:04 GMT { "resultingPathToConsistentHash": "/sometext1234/stream_name/asset_name.m3u8", "deliveryServiceId": "demo1", From 80e58cba22c587cceed47ff09fbc26a0f9632eb4 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Mon, 4 Nov 2019 14:27:50 -0600 Subject: [PATCH 07/18] No need to note that API output is beautified --- .../traffic_router/traffic_router_api.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/development/traffic_router/traffic_router_api.rst b/docs/source/development/traffic_router/traffic_router_api.rst index f2c4648db3..9fc1d6adf1 100644 --- a/docs/source/development/traffic_router/traffic_router_api.rst +++ b/docs/source/development/traffic_router/traffic_router_api.rst @@ -76,7 +76,7 @@ Request Structure Response Structure ------------------ .. code-block:: http - :caption: Response Example (JSON expanded) + :caption: Response Example HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 @@ -159,7 +159,7 @@ Request Structure Response Structure ------------------ .. code-block:: http - :caption: Response Example (JSON expanded) + :caption: Response Example HTTP/1.1 200 OK Content-Disposition: inline;filename=f.txt @@ -202,7 +202,7 @@ Response Structure :locations: An array of strings that are the :ref:`Names of Cache Groups ` to which this Traffic Router is capable of routing client traffic .. code-block:: http - :caption: Response Example (JSON expanded) + :caption: Response Example HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 @@ -232,7 +232,7 @@ Request Structure Response Structure ------------------ .. code-block:: http - :caption: Response Example (JSON expanded) + :caption: Response Example HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 @@ -288,7 +288,7 @@ Request Structure Response Structure ------------------ .. code-block:: http - :caption: Response Example (JSON expanded) + :caption: Response Example HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 @@ -415,7 +415,7 @@ Request Structure Response Structure ------------------ .. code-block:: http - :caption: Response Example (JSON expanded) + :caption: Response Example HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 @@ -565,7 +565,7 @@ Request Structure Response Structure ------------------ .. code-block:: http - :caption: Response Example (JSON expanded) + :caption: Response Example HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 @@ -606,7 +606,7 @@ Request Structure Response Structure ------------------ .. code-block:: http - :caption: Response Example (JSON expanded) + :caption: Response Example HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 From d341af1e7fdbc7cdedc733258b5da8dd94ad215c Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Wed, 13 Nov 2019 23:05:53 -0600 Subject: [PATCH 08/18] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1940576bf7..03ebed7a53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -211,6 +211,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Updated `t3c-apply` to reduce mutable state in `TrafficOpsReq` struct. - Updated Golang dependencies - [#6506](https://github.com/apache/trafficcontrol/pull/6506) - Updated `jackson-databind` and `jackson-annotations` Traffic Router dependencies to version 2.13.1 +- Traffic Router now includes a `Content-Length` header in each response. ### Deprecated - Deprecated the endpoints and docs associated with `/api_capability` and `/capabilities`. From a418140236ebf3f78d3b7c46abb88b3ab3a5c31f Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Sun, 21 Nov 2021 21:13:47 -0700 Subject: [PATCH 09/18] Use wrapper class instead of reflection to get Content-Length --- .../core/http/BufferedResponse.java | 54 +++++++++++++++++++ .../core/http/BufferedResponseFilter.java | 28 ++-------- 2 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponse.java diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponse.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponse.java new file mode 100644 index 0000000000..3d2cd2e622 --- /dev/null +++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponse.java @@ -0,0 +1,54 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.traffic_control.traffic_router.core.http; + +import org.springframework.web.util.ContentCachingResponseWrapper; + +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class BufferedResponse extends ContentCachingResponseWrapper { + protected int contentLength; + protected ServletResponse response; + + public BufferedResponse(final HttpServletResponse response) { + super(response); + this.response = response; + } + + @Override + public void setContentLength(final int len) { + contentLength = len; + super.setContentLength(len); + } + + @Override + public void setContentLengthLong(final long len) { + contentLength = (int) len; + super.setContentLengthLong(len); + } + + @Override + public void copyBodyToResponse() throws IOException { + if (this.getContentSize() == 0) { + response.setContentLength(contentLength); + } else { + // When the content size is greater than 0, copyBodyToResponse() + // will set Content-Length. + super.copyBodyToResponse(); + } + } +} diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java index e0612b8596..37aa11ae99 100644 --- a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java +++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java @@ -15,47 +15,25 @@ package org.apache.traffic_control.traffic_router.core.http; +import com.google.common.net.HttpHeaders; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.web.filter.OncePerRequestFilter; -import org.springframework.web.util.ContentCachingResponseWrapper; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.lang.reflect.Field; public class BufferedResponseFilter extends OncePerRequestFilter { public static final Logger LOGGER = LogManager.getLogger(BufferedResponseFilter.class); public void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException { - final ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); + final BufferedResponse responseWrapper = new BufferedResponse(response); chain.doFilter(request, responseWrapper); - if (responseWrapper.getContentSize() == 0) { - int contentLength = 0; - - try { - final Field contentLengthField = responseWrapper.getClass().getDeclaredField("contentLength"); - contentLengthField.setAccessible(true); - final Object contentLengthObject = contentLengthField.get(responseWrapper); - /* E.g., a HEAD request whose corresponding GET request has a non-empty body */ - if (contentLengthObject != null) { - contentLength = (Integer) contentLengthObject; - } - } catch (ReflectiveOperationException e) { - LOGGER.error("Encountered a " + e.getClass() + "exception in " + this.getClass() + ": " + e.getMessage()); - } finally { - response.setContentLength(contentLength); - } - } else { - /* When the content size is greater than 0, copyBodyToResponse() - * will set Content-Length. - */ - responseWrapper.copyBodyToResponse(); - } + responseWrapper.copyBodyToResponse(); } } From 5d707dc7f31d1467a90d54c5adb20e6ada61d7f4 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Sun, 21 Nov 2021 21:21:28 -0700 Subject: [PATCH 10/18] Close the connection after sending the response --- .../development/traffic_router/traffic_router_api.rst | 8 ++++++++ .../traffic_router/core/http/BufferedResponseFilter.java | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/docs/source/development/traffic_router/traffic_router_api.rst b/docs/source/development/traffic_router/traffic_router_api.rst index 9fc1d6adf1..471b9c079c 100644 --- a/docs/source/development/traffic_router/traffic_router_api.rst +++ b/docs/source/development/traffic_router/traffic_router_api.rst @@ -79,6 +79,7 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK + Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 1214 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -162,6 +163,7 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK + Connection: close Content-Disposition: inline;filename=f.txt Content-Type: application/json;charset=UTF-8 Content-Length: 131 @@ -205,6 +207,7 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK + Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 35 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -235,6 +238,7 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK + Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 278 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -291,6 +295,7 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK + Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 253 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -418,6 +423,7 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK + Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 828 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -568,6 +574,7 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK + Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 137 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -609,6 +616,7 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK + Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 163 Date: Mon, 04 Nov 2019 19:48:04 GMT diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java index 37aa11ae99..bc94bbc0e4 100644 --- a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java +++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java @@ -34,6 +34,14 @@ public void doFilterInternal(final HttpServletRequest request, final HttpServlet chain.doFilter(request, responseWrapper); + // Close the connection without waiting for the 10-second connect timeout, + // in case the client does not close the connection. Even though this is + // the only case for which we are interested in sending Connection: close, + // sending it sometimes means we must always send it. From RFC 2616: + // > HTTP/1.1 applications that do not support persistent connections MUST + // > include the "close" connection option in every message. + response.addHeader(HttpHeaders.CONNECTION, "close"); + responseWrapper.copyBodyToResponse(); } } From 7bc9865939388fb3aaa122b9640b47ecf15752b8 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Fri, 4 Nov 2022 14:11:28 -0600 Subject: [PATCH 11/18] Use constants for headers and charsets --- .../core/external/BufferedResponseTest.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java index d0d3aee815..4815a56f3b 100644 --- a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java +++ b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java @@ -17,9 +17,11 @@ import org.apache.traffic_control.traffic_router.core.http.RouterFilter; import org.apache.traffic_control.traffic_router.core.util.ExternalTest; + import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.catalina.LifecycleException; +import org.apache.http.HttpHeaders; import org.apache.http.Header; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -36,6 +38,7 @@ import java.io.IOException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -61,15 +64,15 @@ public void after() throws Exception { @Test public void itSetsContentLengthHeaderFor404() throws IOException { - final String encodedUrl = URLEncoder.encode("http://trafficrouter01.somedeliveryservice.somecdn.domain.foo/stuff", "utf-8"); + final String encodedUrl = URLEncoder.encode("http://trafficrouter01.somedeliveryservice.somecdn.domain.foo/stuff", StandardCharsets.UTF_8); final HttpGet httpGet = new HttpGet("http://localhost:3333/crs/deliveryservices?url=" + encodedUrl); CloseableHttpResponse response = null; try { response = httpClient.execute(httpGet); assertThat(response.getStatusLine().getStatusCode(), equalTo(404)); - assertThat(response.getFirstHeader("Transfer-Encoding"), nullValue()); - assertThat(response.getFirstHeader("Content-Length"), notNullValue()); + assertThat(response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING), nullValue()); + assertThat(response.getFirstHeader(HttpHeaders.CONTENT_LENGTH), notNullValue()); } finally { if (response != null) response.close(); } @@ -94,9 +97,9 @@ public void itSetsTheSameContentLengthForHeadAndGet() throws IOException { try { response = httpClient.execute(request); - final Header contentLengthHeader = response.getFirstHeader("Content-Length"); + final Header contentLengthHeader = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); - assertThat(response.getFirstHeader("Transfer-Encoding"), nullValue()); + assertThat(response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING), nullValue()); assertThat(contentLengthHeader, notNullValue()); contentLengths.add(Integer.parseInt(contentLengthHeader.getValue())); } finally { @@ -125,14 +128,14 @@ public void itSetsAnAccurateContentLengthForGet() throws IOException { final ObjectMapper objectMapper = new ObjectMapper(new JsonFactory()); final String json = EntityUtils.toString(response.getEntity()); - final Header contentLengthHeader = response.getFirstHeader("Content-Length"); + final Header contentLengthHeader = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); /* If the content length is too low and cuts off the response * body, objectMapper.readTree(json) will likely throw a * JsonProcessingException. */ objectMapper.readTree(json); - assertThat(response.getFirstHeader("Transfer-Encoding"), nullValue()); + assertThat(response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING), nullValue()); assertThat(contentLengthHeader, notNullValue()); assertThat(Integer.parseInt(contentLengthHeader.getValue()), equalTo(json.length())); } finally { @@ -166,9 +169,8 @@ public void itSetsContentLengthHeaderForDeliveryServiceSteering() throws IOExcep try { response = httpClient.execute(request); - final Header contentLengthHeader = response.getFirstHeader("Content-Length"); - - assertThat(response.getFirstHeader("Transfer-Encoding"), nullValue()); + final Header contentLengthHeader = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); + assertThat(response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING), nullValue()); assertThat(contentLengthHeader, notNullValue()); contentLengths.add(Integer.parseInt(contentLengthHeader.getValue())); } finally { From e27cf5e678c2fe2b9965fecb164138e721919266 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Fri, 4 Nov 2022 14:15:24 -0600 Subject: [PATCH 12/18] Use try block with resources --- .../core/external/BufferedResponseTest.java | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java index 4815a56f3b..7aab37c2a3 100644 --- a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java +++ b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java @@ -66,15 +66,11 @@ public void after() throws Exception { public void itSetsContentLengthHeaderFor404() throws IOException { final String encodedUrl = URLEncoder.encode("http://trafficrouter01.somedeliveryservice.somecdn.domain.foo/stuff", StandardCharsets.UTF_8); final HttpGet httpGet = new HttpGet("http://localhost:3333/crs/deliveryservices?url=" + encodedUrl); - CloseableHttpResponse response = null; - try { - response = httpClient.execute(httpGet); + try (CloseableHttpResponse response = httpClient.execute(httpGet)) { assertThat(response.getStatusLine().getStatusCode(), equalTo(404)); assertThat(response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING), nullValue()); assertThat(response.getFirstHeader(HttpHeaders.CONTENT_LENGTH), notNullValue()); - } finally { - if (response != null) response.close(); } } @@ -93,17 +89,13 @@ public void itSetsTheSameContentLengthForHeadAndGet() throws IOException { final List contentLengths = new ArrayList<>(); for (final HttpRequestBase request : requests) { - CloseableHttpResponse response = null; - try { - response = httpClient.execute(request); + try (CloseableHttpResponse response = httpClient.execute(request)) { final Header contentLengthHeader = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); assertThat(response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING), nullValue()); assertThat(contentLengthHeader, notNullValue()); contentLengths.add(Integer.parseInt(contentLengthHeader.getValue())); - } finally { - if (response != null) response.close(); } } @@ -163,18 +155,15 @@ public void itSetsContentLengthHeaderForDeliveryServiceSteering() throws IOExcep final List contentLengths = new ArrayList<>(); for (final HttpRequestBase request : requests) { - CloseableHttpResponse response = null; + request.setConfig(config); request.addHeader("Host", testHostName); - - try { - response = httpClient.execute(request); + try (CloseableHttpResponse response = httpClient.execute(request)) { final Header contentLengthHeader = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH); + assertThat(response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING), nullValue()); assertThat(contentLengthHeader, notNullValue()); contentLengths.add(Integer.parseInt(contentLengthHeader.getValue())); - } finally { - if (response != null) response.close(); } } From 0f49e64b22555f39188df87e042ce4aa8bfd8e4b Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Fri, 4 Nov 2022 14:17:15 -0600 Subject: [PATCH 13/18] Space before "to" --- .../traffic_router/core/external/BufferedResponseTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java index 7aab37c2a3..4cac8b2843 100644 --- a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java +++ b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java @@ -100,7 +100,7 @@ public void itSetsTheSameContentLengthForHeadAndGet() throws IOException { } assertThat(contentLengths.size(), equalTo(2)); - assertThat("Expected HEAD and GET requests for " + path + "to have the same Content-Length", contentLengths.get(0), equalTo(contentLengths.get(1))); + assertThat("Expected HEAD and GET requests for " + path + " to have the same Content-Length", contentLengths.get(0), equalTo(contentLengths.get(1))); } } @@ -168,7 +168,7 @@ public void itSetsContentLengthHeaderForDeliveryServiceSteering() throws IOExcep } assertThat(contentLengths.size(), equalTo(2)); - assertThat("Expected HEAD and GET requests for " + testHostName + path + "to have the same Content-Length", contentLengths.get(0), equalTo(contentLengths.get(1))); + assertThat("Expected HEAD and GET requests for " + testHostName + path + " to have the same Content-Length", contentLengths.get(0), equalTo(contentLengths.get(1))); } } } From 3e21d03c56ab2edab10bfe6458df991fd720c189 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Fri, 4 Nov 2022 14:18:36 -0600 Subject: [PATCH 14/18] Use MatcherAssert.assertThat because Assert.assertThat is deprecated --- .../traffic_router/core/external/BufferedResponseTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java index 4cac8b2843..9f0bbbf352 100644 --- a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java +++ b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/BufferedResponseTest.java @@ -42,10 +42,10 @@ import java.util.ArrayList; import java.util.List; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; @Category(ExternalTest.class) public class BufferedResponseTest { From 093dce882a36e38be026f8e2baf6df34e102d966 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Fri, 4 Nov 2022 14:22:11 -0600 Subject: [PATCH 15/18] Assert Content-Length header is set --- .../traffic_router/core/external/LocationsTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/LocationsTest.java b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/LocationsTest.java index b144d964ae..ff24050893 100644 --- a/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/LocationsTest.java +++ b/traffic_router/core/src/test/java/org/apache/traffic_control/traffic_router/core/external/LocationsTest.java @@ -15,6 +15,7 @@ package org.apache.traffic_control.traffic_router.core.external; +import org.apache.http.HttpHeaders; import org.apache.traffic_control.traffic_router.core.util.ExternalTest; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.JsonNode; @@ -35,6 +36,7 @@ import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.core.AnyOf.anyOf; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsNot.not; @@ -173,6 +175,7 @@ public void itHandlesHeadRequests() throws Exception { try { response = closeableHttpClient.execute(httpHead); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); + assertThat(response.getFirstHeader(HttpHeaders.CONTENT_LENGTH), notNullValue()); } finally { if (response != null) response.close(); } From ed3b795c3745d0d1e72ce0910d6eadd7b9d97b5e Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Fri, 4 Nov 2022 15:39:42 -0600 Subject: [PATCH 16/18] Revert "Close the connection after sending the response" This reverts commit b24649869407d902301ce6997c629e367285b44f. --- .../development/traffic_router/traffic_router_api.rst | 8 -------- .../core/http/BufferedResponseFilter.java | 10 ---------- 2 files changed, 18 deletions(-) diff --git a/docs/source/development/traffic_router/traffic_router_api.rst b/docs/source/development/traffic_router/traffic_router_api.rst index 471b9c079c..9fc1d6adf1 100644 --- a/docs/source/development/traffic_router/traffic_router_api.rst +++ b/docs/source/development/traffic_router/traffic_router_api.rst @@ -79,7 +79,6 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK - Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 1214 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -163,7 +162,6 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK - Connection: close Content-Disposition: inline;filename=f.txt Content-Type: application/json;charset=UTF-8 Content-Length: 131 @@ -207,7 +205,6 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK - Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 35 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -238,7 +235,6 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK - Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 278 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -295,7 +291,6 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK - Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 253 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -423,7 +418,6 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK - Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 828 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -574,7 +568,6 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK - Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 137 Date: Mon, 04 Nov 2019 19:48:04 GMT @@ -616,7 +609,6 @@ Response Structure :caption: Response Example HTTP/1.1 200 OK - Connection: close Content-Type: application/json;charset=UTF-8 Content-Length: 163 Date: Mon, 04 Nov 2019 19:48:04 GMT diff --git a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java index bc94bbc0e4..f4224d8255 100644 --- a/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java +++ b/traffic_router/core/src/main/java/org/apache/traffic_control/traffic_router/core/http/BufferedResponseFilter.java @@ -31,17 +31,7 @@ public class BufferedResponseFilter extends OncePerRequestFilter { public void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws IOException, ServletException { final BufferedResponse responseWrapper = new BufferedResponse(response); - chain.doFilter(request, responseWrapper); - - // Close the connection without waiting for the 10-second connect timeout, - // in case the client does not close the connection. Even though this is - // the only case for which we are interested in sending Connection: close, - // sending it sometimes means we must always send it. From RFC 2616: - // > HTTP/1.1 applications that do not support persistent connections MUST - // > include the "close" connection option in every message. - response.addHeader(HttpHeaders.CONNECTION, "close"); - responseWrapper.copyBodyToResponse(); } } From 88e2490439d00d8b5420b196d75e5140f9c7069c Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Fri, 4 Nov 2022 15:52:02 -0600 Subject: [PATCH 17/18] Move changelog entry to unreleased section --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03ebed7a53..b441f72d25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - [#7158](https://github.com/apache/trafficcontrol/issues/7158) *Traffic Vault* Fix the `reencrypt` utility to uniquely reencrypt each version of the SSL Certificates. - [#7137](https://github.com/apache/trafficcontrol/pull/7137) *Cache Config* parent.config simulate topology for non topo delivery services. - Adds an extra T3C check for validity of an ssl cert (crash fix). +- Traffic Router now always includes a `Content-Length` header in the response. ## [7.0.0] - 2022-07-19 ### Added From 7774d21768a82c2c18c1fc77407f293c7349a800 Mon Sep 17 00:00:00 2001 From: Zach Hoffman Date: Wed, 9 Nov 2022 10:12:12 -0700 Subject: [PATCH 18/18] Remove unused 6.1.0 changelog entry --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b441f72d25..62e829d38e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -212,7 +212,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Updated `t3c-apply` to reduce mutable state in `TrafficOpsReq` struct. - Updated Golang dependencies - [#6506](https://github.com/apache/trafficcontrol/pull/6506) - Updated `jackson-databind` and `jackson-annotations` Traffic Router dependencies to version 2.13.1 -- Traffic Router now includes a `Content-Length` header in each response. ### Deprecated - Deprecated the endpoints and docs associated with `/api_capability` and `/capabilities`.