| /* 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.catalina.valves; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.servlet.ServletContext; |
| import javax.servlet.SessionCookieConfig; |
| import javax.servlet.http.Cookie; |
| |
| import org.junit.Test; |
| |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Valve; |
| import org.apache.catalina.connector.Request; |
| import org.apache.catalina.connector.Response; |
| import org.apache.catalina.core.StandardPipeline; |
| import org.easymock.EasyMock; |
| import org.easymock.IMocksControl; |
| |
| public class TestLoadBalancerDrainingValve { |
| |
| static class MockResponse extends Response { |
| private List<Cookie> cookies; |
| @Override |
| public boolean isCommitted() { |
| return false; |
| } |
| @Override |
| public void addCookie(Cookie cookie) |
| { |
| if(null == cookies) |
| cookies = new ArrayList<>(1); |
| cookies.add(cookie); |
| } |
| @Override |
| public List<Cookie> getCookies() { |
| return cookies; |
| } |
| } |
| |
| static class CookieConfig implements SessionCookieConfig { |
| |
| private String name; |
| private String domain; |
| private String path; |
| private String comment; |
| private boolean httpOnly; |
| private boolean secure; |
| private int maxAge; |
| |
| @Override |
| public String getName() { |
| return name; |
| } |
| @Override |
| public void setName(String name) { |
| this.name = name; |
| } |
| @Override |
| public String getDomain() { |
| return domain; |
| } |
| @Override |
| public void setDomain(String domain) { |
| this.domain = domain; |
| } |
| @Override |
| public String getPath() { |
| return path; |
| } |
| @Override |
| public void setPath(String path) { |
| this.path = path; |
| } |
| @Override |
| public String getComment() { |
| return comment; |
| } |
| @Override |
| public void setComment(String comment) { |
| this.comment = comment; |
| } |
| @Override |
| public boolean isHttpOnly() { |
| return httpOnly; |
| } |
| @Override |
| public void setHttpOnly(boolean httpOnly) { |
| this.httpOnly = httpOnly; |
| } |
| @Override |
| public boolean isSecure() { |
| return secure; |
| } |
| @Override |
| public void setSecure(boolean secure) { |
| this.secure = secure; |
| } |
| @Override |
| public int getMaxAge() { |
| return maxAge; |
| } |
| @Override |
| public void setMaxAge(int maxAge) { |
| this.maxAge = maxAge; |
| } |
| } |
| |
| // A Cookie subclass that knows how to compare itself to other Cookie objects |
| static class MyCookie extends Cookie { |
| private static final long serialVersionUID = 1L; |
| |
| public MyCookie(String name, String value) { super(name, value); } |
| |
| @Override |
| public boolean equals(Object o) { |
| if(!(o instanceof MyCookie)) { |
| return false; |
| } |
| |
| MyCookie mc = (MyCookie)o; |
| return mc.getName().equals(this.getName()) |
| && mc.getPath().equals(this.getPath()) |
| && mc.getValue().equals(this.getValue()) |
| && mc.getMaxAge() == this.getMaxAge(); |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + getMaxAge(); |
| result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); |
| result = prime * result + ((getPath() == null) ? 0 : getPath().hashCode()); |
| result = prime * result + ((getValue() == null) ? 0 : getValue().hashCode()); |
| return result; |
| } |
| |
| |
| @Override |
| public String toString() { |
| return "Cookie { name=" + getName() + ", value=" + getValue() + ", path=" + getPath() + ", maxAge=" + getMaxAge() + " }"; |
| } |
| } |
| |
| @Test |
| public void testNormalRequest() throws Exception { |
| runValve("ACT", true, true, false, null); |
| } |
| |
| @Test |
| public void testDisabledValidSession() throws Exception { |
| runValve("DIS", true, true, false, null); |
| } |
| |
| @Test |
| public void testDisabledInvalidSession() throws Exception { |
| runValve("DIS", false, false, false, "foo=bar"); |
| } |
| |
| @Test |
| public void testDisabledInvalidSessionWithIgnore() throws Exception { |
| runValve("DIS", false, true, true, "foo=bar"); |
| } |
| |
| private void runValve(String jkActivation, |
| boolean validSessionId, |
| boolean expectInvokeNext, |
| boolean enableIgnore, |
| String queryString) throws Exception { |
| IMocksControl control = EasyMock.createControl(); |
| ServletContext servletContext = control.createMock(ServletContext.class); |
| Context ctx = control.createMock(Context.class); |
| Request request = control.createMock(Request.class); |
| Response response = control.createMock(Response.class); |
| |
| String sessionCookieName = "JSESSIONID"; |
| String sessionId = "cafebabe"; |
| String requestURI = "/test/path"; |
| SessionCookieConfig cookieConfig = new CookieConfig(); |
| cookieConfig.setDomain("example.com"); |
| cookieConfig.setName(sessionCookieName); |
| cookieConfig.setPath("/"); |
| |
| // Valve.init requires all of this stuff |
| EasyMock.expect(ctx.getMBeanKeyProperties()).andStubReturn(""); |
| EasyMock.expect(ctx.getName()).andStubReturn(""); |
| EasyMock.expect(ctx.getPipeline()).andStubReturn(new StandardPipeline()); |
| EasyMock.expect(ctx.getDomain()).andStubReturn("foo"); |
| EasyMock.expect(ctx.getLogger()).andStubReturn(org.apache.juli.logging.LogFactory.getLog(LoadBalancerDrainingValve.class)); |
| EasyMock.expect(ctx.getServletContext()).andStubReturn(servletContext); |
| |
| // Set up the actual test |
| EasyMock.expect(request.getAttribute(LoadBalancerDrainingValve.ATTRIBUTE_KEY_JK_LB_ACTIVATION)).andStubReturn(jkActivation); |
| EasyMock.expect(Boolean.valueOf(request.isRequestedSessionIdValid())).andStubReturn(Boolean.valueOf(validSessionId)); |
| |
| ArrayList<Cookie> cookies = new ArrayList<>(); |
| if(enableIgnore) { |
| cookies.add(new Cookie("ignore", "true")); |
| } |
| |
| if(!validSessionId) { |
| MyCookie cookie = new MyCookie(cookieConfig.getName(), sessionId); |
| cookie.setPath(cookieConfig.getPath()); |
| cookie.setValue(sessionId); |
| |
| cookies.add(cookie); |
| |
| EasyMock.expect(request.getRequestedSessionId()).andStubReturn(sessionId); |
| EasyMock.expect(request.getRequestURI()).andStubReturn(requestURI); |
| EasyMock.expect(request.getCookies()).andStubReturn(cookies.toArray(new Cookie[cookies.size()])); |
| EasyMock.expect(request.getContext()).andStubReturn(ctx); |
| EasyMock.expect(ctx.getSessionCookieName()).andStubReturn(sessionCookieName); |
| EasyMock.expect(servletContext.getSessionCookieConfig()).andStubReturn(cookieConfig); |
| EasyMock.expect(request.getQueryString()).andStubReturn(queryString); |
| EasyMock.expect(ctx.getSessionCookiePath()).andStubReturn("/"); |
| |
| if (!enableIgnore) { |
| EasyMock.expect(Boolean.valueOf(ctx.getSessionCookiePathUsesTrailingSlash())).andStubReturn(Boolean.TRUE); |
| EasyMock.expect(request.getQueryString()).andStubReturn(queryString); |
| // Response will have cookie deleted |
| MyCookie expectedCookie = new MyCookie(cookieConfig.getName(), ""); |
| expectedCookie.setPath(cookieConfig.getPath()); |
| expectedCookie.setMaxAge(0); |
| |
| // These two lines just mean EasyMock.expect(response.addCookie) but for a void method |
| response.addCookie(expectedCookie); |
| EasyMock.expect(ctx.getSessionCookieName()).andReturn(sessionCookieName); // Indirect call |
| String expectedRequestURI = requestURI; |
| if(null != queryString) |
| expectedRequestURI = expectedRequestURI + '?' + queryString; |
| response.setHeader("Location", expectedRequestURI); |
| response.setStatus(307); |
| } |
| } |
| |
| Valve next = control.createMock(Valve.class); |
| |
| if(expectInvokeNext) { |
| // Expect the "next" Valve to fire |
| // Next 2 lines are basically EasyMock.expect(next.invoke(req,res)) but for a void method |
| next.invoke(request, response); |
| EasyMock.expectLastCall(); |
| } |
| |
| // Get set to actually test |
| control.replay(); |
| |
| LoadBalancerDrainingValve valve = new LoadBalancerDrainingValve(); |
| valve.setContainer(ctx); |
| valve.init(); |
| valve.setNext(next); |
| valve.setIgnoreCookieName("ignore"); |
| valve.setIgnoreCookieValue("true"); |
| |
| valve.invoke(request, response); |
| |
| control.verify(); |
| } |
| } |