Fixes #308864 (Update test suite to JUnit4 - Module jetty-servlets).
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@1651 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
f841262232
commit
6ff56da2fb
|
@ -1,4 +1,3 @@
|
|||
|
||||
jetty-7.1.0.RC1-SNAPSHOT
|
||||
+ 291448 SessionManager has isCheckingRemoteSessionIdEncoding
|
||||
+ 297104 HTTP CONNECT does not work correct with SSL destinations
|
||||
|
@ -16,7 +15,8 @@ jetty-7.1.0.RC1-SNAPSHOT
|
|||
+ 308865 Update test suite to JUnit4 - Module jetty-start
|
||||
+ 309153 Hide extracted WEB-INF/lib when running a non-extracted war
|
||||
+ 309686 Fixed response buffers usage
|
||||
+ 311362 Optional org.eclipse.jetty.util.log.stderr.SOURCE
|
||||
+ 311362 Optional org.eclipse.jetty.util.log.stderr.SOURCE
|
||||
+ 308864 Update test suite to JUnit4 - Module jetty-servlets
|
||||
|
||||
jetty-7.1.0.RC0 27 April 2010
|
||||
+ 294563 Websocket client connection
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit4-version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
// 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
|
||||
// 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.
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
|
||||
package org.eclipse.jetty.servlets;
|
||||
|
@ -21,7 +21,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
|
@ -43,7 +42,7 @@ import org.eclipse.jetty.util.thread.Timeout;
|
|||
|
||||
/**
|
||||
* Denial of Service filter
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* This filter is based on the {@link QoSFilter}. it is useful for limiting
|
||||
* exposure to abuse from request flooding, whether malicious, or as a result of
|
||||
|
@ -62,33 +61,33 @@ import org.eclipse.jetty.util.thread.Timeout;
|
|||
* implemented, in order to uniquely identify authenticated users.
|
||||
* <p>
|
||||
* The following init parameters control the behavior of the filter:
|
||||
*
|
||||
*
|
||||
* maxRequestsPerSec the maximum number of requests from a connection per
|
||||
* second. Requests in excess of this are first delayed,
|
||||
* second. Requests in excess of this are first delayed,
|
||||
* then throttled.
|
||||
*
|
||||
* delayMs is the delay given to all requests over the rate limit,
|
||||
* before they are considered at all. -1 means just reject request,
|
||||
*
|
||||
* delayMs is the delay given to all requests over the rate limit,
|
||||
* before they are considered at all. -1 means just reject request,
|
||||
* 0 means no delay, otherwise it is the delay.
|
||||
*
|
||||
*
|
||||
* maxWaitMs how long to blocking wait for the throttle semaphore.
|
||||
*
|
||||
*
|
||||
* throttledRequests is the number of requests over the rate limit able to be
|
||||
* considered at once.
|
||||
*
|
||||
*
|
||||
* throttleMs how long to async wait for semaphore.
|
||||
*
|
||||
*
|
||||
* maxRequestMs how long to allow this request to run.
|
||||
*
|
||||
* maxIdleTrackerMs how long to keep track of request rates for a connection,
|
||||
*
|
||||
* maxIdleTrackerMs how long to keep track of request rates for a connection,
|
||||
* before deciding that the user has gone away, and discarding it
|
||||
*
|
||||
*
|
||||
* insertHeaders if true , insert the DoSFilter headers into the response. Defaults to true.
|
||||
*
|
||||
*
|
||||
* trackSessions if true, usage rate is tracked by session if a session exists. Defaults to true.
|
||||
*
|
||||
*
|
||||
* remotePort if true and session tracking is not used, then rate is tracked by IP+port (effectively connection). Defaults to false.
|
||||
*
|
||||
*
|
||||
* ipWhitelist a comma-separated list of IP addresses that will not be rate limited
|
||||
*/
|
||||
|
||||
|
@ -138,8 +137,8 @@ public class DoSFilter implements Filter
|
|||
|
||||
protected int _maxRequestsPerSec;
|
||||
protected final ConcurrentHashMap<String, RateTracker> _rateTrackers=new ConcurrentHashMap<String, RateTracker>();
|
||||
private HashSet<String> _whitelist;
|
||||
|
||||
private final HashSet<String> _whitelist = new HashSet<String>();
|
||||
|
||||
private final Timeout _requestTimeoutQ = new Timeout();
|
||||
private final Timeout _trackerTimeoutQ = new Timeout();
|
||||
|
||||
|
@ -155,12 +154,13 @@ public class DoSFilter implements Filter
|
|||
for (int p = 0; p < _queue.length; p++)
|
||||
{
|
||||
_queue[p] = new ConcurrentLinkedQueue<Continuation>();
|
||||
|
||||
|
||||
final int priority=p;
|
||||
_listener[p] = new ContinuationListener()
|
||||
{
|
||||
public void onComplete(Continuation continuation)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
public void onTimeout(Continuation continuation)
|
||||
{
|
||||
|
@ -169,6 +169,9 @@ public class DoSFilter implements Filter
|
|||
};
|
||||
}
|
||||
|
||||
_rateTrackers.clear();
|
||||
_whitelist.clear();
|
||||
|
||||
int baseRateLimit = __DEFAULT_MAX_REQUESTS_PER_SEC;
|
||||
if (filterConfig.getInitParameter(MAX_REQUESTS_PER_S_INIT_PARAM) != null)
|
||||
baseRateLimit = Integer.parseInt(filterConfig.getInitParameter(MAX_REQUESTS_PER_S_INIT_PARAM));
|
||||
|
@ -203,39 +206,35 @@ public class DoSFilter implements Filter
|
|||
if (filterConfig.getInitParameter(MAX_IDLE_TRACKER_MS_INIT_PARAM) != null )
|
||||
maxIdleTrackerMs = Long.parseLong(filterConfig.getInitParameter(MAX_IDLE_TRACKER_MS_INIT_PARAM));
|
||||
_maxIdleTrackerMs = maxIdleTrackerMs;
|
||||
|
||||
|
||||
String whitelistString = "";
|
||||
if (filterConfig.getInitParameter(IP_WHITELIST_INIT_PARAM) !=null )
|
||||
whitelistString = filterConfig.getInitParameter(IP_WHITELIST_INIT_PARAM);
|
||||
|
||||
// empty
|
||||
if (whitelistString.length() == 0 )
|
||||
_whitelist = new HashSet<String>();
|
||||
else
|
||||
|
||||
if (whitelistString.length() > 0)
|
||||
{
|
||||
StringTokenizer tokenizer = new StringTokenizer(whitelistString, ",");
|
||||
_whitelist = new HashSet<String>(tokenizer.countTokens());
|
||||
while (tokenizer.hasMoreTokens())
|
||||
_whitelist.add(tokenizer.nextToken().trim());
|
||||
|
||||
|
||||
Log.info("Whitelisted IP addresses: {}", _whitelist.toString());
|
||||
}
|
||||
|
||||
String tmp = filterConfig.getInitParameter(INSERT_HEADERS_INIT_PARAM);
|
||||
_insertHeaders = tmp==null || Boolean.parseBoolean(tmp);
|
||||
|
||||
_insertHeaders = tmp==null || Boolean.parseBoolean(tmp);
|
||||
|
||||
tmp = filterConfig.getInitParameter(TRACK_SESSIONS_INIT_PARAM);
|
||||
_trackSessions = tmp==null || Boolean.parseBoolean(tmp);
|
||||
|
||||
|
||||
tmp = filterConfig.getInitParameter(REMOTE_PORT_INIT_PARAM);
|
||||
_remotePort = tmp!=null&& Boolean.parseBoolean(tmp);
|
||||
|
||||
_requestTimeoutQ.setNow();
|
||||
_requestTimeoutQ.setDuration(_maxRequestMs);
|
||||
|
||||
|
||||
_trackerTimeoutQ.setNow();
|
||||
_trackerTimeoutQ.setDuration(_maxIdleTrackerMs);
|
||||
|
||||
|
||||
_running=true;
|
||||
_timerThread = (new Thread()
|
||||
{
|
||||
|
@ -245,12 +244,15 @@ public class DoSFilter implements Filter
|
|||
{
|
||||
while (_running)
|
||||
{
|
||||
long now;
|
||||
synchronized (_requestTimeoutQ)
|
||||
{
|
||||
_requestTimeoutQ.setNow();
|
||||
now = _requestTimeoutQ.setNow();
|
||||
_requestTimeoutQ.tick();
|
||||
|
||||
_trackerTimeoutQ.setNow(_requestTimeoutQ.getNow());
|
||||
}
|
||||
synchronized (_trackerTimeoutQ)
|
||||
{
|
||||
_trackerTimeoutQ.setNow(now);
|
||||
_trackerTimeoutQ.tick();
|
||||
}
|
||||
try
|
||||
|
@ -271,25 +273,25 @@ public class DoSFilter implements Filter
|
|||
});
|
||||
_timerThread.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterchain) throws IOException, ServletException
|
||||
{
|
||||
final HttpServletRequest srequest = (HttpServletRequest)request;
|
||||
final HttpServletResponse sresponse = (HttpServletResponse)response;
|
||||
|
||||
|
||||
final long now=_requestTimeoutQ.getNow();
|
||||
|
||||
|
||||
// Look for the rate tracker for this request
|
||||
RateTracker tracker = (RateTracker)request.getAttribute(__TRACKER);
|
||||
|
||||
|
||||
if (tracker==null)
|
||||
{
|
||||
// This is the first time we have seen this request.
|
||||
|
||||
|
||||
// get a rate tracker associated with this request, and record one hit
|
||||
tracker = getRateTracker(request);
|
||||
|
||||
|
||||
// Calculate the rate and check it is over the allowed limit
|
||||
final boolean overRateLimit = tracker.isRateExceeded(now);
|
||||
|
||||
|
@ -298,15 +300,15 @@ public class DoSFilter implements Filter
|
|||
{
|
||||
doFilterChain(filterchain,srequest,sresponse);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// We are over the limit.
|
||||
Log.warn("DOS ALERT: ip="+srequest.getRemoteAddr()+",session="+srequest.getRequestedSessionId()+",user="+srequest.getUserPrincipal());
|
||||
|
||||
|
||||
// So either reject it, delay it or throttle it
|
||||
switch((int)_delayMs)
|
||||
{
|
||||
case -1:
|
||||
case -1:
|
||||
{
|
||||
// Reject this request
|
||||
if (_insertHeaders)
|
||||
|
@ -346,7 +348,7 @@ public class DoSFilter implements Filter
|
|||
{
|
||||
// we were not accepted, so either we suspend to wait,or if we were woken up we insist or we fail
|
||||
final Continuation continuation = ContinuationSupport.getContinuation(request);
|
||||
|
||||
|
||||
Boolean throttled = (Boolean)request.getAttribute(__THROTTLED);
|
||||
if (throttled!=Boolean.TRUE && _throttleMs>0)
|
||||
{
|
||||
|
@ -370,12 +372,12 @@ public class DoSFilter implements Filter
|
|||
accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if we were accepted (either immediately or after throttle)
|
||||
if (accepted)
|
||||
if (accepted)
|
||||
// call the chain
|
||||
doFilterChain(filterchain,srequest,sresponse);
|
||||
else
|
||||
else
|
||||
{
|
||||
// fail the request
|
||||
if (_insertHeaders)
|
||||
|
@ -414,11 +416,11 @@ public class DoSFilter implements Filter
|
|||
* @throws IOException
|
||||
* @throws ServletException
|
||||
*/
|
||||
protected void doFilterChain(FilterChain chain, final HttpServletRequest request, final HttpServletResponse response)
|
||||
protected void doFilterChain(FilterChain chain, final HttpServletRequest request, final HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
final Thread thread=Thread.currentThread();
|
||||
|
||||
|
||||
final Timeout.Task requestTimeout = new Timeout.Task()
|
||||
{
|
||||
public void expired()
|
||||
|
@ -458,7 +460,7 @@ public class DoSFilter implements Filter
|
|||
{
|
||||
response.setHeader("Connection", "close");
|
||||
}
|
||||
try
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -473,14 +475,14 @@ public class DoSFilter implements Filter
|
|||
{
|
||||
Log.warn(e);
|
||||
}
|
||||
|
||||
|
||||
// interrupt the handling thread
|
||||
thread.interrupt();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get priority for this request, based on user type
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param tracker
|
||||
* @return priority
|
||||
|
@ -507,39 +509,44 @@ public class DoSFilter implements Filter
|
|||
* track of this connection's request rate. If this is not the first request
|
||||
* from this connection, return the existing object with the stored stats.
|
||||
* If it is the first request, then create a new request tracker.
|
||||
*
|
||||
*
|
||||
* Assumes that each connection has an identifying characteristic, and goes
|
||||
* through them in order, taking the first that matches: user id (logged
|
||||
* in), session id, client IP address. Unidentifiable connections are lumped
|
||||
* into one.
|
||||
*
|
||||
*
|
||||
* When a session expires, its rate tracker is automatically deleted.
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @return the request rate tracker for the current connection
|
||||
*/
|
||||
public RateTracker getRateTracker(ServletRequest request)
|
||||
{
|
||||
HttpServletRequest srequest = (HttpServletRequest)request;
|
||||
|
||||
String loadId;
|
||||
final int type;
|
||||
|
||||
loadId = extractUserId(request);
|
||||
HttpSession session=srequest.getSession(false);
|
||||
if (_trackSessions && session!=null && !session.isNew())
|
||||
|
||||
String loadId = extractUserId(request);
|
||||
final int type;
|
||||
if (loadId != null)
|
||||
{
|
||||
loadId=session.getId();
|
||||
type = USER_SESSION;
|
||||
type = USER_AUTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadId = _remotePort?(request.getRemoteAddr()+request.getRemotePort()):request.getRemoteAddr();
|
||||
type = USER_IP;
|
||||
if (_trackSessions && session!=null && !session.isNew())
|
||||
{
|
||||
loadId=session.getId();
|
||||
type = USER_SESSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadId = _remotePort?(request.getRemoteAddr()+request.getRemotePort()):request.getRemoteAddr();
|
||||
type = USER_IP;
|
||||
}
|
||||
}
|
||||
|
||||
RateTracker tracker=_rateTrackers.get(loadId);
|
||||
|
||||
|
||||
if (tracker==null)
|
||||
{
|
||||
RateTracker t;
|
||||
|
@ -551,11 +558,11 @@ public class DoSFilter implements Filter
|
|||
{
|
||||
t = new RateTracker(loadId,type,_maxRequestsPerSec);
|
||||
}
|
||||
|
||||
|
||||
tracker=_rateTrackers.putIfAbsent(loadId,t);
|
||||
if (tracker==null)
|
||||
tracker=t;
|
||||
|
||||
|
||||
if (type == USER_IP)
|
||||
{
|
||||
// USER_IP expiration from _rateTrackers is handled by the _trackerTimeoutQ
|
||||
|
@ -579,14 +586,19 @@ public class DoSFilter implements Filter
|
|||
synchronized (_requestTimeoutQ)
|
||||
{
|
||||
_requestTimeoutQ.cancelAll();
|
||||
}
|
||||
synchronized (_trackerTimeoutQ)
|
||||
{
|
||||
_trackerTimeoutQ.cancelAll();
|
||||
}
|
||||
_rateTrackers.clear();
|
||||
_whitelist.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user id, used to track this connection.
|
||||
* This SHOULD be overridden by subclasses.
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @return a unique user id, if logged in; otherwise null.
|
||||
*/
|
||||
|
@ -605,7 +617,7 @@ public class DoSFilter implements Filter
|
|||
protected final int _type;
|
||||
protected final long[] _timestamps;
|
||||
protected int _next;
|
||||
|
||||
|
||||
public RateTracker(String id, int type,int maxRequestsPerSecond)
|
||||
{
|
||||
_id = id;
|
||||
|
@ -642,7 +654,7 @@ public class DoSFilter implements Filter
|
|||
return _type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void valueBound(HttpSessionBindingEvent event)
|
||||
{
|
||||
}
|
||||
|
@ -651,14 +663,14 @@ public class DoSFilter implements Filter
|
|||
{
|
||||
_rateTrackers.remove(_id);
|
||||
}
|
||||
|
||||
|
||||
public void expired()
|
||||
{
|
||||
long now = _trackerTimeoutQ.getNow();
|
||||
int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length;
|
||||
int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length;
|
||||
long last=_timestamps[latestIndex];
|
||||
boolean hasRecentRequest = last != 0 && (now-last)<1000L;
|
||||
|
||||
|
||||
if (hasRecentRequest)
|
||||
reschedule();
|
||||
else
|
||||
|
@ -671,7 +683,7 @@ public class DoSFilter implements Filter
|
|||
return "RateTracker/"+_id+"/"+_type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FixedRateTracker extends RateTracker
|
||||
{
|
||||
public FixedRateTracker(String id, int type, int numRecentRequestsTracked)
|
||||
|
@ -693,11 +705,11 @@ public class DoSFilter implements Filter
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Fixed"+super.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
package org.eclipse.jetty.servlets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.testing.ServletTester;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @version $Revision$ $Date$
|
||||
*/
|
||||
public abstract class AbstractDoSFilterTest
|
||||
{
|
||||
private static ServletTester _tester;
|
||||
private static String _host;
|
||||
private static int _port;
|
||||
private static long _requestMaxTime = 200;
|
||||
private static FilterHolder _dosFilter;
|
||||
private static FilterHolder _timeoutFilter;
|
||||
|
||||
public static void startServer(Class<? extends Filter> filter) throws Exception
|
||||
{
|
||||
_tester = new ServletTester();
|
||||
HttpURI uri = new HttpURI(_tester.createChannelConnector(true));
|
||||
_host = uri.getHost();
|
||||
_port = uri.getPort();
|
||||
|
||||
_tester.setContextPath("/ctx");
|
||||
_tester.addServlet(TestServlet.class, "/*");
|
||||
|
||||
_dosFilter = _tester.addFilter(filter, "/dos/*", 0);
|
||||
_dosFilter.setInitParameter("maxRequestsPerSec", "4");
|
||||
_dosFilter.setInitParameter("delayMs", "200");
|
||||
_dosFilter.setInitParameter("throttledRequests", "1");
|
||||
_dosFilter.setInitParameter("waitMs", "10");
|
||||
_dosFilter.setInitParameter("throttleMs", "4000");
|
||||
_dosFilter.setInitParameter("remotePort", "false");
|
||||
_dosFilter.setInitParameter("insertHeaders", "true");
|
||||
|
||||
_timeoutFilter = _tester.addFilter(filter, "/timeout/*", 0);
|
||||
_timeoutFilter.setInitParameter("maxRequestsPerSec", "4");
|
||||
_timeoutFilter.setInitParameter("delayMs", "200");
|
||||
_timeoutFilter.setInitParameter("throttledRequests", "1");
|
||||
_timeoutFilter.setInitParameter("waitMs", "10");
|
||||
_timeoutFilter.setInitParameter("throttleMs", "4000");
|
||||
_timeoutFilter.setInitParameter("remotePort", "false");
|
||||
_timeoutFilter.setInitParameter("insertHeaders", "true");
|
||||
_timeoutFilter.setInitParameter("maxRequestMs", _requestMaxTime + "");
|
||||
|
||||
_tester.start();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer() throws Exception
|
||||
{
|
||||
_tester.stop();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void startFilters() throws Exception
|
||||
{
|
||||
_dosFilter.start();
|
||||
_timeoutFilter.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopFilters() throws Exception
|
||||
{
|
||||
_timeoutFilter.stop();
|
||||
_dosFilter.stop();
|
||||
}
|
||||
|
||||
private String doRequests(String requests, int loops, long pause0, long pause1, String request) throws Exception
|
||||
{
|
||||
Socket socket = new Socket(_host, _port);
|
||||
socket.setSoTimeout(30000);
|
||||
|
||||
for (int i=loops;i-->0;)
|
||||
{
|
||||
socket.getOutputStream().write(requests.getBytes("UTF-8"));
|
||||
socket.getOutputStream().flush();
|
||||
if (i>0 && pause0>0)
|
||||
Thread.sleep(pause0);
|
||||
}
|
||||
if (pause1>0)
|
||||
Thread.sleep(pause1);
|
||||
socket.getOutputStream().write(request.getBytes("UTF-8"));
|
||||
socket.getOutputStream().flush();
|
||||
|
||||
|
||||
String response;
|
||||
if (requests.contains("/unresponsive"))
|
||||
{
|
||||
// don't read in anything, forcing the request to time out
|
||||
Thread.sleep(_requestMaxTime * 2);
|
||||
response = IO.toString(socket.getInputStream(),"UTF-8");
|
||||
}
|
||||
else
|
||||
{
|
||||
response = IO.toString(socket.getInputStream(),"UTF-8");
|
||||
}
|
||||
socket.close();
|
||||
return response;
|
||||
}
|
||||
|
||||
private int count(String responses,String substring)
|
||||
{
|
||||
int count=0;
|
||||
int i=responses.indexOf(substring);
|
||||
while (i>=0)
|
||||
{
|
||||
count++;
|
||||
i=responses.indexOf(substring,i+substring.length());
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEvenLowRateIP() throws Exception
|
||||
{
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request,11,300,300,last);
|
||||
assertEquals(12,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(0,count(responses,"DoSFilter:"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBurstLowRateIP() throws Exception
|
||||
{
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,2,1100,1100,last);
|
||||
|
||||
assertEquals(9,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(0,count(responses,"DoSFilter:"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelayedIP() throws Exception
|
||||
{
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request+request,2,1100,1100,last);
|
||||
|
||||
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(2,count(responses,"DoSFilter: delayed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrottledIP() throws Exception
|
||||
{
|
||||
Thread other = new Thread()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Cause a delay, then sleep while holding pass
|
||||
String request="GET /ctx/dos/sleeper HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/sleeper?sleep=2000 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,1,0,0,last);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
other.start();
|
||||
Thread.sleep(1500);
|
||||
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,1,0,0,last);
|
||||
//System.out.println("responses are " + responses);
|
||||
assertEquals(5,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(1,count(responses,"DoSFilter: delayed"));
|
||||
assertEquals(1,count(responses,"DoSFilter: throttled"));
|
||||
assertEquals(0,count(responses,"DoSFilter: unavailable"));
|
||||
|
||||
other.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnavailableIP() throws Exception
|
||||
{
|
||||
Thread other = new Thread()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Cause a delay, then sleep while holding pass
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test?sleep=5000 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,1,0,0,last);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
other.start();
|
||||
Thread.sleep(500);
|
||||
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,1,0,0,last);
|
||||
|
||||
assertEquals(4,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(1,count(responses,"HTTP/1.1 503"));
|
||||
assertEquals(1,count(responses,"DoSFilter: delayed"));
|
||||
assertEquals(1,count(responses,"DoSFilter: throttled"));
|
||||
assertEquals(1,count(responses,"DoSFilter: unavailable"));
|
||||
|
||||
other.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionTracking() throws Exception
|
||||
{
|
||||
// get a session, first
|
||||
String requestSession="GET /ctx/dos/test?session=true HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String response=doRequests("",1,0,0,requestSession);
|
||||
String sessionId=response.substring(response.indexOf("Set-Cookie: ")+12, response.indexOf(";"));
|
||||
|
||||
// all other requests use this session
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nCookie: " + sessionId + "\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nCookie: " + sessionId + "\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request+request,2,1100,1100,last);
|
||||
|
||||
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(2,count(responses,"DoSFilter: delayed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleSessionTracking() throws Exception
|
||||
{
|
||||
// get some session ids, first
|
||||
String requestSession="GET /ctx/dos/test?session=true HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String closeRequest="GET /ctx/dos/test?session=true HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String response=doRequests(requestSession+requestSession,1,0,0,closeRequest);
|
||||
|
||||
String[] sessions = response.split("\r\n\r\n");
|
||||
|
||||
String sessionId1=sessions[0].substring(sessions[0].indexOf("Set-Cookie: ")+12, sessions[0].indexOf(";"));
|
||||
String sessionId2=sessions[1].substring(sessions[1].indexOf("Set-Cookie: ")+12, sessions[1].indexOf(";"));
|
||||
|
||||
// alternate between sessions
|
||||
String request1="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nCookie: " + sessionId1 + "\r\n\r\n";
|
||||
String request2="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nCookie: " + sessionId2 + "\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nCookie: " + sessionId2 + "\r\n\r\n";
|
||||
|
||||
// ensure the sessions are new
|
||||
String responses = doRequests(request1+request2,1,1100,1100,last);
|
||||
Thread.sleep(1000);
|
||||
|
||||
responses = doRequests(request1+request2+request1+request2+request1,2,1100,1100,last);
|
||||
|
||||
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(0,count(responses,"DoSFilter: delayed"));
|
||||
|
||||
// alternate between sessions
|
||||
responses = doRequests(request1+request2+request1+request2+request1,2,550,550,last);
|
||||
|
||||
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
|
||||
int delayedRequests = count(responses,"DoSFilter: delayed");
|
||||
assertTrue(delayedRequests >= 2 && delayedRequests <= 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnresponsiveClient() throws Exception
|
||||
{
|
||||
int numRequests = 1000;
|
||||
|
||||
String last="GET /ctx/timeout/unresponsive?lines="+numRequests+" HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests("",0,0,0,last);
|
||||
// was expired, and stopped before reaching the end of the requests
|
||||
int responseLines = count(responses, "Line:");
|
||||
assertTrue(responses.contains("DoSFilter: timeout"));
|
||||
assertTrue(responseLines > 0 && responseLines < numRequests);
|
||||
}
|
||||
|
||||
public static class TestServlet extends HttpServlet implements Servlet
|
||||
{
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
if (request.getParameter("session")!=null)
|
||||
request.getSession(true);
|
||||
if (request.getParameter("sleep")!=null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(Long.parseLong(request.getParameter("sleep")));
|
||||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (request.getParameter("lines")!=null)
|
||||
{
|
||||
int count = Integer.parseInt(request.getParameter("lines"));
|
||||
for(int i = 0; i < count; ++i)
|
||||
{
|
||||
response.getWriter().append("Line: " + i+"\n");
|
||||
response.flushBuffer();
|
||||
|
||||
try
|
||||
{
|
||||
Thread.sleep(10);
|
||||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
response.setContentType("text/plain");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,11 +4,11 @@
|
|||
// 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
|
||||
// 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.
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
|
||||
package org.eclipse.jetty.servlets;
|
||||
|
@ -16,60 +16,31 @@ package org.eclipse.jetty.servlets;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.testing.ServletTester;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
public class CloseableDoSFilterTest extends DoSFilterTest
|
||||
public class CloseableDoSFilterTest extends AbstractDoSFilterTest
|
||||
{
|
||||
protected void setUp() throws Exception
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception
|
||||
{
|
||||
_tester = new ServletTester();
|
||||
HttpURI uri=new HttpURI(_tester.createSocketConnector(true));
|
||||
_host=uri.getHost();
|
||||
_port=uri.getPort();
|
||||
|
||||
_tester.setContextPath("/ctx");
|
||||
_tester.addServlet(TestServlet.class, "/*");
|
||||
|
||||
FilterHolder dos=_tester.addFilter(CloseableDoSFilter2.class,"/dos/*",0);
|
||||
dos.setInitParameter("maxRequestsPerSec","4");
|
||||
dos.setInitParameter("delayMs","200");
|
||||
dos.setInitParameter("throttledRequests","1");
|
||||
dos.setInitParameter("waitMs","10");
|
||||
dos.setInitParameter("throttleMs","4000");
|
||||
dos.setInitParameter("remotePort", "false");
|
||||
dos.setInitParameter("insertHeaders", "true");
|
||||
|
||||
FilterHolder quickTimeout = _tester.addFilter(CloseableDoSFilter2.class,"/timeout/*",0);
|
||||
quickTimeout.setInitParameter("maxRequestsPerSec","4");
|
||||
quickTimeout.setInitParameter("delayMs","200");
|
||||
quickTimeout.setInitParameter("throttledRequests","1");
|
||||
quickTimeout.setInitParameter("waitMs","10");
|
||||
quickTimeout.setInitParameter("throttleMs","4000");
|
||||
quickTimeout.setInitParameter("remotePort", "false");
|
||||
quickTimeout.setInitParameter("insertHeaders", "true");
|
||||
quickTimeout.setInitParameter("maxRequestMs", _maxRequestMs + "");
|
||||
|
||||
_tester.start();
|
||||
|
||||
startServer(CloseableDoSFilter2.class);
|
||||
}
|
||||
|
||||
|
||||
public static class CloseableDoSFilter2 extends CloseableDoSFilter
|
||||
{
|
||||
{
|
||||
public void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
|
||||
{
|
||||
try
|
||||
{
|
||||
response.getWriter().append("DoSFilter: timeout");
|
||||
response.flushBuffer();
|
||||
super.closeConnection(request,response,thread);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.warn(e);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
response.getWriter().append("DoSFilter: timeout");
|
||||
response.flushBuffer();
|
||||
super.closeConnection(request, response, thread);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// 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,
|
||||
|
@ -14,325 +14,20 @@
|
|||
|
||||
package org.eclipse.jetty.servlets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.testing.ServletTester;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
public class DoSFilterTest extends TestCase
|
||||
public class DoSFilterTest extends AbstractDoSFilterTest
|
||||
{
|
||||
protected ServletTester _tester;
|
||||
protected String _host;
|
||||
protected int _port;
|
||||
|
||||
protected int _maxRequestMs = 200;
|
||||
protected void setUp() throws Exception
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception
|
||||
{
|
||||
_tester = new ServletTester();
|
||||
HttpURI uri=new HttpURI(_tester.createChannelConnector(true));
|
||||
_host=uri.getHost();
|
||||
_port=uri.getPort();
|
||||
|
||||
_tester.setContextPath("/ctx");
|
||||
_tester.addServlet(TestServlet.class, "/*");
|
||||
|
||||
FilterHolder dos=_tester.addFilter(DoSFilter2.class,"/dos/*",0);
|
||||
dos.setInitParameter("maxRequestsPerSec","4");
|
||||
dos.setInitParameter("delayMs","200");
|
||||
dos.setInitParameter("throttledRequests","1");
|
||||
dos.setInitParameter("waitMs","10");
|
||||
dos.setInitParameter("throttleMs","4000");
|
||||
dos.setInitParameter("remotePort", "false");
|
||||
dos.setInitParameter("insertHeaders", "true");
|
||||
|
||||
FilterHolder quickTimeout = _tester.addFilter(DoSFilter2.class,"/timeout/*",0);
|
||||
quickTimeout.setInitParameter("maxRequestsPerSec","4");
|
||||
quickTimeout.setInitParameter("delayMs","200");
|
||||
quickTimeout.setInitParameter("throttledRequests","1");
|
||||
quickTimeout.setInitParameter("waitMs","10");
|
||||
quickTimeout.setInitParameter("throttleMs","4000");
|
||||
quickTimeout.setInitParameter("remotePort", "false");
|
||||
quickTimeout.setInitParameter("insertHeaders", "true");
|
||||
quickTimeout.setInitParameter("maxRequestMs", _maxRequestMs + "");
|
||||
|
||||
_tester.start();
|
||||
|
||||
}
|
||||
|
||||
protected void tearDown() throws Exception
|
||||
{
|
||||
_tester.stop();
|
||||
}
|
||||
|
||||
private String doRequests(String requests, int loops, long pause0,long pause1,String request)
|
||||
throws Exception
|
||||
{
|
||||
Socket socket = new Socket(_host,_port);
|
||||
socket.setSoTimeout(30000);
|
||||
|
||||
for (int i=loops;i-->0;)
|
||||
{
|
||||
socket.getOutputStream().write(requests.getBytes("UTF-8"));
|
||||
socket.getOutputStream().flush();
|
||||
if (i>0 && pause0>0)
|
||||
Thread.sleep(pause0);
|
||||
}
|
||||
if (pause1>0)
|
||||
Thread.sleep(pause1);
|
||||
socket.getOutputStream().write(request.getBytes("UTF-8"));
|
||||
socket.getOutputStream().flush();
|
||||
|
||||
|
||||
String response = "";
|
||||
|
||||
if (requests.contains("/unresponsive"))
|
||||
{
|
||||
// don't read in anything, forcing the request to time out
|
||||
Thread.sleep(_maxRequestMs * 2);
|
||||
response = IO.toString(socket.getInputStream(),"UTF-8");
|
||||
}
|
||||
else
|
||||
{
|
||||
response = IO.toString(socket.getInputStream(),"UTF-8");
|
||||
}
|
||||
socket.close();
|
||||
return response;
|
||||
}
|
||||
|
||||
private int count(String responses,String substring)
|
||||
{
|
||||
int count=0;
|
||||
int i=responses.indexOf(substring);
|
||||
while (i>=0)
|
||||
{
|
||||
count++;
|
||||
i=responses.indexOf(substring,i+substring.length());
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void testEvenLowRateIP()
|
||||
throws Exception
|
||||
{
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request,11,300,300,last);
|
||||
assertEquals(12,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(0,count(responses,"DoSFilter:"));
|
||||
}
|
||||
|
||||
public void testBurstLowRateIP()
|
||||
throws Exception
|
||||
{
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,2,1100,1100,last);
|
||||
|
||||
assertEquals(9,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(0,count(responses,"DoSFilter:"));
|
||||
}
|
||||
|
||||
public void testDelayedIP()
|
||||
throws Exception
|
||||
{
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request+request,2,1100,1100,last);
|
||||
|
||||
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(2,count(responses,"DoSFilter: delayed"));
|
||||
}
|
||||
|
||||
public void testThrottledIP()
|
||||
throws Exception
|
||||
{
|
||||
Thread other = new Thread()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Cause a delay, then sleep while holding pass
|
||||
String request="GET /ctx/dos/sleeper HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/sleeper?sleep=2000 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,1,0,0,last);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
other.start();
|
||||
Thread.sleep(1500);
|
||||
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,1,0,0,last);
|
||||
//System.out.println("responses are " + responses);
|
||||
assertEquals(5,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(1,count(responses,"DoSFilter: delayed"));
|
||||
assertEquals(1,count(responses,"DoSFilter: throttled"));
|
||||
assertEquals(0,count(responses,"DoSFilter: unavailable"));
|
||||
|
||||
other.join();
|
||||
}
|
||||
|
||||
public void testUnavailableIP()
|
||||
throws Exception
|
||||
{
|
||||
Thread other = new Thread()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Cause a delay, then sleep while holding pass
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test?sleep=5000 HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,1,0,0,last);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
other.start();
|
||||
Thread.sleep(500);
|
||||
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request,1,0,0,last);
|
||||
|
||||
assertEquals(4,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(1,count(responses,"HTTP/1.1 503"));
|
||||
assertEquals(1,count(responses,"DoSFilter: delayed"));
|
||||
assertEquals(1,count(responses,"DoSFilter: throttled"));
|
||||
assertEquals(1,count(responses,"DoSFilter: unavailable"));
|
||||
|
||||
other.join();
|
||||
}
|
||||
|
||||
public void testSessionTracking()
|
||||
throws Exception
|
||||
{
|
||||
// get a session, first
|
||||
String requestSession="GET /ctx/dos/test?session=true HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String response=doRequests("",1,0,0,requestSession);
|
||||
String sessionId=response.substring(response.indexOf("Set-Cookie: ")+12, response.indexOf(";"));
|
||||
|
||||
// all other requests use this session
|
||||
String request="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nCookie: " + sessionId + "\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nCookie: " + sessionId + "\r\n\r\n";
|
||||
String responses = doRequests(request+request+request+request+request,2,1100,1100,last);
|
||||
|
||||
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(2,count(responses,"DoSFilter: delayed"));
|
||||
startServer(DoSFilter2.class);
|
||||
}
|
||||
|
||||
public void testMultipleSessionTracking()
|
||||
throws Exception
|
||||
{
|
||||
// get some session ids, first
|
||||
String requestSession="GET /ctx/dos/test?session=true HTTP/1.1\r\nHost: localhost\r\n\r\n";
|
||||
String closeRequest="GET /ctx/dos/test?session=true HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String response=doRequests(requestSession+requestSession,1,0,0,closeRequest);
|
||||
|
||||
String[] sessions = response.split("\r\n\r\n");
|
||||
|
||||
String sessionId1=sessions[0].substring(sessions[0].indexOf("Set-Cookie: ")+12, sessions[0].indexOf(";"));
|
||||
String sessionId2=sessions[1].substring(sessions[1].indexOf("Set-Cookie: ")+12, sessions[1].indexOf(";"));
|
||||
|
||||
// alternate between sessions
|
||||
String request1="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nCookie: " + sessionId1 + "\r\n\r\n";
|
||||
String request2="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nCookie: " + sessionId2 + "\r\n\r\n";
|
||||
String last="GET /ctx/dos/test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nCookie: " + sessionId2 + "\r\n\r\n";
|
||||
|
||||
// ensure the sessions are new
|
||||
String responses = doRequests(request1+request2,1,1100,1100,last);
|
||||
Thread.sleep(1000);
|
||||
|
||||
responses = doRequests(request1+request2+request1+request2+request1,2,1100,1100,last);
|
||||
|
||||
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
|
||||
assertEquals(0,count(responses,"DoSFilter: delayed"));
|
||||
|
||||
// alternate between sessions
|
||||
responses = doRequests(request1+request2+request1+request2+request1,2,550,550,last);
|
||||
|
||||
assertEquals(11,count(responses,"HTTP/1.1 200 OK"));
|
||||
int delayedRequests = count(responses,"DoSFilter: delayed");
|
||||
assertTrue(delayedRequests >= 2 && delayedRequests <= 3);
|
||||
}
|
||||
|
||||
public void testUnresponsiveClient()
|
||||
throws Exception
|
||||
{
|
||||
int numRequests = 1000;
|
||||
|
||||
String last="GET /ctx/timeout/unresponsive?lines="+numRequests+" HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
|
||||
String responses = doRequests("",0,0,0,last);
|
||||
// was expired, and stopped before reaching the end of the requests
|
||||
int responseLines = count(responses, "Line:");
|
||||
assertTrue(responses.contains("DoSFilter: timeout"));
|
||||
assertTrue(responseLines > 0 && responseLines < numRequests);
|
||||
}
|
||||
|
||||
public static class TestServlet extends HttpServlet implements Servlet
|
||||
{
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
if (request.getParameter("session")!=null)
|
||||
request.getSession(true);
|
||||
if (request.getParameter("sleep")!=null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(Long.parseLong(request.getParameter("sleep")));
|
||||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (request.getParameter("lines")!=null)
|
||||
{
|
||||
int count = Integer.parseInt(request.getParameter("lines"));
|
||||
for(int i = 0; i < count; ++i)
|
||||
{
|
||||
response.getWriter().append("Line: " + i+"\n");
|
||||
response.flushBuffer();
|
||||
|
||||
try
|
||||
{
|
||||
Thread.sleep(10);
|
||||
}
|
||||
catch(InterruptedException e)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
response.setContentType("text/plain");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class DoSFilter2 extends DoSFilter
|
||||
{
|
||||
public void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
|
||||
|
@ -346,5 +41,5 @@ public class DoSFilterTest extends TestCase
|
|||
Log.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
// 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
|
||||
// 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.
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
|
||||
package org.eclipse.jetty.servlets;
|
||||
|
@ -18,30 +18,33 @@ import java.io.FileInputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.testing.HttpTester;
|
||||
import org.eclipse.jetty.testing.ServletTester;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PutFilterTest extends TestCase
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PutFilterTest
|
||||
{
|
||||
File _dir;
|
||||
ServletTester tester;
|
||||
|
||||
protected void setUp() throws Exception
|
||||
private File _dir;
|
||||
private ServletTester tester;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
_dir = File.createTempFile("testPutFilter",null);
|
||||
_dir.delete();
|
||||
_dir.mkdir();
|
||||
assertTrue(_dir.delete());
|
||||
assertTrue(_dir.mkdir());
|
||||
_dir.deleteOnExit();
|
||||
assertTrue(_dir.isDirectory());
|
||||
|
||||
super.setUp();
|
||||
|
||||
tester=new ServletTester();
|
||||
tester.setContextPath("/context");
|
||||
tester.setResourceBase(_dir.getCanonicalPath());
|
||||
|
@ -49,21 +52,21 @@ public class PutFilterTest extends TestCase
|
|||
FilterHolder holder = tester.addFilter(PutFilter.class,"/*",0);
|
||||
holder.setInitParameter("delAllowed","true");
|
||||
tester.start();
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected void tearDown() throws Exception
|
||||
@After
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
super.tearDown();
|
||||
tester.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePut() throws Exception
|
||||
{
|
||||
// generated and parsed test
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
|
||||
|
||||
// test GET
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.0");
|
||||
|
@ -82,7 +85,7 @@ public class PutFilterTest extends TestCase
|
|||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_CREATED,response.getStatus());
|
||||
|
||||
|
||||
File file=new File(_dir,"file.txt");
|
||||
assertTrue(file.exists());
|
||||
assertEquals(data0,IO.toString(new FileInputStream(file)));
|
||||
|
@ -106,12 +109,10 @@ public class PutFilterTest extends TestCase
|
|||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
|
||||
|
||||
file=new File(_dir,"file.txt");
|
||||
assertTrue(file.exists());
|
||||
assertEquals(data1,IO.toString(new FileInputStream(file)));
|
||||
|
||||
|
||||
|
||||
// test PUT2
|
||||
request.setMethod("PUT");
|
||||
|
@ -142,7 +143,7 @@ public class PutFilterTest extends TestCase
|
|||
out.write(to_send.substring(l-5).getBytes());
|
||||
out.flush();
|
||||
String in=IO.toString(socket.getInputStream());
|
||||
|
||||
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
|
@ -151,10 +152,9 @@ public class PutFilterTest extends TestCase
|
|||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_OK,response.getStatus());
|
||||
assertEquals(data2,response.getContent());
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleDelete() throws Exception
|
||||
{
|
||||
// generated and parsed test
|
||||
|
@ -172,20 +172,18 @@ public class PutFilterTest extends TestCase
|
|||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_CREATED,response.getStatus());
|
||||
|
||||
|
||||
File file=new File(_dir,"file.txt");
|
||||
assertTrue(file.exists());
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
assertEquals(data1,IO.toString(fis));
|
||||
fis.close();
|
||||
|
||||
|
||||
request.setMethod("DELETE");
|
||||
request.setURI("/context/file.txt");
|
||||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_NO_CONTENT,response.getStatus());
|
||||
|
||||
|
||||
assertTrue(!file.exists());
|
||||
|
||||
|
@ -194,10 +192,9 @@ public class PutFilterTest extends TestCase
|
|||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_FORBIDDEN,response.getStatus());
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleMove() throws Exception
|
||||
{
|
||||
// generated and parsed test
|
||||
|
@ -215,13 +212,12 @@ public class PutFilterTest extends TestCase
|
|||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_CREATED,response.getStatus());
|
||||
|
||||
|
||||
File file=new File(_dir,"file.txt");
|
||||
assertTrue(file.exists());
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
assertEquals(data1,IO.toString(fis));
|
||||
fis.close();
|
||||
|
||||
|
||||
request.setMethod("MOVE");
|
||||
request.setURI("/context/file.txt");
|
||||
|
@ -229,22 +225,22 @@ public class PutFilterTest extends TestCase
|
|||
response.parse(tester.getResponses(request.generate()));
|
||||
assertTrue(response.getMethod()==null);
|
||||
assertEquals(HttpServletResponse.SC_NO_CONTENT,response.getStatus());
|
||||
|
||||
|
||||
assertTrue(!file.exists());
|
||||
|
||||
File n_file=new File(_dir,"blah.txt");
|
||||
assertTrue(n_file.exists());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleOptions()
|
||||
{
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPassConditionalHeaders()
|
||||
{
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,20 +4,18 @@
|
|||
// 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
|
||||
// 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.
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
package org.eclipse.jetty.servlets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
|
@ -25,16 +23,20 @@ import javax.servlet.http.HttpServlet;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.FilterMapping;
|
||||
import org.eclipse.jetty.testing.HttpTester;
|
||||
import org.eclipse.jetty.testing.ServletTester;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class QoSFilterTest extends TestCase
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class QoSFilterTest
|
||||
{
|
||||
private ServletTester _tester;
|
||||
private LocalConnector[] _connectors;
|
||||
|
@ -42,42 +44,46 @@ public class QoSFilterTest extends TestCase
|
|||
private final int NUM_CONNECTIONS = 8;
|
||||
private final int NUM_LOOPS = 6;
|
||||
private final int MAX_QOS = 4;
|
||||
|
||||
protected void setUp() throws Exception
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
_tester = new ServletTester();
|
||||
_tester.setContextPath("/context");
|
||||
_tester.addServlet(TestServlet.class, "/test");
|
||||
TestServlet.__maxSleepers=0;
|
||||
TestServlet.__sleepers=0;
|
||||
|
||||
|
||||
_connectors = new LocalConnector[NUM_CONNECTIONS];
|
||||
for(int i = 0; i < _connectors.length; ++i)
|
||||
_connectors[i] = _tester.createLocalConnector();
|
||||
|
||||
|
||||
_doneRequests = new CountDownLatch(NUM_CONNECTIONS*NUM_LOOPS);
|
||||
|
||||
|
||||
_tester.start();
|
||||
}
|
||||
|
||||
protected void tearDown() throws Exception
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
_tester.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoFilter() throws Exception
|
||||
{
|
||||
{
|
||||
for(int i = 0; i < NUM_CONNECTIONS; ++i )
|
||||
{
|
||||
new Thread(new Worker(i)).start();
|
||||
}
|
||||
|
||||
|
||||
_doneRequests.await(10,TimeUnit.SECONDS);
|
||||
|
||||
|
||||
assertFalse("TEST WAS NOT PARALLEL ENOUGH!",TestServlet.__maxSleepers<=MAX_QOS);
|
||||
assertTrue(TestServlet.__maxSleepers<=NUM_CONNECTIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockingQosFilter() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(QoSFilter2.class);
|
||||
|
@ -95,23 +101,24 @@ public class QoSFilterTest extends TestCase
|
|||
assertTrue(TestServlet.__maxSleepers==MAX_QOS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQosFilter() throws Exception
|
||||
{
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(QoSFilter2.class);
|
||||
holder.setAsyncSupported(true);
|
||||
holder.setInitParameter(QoSFilter.MAX_REQUESTS_INIT_PARAM, ""+MAX_QOS);
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",FilterMapping.DEFAULT);
|
||||
|
||||
|
||||
for(int i = 0; i < NUM_CONNECTIONS; ++i )
|
||||
{
|
||||
new Thread(new Worker2(i)).start();
|
||||
}
|
||||
|
||||
|
||||
_doneRequests.await(20,TimeUnit.SECONDS);
|
||||
assertFalse("TEST WAS NOT PARALLEL ENOUGH!",TestServlet.__maxSleepers<MAX_QOS);
|
||||
assertTrue(TestServlet.__maxSleepers<=MAX_QOS);
|
||||
}
|
||||
|
||||
|
||||
class Worker implements Runnable {
|
||||
private int _num;
|
||||
public Worker(int num)
|
||||
|
@ -124,7 +131,6 @@ public class QoSFilterTest extends TestCase
|
|||
for (int i=0;i<NUM_LOOPS;i++)
|
||||
{
|
||||
HttpTester request = new HttpTester();
|
||||
HttpTester response = new HttpTester();
|
||||
|
||||
request.setMethod("GET");
|
||||
request.setHeader("host", "tester");
|
||||
|
@ -133,22 +139,14 @@ public class QoSFilterTest extends TestCase
|
|||
try
|
||||
{
|
||||
String responseString = _tester.getResponses(request.generate(), _connectors[_num]);
|
||||
int index=-1;
|
||||
if((index = responseString.indexOf("HTTP", index+1))!=-1)
|
||||
if(responseString.indexOf("HTTP")!=-1)
|
||||
{
|
||||
responseString = response.parse(responseString);
|
||||
_doneRequests.countDown();
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (Exception x)
|
||||
{
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
assertTrue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,25 +169,24 @@ public class QoSFilterTest extends TestCase
|
|||
{
|
||||
url=new URL(addr+"/context/test?priority="+(_num%QoSFilter.__DEFAULT_MAX_PRIORITY)+"&n="+_num+"&l="+i);
|
||||
// System.err.println(_num+"-"+i+" Try "+url);
|
||||
InputStream in = (InputStream)url.getContent();
|
||||
url.getContent();
|
||||
_doneRequests.countDown();
|
||||
// System.err.println(_num+"-"+i+" Got "+IO.toString(in)+" "+_doneRequests.getCount());
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.warn(url.toString());
|
||||
Log.warn(String.valueOf(url));
|
||||
Log.debug(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class TestServlet extends HttpServlet implements Servlet
|
||||
{
|
||||
private int _count;
|
||||
private static int __sleepers;
|
||||
private static int __maxSleepers;
|
||||
|
||||
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
try
|
||||
|
@ -212,25 +209,24 @@ public class QoSFilterTest extends TestCase
|
|||
}
|
||||
|
||||
response.setContentType("text/plain");
|
||||
response.getWriter().println("DONE!");
|
||||
response.getWriter().println("DONE!");
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
response.sendError(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class QoSFilter2 extends QoSFilter
|
||||
{
|
||||
public int getPriority(ServletRequest request)
|
||||
{
|
||||
String p = ((HttpServletRequest)request).getParameter("priority");
|
||||
String p = request.getParameter("priority");
|
||||
if (p!=null)
|
||||
return Integer.parseInt(p);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue