/*
* Copyright 2015 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.decorator;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.shared.dns.DnsService;
import com.netflix.discovery.shared.resolver.EurekaEndpoint;
import com.netflix.discovery.shared.transport.EurekaHttpClient;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import com.netflix.discovery.shared.transport.TransportException;
import org.junit.Test;
import org.mockito.Matchers;
import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEurekaHttpResponse;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* @author Tomasz Bak
*/
public class RedirectingEurekaHttpClientTest {
private static final String SERVICE_URL = "http://mydiscovery.test";
private final TransportClientFactory factory = mock(TransportClientFactory.class);
private final EurekaHttpClient sourceClient = mock(EurekaHttpClient.class);
private final EurekaHttpClient redirectedClient = mock(EurekaHttpClient.class);
private final DnsService dnsService = mock(DnsService.class);
public void setupRedirect() {
when(factory.newClient(Matchers.<EurekaEndpoint>anyVararg())).thenReturn(sourceClient, redirectedClient);
when(sourceClient.getApplications()).thenReturn(
anEurekaHttpResponse(302, Applications.class)
.headers(HttpHeaders.LOCATION, "http://another.discovery.test/eureka/v2/apps")
.build()
);
when(dnsService.resolveIp("another.discovery.test")).thenReturn("192.168.0.1");
when(redirectedClient.getApplications()).thenReturn(
anEurekaHttpResponse(200, new Applications()).type(MediaType.APPLICATION_JSON_TYPE).build()
);
}
@Test
public void testNonRedirectedRequestsAreServedByFirstClient() throws Exception {
when(factory.newClient(Matchers.<EurekaEndpoint>anyVararg())).thenReturn(sourceClient);
when(sourceClient.getApplications()).thenReturn(
anEurekaHttpResponse(200, new Applications()).type(MediaType.APPLICATION_JSON_TYPE).build()
);
RedirectingEurekaHttpClient httpClient = new RedirectingEurekaHttpClient(SERVICE_URL, factory, dnsService);
httpClient.getApplications();
verify(factory, times(1)).newClient(Matchers.<EurekaEndpoint>anyVararg());
verify(sourceClient, times(1)).getApplications();
}
@Test
public void testRedirectsAreFollowedAndClientIsPinnedToTheLastServer() throws Exception {
setupRedirect();
RedirectingEurekaHttpClient httpClient = new RedirectingEurekaHttpClient(SERVICE_URL, factory, dnsService);
// First call pins client to resolved IP
httpClient.getApplications();
verify(factory, times(2)).newClient(Matchers.<EurekaEndpoint>anyVararg());
verify(sourceClient, times(1)).getApplications();
verify(dnsService, times(1)).resolveIp("another.discovery.test");
verify(redirectedClient, times(1)).getApplications();
// Second call goes straight to the same address
httpClient.getApplications();
verify(factory, times(2)).newClient(Matchers.<EurekaEndpoint>anyVararg());
verify(sourceClient, times(1)).getApplications();
verify(dnsService, times(1)).resolveIp("another.discovery.test");
verify(redirectedClient, times(2)).getApplications();
}
@Test
public void testOnConnectionErrorPinnedClientIsDestroyed() throws Exception {
setupRedirect();
RedirectingEurekaHttpClient httpClient = new RedirectingEurekaHttpClient(SERVICE_URL, factory, dnsService);
// First call pins client to resolved IP
httpClient.getApplications();
verify(redirectedClient, times(1)).getApplications();
// Trigger connection error
when(redirectedClient.getApplications()).thenThrow(new TransportException("simulated network error"));
try {
httpClient.getApplications();
fail("Expected transport error");
} catch (Exception ignored) {
}
// Subsequent connection shall create new httpClient
reset(factory, sourceClient, dnsService, redirectedClient);
setupRedirect();
httpClient.getApplications();
verify(factory, times(2)).newClient(Matchers.<EurekaEndpoint>anyVararg());
verify(sourceClient, times(1)).getApplications();
verify(dnsService, times(1)).resolveIp("another.discovery.test");
verify(redirectedClient, times(1)).getApplications();
}
}