Split ProxyRequestHelper to function without Actuator.

Previously ProxyRequestHelper contained a TraceRepository field. This caused zuul apps to fail with a class not found exception if they excluded actuator. This splits TraceRepository functionality into a new TraceProxyRequestHelper that extends ProxyRequestHelper. Auto configuration creates the appropriate ProxyRequestHelper based on the existence or not of actuator classes. fixes gh-1135
parent 5742f5e8
......@@ -21,6 +21,7 @@ import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.discovery.DiscoveryClient;
......@@ -31,6 +32,7 @@ import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.TraceProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.discovery.ServiceRouteMapper;
......@@ -55,9 +57,6 @@ import org.springframework.context.annotation.Configuration;
@Configuration
public class ZuulProxyConfiguration extends ZuulConfiguration {
@Autowired(required = false)
private TraceRepository traces;
@Autowired
private DiscoveryClient discovery;
......@@ -133,17 +132,6 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
}
@Bean
public ProxyRequestHelper proxyRequestHelper() {
ProxyRequestHelper helper = new ProxyRequestHelper();
if (this.traces != null) {
helper.setTraces(this.traces);
}
helper.setIgnoredHeaders(this.zuulProperties.getIgnoredHeaders());
helper.setTraceRequestBody(this.zuulProperties.isTraceRequestBody());
return helper;
}
@Bean
public ApplicationListener<ApplicationEvent> zuulDiscoveryRefreshRoutesListener() {
return new ZuulDiscoveryRefreshListener();
}
......@@ -155,14 +143,41 @@ public class ZuulProxyConfiguration extends ZuulConfiguration {
}
@Configuration
@ConditionalOnMissingClass("org.springframework.boot.actuate.endpoint.Endpoint")
protected static class NoActuatorConfiguration {
@Bean
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
ProxyRequestHelper helper = new ProxyRequestHelper();
helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders());
helper.setTraceRequestBody(zuulProperties.isTraceRequestBody());
return helper;
}
}
@Configuration
@ConditionalOnClass(Endpoint.class)
protected static class RoutesEndpointConfiguration {
@Autowired(required = false)
private TraceRepository traces;
@Bean
public RoutesEndpoint zuulEndpoint(RouteLocator routeLocator) {
return new RoutesEndpoint(routeLocator);
}
@Bean
public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) {
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
if (this.traces != null) {
helper.setTraces(this.traces);
}
helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders());
helper.setTraceRequestBody(zuulProperties.isTraceRequestBody());
return helper;
}
}
private static class ZuulDiscoveryRefreshListener
......
......@@ -16,10 +16,11 @@
package org.springframework.cloud.netflix.zuul.filters;
import static org.springframework.http.HttpHeaders.CONTENT_ENCODING;
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
......@@ -33,7 +34,6 @@ import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.util.LinkedMultiValueMap;
......@@ -45,14 +45,12 @@ import org.springframework.web.util.WebUtils;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.util.HTTPRequestUtils;
import static org.springframework.http.HttpHeaders.CONTENT_ENCODING;
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
import lombok.extern.apachecommons.CommonsLog;
/**
* @author Dave Syer
* @author Marcos Barbero
* @author Spencer Gibb
*/
@CommonsLog
public class ProxyRequestHelper {
......@@ -63,8 +61,6 @@ public class ProxyRequestHelper {
*/
public static final String IGNORED_HEADERS = "ignoredHeaders";
private TraceRepository traces;
private Set<String> ignoredHeaders = new LinkedHashSet<>();
private Set<String> sensitiveHeaders = new LinkedHashSet<>();
......@@ -85,10 +81,6 @@ public class ProxyRequestHelper {
this.ignoredHeaders.addAll(ignoredHeaders);
}
public void setTraces(TraceRepository traces) {
this.traces = traces;
}
public void setTraceRequestBody(boolean traceRequestBody) {
this.traceRequestBody = traceRequestBody;
}
......@@ -238,32 +230,10 @@ public class ProxyRequestHelper {
MultiValueMap<String, String> headers, MultiValueMap<String, String> params,
InputStream requestEntity) throws IOException {
Map<String, Object> info = new LinkedHashMap<>();
if (this.traces != null) {
RequestContext context = RequestContext.getCurrentContext();
info.put("method", verb);
info.put("path", uri);
info.put("query", getQueryString(params));
info.put("remote", true);
info.put("proxy", context.get("proxy"));
Map<String, Object> trace = new LinkedHashMap<>();
Map<String, Object> input = new LinkedHashMap<>();
trace.put("request", input);
info.put("headers", trace);
transformHeaders(headers, input);
RequestContext ctx = RequestContext.getCurrentContext();
if (shouldDebugBody(ctx)) {
// Prevent input stream from being read if it needs to go downstream
if (requestEntity != null) {
debugRequestEntity(info, ctx.getRequest().getInputStream());
}
}
this.traces.add(info);
return info;
}
return info;
}
/* for tests */ boolean shouldDebugBody(RequestContext ctx) {
protected boolean shouldDebugBody(RequestContext ctx) {
HttpServletRequest request = ctx.getRequest();
if (!this.traceRequestBody || ctx.isChunkedRequestBody()
|| RequestUtils.isZuulServletRequest()) {
......@@ -277,40 +247,6 @@ public class ProxyRequestHelper {
public void appendDebug(Map<String, Object> info, int status,
MultiValueMap<String, String> headers) {
if (this.traces != null) {
@SuppressWarnings("unchecked")
Map<String, Object> trace = (Map<String, Object>) info.get("headers");
Map<String, Object> output = new LinkedHashMap<>();
trace.put("response", output);
transformHeaders(headers, output);
output.put("status", "" + status);
}
}
void transformHeaders(MultiValueMap<String, String> headers, Map<String, Object> output) {
for (Entry<String, List<String>> key : headers.entrySet()) {
Collection<String> collection = key.getValue();
Object value = collection;
if (collection.size() < 2) {
value = collection.isEmpty() ? "" : collection.iterator().next();
}
output.put(key.getKey(), value);
}
}
private void debugRequestEntity(Map<String, Object> info, InputStream inputStream)
throws IOException {
if (RequestContext.getCurrentContext().isChunkedRequestBody()) {
info.put("body", "<chunked>");
return;
}
char[] buffer = new char[4096];
int count = new InputStreamReader(inputStream, Charset.forName("UTF-8"))
.read(buffer, 0, buffer.length);
if (count > 0) {
String entity = new String(buffer).substring(0, count);
info.put("body", entity.length() < 4096 ? entity : entity + "<truncated>");
}
}
public String getQueryString(MultiValueMap<String, String> params) {
......
/*
* Copyright 2013-2016 the original author or authors.
*
* 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.springframework.cloud.netflix.zuul.filters;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.util.MultiValueMap;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.apachecommons.CommonsLog;
/**
* @author Spencer Gibb
*/
@CommonsLog
public class TraceProxyRequestHelper extends ProxyRequestHelper {
private TraceRepository traces;
public void setTraces(TraceRepository traces) {
this.traces = traces;
}
@Override
public Map<String, Object> debug(String verb, String uri,
MultiValueMap<String, String> headers, MultiValueMap<String, String> params,
InputStream requestEntity) throws IOException {
Map<String, Object> info = new LinkedHashMap<>();
if (this.traces != null) {
RequestContext context = RequestContext.getCurrentContext();
info.put("method", verb);
info.put("path", uri);
info.put("query", getQueryString(params));
info.put("remote", true);
info.put("proxy", context.get("proxy"));
Map<String, Object> trace = new LinkedHashMap<>();
Map<String, Object> input = new LinkedHashMap<>();
trace.put("request", input);
info.put("headers", trace);
debugHeaders(headers, input);
RequestContext ctx = RequestContext.getCurrentContext();
if (shouldDebugBody(ctx)) {
// Prevent input stream from being read if it needs to go downstream
if (requestEntity != null) {
debugRequestEntity(info, ctx.getRequest().getInputStream());
}
}
this.traces.add(info);
return info;
}
return info;
}
void debugHeaders(MultiValueMap<String, String> headers, Map<String, Object> map) {
for (Entry<String, List<String>> entry : headers.entrySet()) {
Collection<String> collection = entry.getValue();
Object value = collection;
if (collection.size() < 2) {
value = collection.isEmpty() ? "" : collection.iterator().next();
}
map.put(entry.getKey(), value);
}
}
public void appendDebug(Map<String, Object> info, int status,
MultiValueMap<String, String> headers) {
if (this.traces != null) {
@SuppressWarnings("unchecked")
Map<String, Object> trace = (Map<String, Object>) info.get("headers");
Map<String, Object> output = new LinkedHashMap<String, Object>();
trace.put("response", output);
debugHeaders(headers, output);
output.put("status", "" + status);
}
}
private void debugRequestEntity(Map<String, Object> info, InputStream inputStream)
throws IOException {
if (RequestContext.getCurrentContext().isChunkedRequestBody()) {
info.put("body", "<chunked>");
return;
}
char[] buffer = new char[4096];
int count = new InputStreamReader(inputStream, Charset.forName("UTF-8"))
.read(buffer, 0, buffer.length);
if (count > 0) {
String entity = new String(buffer).substring(0, count);
info.put("body", entity.length() < 4096 ? entity : entity + "<truncated>");
}
}
}
......@@ -72,7 +72,7 @@ public class ProxyRequestHelperTests {
request.addHeader("multiName", "multiValue2");
RequestContext.getCurrentContext().setRequest(request);
ProxyRequestHelper helper = new ProxyRequestHelper();
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
this.traceRepository = new InMemoryTraceRepository();
helper.setTraces(this.traceRepository);
......@@ -172,7 +172,7 @@ public class ProxyRequestHelperTests {
request.addHeader("multiName", "multiValue1");
request.addHeader("multiName", "multiValue2");
ProxyRequestHelper helper = new ProxyRequestHelper();
TraceProxyRequestHelper helper = new TraceProxyRequestHelper();
helper.setTraces(this.traceRepository);
MultiValueMap<String, String> headers = helper.buildZuulRequestHeaders(request);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment