Browse Source

Try to guess whether a ribbon client is looking for HTTPS

There are usually some hints available, especially if the Server
came from Eureka. Unfortunately Server on it's own doesn't contain
enough information (why?), but as a fallback we can guess that
anyone using port 443 is secure.

Fixes gh-459
pull/6/head
Dave Syer 10 years ago
parent
commit
a38ba34049
  1. 10
      docs/src/main/asciidoc/spring-cloud-netflix.adoc
  2. 31
      spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java
  3. 37
      spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java

10
docs/src/main/asciidoc/spring-cloud-netflix.adoc

@ -108,6 +108,16 @@ information will have a secure health check URL. Because of the way @@ -108,6 +108,16 @@ information will have a secure health check URL. Because of the way
Eureka works internally, it will still publish a non-secure URL for
status and home page unless you also override those explicitly.
NOTE: If your app is running behind a proxy, and the SSL termination
is in the proxy (e.g. if you run in Cloud Foundry or other platforms
as a service) then you will need to ensure that the proxy "forwarded"
headers are intercepted and handled by the application. An embedded
Tomcat container in a Spring Boot app does this automatically for most
proxies (since 1.3.0), but other containers might need explicit
configuration for the 'X-Forwarded-\*` headers. A sign that you got
this wrong will be that the links rendered by your app to itslef will be
wrong (the wrong host, port or protocol).
=== Eureka's Health Checks
By default, Eureka uses the client heartbeat to determine if a client is up.

31
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
package org.springframework.cloud.netflix.ribbon;
import java.net.URI;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.springframework.beans.factory.annotation.Value;
@ -24,7 +26,10 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties @@ -24,7 +26,10 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.ClassUtils;
import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.appinfo.InstanceInfo.PortType;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ConfigurationBasedServerList;
@ -39,6 +44,7 @@ import com.netflix.loadbalancer.ServerListFilter; @@ -39,6 +44,7 @@ import com.netflix.loadbalancer.ServerListFilter;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
import com.netflix.niws.client.http.RestClient;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import com.netflix.servo.monitor.Monitors;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.client.apache4.ApacheHttpClient4;
@ -90,9 +96,9 @@ public class RibbonClientConfiguration { @@ -90,9 +96,9 @@ public class RibbonClientConfiguration {
/**
* Create a Netflix {@link RestClient} integrated with Ribbon if none already exists in the
* application context. It is not required for Ribbon to work properly and is therefore
* application context. It is not required for Ribbon to work properly and is therefore
* created lazily if ever another component requires it.
*
*
* @param config the configuration to use by the underlying Ribbon instance
* @param loadBalancer the load balancer to use by the underlying Ribbon instance
* @return a {@link RestClient} instances backed by Ribbon
@ -141,6 +147,27 @@ public class RibbonClientConfiguration { @@ -141,6 +147,27 @@ public class RibbonClientConfiguration {
initWithNiwsConfig(ncc);
}
@Override
public URI reconstructURIWithServer(Server server, URI original) {
String scheme = original.getScheme();
if (!"https".equals(scheme) && isSecure(server)) {
original = UriComponentsBuilder.fromUri(original).scheme("https").build().toUri();
}
return super.reconstructURIWithServer(server, original);
}
private boolean isSecure(Server server) {
if (ClassUtils.isPresent("com.netflix.niws.loadbalancer.DiscoveryEnabledServer",
null)) {
if (server instanceof DiscoveryEnabledServer) {
DiscoveryEnabledServer enabled = (DiscoveryEnabledServer) server;
return enabled.getInstanceInfo().isPortEnabled(PortType.SECURE);
}
}
// Can we do better?
return (""+server.getPort()).endsWith("443");
}
@Override
protected Client apacheHttpClientSpecificInitialization() {
ApacheHttpClient4 apache = (ApacheHttpClient4) super

37
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonLoadBalancerClient.java

@ -24,14 +24,17 @@ import org.springframework.cloud.client.ServiceInstance; @@ -24,14 +24,17 @@ import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.appinfo.InstanceInfo.PortType;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerStats;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import com.netflix.servo.monitor.Stopwatch;
/**
@ -53,9 +56,9 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { @@ -53,9 +56,9 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
Server server = new Server(instance.getHost(), instance.getPort());
boolean secure = isSecure(this.clientFactory, serviceId);
boolean secure = isSecure(this.clientFactory, server, serviceId);
URI uri = original;
if(secure) {
if (secure) {
uri = UriComponentsBuilder.fromUri(uri).scheme("https").build().toUri();
}
return context.reconstructURIWithServer(server, uri);
@ -67,7 +70,8 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { @@ -67,7 +70,8 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(this.clientFactory, serviceId));
return new RibbonServer(serviceId, server,
isSecure(this.clientFactory, server, serviceId));
}
@Override
@ -76,7 +80,8 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { @@ -76,7 +80,8 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
Server server = getServer(loadBalancer);
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(clientFactory, serviceId));
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(this.clientFactory, server, serviceId));
ServerStats serverStats = context.getServerStats(server);
context.noteOpenConnection(serverStats);
@ -93,20 +98,30 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { @@ -93,20 +98,30 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
}
return null;
}
private boolean isSecure(SpringClientFactory clientFactory, String serviceId) {
private boolean isSecure(SpringClientFactory clientFactory, Server server,
String serviceId) {
IClientConfig config = clientFactory.getClientConfig(serviceId);
if(config != null) {
if (config != null) {
return config.get(CommonClientConfigKey.IsSecure, false);
}
return false;
if (ClassUtils.isPresent("com.netflix.niws.loadbalancer.DiscoveryEnabledServer",
null)) {
if (server instanceof DiscoveryEnabledServer) {
DiscoveryEnabledServer enabled = (DiscoveryEnabledServer) server;
return enabled.getInstanceInfo().isPortEnabled(PortType.SECURE);
}
}
// Can we do better?
return ("" + server.getPort()).endsWith("443");
}
private void recordStats(RibbonLoadBalancerContext context, Stopwatch tracer,
ServerStats serverStats, Object entity, Throwable exception) {
tracer.stop();
long duration = tracer.getDuration(TimeUnit.MILLISECONDS);
context.noteRequestCompletion(serverStats, entity, exception, duration, null/* errorHandler */);
context.noteRequestCompletion(serverStats, entity, exception, duration,
null/* errorHandler */);
}
protected Server getServer(String serviceId) {
@ -117,7 +132,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { @@ -117,7 +132,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer("default"); //TODO: better handling of key
return loadBalancer.chooseServer("default"); // TODO: better handling of key
}
protected ILoadBalancer getLoadBalancer(String serviceId) {
@ -128,7 +143,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient { @@ -128,7 +143,7 @@ public class RibbonLoadBalancerClient implements LoadBalancerClient {
private String serviceId;
private Server server;
private boolean secure;
protected RibbonServer(String serviceId, Server server) {
this(serviceId, server, false);
}

Loading…
Cancel
Save