diff --git a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClient.java b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClient.java index 29024ea290..5f0e735f27 100644 --- a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClient.java +++ b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClient.java @@ -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; @@ -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; @@ -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 @@ -67,6 +89,7 @@ public EurekaHttpResponse 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) @@ -90,6 +113,7 @@ public EurekaHttpResponse 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(); @@ -116,6 +140,7 @@ public EurekaHttpResponse 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 eurekaResponseBuilder = anEurekaHttpResponse(response.getStatus(), InstanceInfo.class).headers(headersOf(response)); @@ -143,6 +168,7 @@ public EurekaHttpResponse 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(); @@ -165,6 +191,7 @@ public EurekaHttpResponse 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(); @@ -204,6 +231,7 @@ public EurekaHttpResponse 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(); @@ -230,6 +258,7 @@ private EurekaHttpResponse 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(); @@ -262,6 +291,7 @@ private EurekaHttpResponse 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(); @@ -282,7 +312,13 @@ private EurekaHttpResponse 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) { diff --git a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientFactory.java b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientFactory.java index 5dee7a8903..77683d6d32 100644 --- a/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientFactory.java +++ b/eureka-client-jersey2/src/main/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientFactory.java @@ -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; @@ -67,11 +70,22 @@ public static Jersey2ApplicationClientFactoryBuilder newBuilder() { public static class Jersey2ApplicationClientFactoryBuilder extends EurekaClientFactoryBuilder { + private List 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); diff --git a/eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientTest.java b/eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientTest.java index e460579914..7a4d44a5b9 100644 --- a/eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientTest.java +++ b/eureka-client-jersey2/src/test/java/com/netflix/discovery/shared/transport/jersey2/Jersey2ApplicationClientTest.java @@ -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 @@ -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 { @@ -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; } } \ No newline at end of file diff --git a/eureka-client/src/main/java/com/netflix/discovery/shared/resolver/aws/AwsEndpoint.java b/eureka-client/src/main/java/com/netflix/discovery/shared/resolver/aws/AwsEndpoint.java index 0058d3abad..6316b184f9 100644 --- a/eureka-client/src/main/java/com/netflix/discovery/shared/resolver/aws/AwsEndpoint.java +++ b/eureka-client/src/main/java/com/netflix/discovery/shared/resolver/aws/AwsEndpoint.java @@ -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; diff --git a/eureka-client/src/main/java/com/netflix/discovery/shared/resolver/aws/ConfigClusterResolver.java b/eureka-client/src/main/java/com/netflix/discovery/shared/resolver/aws/ConfigClusterResolver.java index ebf414bb24..ff69be918a 100644 --- a/eureka-client/src/main/java/com/netflix/discovery/shared/resolver/aws/ConfigClusterResolver.java +++ b/eureka-client/src/main/java/com/netflix/discovery/shared/resolver/aws/ConfigClusterResolver.java @@ -80,16 +80,8 @@ private List 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); } } diff --git a/eureka-client/src/test/java/com/netflix/discovery/shared/resolver/aws/ConfigClusterResolverTest.java b/eureka-client/src/test/java/com/netflix/discovery/shared/resolver/aws/ConfigClusterResolverTest.java index ccf2241c89..acc0d0c887 100644 --- a/eureka-client/src/test/java/com/netflix/discovery/shared/resolver/aws/ConfigClusterResolverTest.java +++ b/eureka-client/src/test/java/com/netflix/discovery/shared/resolver/aws/ConfigClusterResolverTest.java @@ -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; @@ -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 endpointsE = Arrays.asList( - "https://1.1.3.1/eureka/v2/" + private final List endpointsWithBasicAuth = Arrays.asList( + "https://myuser:mypassword@1.1.3.1/eureka/v2/" ); private ConfigClusterResolver resolver; @@ -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)) @@ -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)); } } } diff --git a/eureka-client/src/test/java/com/netflix/discovery/shared/transport/jersey/JerseyApplicationClientTest.java b/eureka-client/src/test/java/com/netflix/discovery/shared/transport/jersey/JerseyApplicationClientTest.java index 37923415b1..6512c14cca 100644 --- a/eureka-client/src/test/java/com/netflix/discovery/shared/transport/jersey/JerseyApplicationClientTest.java +++ b/eureka-client/src/test/java/com/netflix/discovery/shared/transport/jersey/JerseyApplicationClientTest.java @@ -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 { @@ -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; } } \ No newline at end of file diff --git a/eureka-test-utils/src/main/java/com/netflix/discovery/junit/resource/DiscoveryClientResource.java b/eureka-test-utils/src/main/java/com/netflix/discovery/junit/resource/DiscoveryClientResource.java index a1a451a33c..5414e975ff 100644 --- a/eureka-test-utils/src/main/java/com/netflix/discovery/junit/resource/DiscoveryClientResource.java +++ b/eureka-test-utils/src/main/java/com/netflix/discovery/junit/resource/DiscoveryClientResource.java @@ -1,5 +1,7 @@ package com.netflix.discovery.junit.resource; +import javax.ws.rs.core.UriBuilder; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -10,6 +12,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import com.google.common.base.Preconditions; import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.DataCenterInfo; import com.netflix.appinfo.EurekaInstanceConfig; @@ -51,6 +54,8 @@ public class DiscoveryClientResource extends ExternalResource { private final Callable portResolverCallable; private final List remoteRegions; private final String vipFetch; + private final String userName; + private final String password; private EventBus eventBus; private ApplicationInfoManager applicationManager; @@ -67,6 +72,8 @@ public class DiscoveryClientResource extends ExternalResource { this.instance = builder.instance; this.remoteRegions = builder.remoteRegions; this.vipFetch = builder.vipFetch; + this.userName = builder.userName; + this.password = builder.password; } public InstanceInfo getMyInstanceInfo() { @@ -142,15 +149,20 @@ public int getLeaseRenewalIntervalInSeconds() { private EurekaClientConfig createEurekaClientConfig() throws Exception { // Cluster connectivity - String serviceURI; + URI serviceURI; if (portResolverCallable != null) { - serviceURI = "http://localhost:" + portResolverCallable.call() + "/eureka/v2/"; + serviceURI = new URI("http://localhost:" + portResolverCallable.call() + "/eureka/v2/"); } else if (eurekaHttpServer != null) { - serviceURI = eurekaHttpServer.getServiceURI().toString(); + serviceURI = eurekaHttpServer.getServiceURI(); } else { throw new IllegalStateException("Either port or EurekaHttpServer must be configured"); } - bindProperty(EUREKA_TEST_NAMESPACE + "serviceUrl.default", serviceURI); + + if (userName != null) { + serviceURI = UriBuilder.fromUri(serviceURI).userInfo(userName + ':' + password).build(); + } + + bindProperty(EUREKA_TEST_NAMESPACE + "serviceUrl.default", serviceURI.toString()); if (remoteRegions != null && !remoteRegions.isEmpty()) { StringBuilder regions = new StringBuilder(); for (String region : remoteRegions) { @@ -283,6 +295,8 @@ public static class DiscoveryClientRuleBuilder { private SimpleEurekaHttpServer eurekaHttpServer; private List remoteRegions; private String vipFetch; + private String userName; + private String password; public DiscoveryClientRuleBuilder withInstanceInfo(InstanceInfo instance) { this.instance = instance; @@ -322,6 +336,14 @@ public DiscoveryClientRuleBuilder withVipFetch(String vipFetch) { return this; } + public DiscoveryClientRuleBuilder basicAuthentication(String userName, String password) { + Preconditions.checkNotNull(userName, "HTTP basic authentication user name is null"); + Preconditions.checkNotNull(password, "HTTP basic authentication password is null"); + this.userName = userName; + this.password = password; + return this; + } + public DiscoveryClientResource build() { return new DiscoveryClientResource(this); } diff --git a/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaHttpClientCompatibilityTestSuite.java b/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaHttpClientCompatibilityTestSuite.java index 74235120f5..ec50c33994 100644 --- a/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaHttpClientCompatibilityTestSuite.java +++ b/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaHttpClientCompatibilityTestSuite.java @@ -17,12 +17,18 @@ package com.netflix.discovery.shared.transport; import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.UriBuilder; +import java.net.URI; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import com.netflix.appinfo.InstanceInfo; import com.netflix.appinfo.InstanceInfo.InstanceStatus; import com.netflix.discovery.shared.Applications; import com.netflix.discovery.util.EurekaEntityComparators; import com.netflix.discovery.util.InstanceInfoGenerator; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEurekaHttpResponse; @@ -41,22 +47,42 @@ public abstract class EurekaHttpClientCompatibilityTestSuite { private static final String REMOTE_REGION = "us-east-1"; private final EurekaHttpClient requestHandler = mock(EurekaHttpClient.class); + + private final List observedHttpRequests = new CopyOnWriteArrayList<>(); + private final EurekaTransportEventListener transportEventListener = new EurekaTransportEventListener() { + @Override + public void onHttpRequest(EurekaHttpRequest request) { + observedHttpRequests.add(request); + } + }; + private SimpleEurekaHttpServer httpServer; protected EurekaHttpClientCompatibilityTestSuite() { } + @Before public void setUp() throws Exception { - httpServer = new SimpleEurekaHttpServer(requestHandler); + httpServer = new SimpleEurekaHttpServer(requestHandler, transportEventListener); } + @After public void tearDown() throws Exception { httpServer.shutdown(); } - public abstract EurekaHttpClient getEurekaHttpClient(); + protected abstract EurekaHttpClient getEurekaHttpClient(URI serviceURI); + + protected EurekaHttpClient getEurekaHttpClient() { + return getEurekaHttpClient(getHttpServer().getServiceURI()); + } - public SimpleEurekaHttpServer getHttpServer() { + protected EurekaHttpClient getEurekaClientWithBasicAuthentication(String userName, String password) { + URI serviceURI = UriBuilder.fromUri(getHttpServer().getServiceURI()).userInfo(userName + ':' + password).build(); + return getEurekaHttpClient(serviceURI); + } + + protected SimpleEurekaHttpServer getHttpServer() { return httpServer; } @@ -202,6 +228,16 @@ public void testStatusUpdateDeleteRequest() throws Exception { assertThat(httpResponse.getStatusCode(), is(equalTo(200))); } + @Test + public void testBasicAuthentication() throws Exception { + InstanceInfo instance = InstanceInfoGenerator.takeOne(); + when(requestHandler.register(instance)).thenReturn(EurekaHttpResponse.status(204)); + + EurekaHttpResponse httpResponse = getEurekaClientWithBasicAuthentication("myuser", "mypassword").register(instance); + assertThat(httpResponse.getStatusCode(), is(equalTo(204))); + assertThat(observedHttpRequests.get(0).getHeaders().containsKey(HttpHeaders.AUTHORIZATION), is(true)); + } + private static void verifyResponseOkWithEntity(Applications original, EurekaHttpResponse httpResponse) { assertThat(httpResponse.getStatusCode(), is(equalTo(200))); assertThat(httpResponse.getEntity(), is(notNullValue())); diff --git a/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaHttpRequest.java b/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaHttpRequest.java new file mode 100644 index 0000000000..819cb0e84d --- /dev/null +++ b/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaHttpRequest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * 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 com.netflix.discovery.shared.transport; + +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + */ +public class EurekaHttpRequest { + + private final String requestMethod; + private final URI requestURI; + private final Map headers; + + public EurekaHttpRequest(String requestMethod, URI requestURI, Map headers) { + this.requestMethod = requestMethod; + this.requestURI = requestURI; + this.headers = Collections.unmodifiableMap(new HashMap(headers)); + } + + public String getRequestMethod() { + return requestMethod; + } + + public URI getRequestURI() { + return requestURI; + } + + public Map getHeaders() { + return headers; + } +} diff --git a/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaTransportEventListener.java b/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaTransportEventListener.java new file mode 100644 index 0000000000..a5292ca68f --- /dev/null +++ b/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/EurekaTransportEventListener.java @@ -0,0 +1,25 @@ +/* + * Copyright 2016 Netflix, Inc. + * + * 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 com.netflix.discovery.shared.transport; + +/** + */ +public interface EurekaTransportEventListener { + + void onHttpRequest(EurekaHttpRequest request); + +} diff --git a/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/SimpleEurekaHttpServer.java b/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/SimpleEurekaHttpServer.java index 585f0d2e58..2c89e31d80 100644 --- a/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/SimpleEurekaHttpServer.java +++ b/eureka-test-utils/src/main/java/com/netflix/discovery/shared/transport/SimpleEurekaHttpServer.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; +import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -34,6 +35,7 @@ import com.netflix.discovery.converters.wrappers.DecoderWrapper; import com.netflix.discovery.converters.wrappers.EncoderWrapper; import com.netflix.discovery.shared.Applications; +import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; @@ -52,13 +54,19 @@ public class SimpleEurekaHttpServer { private static final Logger logger = LoggerFactory.getLogger(SimpleEurekaHttpServer.class); private final EurekaHttpClient requestHandler; + private final EurekaTransportEventListener eventListener; private final HttpServer httpServer; private final EncoderWrapper encoder = CodecWrappers.getEncoder(JacksonJson.class); private final DecoderWrapper decoder = CodecWrappers.getDecoder(JacksonJson.class); public SimpleEurekaHttpServer(EurekaHttpClient requestHandler) throws IOException { + this(requestHandler, null); + } + + public SimpleEurekaHttpServer(EurekaHttpClient requestHandler, EurekaTransportEventListener eventListener) throws IOException { this.requestHandler = requestHandler; + this.eventListener = eventListener; this.httpServer = HttpServer.create(new InetSocketAddress(0), 1); httpServer.createContext("/v2", createEurekaV2Handle()); @@ -86,6 +94,9 @@ private HttpHandler createEurekaV2Handle() { return new HttpHandler() { @Override public void handle(HttpExchange httpExchange) throws IOException { + if(eventListener != null) { + eventListener.onHttpRequest(mapToEurekaHttpRequest(httpExchange)); + } try { String method = httpExchange.getRequestMethod(); String path = httpExchange.getRequestURI().getPath(); @@ -230,6 +241,16 @@ private void handleInstanceGET(HttpExchange httpExchange) throws IOException { } } + private EurekaHttpRequest mapToEurekaHttpRequest(HttpExchange httpExchange) { + Headers exchangeHeaders = httpExchange.getRequestHeaders(); + Map headers = new HashMap<>(); + + for(String key: exchangeHeaders.keySet()) { + headers.put(key, exchangeHeaders.getFirst(key)); + } + return new EurekaHttpRequest(httpExchange.getRequestMethod(), httpExchange.getRequestURI(), headers); + } + private void mapResponse(HttpExchange httpExchange, EurekaHttpResponse response) throws IOException { // Add headers for (Map.Entry headerEntry : response.getHeaders().entrySet()) { diff --git a/eureka-test-utils/src/test/java/com/netflix/discovery/shared/transport/SimpleEurekaHttpServerTest.java b/eureka-test-utils/src/test/java/com/netflix/discovery/shared/transport/SimpleEurekaHttpServerTest.java index 1a07a298b9..e5f6856879 100644 --- a/eureka-test-utils/src/test/java/com/netflix/discovery/shared/transport/SimpleEurekaHttpServerTest.java +++ b/eureka-test-utils/src/test/java/com/netflix/discovery/shared/transport/SimpleEurekaHttpServerTest.java @@ -16,12 +16,14 @@ package com.netflix.discovery.shared.transport; +import java.net.URI; + +import com.google.common.base.Preconditions; import com.netflix.appinfo.EurekaAccept; import com.netflix.discovery.converters.wrappers.CodecWrappers.JacksonJson; import com.netflix.discovery.shared.resolver.DefaultEndpoint; import com.netflix.discovery.shared.transport.jersey.JerseyEurekaHttpClientFactory; import org.junit.After; -import org.junit.Before; /** * @author Tomasz Bak @@ -32,9 +34,15 @@ public class SimpleEurekaHttpServerTest extends EurekaHttpClientCompatibilityTes private EurekaHttpClient eurekaHttpClient; @Override - @Before - public void setUp() throws Exception { - super.setUp(); + @After + public void tearDown() throws Exception { + httpClientFactory.shutdown(); + super.tearDown(); + } + + @Override + protected EurekaHttpClient getEurekaHttpClient(URI serviceURI) { + Preconditions.checkState(eurekaHttpClient == null, "EurekaHttpClient has been already created"); httpClientFactory = JerseyEurekaHttpClientFactory.newBuilder() .withClientName("test") @@ -43,19 +51,8 @@ public void setUp() throws Exception { .withDecoder(JacksonJson.class.getSimpleName(), EurekaAccept.full.name()) .withEncoder(JacksonJson.class.getSimpleName()) .build(); - int port = getHttpServer().getServerPort(); - this.eurekaHttpClient = httpClientFactory.newClient(new DefaultEndpoint("http://localhost:" + port + "/v2")); - } + this.eurekaHttpClient = httpClientFactory.newClient(new DefaultEndpoint(serviceURI.toString())); - @Override - @After - public void tearDown() throws Exception { - httpClientFactory.shutdown(); - super.tearDown(); - } - - @Override - public EurekaHttpClient getEurekaHttpClient() { return eurekaHttpClient; } } \ No newline at end of file