1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.servlets;
20
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Queue;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.ConcurrentLinkedQueue;
28 import java.util.concurrent.CopyOnWriteArrayList;
29 import java.util.concurrent.Semaphore;
30 import java.util.concurrent.TimeUnit;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 import javax.servlet.Filter;
34 import javax.servlet.FilterChain;
35 import javax.servlet.FilterConfig;
36 import javax.servlet.ServletContext;
37 import javax.servlet.ServletException;
38 import javax.servlet.ServletRequest;
39 import javax.servlet.ServletResponse;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpServletResponse;
42 import javax.servlet.http.HttpSession;
43 import javax.servlet.http.HttpSessionActivationListener;
44 import javax.servlet.http.HttpSessionBindingEvent;
45 import javax.servlet.http.HttpSessionBindingListener;
46 import javax.servlet.http.HttpSessionEvent;
47
48 import org.eclipse.jetty.continuation.Continuation;
49 import org.eclipse.jetty.continuation.ContinuationListener;
50 import org.eclipse.jetty.continuation.ContinuationSupport;
51 import org.eclipse.jetty.server.handler.ContextHandler;
52 import org.eclipse.jetty.util.log.Log;
53 import org.eclipse.jetty.util.log.Logger;
54 import org.eclipse.jetty.util.thread.Timeout;
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 public class DoSFilter implements Filter
125 {
126 private static final Logger LOG = Log.getLogger(DoSFilter.class);
127
128 private static final String IPv4_GROUP = "(\\d{1,3})";
129 private static final Pattern IPv4_PATTERN = Pattern.compile(IPv4_GROUP+"\\."+IPv4_GROUP+"\\."+IPv4_GROUP+"\\."+IPv4_GROUP);
130 private static final String IPv6_GROUP = "(\\p{XDigit}{1,4})";
131 private static final Pattern IPv6_PATTERN = Pattern.compile(IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP+":"+IPv6_GROUP);
132 private static final Pattern CIDR_PATTERN = Pattern.compile("([^/]+)/(\\d+)");
133
134 private static final String __TRACKER = "DoSFilter.Tracker";
135 private static final String __THROTTLED = "DoSFilter.Throttled";
136
137 private static final int __DEFAULT_MAX_REQUESTS_PER_SEC = 25;
138 private static final int __DEFAULT_DELAY_MS = 100;
139 private static final int __DEFAULT_THROTTLE = 5;
140 private static final int __DEFAULT_MAX_WAIT_MS = 50;
141 private static final long __DEFAULT_THROTTLE_MS = 30000L;
142 private static final long __DEFAULT_MAX_REQUEST_MS_INIT_PARAM = 30000L;
143 private static final long __DEFAULT_MAX_IDLE_TRACKER_MS_INIT_PARAM = 30000L;
144
145 static final String MANAGED_ATTR_INIT_PARAM = "managedAttr";
146 static final String MAX_REQUESTS_PER_S_INIT_PARAM = "maxRequestsPerSec";
147 static final String DELAY_MS_INIT_PARAM = "delayMs";
148 static final String THROTTLED_REQUESTS_INIT_PARAM = "throttledRequests";
149 static final String MAX_WAIT_INIT_PARAM = "maxWaitMs";
150 static final String THROTTLE_MS_INIT_PARAM = "throttleMs";
151 static final String MAX_REQUEST_MS_INIT_PARAM = "maxRequestMs";
152 static final String MAX_IDLE_TRACKER_MS_INIT_PARAM = "maxIdleTrackerMs";
153 static final String INSERT_HEADERS_INIT_PARAM = "insertHeaders";
154 static final String TRACK_SESSIONS_INIT_PARAM = "trackSessions";
155 static final String REMOTE_PORT_INIT_PARAM = "remotePort";
156 static final String IP_WHITELIST_INIT_PARAM = "ipWhitelist";
157 static final String ENABLED_INIT_PARAM = "enabled";
158
159 private static final int USER_AUTH = 2;
160 private static final int USER_SESSION = 2;
161 private static final int USER_IP = 1;
162 private static final int USER_UNKNOWN = 0;
163
164 private ServletContext _context;
165 private volatile long _delayMs;
166 private volatile long _throttleMs;
167 private volatile long _maxWaitMs;
168 private volatile long _maxRequestMs;
169 private volatile long _maxIdleTrackerMs;
170 private volatile boolean _insertHeaders;
171 private volatile boolean _trackSessions;
172 private volatile boolean _remotePort;
173 private volatile boolean _enabled;
174 private Semaphore _passes;
175 private volatile int _throttledRequests;
176 private volatile int _maxRequestsPerSec;
177 private Queue<Continuation>[] _queue;
178 private ContinuationListener[] _listeners;
179 private final ConcurrentHashMap<String, RateTracker> _rateTrackers = new ConcurrentHashMap<String, RateTracker>();
180 private final List<String> _whitelist = new CopyOnWriteArrayList<String>();
181 private final Timeout _requestTimeoutQ = new Timeout();
182 private final Timeout _trackerTimeoutQ = new Timeout();
183 private Thread _timerThread;
184 private volatile boolean _running;
185
186 public void init(FilterConfig filterConfig)
187 {
188 _context = filterConfig.getServletContext();
189
190 _queue = new Queue[getMaxPriority() + 1];
191 _listeners = new ContinuationListener[getMaxPriority() + 1];
192 for (int p = 0; p < _queue.length; p++)
193 {
194 _queue[p] = new ConcurrentLinkedQueue<Continuation>();
195
196 final int priority = p;
197 _listeners[p] = new ContinuationListener()
198 {
199 public void onComplete(Continuation continuation)
200 {
201 }
202
203 public void onTimeout(Continuation continuation)
204 {
205 _queue[priority].remove(continuation);
206 }
207 };
208 }
209
210 _rateTrackers.clear();
211
212 int maxRequests = __DEFAULT_MAX_REQUESTS_PER_SEC;
213 String parameter = filterConfig.getInitParameter(MAX_REQUESTS_PER_S_INIT_PARAM);
214 if (parameter != null)
215 maxRequests = Integer.parseInt(parameter);
216 setMaxRequestsPerSec(maxRequests);
217
218 long delay = __DEFAULT_DELAY_MS;
219 parameter = filterConfig.getInitParameter(DELAY_MS_INIT_PARAM);
220 if (parameter != null)
221 delay = Long.parseLong(parameter);
222 setDelayMs(delay);
223
224 int throttledRequests = __DEFAULT_THROTTLE;
225 parameter = filterConfig.getInitParameter(THROTTLED_REQUESTS_INIT_PARAM);
226 if (parameter != null)
227 throttledRequests = Integer.parseInt(parameter);
228 setThrottledRequests(throttledRequests);
229
230 long maxWait = __DEFAULT_MAX_WAIT_MS;
231 parameter = filterConfig.getInitParameter(MAX_WAIT_INIT_PARAM);
232 if (parameter != null)
233 maxWait = Long.parseLong(parameter);
234 setMaxWaitMs(maxWait);
235
236 long throttle = __DEFAULT_THROTTLE_MS;
237 parameter = filterConfig.getInitParameter(THROTTLE_MS_INIT_PARAM);
238 if (parameter != null)
239 throttle = Long.parseLong(parameter);
240 setThrottleMs(throttle);
241
242 long maxRequestMs = __DEFAULT_MAX_REQUEST_MS_INIT_PARAM;
243 parameter = filterConfig.getInitParameter(MAX_REQUEST_MS_INIT_PARAM);
244 if (parameter != null)
245 maxRequestMs = Long.parseLong(parameter);
246 setMaxRequestMs(maxRequestMs);
247
248 long maxIdleTrackerMs = __DEFAULT_MAX_IDLE_TRACKER_MS_INIT_PARAM;
249 parameter = filterConfig.getInitParameter(MAX_IDLE_TRACKER_MS_INIT_PARAM);
250 if (parameter != null)
251 maxIdleTrackerMs = Long.parseLong(parameter);
252 setMaxIdleTrackerMs(maxIdleTrackerMs);
253
254 String whiteList = "";
255 parameter = filterConfig.getInitParameter(IP_WHITELIST_INIT_PARAM);
256 if (parameter != null)
257 whiteList = parameter;
258 setWhitelist(whiteList);
259
260 parameter = filterConfig.getInitParameter(INSERT_HEADERS_INIT_PARAM);
261 setInsertHeaders(parameter == null || Boolean.parseBoolean(parameter));
262
263 parameter = filterConfig.getInitParameter(TRACK_SESSIONS_INIT_PARAM);
264 setTrackSessions(parameter == null || Boolean.parseBoolean(parameter));
265
266 parameter = filterConfig.getInitParameter(REMOTE_PORT_INIT_PARAM);
267 setRemotePort(parameter != null && Boolean.parseBoolean(parameter));
268
269 parameter = filterConfig.getInitParameter(ENABLED_INIT_PARAM);
270 setEnabled(parameter == null || Boolean.parseBoolean(parameter));
271
272 _requestTimeoutQ.setNow();
273 _requestTimeoutQ.setDuration(_maxRequestMs);
274
275 _trackerTimeoutQ.setNow();
276 _trackerTimeoutQ.setDuration(_maxIdleTrackerMs);
277
278 _running = true;
279 _timerThread = (new Thread()
280 {
281 public void run()
282 {
283 try
284 {
285 while (_running)
286 {
287 long now = _requestTimeoutQ.setNow();
288 _requestTimeoutQ.tick();
289 _trackerTimeoutQ.setNow(now);
290 _trackerTimeoutQ.tick();
291 try
292 {
293 Thread.sleep(100);
294 }
295 catch (InterruptedException e)
296 {
297 LOG.ignore(e);
298 }
299 }
300 }
301 finally
302 {
303 LOG.debug("DoSFilter timer exited");
304 }
305 }
306 });
307 _timerThread.start();
308
309 if (_context != null && Boolean.parseBoolean(filterConfig.getInitParameter(MANAGED_ATTR_INIT_PARAM)))
310 _context.setAttribute(filterConfig.getFilterName(), this);
311 }
312
313 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
314 {
315 doFilter((HttpServletRequest)request, (HttpServletResponse)response, filterChain);
316 }
317
318 protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException
319 {
320 if (!isEnabled())
321 {
322 filterChain.doFilter(request, response);
323 return;
324 }
325
326 final long now = _requestTimeoutQ.getNow();
327
328
329 RateTracker tracker = (RateTracker)request.getAttribute(__TRACKER);
330
331 if (tracker == null)
332 {
333
334
335
336 tracker = getRateTracker(request);
337
338
339 final boolean overRateLimit = tracker.isRateExceeded(now);
340
341
342 if (!overRateLimit)
343 {
344 doFilterChain(filterChain, request, response);
345 return;
346 }
347
348
349 LOG.warn("DOS ALERT: ip=" + request.getRemoteAddr() + ",session=" + request.getRequestedSessionId() + ",user=" + request.getUserPrincipal());
350
351
352 long delayMs = getDelayMs();
353 boolean insertHeaders = isInsertHeaders();
354 switch ((int)delayMs)
355 {
356 case -1:
357 {
358
359 if (insertHeaders)
360 response.addHeader("DoSFilter", "unavailable");
361 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
362 return;
363 }
364 case 0:
365 {
366
367 request.setAttribute(__TRACKER, tracker);
368 break;
369 }
370 default:
371 {
372
373 if (insertHeaders)
374 response.addHeader("DoSFilter", "delayed");
375 Continuation continuation = ContinuationSupport.getContinuation(request);
376 request.setAttribute(__TRACKER, tracker);
377 if (delayMs > 0)
378 continuation.setTimeout(delayMs);
379 continuation.suspend();
380 return;
381 }
382 }
383 }
384
385
386 boolean accepted = false;
387 try
388 {
389
390 accepted = _passes.tryAcquire(getMaxWaitMs(), TimeUnit.MILLISECONDS);
391
392 if (!accepted)
393 {
394
395 final Continuation continuation = ContinuationSupport.getContinuation(request);
396
397 Boolean throttled = (Boolean)request.getAttribute(__THROTTLED);
398 long throttleMs = getThrottleMs();
399 if (throttled != Boolean.TRUE && throttleMs > 0)
400 {
401 int priority = getPriority(request, tracker);
402 request.setAttribute(__THROTTLED, Boolean.TRUE);
403 if (isInsertHeaders())
404 response.addHeader("DoSFilter", "throttled");
405 if (throttleMs > 0)
406 continuation.setTimeout(throttleMs);
407 continuation.suspend();
408
409 continuation.addContinuationListener(_listeners[priority]);
410 _queue[priority].add(continuation);
411 return;
412 }
413
414 else if (request.getAttribute("javax.servlet.resumed") == Boolean.TRUE)
415 {
416
417 _passes.acquire();
418 accepted = true;
419 }
420 }
421
422
423 if (accepted)
424
425 doFilterChain(filterChain, request, response);
426 else
427 {
428
429 if (isInsertHeaders())
430 response.addHeader("DoSFilter", "unavailable");
431 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
432 }
433 }
434 catch (InterruptedException e)
435 {
436 _context.log("DoS", e);
437 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
438 }
439 finally
440 {
441 if (accepted)
442 {
443
444 for (int p = _queue.length; p-- > 0; )
445 {
446 Continuation continuation = _queue[p].poll();
447 if (continuation != null && continuation.isSuspended())
448 {
449 continuation.resume();
450 break;
451 }
452 }
453 _passes.release();
454 }
455 }
456 }
457
458 protected void doFilterChain(FilterChain chain, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException
459 {
460 final Thread thread = Thread.currentThread();
461
462 final Timeout.Task requestTimeout = new Timeout.Task()
463 {
464 public void expired()
465 {
466 closeConnection(request, response, thread);
467 }
468 };
469
470 try
471 {
472 _requestTimeoutQ.schedule(requestTimeout);
473 chain.doFilter(request, response);
474 }
475 finally
476 {
477 requestTimeout.cancel();
478 }
479 }
480
481
482
483
484
485
486
487
488
489 protected void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread)
490 {
491
492 if (!response.isCommitted())
493 {
494 response.setHeader("Connection", "close");
495 }
496 try
497 {
498 try
499 {
500 response.getWriter().close();
501 }
502 catch (IllegalStateException e)
503 {
504 response.getOutputStream().close();
505 }
506 }
507 catch (IOException e)
508 {
509 LOG.warn(e);
510 }
511
512
513 thread.interrupt();
514 }
515
516
517
518
519
520
521
522
523 protected int getPriority(HttpServletRequest request, RateTracker tracker)
524 {
525 if (extractUserId(request) != null)
526 return USER_AUTH;
527 if (tracker != null)
528 return tracker.getType();
529 return USER_UNKNOWN;
530 }
531
532
533
534
535 protected int getMaxPriority()
536 {
537 return USER_AUTH;
538 }
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556 public RateTracker getRateTracker(ServletRequest request)
557 {
558 HttpSession session = ((HttpServletRequest)request).getSession(false);
559
560 String loadId = extractUserId(request);
561 final int type;
562 if (loadId != null)
563 {
564 type = USER_AUTH;
565 }
566 else
567 {
568 if (_trackSessions && session != null && !session.isNew())
569 {
570 loadId = session.getId();
571 type = USER_SESSION;
572 }
573 else
574 {
575 loadId = _remotePort ? (request.getRemoteAddr() + request.getRemotePort()) : request.getRemoteAddr();
576 type = USER_IP;
577 }
578 }
579
580 RateTracker tracker = _rateTrackers.get(loadId);
581
582 if (tracker == null)
583 {
584 boolean allowed = checkWhitelist(_whitelist, request.getRemoteAddr());
585 tracker = allowed ? new FixedRateTracker(loadId, type, _maxRequestsPerSec)
586 : new RateTracker(loadId, type, _maxRequestsPerSec);
587 RateTracker existing = _rateTrackers.putIfAbsent(loadId, tracker);
588 if (existing != null)
589 tracker = existing;
590
591 if (type == USER_IP)
592 {
593
594 _trackerTimeoutQ.schedule(tracker);
595 }
596 else if (session != null)
597 {
598
599 session.setAttribute(__TRACKER, tracker);
600 }
601 }
602
603 return tracker;
604 }
605
606 protected boolean checkWhitelist(List<String> whitelist, String candidate)
607 {
608 for (String address : whitelist)
609 {
610 if (address.contains("/"))
611 {
612 if (subnetMatch(address, candidate))
613 return true;
614 }
615 else
616 {
617 if (address.equals(candidate))
618 return true;
619 }
620 }
621 return false;
622 }
623
624 protected boolean subnetMatch(String subnetAddress, String address)
625 {
626 Matcher cidrMatcher = CIDR_PATTERN.matcher(subnetAddress);
627 if (!cidrMatcher.matches())
628 return false;
629
630 String subnet = cidrMatcher.group(1);
631 int prefix;
632 try
633 {
634 prefix = Integer.parseInt(cidrMatcher.group(2));
635 }
636 catch (NumberFormatException x)
637 {
638 LOG.info("Ignoring malformed CIDR address {}", subnetAddress);
639 return false;
640 }
641
642 byte[] subnetBytes = addressToBytes(subnet);
643 if (subnetBytes == null)
644 {
645 LOG.info("Ignoring malformed CIDR address {}", subnetAddress);
646 return false;
647 }
648 byte[] addressBytes = addressToBytes(address);
649 if (addressBytes == null)
650 {
651 LOG.info("Ignoring malformed remote address {}", address);
652 return false;
653 }
654
655
656 int length = subnetBytes.length;
657 if (length != addressBytes.length)
658 return false;
659
660 byte[] mask = prefixToBytes(prefix, length);
661
662 for (int i = 0; i < length; ++i)
663 {
664 if ((subnetBytes[i] & mask[i]) != (addressBytes[i] & mask[i]))
665 return false;
666 }
667
668 return true;
669 }
670
671 private byte[] addressToBytes(String address)
672 {
673 Matcher ipv4Matcher = IPv4_PATTERN.matcher(address);
674 if (ipv4Matcher.matches())
675 {
676 byte[] result = new byte[4];
677 for (int i = 0; i < result.length; ++i)
678 result[i] = Integer.valueOf(ipv4Matcher.group(i + 1)).byteValue();
679 return result;
680 }
681 else
682 {
683 Matcher ipv6Matcher = IPv6_PATTERN.matcher(address);
684 if (ipv6Matcher.matches())
685 {
686 byte[] result = new byte[16];
687 for (int i = 0; i < result.length; i += 2)
688 {
689 int word = Integer.valueOf(ipv6Matcher.group(i / 2 + 1), 16);
690 result[i] = (byte)((word & 0xFF00) >>> 8);
691 result[i + 1] = (byte)(word & 0xFF);
692 }
693 return result;
694 }
695 }
696 return null;
697 }
698
699 private byte[] prefixToBytes(int prefix, int length)
700 {
701 byte[] result = new byte[length];
702 int index = 0;
703 while (prefix / 8 > 0)
704 {
705 result[index] = -1;
706 prefix -= 8;
707 ++index;
708 }
709
710 result[index] = (byte)~((1 << (8 - prefix)) - 1);
711 return result;
712 }
713
714 public void destroy()
715 {
716 _running = false;
717 _timerThread.interrupt();
718 _requestTimeoutQ.cancelAll();
719 _trackerTimeoutQ.cancelAll();
720 _rateTrackers.clear();
721 _whitelist.clear();
722 }
723
724
725
726
727
728
729
730
731 protected String extractUserId(ServletRequest request)
732 {
733 return null;
734 }
735
736
737
738
739
740
741
742
743 public int getMaxRequestsPerSec()
744 {
745 return _maxRequestsPerSec;
746 }
747
748
749
750
751
752
753
754
755 public void setMaxRequestsPerSec(int value)
756 {
757 _maxRequestsPerSec = value;
758 }
759
760
761
762
763
764 public long getDelayMs()
765 {
766 return _delayMs;
767 }
768
769
770
771
772
773
774
775 public void setDelayMs(long value)
776 {
777 _delayMs = value;
778 }
779
780
781
782
783
784
785
786 public long getMaxWaitMs()
787 {
788 return _maxWaitMs;
789 }
790
791
792
793
794
795
796
797 public void setMaxWaitMs(long value)
798 {
799 _maxWaitMs = value;
800 }
801
802
803
804
805
806
807
808 public int getThrottledRequests()
809 {
810 return _throttledRequests;
811 }
812
813
814
815
816
817
818
819 public void setThrottledRequests(int value)
820 {
821 int permits = _passes == null ? 0 : _passes.availablePermits();
822 _passes = new Semaphore((value - _throttledRequests + permits), true);
823 _throttledRequests = value;
824 }
825
826
827
828
829
830
831 public long getThrottleMs()
832 {
833 return _throttleMs;
834 }
835
836
837
838
839
840
841 public void setThrottleMs(long value)
842 {
843 _throttleMs = value;
844 }
845
846
847
848
849
850
851
852 public long getMaxRequestMs()
853 {
854 return _maxRequestMs;
855 }
856
857
858
859
860
861
862
863 public void setMaxRequestMs(long value)
864 {
865 _maxRequestMs = value;
866 }
867
868
869
870
871
872
873
874
875 public long getMaxIdleTrackerMs()
876 {
877 return _maxIdleTrackerMs;
878 }
879
880
881
882
883
884
885
886
887 public void setMaxIdleTrackerMs(long value)
888 {
889 _maxIdleTrackerMs = value;
890 }
891
892
893
894
895
896
897 public boolean isInsertHeaders()
898 {
899 return _insertHeaders;
900 }
901
902
903
904
905
906
907 public void setInsertHeaders(boolean value)
908 {
909 _insertHeaders = value;
910 }
911
912
913
914
915
916
917 public boolean isTrackSessions()
918 {
919 return _trackSessions;
920 }
921
922
923
924
925
926
927 public void setTrackSessions(boolean value)
928 {
929 _trackSessions = value;
930 }
931
932
933
934
935
936
937
938 public boolean isRemotePort()
939 {
940 return _remotePort;
941 }
942
943
944
945
946
947
948
949 public void setRemotePort(boolean value)
950 {
951 _remotePort = value;
952 }
953
954
955
956
957 public boolean isEnabled()
958 {
959 return _enabled;
960 }
961
962
963
964
965 public void setEnabled(boolean enabled)
966 {
967 _enabled = enabled;
968 }
969
970
971
972
973
974
975 public String getWhitelist()
976 {
977 StringBuilder result = new StringBuilder();
978 for (Iterator<String> iterator = _whitelist.iterator(); iterator.hasNext();)
979 {
980 String address = iterator.next();
981 result.append(address);
982 if (iterator.hasNext())
983 result.append(",");
984 }
985 return result.toString();
986 }
987
988
989
990
991
992
993 public void setWhitelist(String value)
994 {
995 List<String> result = new ArrayList<String>();
996 for (String address : value.split(","))
997 addWhitelistAddress(result, address);
998 _whitelist.clear();
999 _whitelist.addAll(result);
1000 LOG.debug("Whitelisted IP addresses: {}", result);
1001 }
1002
1003 public void clearWhitelist()
1004 {
1005 _whitelist.clear();
1006 }
1007
1008 public boolean addWhitelistAddress(String address)
1009 {
1010 return addWhitelistAddress(_whitelist, address);
1011 }
1012
1013 private boolean addWhitelistAddress(List<String> list, String address)
1014 {
1015 address = address.trim();
1016 return address.length() > 0 && list.add(address);
1017 }
1018
1019 public boolean removeWhitelistAddress(String address)
1020 {
1021 return _whitelist.remove(address);
1022 }
1023
1024
1025
1026
1027
1028 class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener
1029 {
1030 transient protected final String _id;
1031 transient protected final int _type;
1032 transient protected final long[] _timestamps;
1033 transient protected int _next;
1034
1035 public RateTracker(String id, int type, int maxRequestsPerSecond)
1036 {
1037 _id = id;
1038 _type = type;
1039 _timestamps = new long[maxRequestsPerSecond];
1040 _next = 0;
1041 }
1042
1043
1044
1045
1046 public boolean isRateExceeded(long now)
1047 {
1048 final long last;
1049 synchronized (this)
1050 {
1051 last = _timestamps[_next];
1052 _timestamps[_next] = now;
1053 _next = (_next + 1) % _timestamps.length;
1054 }
1055
1056 return last != 0 && (now - last) < 1000L;
1057 }
1058
1059 public String getId()
1060 {
1061 return _id;
1062 }
1063
1064 public int getType()
1065 {
1066 return _type;
1067 }
1068
1069 public void valueBound(HttpSessionBindingEvent event)
1070 {
1071 if (LOG.isDebugEnabled())
1072 LOG.debug("Value bound: {}", getId());
1073 }
1074
1075 public void valueUnbound(HttpSessionBindingEvent event)
1076 {
1077
1078 _rateTrackers.remove(_id);
1079 if (LOG.isDebugEnabled())
1080 LOG.debug("Tracker removed: {}", getId());
1081 }
1082
1083 public void sessionWillPassivate(HttpSessionEvent se)
1084 {
1085
1086
1087 _rateTrackers.remove(_id);
1088 se.getSession().removeAttribute(__TRACKER);
1089 if (LOG.isDebugEnabled()) LOG.debug("Value removed: {}", getId());
1090 }
1091
1092 public void sessionDidActivate(HttpSessionEvent se)
1093 {
1094 LOG.warn("Unexpected session activation");
1095 }
1096
1097 public void expired()
1098 {
1099 long now = _trackerTimeoutQ.getNow();
1100 int latestIndex = _next == 0 ? (_timestamps.length - 1) : (_next - 1);
1101 long last = _timestamps[latestIndex];
1102 boolean hasRecentRequest = last != 0 && (now - last) < 1000L;
1103
1104 if (hasRecentRequest)
1105 reschedule();
1106 else
1107 _rateTrackers.remove(_id);
1108 }
1109
1110 @Override
1111 public String toString()
1112 {
1113 return "RateTracker/" + _id + "/" + _type;
1114 }
1115 }
1116
1117 class FixedRateTracker extends RateTracker
1118 {
1119 public FixedRateTracker(String id, int type, int numRecentRequestsTracked)
1120 {
1121 super(id, type, numRecentRequestsTracked);
1122 }
1123
1124 @Override
1125 public boolean isRateExceeded(long now)
1126 {
1127
1128
1129
1130 synchronized (this)
1131 {
1132 _timestamps[_next] = now;
1133 _next = (_next + 1) % _timestamps.length;
1134 }
1135
1136 return false;
1137 }
1138
1139 @Override
1140 public String toString()
1141 {
1142 return "Fixed" + super.toString();
1143 }
1144 }
1145 }