Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue #756 (restore HTTP authentication support) #765

Merged
merged 1 commit into from
Mar 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand All @@ -39,6 +41,7 @@
import org.glassfish.jersey.client.JerseyClient;
import org.glassfish.jersey.client.JerseyInvocation.Builder;
import org.glassfish.jersey.client.JerseyWebTarget;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -54,11 +57,30 @@ public class Jersey2ApplicationClient implements EurekaHttpClient {
private final JerseyClient jerseyClient;
private final String serviceUrl;
private final boolean allowRedirect;
private final String userName;
private final String password;

public Jersey2ApplicationClient(JerseyClient jerseyClient, String serviceUrl, boolean allowRedirect) {
this.jerseyClient = jerseyClient;
this.serviceUrl = serviceUrl;
this.allowRedirect = allowRedirect;

// Jersey2 does not read credentials from the URI. We extract it here and enable authentication feature.
String localUserName = null;
String localPassword = null;
try {
URI serviceURI = new URI(serviceUrl);
if (serviceURI.getUserInfo() != null) {
String[] credentials = serviceURI.getUserInfo().split(":");
if (credentials.length == 2) {
localUserName = credentials[0];
localPassword = credentials[1];
}
}
} catch (URISyntaxException ignore) {
}
this.userName = localUserName;
this.password = localPassword;
}

@Override
Expand All @@ -67,6 +89,7 @@ public EurekaHttpResponse<Void> register(InstanceInfo info) {
Response response = null;
try {
Builder resourceBuilder = jerseyClient.target(serviceUrl).path(urlPath).request();
addExtraProperties(resourceBuilder);
addExtraHeaders(resourceBuilder);
response = resourceBuilder
.accept(MediaType.APPLICATION_JSON)
Expand All @@ -90,6 +113,7 @@ public EurekaHttpResponse<Void> cancel(String appName, String id) {
Response response = null;
try {
Builder resourceBuilder = jerseyClient.target(serviceUrl).path(urlPath).request();
addExtraProperties(resourceBuilder);
addExtraHeaders(resourceBuilder);
response = resourceBuilder.delete();
return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
Expand All @@ -116,6 +140,7 @@ public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id,
webResource = webResource.queryParam("overriddenstatus", overriddenStatus.name());
}
Builder requestBuilder = webResource.request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.put(Entity.entity("{}", MediaType.APPLICATION_JSON_TYPE)); // Jersey2 refuses to handle PUT with no body
EurekaHttpResponseBuilder<InstanceInfo> eurekaResponseBuilder = anEurekaHttpResponse(response.getStatus(), InstanceInfo.class).headers(headersOf(response));
Expand Down Expand Up @@ -143,6 +168,7 @@ public EurekaHttpResponse<Void> statusUpdate(String appName, String id, Instance
.queryParam("value", newStatus.name())
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.put(Entity.entity("{}", MediaType.APPLICATION_JSON_TYPE)); // Jersey2 refuses to handle PUT with no body
return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
Expand All @@ -165,6 +191,7 @@ public EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id,
.path(urlPath)
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString())
.request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.delete();
return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
Expand Down Expand Up @@ -204,6 +231,7 @@ public EurekaHttpResponse<Application> getApplication(String appName) {
Response response = null;
try {
Builder requestBuilder = jerseyClient.target(serviceUrl).path(urlPath).request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get();

Expand All @@ -230,6 +258,7 @@ private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath,
webTarget = webTarget.queryParam("regions", StringUtil.join(regions));
}
Builder requestBuilder = webTarget.request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get();

Expand Down Expand Up @@ -262,6 +291,7 @@ private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {
Response response = null;
try {
Builder requestBuilder = jerseyClient.target(serviceUrl).path(urlPath).request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
response = requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get();

Expand All @@ -282,7 +312,13 @@ private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {

@Override
public void shutdown() {
}

protected void addExtraProperties(Builder webResource) {
if (userName != null) {
webResource.property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_USERNAME, userName)
.property(HttpAuthenticationFeature.HTTP_AUTHENTICATION_PASSWORD, password);
}
}

protected void addExtraHeaders(Builder webResource) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.HttpHeaders;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.netflix.discovery.provider.DiscoveryJerseyProvider;
import com.netflix.discovery.shared.resolver.EurekaEndpoint;
Expand Down Expand Up @@ -67,11 +70,22 @@ public static Jersey2ApplicationClientFactoryBuilder newBuilder() {

public static class Jersey2ApplicationClientFactoryBuilder extends EurekaClientFactoryBuilder<Jersey2ApplicationClientFactory, Jersey2ApplicationClientFactoryBuilder> {

private List<Feature> features = new ArrayList<>();

public Jersey2ApplicationClientFactoryBuilder withFeature(Feature feature) {
features.add(feature);
return this;
}

@Override
public Jersey2ApplicationClientFactory build() {
ClientBuilder clientBuilder = ClientBuilder.newBuilder();
ClientConfig clientConfig = new ClientConfig();

for (Feature feature : features) {
clientConfig.register(feature);
}

addProviders(clientConfig);
addSSLConfiguration(clientBuilder);
addProxyConfiguration(clientConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@

package com.netflix.discovery.shared.transport.jersey2;

import java.net.URI;

import com.netflix.discovery.shared.resolver.DefaultEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpClientCompatibilityTestSuite;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import com.netflix.discovery.shared.transport.jersey2.Jersey2ApplicationClientFactory.Jersey2ApplicationClientFactoryBuilder;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.junit.After;
import org.junit.Before;

/**
* @author Tomasz Bak
Expand All @@ -30,14 +33,6 @@ public class Jersey2ApplicationClientTest extends EurekaHttpClientCompatibilityT

private Jersey2ApplicationClient jersey2HttpClient;

@Override
@Before
public void setUp() throws Exception {
super.setUp();
TransportClientFactory clientFactory = Jersey2ApplicationClientFactory.newBuilder().build();
jersey2HttpClient = (Jersey2ApplicationClient) clientFactory.newClient(new DefaultEndpoint(getHttpServer().getServiceURI().toString()));
}

@Override
@After
public void tearDown() throws Exception {
Expand All @@ -48,7 +43,13 @@ public void tearDown() throws Exception {
}

@Override
public EurekaHttpClient getEurekaHttpClient() {
protected EurekaHttpClient getEurekaHttpClient(URI serviceURI) {
Jersey2ApplicationClientFactoryBuilder factoryBuilder = Jersey2ApplicationClientFactory.newBuilder();
if (serviceURI.getUserInfo() != null) {
factoryBuilder.withFeature(HttpAuthenticationFeature.basicBuilder().build());
}
TransportClientFactory clientFactory = factoryBuilder.build();
jersey2HttpClient = (Jersey2ApplicationClient) clientFactory.newClient(new DefaultEndpoint(serviceURI.toString()));
return jersey2HttpClient;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ public class AwsEndpoint extends DefaultEndpoint {
protected final String zone;
protected final String region;

public AwsEndpoint(String serviceURI, String region, String zone) {
super(serviceURI);
this.region = region;
this.zone = zone;
}

public AwsEndpoint(String hostName, int port, boolean isSecure, String relativeUri, String region, String zone) {
super(hostName, port, isSecure, relativeUri);
this.region=region;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,8 @@ private List<AwsEndpoint> getClusterEndpointsFromConfig() {
for (String zone : serviceUrls.keySet()) {
for(String url : serviceUrls.get(zone)) {
try {
URI serviceURI = new URI(url);
endpoints.add(new AwsEndpoint(
serviceURI.getHost(),
serviceURI.getPort(),
"https".equalsIgnoreCase(serviceURI.getScheme()),
serviceURI.getPath(),
getRegion(),
zone
));
} catch (URISyntaxException ignore) {
endpoints.add(new AwsEndpoint(url, getRegion(), zone));
} catch (Exception ignore) {
logger.warn("Invalid eureka server URI: ; removing from the server pool", url);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.util.List;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand All @@ -31,8 +32,8 @@ public class ConfigClusterResolverTest {
"http://1.1.2.1:8000/eureka/v2/",
"http://1.1.2.2:8000/eureka/v2/"
);
private final List<String> endpointsE = Arrays.asList(
"https://1.1.3.1/eureka/v2/"
private final List<String> endpointsWithBasicAuth = Arrays.asList(
"https://myuser:mypassword@1.1.3.1/eureka/v2/"
);
private ConfigClusterResolver resolver;

Expand All @@ -43,7 +44,7 @@ public void setUp() {
when(clientConfig.getAvailabilityZones("us-east-1")).thenReturn(new String[]{"us-east-1c", "us-east-1d", "us-east-1e"});
when(clientConfig.getEurekaServerServiceUrls("us-east-1c")).thenReturn(endpointsC);
when(clientConfig.getEurekaServerServiceUrls("us-east-1d")).thenReturn(endpointsD);
when(clientConfig.getEurekaServerServiceUrls("us-east-1e")).thenReturn(endpointsE);
when(clientConfig.getEurekaServerServiceUrls("us-east-1e")).thenReturn(endpointsWithBasicAuth);

InstanceInfo instanceInfo = new InstanceInfo.Builder(InstanceInfoGenerator.takeOne())
.setDataCenterInfo(new MyDataCenterInfo(DataCenterInfo.Name.MyOwn))
Expand All @@ -59,8 +60,9 @@ public void testReadFromConfig() {

for (AwsEndpoint endpoint : endpoints) {
if (endpoint.getZone().equals("us-east-1e")) {
assertThat("secure was wrong", endpoint.isSecure(), equalTo(true));
assertThat("serviceUrl contains -1", endpoint.getServiceUrl().contains("-1"), equalTo(false));
assertThat("secure was wrong", endpoint.isSecure(), is(true));
assertThat("serviceUrl contains -1", endpoint.getServiceUrl().contains("-1"), is(false));
assertThat("BASIC auth credentials expected", endpoint.getServiceUrl().contains("myuser:mypassword"), is(true));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,19 @@

package com.netflix.discovery.shared.transport.jersey;

import java.net.URI;

import com.google.common.base.Preconditions;
import com.netflix.discovery.shared.resolver.DefaultEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.EurekaHttpClientCompatibilityTestSuite;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import org.junit.After;
import org.junit.Before;

public class JerseyApplicationClientTest extends EurekaHttpClientCompatibilityTestSuite {

private JerseyApplicationClient jerseyHttpClient;

@Override
@Before
public void setUp() throws Exception {
super.setUp();
TransportClientFactory clientFactory = JerseyEurekaHttpClientFactory.newBuilder()
.withClientName("compatibilityTestClient")
.build();
jerseyHttpClient = (JerseyApplicationClient) clientFactory.newClient(new DefaultEndpoint(getHttpServer().getServiceURI().toString()));
}

@Override
@After
public void tearDown() throws Exception {
Expand All @@ -47,7 +39,14 @@ public void tearDown() throws Exception {
}

@Override
public EurekaHttpClient getEurekaHttpClient() {
protected EurekaHttpClient getEurekaHttpClient(URI serviceURI) {
Preconditions.checkState(jerseyHttpClient == null, "EurekaHttpClient has been already created");

TransportClientFactory clientFactory = JerseyEurekaHttpClientFactory.newBuilder()
.withClientName("compatibilityTestClient")
.build();
jerseyHttpClient = (JerseyApplicationClient) clientFactory.newClient(new DefaultEndpoint(serviceURI.toString()));

return jerseyHttpClient;
}
}
Loading