diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java index 9920479b944..e5c9606d783 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java @@ -583,7 +583,7 @@ public class DoSFilter implements Filter } else { - loadId = isRemotePort() ? (request.getRemoteAddr() + request.getRemotePort()) : request.getRemoteAddr(); + loadId = isRemotePort() ? createRemotePortId(request) : request.getRemoteAddr(); type = USER_IP; } } @@ -616,6 +616,10 @@ public class DoSFilter implements Filter return tracker; } + + + + public void addToRateTracker (RateTracker tracker) { _rateTrackers.put(tracker.getId(), tracker); @@ -1356,4 +1360,12 @@ public class DoSFilter implements Filter super.onTimeout(event); } } + + private String createRemotePortId(final ServletRequest request) { + final String addr = request.getRemoteAddr(); + final int port = request.getRemotePort(); + if (addr.contains(":")) return "[" + addr + "]:" + port; + return addr + ":" + port; + } + } diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java index bbbe09bb5e2..73c0cbcf0de 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/DoSFilterTest.java @@ -18,10 +18,17 @@ package org.eclipse.jetty.servlets; +import java.net.InetAddress; +import java.net.InetSocketAddress; import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.servlets.DoSFilter.RateTracker; +import org.eclipse.jetty.servlets.mocks.MockFilterConfig; +import org.eclipse.jetty.servlets.mocks.MockHttpServletRequest; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; @@ -34,6 +41,41 @@ public class DoSFilterTest extends AbstractDoSFilterTest { startServer(DoSFilter.class); } + + + @Test + public void testRemotePortLoadIdCreation_ipv6() throws ServletException { + final ServletRequest request = newMockHttpServletRequest("::192.9.5.5", 12345); + DoSFilter doSFilter = new DoSFilter(); + doSFilter.init(new MockFilterConfig()); + doSFilter.setRemotePort(true); + + try { + RateTracker tracker = doSFilter.getRateTracker(request); + Assert.assertEquals("[::192.9.5.5]:12345", tracker.getId()); + } finally { + doSFilter.stopScheduler(); + } + } + + + @Test + public void testRemotePortLoadIdCreation_ipv4() throws ServletException { + final ServletRequest request = newMockHttpServletRequest("127.0.0.1", 12345); + DoSFilter doSFilter = new DoSFilter(); + doSFilter.init(new MockFilterConfig()); + doSFilter.setRemotePort(true); + + try { + RateTracker tracker = doSFilter.getRateTracker(request); + Assert.assertEquals("127.0.0.1:12345", tracker.getId()); + } finally { + doSFilter.stopScheduler(); + } + } + + + @Test public void testRateIsRateExceeded() throws InterruptedException @@ -87,4 +129,21 @@ public class DoSFilterTest extends AbstractDoSFilterTest } return exceeded; } + + + + private HttpServletRequest newMockHttpServletRequest(final String remoteAddr, + final int remotePort) { + return new MockHttpServletRequest() { + @Override + public String getRemoteAddr() { + return remoteAddr; + } + + @Override + public int getRemotePort() { + return remotePort; + } + }; + } } diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/mocks/MockFilterConfig.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/mocks/MockFilterConfig.java new file mode 100644 index 00000000000..5083fa88cdf --- /dev/null +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/mocks/MockFilterConfig.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.servlets.mocks; + +import java.util.Collections; +import java.util.Enumeration; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import org.eclipse.jetty.server.handler.ContextHandler; + + +public class MockFilterConfig implements FilterConfig { + + @Override + public String getFilterName() { + return ""; + } + + @Override + public ServletContext getServletContext() { + return new ContextHandler.StaticContext(); + } + + @Override + public String getInitParameter(String string) { + return null; + } + + @Override + public Enumeration getInitParameterNames() { + return Collections.emptyEnumeration(); + } + +} diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/mocks/MockHttpServletRequest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/mocks/MockHttpServletRequest.java new file mode 100644 index 00000000000..1af3d7c0cc9 --- /dev/null +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/mocks/MockHttpServletRequest.java @@ -0,0 +1,389 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.servlets.mocks; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpUpgradeHandler; +import javax.servlet.http.Part; + + +public class MockHttpServletRequest implements HttpServletRequest { + + @Override + public String getAuthType() { + return ""; + } + + @Override + public Cookie[] getCookies() { + return new Cookie[0]; + } + + @Override + public long getDateHeader(String string) { + return 0; + } + + @Override + public String getHeader(String string) { + return ""; + } + + @Override + public Enumeration getHeaders(String string) { + return Collections.emptyEnumeration(); + } + + @Override + public Enumeration getHeaderNames() { + return Collections.emptyEnumeration(); + } + + @Override + public int getIntHeader(String string) { + return 0; + } + + @Override + public String getMethod() { + return ""; + } + + @Override + public String getPathInfo() { + return ""; + } + + @Override + public String getPathTranslated() { + return ""; + } + + @Override + public String getContextPath() { + return ""; + } + + @Override + public String getQueryString() { + return ""; + } + + @Override + public String getRemoteUser() { + return ""; + } + + @Override + public boolean isUserInRole(String string) { + return false; + } + + @Override + public Principal getUserPrincipal() { + return null; + } + + @Override + public String getRequestedSessionId() { + return ""; + } + + @Override + public String getRequestURI() { + return ""; + } + + @Override + public StringBuffer getRequestURL() { + return new StringBuffer(0); + } + + @Override + public String getServletPath() { + return ""; + } + + @Override + public HttpSession getSession(boolean bln) { + return null; + } + + @Override + public HttpSession getSession() { + return null; + } + + @Override + public String changeSessionId() { + return ""; + } + + @Override + public boolean isRequestedSessionIdValid() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL() { + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + return false; + } + + @Override + public boolean authenticate(HttpServletResponse hsr) throws IOException, ServletException { + return false; + } + + @Override + public void login(String string, String string1) throws ServletException { + } + + @Override + public void logout() throws ServletException { + } + + @Override + public Collection getParts() throws IOException, ServletException { + return Collections.emptyList(); + } + + @Override + public Part getPart(String string) throws IOException, ServletException { + return null; + } + + @Override + public T upgrade(Class type) throws IOException, ServletException { + return null; + } + + @Override + public Object getAttribute(String string) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return Collections.emptyEnumeration(); + } + + @Override + public String getCharacterEncoding() { + return ""; + } + + @Override + public void setCharacterEncoding(String string) throws UnsupportedEncodingException { + } + + @Override + public int getContentLength() { + return 0; + } + + @Override + public long getContentLengthLong() { + return 0; + } + + @Override + public String getContentType() { + return ""; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + return null; + } + + @Override + public String getParameter(String string) { + return ""; + } + + @Override + public Enumeration getParameterNames() { + return Collections.emptyEnumeration(); + } + + @Override + public String[] getParameterValues(String string) { + return new String[0]; + } + + @Override + public Map getParameterMap() { + return Collections.emptyMap(); + } + + @Override + public String getProtocol() { + return ""; + } + + @Override + public String getScheme() { + return ""; + } + + @Override + public String getServerName() { + return ""; + } + + @Override + public int getServerPort() { + return 0; + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader(new StringReader("")); + } + + @Override + public String getRemoteAddr() { + return ""; + } + + @Override + public String getRemoteHost() { + return ""; + } + + @Override + public void setAttribute(String string, Object o) { + } + + @Override + public void removeAttribute(String string) { + } + + @Override + public Locale getLocale() { + return Locale.ENGLISH; + } + + @Override + public Enumeration getLocales() { + return Collections.emptyEnumeration(); + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public RequestDispatcher getRequestDispatcher(String string) { + return null; + } + + @Override + public String getRealPath(String string) { + return ""; + } + + @Override + public int getRemotePort() { + return 0; + } + + @Override + public String getLocalName() { + return ""; + } + + @Override + public String getLocalAddr() { + return ""; + } + + @Override + public int getLocalPort() { + return 0; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + return null; + } + + @Override + public AsyncContext startAsync(ServletRequest sr, ServletResponse sr1) throws IllegalStateException { + return null; + } + + @Override + public boolean isAsyncStarted() { + return false; + } + + @Override + public boolean isAsyncSupported() { + return false; + } + + @Override + public AsyncContext getAsyncContext() { + return null; + } + + @Override + public DispatcherType getDispatcherType() { + return null; + } + +}