| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.knox.gateway.ha.dispatch; |
| |
| import org.apache.http.Header; |
| import org.apache.http.HeaderElement; |
| import org.apache.http.HttpEntity; |
| import org.apache.http.HttpStatus; |
| import org.apache.http.StatusLine; |
| import org.apache.http.client.methods.CloseableHttpResponse; |
| import org.apache.http.impl.client.HttpClients; |
| import org.apache.knox.gateway.config.GatewayConfig; |
| import org.apache.knox.gateway.ha.provider.HaDescriptor; |
| import org.apache.knox.gateway.ha.provider.HaProvider; |
| import org.apache.knox.gateway.ha.provider.HaServletContextListener; |
| import org.apache.knox.gateway.ha.provider.impl.DefaultHaProvider; |
| import org.apache.knox.gateway.ha.provider.impl.HaDescriptorFactory; |
| import org.apache.knox.gateway.ha.provider.impl.HaServiceConfigConstants; |
| import org.apache.knox.gateway.servlet.SynchronousServletOutputStreamAdapter; |
| import org.apache.http.client.methods.HttpRequestBase; |
| import org.apache.http.client.methods.HttpUriRequest; |
| import org.apache.http.impl.client.CloseableHttpClient; |
| import org.apache.http.impl.client.HttpClientBuilder; |
| import org.apache.http.params.BasicHttpParams; |
| import org.easymock.Capture; |
| import org.easymock.CaptureType; |
| import org.easymock.EasyMock; |
| import org.easymock.IAnswer; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| import javax.servlet.FilterConfig; |
| import javax.servlet.ServletContext; |
| import javax.servlet.http.Cookie; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import java.io.ByteArrayInputStream; |
| import java.io.IOException; |
| import java.lang.reflect.Field; |
| import java.net.URI; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import static org.easymock.EasyMock.anyString; |
| |
| public class DefaultHaDispatchTest { |
| |
| @Test |
| public void testConnectivityFailover() throws Exception { |
| String serviceName = "OOZIE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, null, null, null, null, null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://unreachable-host.invalid" ); |
| URI uri2 = new URI( "http://reachable-host.invalid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| throw new IOException( "unreachable-host.invalid" ); |
| } |
| }; |
| } |
| }).once(); |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, outboundResponse); |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| HttpClientBuilder builder = HttpClientBuilder.create(); |
| CloseableHttpClient client = builder.build(); |
| dispatch.setHttpClient(client); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| long startTime = System.currentTimeMillis(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| long elapsedTime = System.currentTimeMillis() - startTime; |
| Assert.assertEquals(uri2.toString(), provider.getActiveURL(serviceName)); |
| //test to make sure the sleep took place |
| Assert.assertTrue(elapsedTime > 1000); |
| } |
| |
| /** |
| * Test failover when loadbalancing=false, sticky=true, nofallback=true. |
| * should failover. |
| * @throws Exception |
| */ |
| @Test |
| public void testNoLoadbalancingStickyFailoverNoFallback() throws Exception { |
| String serviceName = "OOZIE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, null, "true", null, "true", null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://unreachable-host.invalid" ); |
| URI uri2 = new URI( "http://reachable-host.invalid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| throw new IOException( "unreachable-host.invalid" ); |
| } |
| }; |
| } |
| }).once(); |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, outboundResponse); |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| HttpClientBuilder builder = HttpClientBuilder.create(); |
| CloseableHttpClient client = builder.build(); |
| dispatch.setHttpClient(client); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| /* since fallback happens */ |
| Assert.assertEquals(uri2.toString(), provider.getActiveURL(serviceName)); |
| } |
| |
| /** |
| * This is a negative test for noFallback flag |
| * When sticky session is disabled noFallback should not have any effect |
| * i.e. request should failover. |
| * @throws Exception |
| */ |
| @Test |
| public void testNoFallbackWhenStickyDisabled() throws Exception { |
| String serviceName = "OOZIE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, null, null, null, "true", null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://unreachable-host.invalid" ); |
| URI uri2 = new URI( "http://reachable-host.invalid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| throw new IOException( "unreachable-host.invalid" ); |
| } |
| }; |
| } |
| }).once(); |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, outboundResponse); |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| HttpClientBuilder builder = HttpClientBuilder.create(); |
| CloseableHttpClient client = builder.build(); |
| dispatch.setHttpClient(client); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| long startTime = System.currentTimeMillis(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| long elapsedTime = System.currentTimeMillis() - startTime; |
| Assert.assertEquals(uri2.toString(), provider.getActiveURL(serviceName)); |
| //test to make sure the sleep took place |
| Assert.assertTrue(elapsedTime > 1000); |
| } |
| |
| /** |
| * Test the case where loadbalancing is off and sticky session is on |
| * Expected behavior: When loadbalncing is off sticky sessions on is |
| * that there should be no url loadbalancing |
| * @throws Exception |
| */ |
| @Test |
| public void testLoadbalancingOffStickyOn() throws Exception { |
| String serviceName = "OOZIE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, null, "true", null, null,null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://host1.valid" ); |
| URI uri2 = new URI( "http://host2.valid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| /* backend request */ |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes( |
| StandardCharsets.UTF_8)); |
| |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| /* do nothing */ |
| } |
| }; |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, |
| outboundResponse, mockHttpClient, inboundResponse, |
| statusLine, entity, header, context, config); |
| |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| /* make sure the url is not ladbalanced since fallback did not happen */ |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| } |
| |
| /** |
| * Test the case where loadbalancing is on |
| * Expected behavior: When loadbalncing is on then urls should loadbalance |
| * @throws Exception |
| */ |
| @Test |
| public void testLoadbalancingOn() throws Exception { |
| String serviceName = "OOZIE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, "true", null, null, null,null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://host1.valid" ); |
| URI uri2 = new URI( "http://host2.valid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| /* backend request */ |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes( |
| StandardCharsets.UTF_8)); |
| |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| /* do nothing */ |
| } |
| }; |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, |
| outboundResponse, mockHttpClient, inboundResponse, |
| statusLine, entity, header, context, config); |
| |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| /* make sure the url is ladbalanced */ |
| Assert.assertEquals(uri2.toString(), provider.getActiveURL(serviceName)); |
| } |
| |
| /** |
| * Test the case where loadbalancing is on and sticky session is on |
| * Expected behavior: When loadbalncing is on and sticky session |
| * is on = urls should loadbalance with sticky session |
| * @throws Exception |
| */ |
| @Test |
| public void testLoadbalancingOnStickyOn() throws Exception { |
| String serviceName = "OOZIE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, "true", "true", null, null,null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://host1.valid" ); |
| URI uri2 = new URI( "http://host2.valid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| /* backend request */ |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes( |
| StandardCharsets.UTF_8)); |
| |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| /* do nothing */ |
| } |
| }; |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, |
| outboundResponse, mockHttpClient, inboundResponse, |
| statusLine, entity, header, context, config); |
| |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| /* make sure the url is loadbalanced */ |
| Assert.assertEquals(uri2.toString(), provider.getActiveURL(serviceName)); |
| } |
| |
| /** |
| * Test the case where loadbalancing is on, sticky sessions is on, fallback is disabled, and the initial request |
| * fails. |
| * |
| * Expected behavior: Failover should occur until the initial session has been established, regardless of the |
| * noFallback setting. |
| * |
| * KNOX-2619 |
| */ |
| @Test |
| public void testFailoverStickyOnFallbackOff() throws Exception { |
| doTestFailoverStickyOnFallbackOff(false); |
| } |
| |
| /** |
| * Test the case where loadbalancing is on, sticky sessions is on, fallback is disabled, and the initial request |
| * fails. |
| * |
| * Expected behavior: Failover should occur until the initial session has been established, regardless of the |
| * noFallback setting. |
| * |
| * KNOX-2619 |
| */ |
| @Test |
| public void testFailoverStickyOnFallbackOff_SessionEstablished() throws Exception { |
| doTestFailoverStickyOnFallbackOff(true); |
| } |
| |
| /** |
| * Test whether order of sticky session cookie matters. |
| * Test the case where loadbalancing is on, sticky sessions is on, fallback is disabled, and the initial request |
| * fails AND session cookie is not first. |
| * |
| * Expected behavior: Failover should occur until the initial session has been established, regardless of the |
| * noFallback setting. |
| * |
| * KNOX-2619 |
| */ |
| @Test |
| public void testFailoverStickyOnFallbackOff_SessionCookieOrder() throws Exception { |
| Cookie [] sessionCookieFirst = new Cookie[3]; |
| sessionCookieFirst[0] = new Cookie(HaServiceConfigConstants.DEFAULT_STICKY_SESSION_COOKIE_NAME + "-" + "OOZIE", |
| "59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745"); |
| sessionCookieFirst[1] = new Cookie("Test1", "Test1"); |
| sessionCookieFirst[2] = new Cookie("Test2", "Test2"); |
| |
| Cookie [] sessionCookieLast = new Cookie[3]; |
| sessionCookieLast[2] = new Cookie(HaServiceConfigConstants.DEFAULT_STICKY_SESSION_COOKIE_NAME + "-" + "OOZIE", |
| "59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745"); |
| sessionCookieLast[1] = new Cookie("Test1", "Test1"); |
| sessionCookieLast[0] = new Cookie("Test2", "Test2"); |
| |
| /* Test when session cookie is first */ |
| doTestFailoverStickyOnFallbackOff(true, sessionCookieFirst); |
| /* Test when session cookie is last */ |
| doTestFailoverStickyOnFallbackOff(true, sessionCookieLast); |
| } |
| |
| /** |
| * Test whether non-idempotent requests are prevented from failing over |
| * |
| * KNOX-2890 |
| */ |
| @Test |
| public void testFailoverForNonIdempotentRequests() throws Exception { |
| Cookie [] sessionCookieFirst = new Cookie[3]; |
| sessionCookieFirst[0] = new Cookie(HaServiceConfigConstants.DEFAULT_STICKY_SESSION_COOKIE_NAME + "-" + "OOZIE", |
| "59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745"); |
| sessionCookieFirst[1] = new Cookie("Test1", "Test1"); |
| sessionCookieFirst[2] = new Cookie("Test2", "Test2"); |
| |
| final boolean enableLoadBalancing = true; // load-balancing is required for sticky sessions to be enabled |
| final boolean enableStickySession = true; |
| final boolean noFallback = false; |
| final boolean failoverNonIdempotentRequestEnabled = false; |
| |
| final String serviceName = "OOZIE"; |
| |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, |
| true, |
| 1, |
| 1000, |
| null, |
| null, |
| enableStickySession, |
| enableLoadBalancing, |
| null, |
| noFallback, |
| null, |
| failoverNonIdempotentRequestEnabled)); |
| |
| final HaProvider provider = new DefaultHaProvider(descriptor); |
| final URI uri1 = new URI( "http://host1.valid" ); |
| final URI uri2 = new URI( "http://host2.valid" ); |
| final ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "POST" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| // Capture the last request URI to be set on the request |
| Capture<URI> requestURICapture = EasyMock.newCapture(CaptureType.LAST); |
| ((HttpRequestBase) outboundRequest).setURI(EasyMock.capture(requestURICapture)); |
| EasyMock.expectLastCall().anyTimes(); |
| |
| /* backend request */ |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes(StandardCharsets.UTF_8)); |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| // Capture the status code when it is set on the response |
| Capture<Integer> statusCodeCapture = EasyMock.newCapture(CaptureType.FIRST); |
| |
| outboundResponse.setStatus(EasyMock.captureInt(statusCodeCapture)); |
| |
| EasyMock.expectLastCall().once(); |
| EasyMock.expect(outboundResponse.getOutputStream()) |
| .andAnswer((IAnswer<SynchronousServletOutputStreamAdapter>) () -> new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| throw new IOException( "unreachable-host" ); // Fail-over condition |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, |
| servletContext, |
| outboundRequest, |
| inboundRequest, |
| outboundResponse, |
| mockHttpClient, |
| inboundResponse, |
| statusLine, |
| entity, |
| header, |
| context, |
| config); |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| // If the call succeeds then test failed |
| throw new Exception("Expected the request to NOT failover"); |
| } catch (IOException e) { |
| Assert.assertTrue("Expected the request to NOT failover", e.toString().equalsIgnoreCase("java.io.IOException: unreachable-host")); |
| } |
| |
| } |
| |
| /** |
| * Test that non-idempotent requests fail over when |
| * failoverNonIdempotentRequestEnabled=true |
| * |
| * KNOX-2890 |
| */ |
| @Test |
| public void testFailoverWhenNonIdempotentRequestsEnabled() throws Exception { |
| Cookie [] sessionCookieFirst = new Cookie[3]; |
| sessionCookieFirst[0] = new Cookie(HaServiceConfigConstants.DEFAULT_STICKY_SESSION_COOKIE_NAME + "-" + "OOZIE", |
| "59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745"); |
| sessionCookieFirst[1] = new Cookie("Test1", "Test1"); |
| sessionCookieFirst[2] = new Cookie("Test2", "Test2"); |
| |
| final boolean enableLoadBalancing = true; // load-balancing is required for sticky sessions to be enabled |
| final boolean enableStickySession = true; |
| final boolean noFallback = false; |
| final boolean failoverNonIdempotentRequestEnabled = true; |
| |
| final String serviceName = "OOZIE"; |
| |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, |
| true, |
| 1, |
| 1000, |
| null, |
| null, |
| enableStickySession, |
| enableLoadBalancing, |
| null, |
| noFallback, |
| null, |
| failoverNonIdempotentRequestEnabled)); |
| |
| final HaProvider provider = new DefaultHaProvider(descriptor); |
| final URI uri1 = new URI( "http://host1.valid" ); |
| final URI uri2 = new URI( "http://host2.valid" ); |
| final ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "POST" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| // Capture the last request URI to be set on the request |
| Capture<URI> requestURICapture = EasyMock.newCapture(CaptureType.LAST); |
| ((HttpRequestBase) outboundRequest).setURI(EasyMock.capture(requestURICapture)); |
| EasyMock.expectLastCall().anyTimes(); |
| |
| /* backend request */ |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes(StandardCharsets.UTF_8)); |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| // Capture the status code when it is set on the response |
| Capture<Integer> statusCodeCapture = EasyMock.newCapture(CaptureType.FIRST); |
| |
| outboundResponse.setStatus(EasyMock.captureInt(statusCodeCapture)); |
| |
| EasyMock.expectLastCall().once(); |
| EasyMock.expect(outboundResponse.getOutputStream()) |
| .andAnswer((IAnswer<SynchronousServletOutputStreamAdapter>) () -> new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| throw new IOException( "unreachable-host" ); // Fail-over condition |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, |
| servletContext, |
| outboundRequest, |
| inboundRequest, |
| outboundResponse, |
| mockHttpClient, |
| inboundResponse, |
| statusLine, |
| entity, |
| header, |
| context, |
| config); |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| |
| Assert.assertEquals("Expected the request to have failed-over to the alternate host.", uri2, requestURICapture.getValue()); |
| Assert.assertEquals("Expected the failed-over request to succeed.", HttpStatus.SC_OK, statusCodeCapture.getValue().intValue()); |
| |
| } |
| |
| /** |
| * Test that idempotent requests (GET) can fail over. |
| * |
| * KNOX-2890 |
| */ |
| @Test |
| public void testFailoverForIdempotentRequest() throws Exception { |
| Cookie [] sessionCookieFirst = new Cookie[3]; |
| sessionCookieFirst[0] = new Cookie(HaServiceConfigConstants.DEFAULT_STICKY_SESSION_COOKIE_NAME + "-" + "OOZIE", |
| "59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745"); |
| sessionCookieFirst[1] = new Cookie("Test1", "Test1"); |
| sessionCookieFirst[2] = new Cookie("Test2", "Test2"); |
| |
| final boolean enableLoadBalancing = true; // load-balancing is required for sticky sessions to be enabled |
| final boolean enableStickySession = true; |
| final boolean noFallback = false; |
| final boolean failoverNonIdempotentRequestEnabled = false; |
| |
| final String serviceName = "OOZIE"; |
| |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, |
| true, |
| 1, |
| 1000, |
| null, |
| null, |
| enableStickySession, |
| enableLoadBalancing, |
| null, |
| noFallback, |
| null, |
| failoverNonIdempotentRequestEnabled)); |
| |
| final HaProvider provider = new DefaultHaProvider(descriptor); |
| final URI uri1 = new URI( "http://host1.valid" ); |
| final URI uri2 = new URI( "http://host2.valid" ); |
| final ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| // Capture the last request URI to be set on the request |
| Capture<URI> requestURICapture = EasyMock.newCapture(CaptureType.LAST); |
| ((HttpRequestBase) outboundRequest).setURI(EasyMock.capture(requestURICapture)); |
| EasyMock.expectLastCall().anyTimes(); |
| |
| /* backend request */ |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes(StandardCharsets.UTF_8)); |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| // Capture the status code when it is set on the response |
| Capture<Integer> statusCodeCapture = EasyMock.newCapture(CaptureType.FIRST); |
| |
| outboundResponse.setStatus(EasyMock.captureInt(statusCodeCapture)); |
| |
| EasyMock.expectLastCall().once(); |
| EasyMock.expect(outboundResponse.getOutputStream()) |
| .andAnswer((IAnswer<SynchronousServletOutputStreamAdapter>) () -> new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| throw new IOException( "unreachable-host" ); // Fail-over condition |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, |
| servletContext, |
| outboundRequest, |
| inboundRequest, |
| outboundResponse, |
| mockHttpClient, |
| inboundResponse, |
| statusLine, |
| entity, |
| header, |
| context, |
| config); |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| |
| Assert.assertEquals("Expected the request to have failed-over to the alternate host.", uri2, requestURICapture.getValue()); |
| Assert.assertEquals("Expected the failed-over request to succeed.", HttpStatus.SC_OK, statusCodeCapture.getValue().intValue()); |
| |
| } |
| |
| private void doTestFailoverStickyOnFallbackOff(final Boolean withCookie) |
| throws Exception { |
| doTestFailoverStickyOnFallbackOff(withCookie, null); |
| } |
| |
| private void doTestFailoverStickyOnFallbackOff(final Boolean withCookie, final Cookie[] cookies) |
| throws Exception { |
| final String enableLoadBalancing = "true"; // load-balancing is required for sticky sessions to be enabled |
| final String enableStickySession = "true"; |
| final String noFallback = "true"; |
| |
| final String serviceName = "OOZIE"; |
| |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, |
| "true", |
| "1", |
| "1000", |
| null, |
| null, |
| enableLoadBalancing, |
| enableStickySession, |
| null, |
| noFallback, |
| null)); |
| final HaProvider provider = new DefaultHaProvider(descriptor); |
| final URI uri1 = new URI( "http://host1.valid" ); |
| final URI uri2 = new URI( "http://host2.valid" ); |
| final ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| // Capture the last request URI to be set on the request |
| Capture<URI> requestURICapture = EasyMock.newCapture(CaptureType.LAST); |
| ((HttpRequestBase) outboundRequest).setURI(EasyMock.capture(requestURICapture)); |
| EasyMock.expectLastCall().anyTimes(); |
| |
| /* backend request */ |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| if (withCookie) { |
| Cookie[] responseCookies; |
| Cookie sessionCookie = new Cookie(HaServiceConfigConstants.DEFAULT_STICKY_SESSION_COOKIE_NAME + "-" + serviceName, |
| "59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745"); |
| if(cookies != null && cookies.length > 0) { |
| /* Add provided cookies */ |
| responseCookies = cookies; |
| } else { |
| responseCookies = new Cookie[] {sessionCookie}; |
| } |
| |
| inboundRequest.getCookies(); |
| EasyMock.expectLastCall() |
| .andReturn(responseCookies) |
| .anyTimes(); |
| } else { |
| EasyMock.expect(inboundRequest.getCookies()).andReturn(null).anyTimes(); |
| } |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes(StandardCharsets.UTF_8)); |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| // Capture the status code when it is set on the response |
| Capture<Integer> statusCodeCapture = EasyMock.newCapture(CaptureType.FIRST); |
| if (withCookie) { |
| outboundResponse.sendError(EasyMock.captureInt(statusCodeCapture), anyString()); |
| } else { |
| outboundResponse.setStatus(EasyMock.captureInt(statusCodeCapture)); |
| } |
| EasyMock.expectLastCall().once(); |
| EasyMock.expect(outboundResponse.getOutputStream()) |
| .andAnswer((IAnswer<SynchronousServletOutputStreamAdapter>) () -> new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| throw new IOException( "unreachable-host" ); // Fail-over condition |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, |
| servletContext, |
| outboundRequest, |
| inboundRequest, |
| outboundResponse, |
| mockHttpClient, |
| inboundResponse, |
| statusLine, |
| entity, |
| header, |
| context, |
| config); |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| |
| if (withCookie) { |
| Assert.assertEquals("Expected no fail-over because the initial request contained a session cookie.", |
| HttpStatus.SC_BAD_GATEWAY, |
| statusCodeCapture.getValue().intValue()); |
| } else { |
| // The request should have failed over |
| Assert.assertEquals("Expected the request to have failed-over to the alternate host.", uri2, requestURICapture.getValue()); |
| Assert.assertEquals("Expected the failed-over request to succeed.", HttpStatus.SC_OK, statusCodeCapture.getValue().intValue()); |
| } |
| } |
| |
| /** |
| * Test the case where sticky session is on (and loadbalancing is on) |
| * Expected behavior: When |
| * @throws Exception |
| */ |
| @Test |
| public void testStickyOn() throws Exception { |
| String serviceName = "OOZIE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, "true", "true", null, null,null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://host1.valid" ); |
| URI uri2 = new URI( "http://host2.valid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri2 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| /* backend request with cookie for url2 */ |
| //http://host2.valid = 59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745 |
| Cookie[] cookie = new Cookie[] { new Cookie("KNOX_BACKEND-OOZIE","59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745")}; |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getCookies()).andReturn( cookie ).anyTimes(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes( |
| StandardCharsets.UTF_8)); |
| |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| /* do nothing */ |
| } |
| }; |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, |
| outboundResponse, mockHttpClient, inboundResponse, |
| statusLine, entity, header, context, config); |
| |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| /* sticky session is on do not loadbalance */ |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| } |
| |
| /** |
| * Test a case where loadbalancing is turned off for a <b>default</b> list of useragents |
| * should failover. |
| * @throws Exception |
| */ |
| @Test |
| public void testDisableLBDefaultUserAgent() throws Exception { |
| String userAgent = "ClouderaODBCDriverforApacheHive/2.6.11.1011 Thrift/0.9.0 (C++/THttpClient)[\\r][\\n]"; |
| String serviceName = "HIVE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, "true", "true", null, null,null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://host1.valid" ); |
| URI uri2 = new URI( "http://host2.valid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| /* backend request with cookie for url2 */ |
| Cookie[] cookie = new Cookie[] { new Cookie("KNOX_BACKEND-OOZIE","59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745")}; |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getCookies()).andReturn( cookie ).anyTimes(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| EasyMock.expect(inboundRequest.getHeader("User-Agent")).andReturn(userAgent).anyTimes(); |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes( |
| StandardCharsets.UTF_8)); |
| |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| /* do nothing */ |
| } |
| }; |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, |
| outboundResponse, mockHttpClient, inboundResponse, |
| statusLine, entity, header, context, config); |
| |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| /* Make sure thee was no LB'ing */ |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| } |
| |
| @Test |
| public void testDisableLBDActiveURL() throws Exception { |
| final String serviceName1 = "HIVE"; |
| final String serviceName2 = "OOZIE"; |
| HaDescriptor descriptor1 = HaDescriptorFactory.createDescriptor(); |
| descriptor1.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName1, "enableStickySession=true;enableLoadBalancing=true;enabled=true;maxFailoverAttempts=42;failoverSleep=50;maxRetryAttempts=1;disableLoadBalancingForUserAgents=Test User Agent, Test User Agent2,Test User Agent3 ,Test User Agent4 ;retrySleep=1000")); |
| |
| HaDescriptor descriptor2 = HaDescriptorFactory.createDescriptor(); |
| descriptor2.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName2, "enableStickySession=true;enableLoadBalancing=true;enabled=true;maxFailoverAttempts=42;failoverSleep=50;maxRetryAttempts=1;disableLoadBalancingForUserAgents=Test User Agent, Test User Agent2,Test User Agent3 ,Test User Agent4 ;retrySleep=1000")); |
| |
| |
| HaProvider provider1 = new DefaultHaProvider(descriptor1); |
| HaProvider provider2 = new DefaultHaProvider(descriptor2); |
| |
| URI uri1 = new URI( "http://provider1-url1.valid" ); |
| URI uri2 = new URI( "http://provider1-url2.valid" ); |
| ArrayList<String> urlListProvider1 = new ArrayList<>(); |
| urlListProvider1.add(uri1.toString()); |
| urlListProvider1.add(uri2.toString()); |
| provider1.addHaService(serviceName1, urlListProvider1); |
| |
| URI uri3 = new URI( "http://provider2-url1.valid" ); |
| URI uri4 = new URI( "http://provider2-url2.valid" ); |
| ArrayList<String> urlListProvider2 = new ArrayList<>(); |
| urlListProvider2.add(uri3.toString()); |
| urlListProvider2.add(uri4.toString()); |
| provider2.addHaService(serviceName2, urlListProvider2); |
| |
| Class haDispatchClass = ConfigurableHADispatch.class; |
| Field activeURLField = haDispatchClass.getDeclaredField("activeURL"); |
| activeURLField.setAccessible(true); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.replay(mockHttpClient); |
| |
| ConfigurableHADispatch dispatch1 = new ConfigurableHADispatch(); |
| dispatch1.setHttpClient(mockHttpClient); |
| dispatch1.setHaProvider(provider1); |
| dispatch1.setServiceRole(serviceName1); |
| dispatch1.init(); |
| |
| ConfigurableHADispatch dispatch2 = new ConfigurableHADispatch(); |
| dispatch2.setHttpClient(mockHttpClient); |
| dispatch2.setHaProvider(provider2); |
| dispatch2.setServiceRole(serviceName2); |
| dispatch2.init(); |
| |
| /* make sure active URL is what is supposed to be */ |
| Assert.assertEquals(provider1.getActiveURL(serviceName1), ((AtomicReference<String>)activeURLField.get(dispatch1)).get()); |
| /* make sure active URL is what is supposed to be */ |
| Assert.assertEquals(provider2.getActiveURL(serviceName2), ((AtomicReference<String>)activeURLField.get(dispatch2)).get()); |
| |
| } |
| |
| /** |
| * Test a case where loadbalancing is ON when the request user-agent |
| * does not match list of useragents configured to disable loadbalancing |
| * should failover. |
| * @throws Exception |
| */ |
| @Test |
| public void testDisableLBDefaultUserAgentNegativeCase() throws Exception { |
| String userAgent = "JDBCDriverforApacheHive/2.6.11.1011 [\\r][\\n]"; |
| String serviceName = "HIVE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, "true", "true", null, null,null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://host1.valid" ); |
| URI uri2 = new URI( "http://host2.valid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| /* backend request with cookie for url2 */ |
| Cookie[] cookie = new Cookie[] { new Cookie("KNOX_BACKEND-OOZIE","59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745")}; |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getCookies()).andReturn( cookie ).anyTimes(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| EasyMock.expect(inboundRequest.getHeader("User-Agent")).andReturn(userAgent).anyTimes(); |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes( |
| StandardCharsets.UTF_8)); |
| |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| /* do nothing */ |
| } |
| }; |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, |
| outboundResponse, mockHttpClient, inboundResponse, |
| statusLine, entity, header, context, config); |
| |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| /* Make sure loadbalancing is working */ |
| Assert.assertEquals(uri2.toString(), provider.getActiveURL(serviceName)); |
| } |
| |
| /** |
| * Test a case where loadbalancing is turned off for a <b>configured/b> list of useragents |
| * should failover. |
| * @throws Exception |
| */ |
| @Test |
| public void testDisableLBDefaultUserAgentConfiguration() throws Exception { |
| String userAgent = "Test User Agent v0.0.1 [\\r][\\n]"; |
| String serviceName = "HIVE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "enableStickySession=true;enableLoadBalancing=true;enabled=true;maxFailoverAttempts=42;failoverSleep=50;maxRetryAttempts=1;disableLoadBalancingForUserAgents=Test User Agent, Test User Agent2,Test User Agent3 ,Test User Agent4 ;retrySleep=1000")); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://host1.valid" ); |
| URI uri2 = new URI( "http://host2.valid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| /* backend request with cookie for url2 */ |
| Cookie[] cookie = new Cookie[] { new Cookie("KNOX_BACKEND-OOZIE","59973e253ae20de796c6ef413608ec1c80fca24310a4cbdecc0ff97aeea55745")}; |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getCookies()).andReturn( cookie ).anyTimes(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| EasyMock.expect(inboundRequest.getHeader("User-Agent")).andReturn(userAgent).anyTimes(); |
| |
| /* backend response */ |
| CloseableHttpResponse inboundResponse = EasyMock.createNiceMock(CloseableHttpResponse.class); |
| final StatusLine statusLine = EasyMock.createNiceMock(StatusLine.class); |
| final HttpEntity entity = EasyMock.createNiceMock(HttpEntity.class); |
| final Header header = EasyMock.createNiceMock(Header.class); |
| final ServletContext context = EasyMock.createNiceMock(ServletContext.class); |
| final GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class); |
| final ByteArrayInputStream backendResponse = new ByteArrayInputStream("knox-backend".getBytes( |
| StandardCharsets.UTF_8)); |
| |
| |
| EasyMock.expect(inboundResponse.getStatusLine()).andReturn(statusLine).anyTimes(); |
| EasyMock.expect(statusLine.getStatusCode()).andReturn(HttpStatus.SC_OK).anyTimes(); |
| EasyMock.expect(inboundResponse.getEntity()).andReturn(entity).anyTimes(); |
| EasyMock.expect(inboundResponse.getAllHeaders()).andReturn(new Header[0]).anyTimes(); |
| EasyMock.expect(inboundRequest.getServletContext()).andReturn(context).anyTimes(); |
| EasyMock.expect(entity.getContent()).andReturn(backendResponse).anyTimes(); |
| EasyMock.expect(entity.getContentType()).andReturn(header).anyTimes(); |
| EasyMock.expect(header.getElements()).andReturn(new HeaderElement[]{}).anyTimes(); |
| EasyMock.expect(entity.getContentLength()).andReturn(4L).anyTimes(); |
| EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(config).anyTimes(); |
| |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| /* do nothing */ |
| } |
| }; |
| } |
| }).once(); |
| |
| CloseableHttpClient mockHttpClient = EasyMock.createNiceMock(CloseableHttpClient.class); |
| EasyMock.expect(mockHttpClient.execute(outboundRequest)).andReturn(inboundResponse).anyTimes(); |
| |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, |
| outboundResponse, mockHttpClient, inboundResponse, |
| statusLine, entity, header, context, config); |
| |
| |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| ConfigurableHADispatch dispatch = new ConfigurableHADispatch(); |
| dispatch.setHttpClient(mockHttpClient); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequestWrapper(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| /* Make sure thee was no LB'ing */ |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| } |
| |
| @Test |
| public void testConnectivityActive() throws Exception { |
| String serviceName = "OOZIE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "1", "1000", null, null, null, "true", null, null,null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://unreachable-host" ); |
| URI uri2 = new URI( "http://reachable-host" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| throw new IOException( "unreachable-host" ); |
| } |
| }; |
| } |
| }).once(); |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, outboundResponse); |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| DefaultHaDispatch dispatch = new DefaultHaDispatch(); |
| dispatch.setHttpClient(HttpClients.createDefault()); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| } |
| |
| /** |
| * test a case where a service might want to mark a host failed without retrying |
| * so the next request that comes goes to the other HA host. |
| * This can be achieved by using maxFailoverAttempsValue=0 |
| * @throws Exception |
| */ |
| @Test |
| public void testMarkedFailedWithoutRetry() throws Exception { |
| String serviceName = "OOZIE"; |
| HaDescriptor descriptor = HaDescriptorFactory.createDescriptor(); |
| descriptor.addServiceConfig(HaDescriptorFactory.createServiceConfig(serviceName, "true", "0", "1000", null, null, null, null, null, null,null)); |
| HaProvider provider = new DefaultHaProvider(descriptor); |
| URI uri1 = new URI( "http://unreachable-host.invalid" ); |
| URI uri2 = new URI( "http://reachable-host.invalid" ); |
| ArrayList<String> urlList = new ArrayList<>(); |
| urlList.add(uri1.toString()); |
| urlList.add(uri2.toString()); |
| provider.addHaService(serviceName, urlList); |
| FilterConfig filterConfig = EasyMock.createNiceMock(FilterConfig.class); |
| ServletContext servletContext = EasyMock.createNiceMock(ServletContext.class); |
| |
| EasyMock.expect(filterConfig.getServletContext()).andReturn(servletContext).anyTimes(); |
| EasyMock.expect(servletContext.getAttribute(HaServletContextListener.PROVIDER_ATTRIBUTE_NAME)).andReturn(provider).anyTimes(); |
| |
| BasicHttpParams params = new BasicHttpParams(); |
| |
| HttpUriRequest outboundRequest = EasyMock.createNiceMock(HttpRequestBase.class); |
| EasyMock.expect(outboundRequest.getMethod()).andReturn( "GET" ).anyTimes(); |
| EasyMock.expect(outboundRequest.getURI()).andReturn( uri1 ).anyTimes(); |
| EasyMock.expect(outboundRequest.getParams()).andReturn( params ).anyTimes(); |
| |
| HttpServletRequest inboundRequest = EasyMock.createNiceMock(HttpServletRequest.class); |
| EasyMock.expect(inboundRequest.getRequestURL()).andReturn( new StringBuffer(uri2.toString()) ).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(0)).once(); |
| EasyMock.expect(inboundRequest.getAttribute("dispatch.ha.failover.counter")).andReturn(new AtomicInteger(1)).once(); |
| |
| HttpServletResponse outboundResponse = EasyMock.createNiceMock(HttpServletResponse.class); |
| EasyMock.expect(outboundResponse.getOutputStream()).andAnswer( new IAnswer<SynchronousServletOutputStreamAdapter>() { |
| @Override |
| public SynchronousServletOutputStreamAdapter answer() { |
| return new SynchronousServletOutputStreamAdapter() { |
| @Override |
| public void write( int b ) throws IOException { |
| throw new IOException( "unreachable-host.invalid" ); |
| } |
| }; |
| } |
| }).once(); |
| EasyMock.replay(filterConfig, servletContext, outboundRequest, inboundRequest, outboundResponse); |
| Assert.assertEquals(uri1.toString(), provider.getActiveURL(serviceName)); |
| DefaultHaDispatch dispatch = new DefaultHaDispatch(); |
| HttpClientBuilder builder = HttpClientBuilder.create(); |
| CloseableHttpClient client = builder.build(); |
| dispatch.setHttpClient(client); |
| dispatch.setHaProvider(provider); |
| dispatch.setServiceRole(serviceName); |
| dispatch.init(); |
| try { |
| dispatch.executeRequest(outboundRequest, inboundRequest, outboundResponse); |
| } catch (IOException e) { |
| //this is expected after the failover limit is reached |
| } |
| /* make sure active url list got updated */ |
| Assert.assertEquals(uri2.toString(), provider.getActiveURL(serviceName)); |
| } |
| |
| } |