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