Esempio n. 1
0
  protected void doFilter(
      HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws IOException, ServletException {
    if (!isEnabled()) {
      filterChain.doFilter(request, response);
      return;
    }

    // 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.
      if (LOG.isDebugEnabled()) LOG.debug("Filtering {}", 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(System.currentTimeMillis());

      // Pass it through if  we are not currently over the rate limit.
      if (!overRateLimit) {
        if (LOG.isDebugEnabled()) LOG.debug("Allowing {}", request);
        doFilterChain(filterChain, request, response);
        return;
      }

      // We are over the limit.

      // So either reject it, delay it or throttle it.
      long delayMs = getDelayMs();
      boolean insertHeaders = isInsertHeaders();
      switch ((int) delayMs) {
        case -1:
          {
            // Reject this request.
            LOG.warn(
                "DOS ALERT: Request rejected ip={}, session={}, user={}",
                request.getRemoteAddr(),
                request.getRequestedSessionId(),
                request.getUserPrincipal());
            if (insertHeaders) response.addHeader("DoSFilter", "unavailable");
            response.sendError(getTooManyCode());
            return;
          }
        case 0:
          {
            // Fall through to throttle the request.
            LOG.warn(
                "DOS ALERT: Request throttled ip={}, session={}, user={}",
                request.getRemoteAddr(),
                request.getRequestedSessionId(),
                request.getUserPrincipal());
            request.setAttribute(__TRACKER, tracker);
            break;
          }
        default:
          {
            // Insert a delay before throttling the request,
            // using the suspend+timeout mechanism of AsyncContext.
            LOG.warn(
                "DOS ALERT: Request delayed={}ms, ip={}, session={}, user={}",
                delayMs,
                request.getRemoteAddr(),
                request.getRequestedSessionId(),
                request.getUserPrincipal());
            if (insertHeaders) response.addHeader("DoSFilter", "delayed");
            request.setAttribute(__TRACKER, tracker);
            AsyncContext asyncContext = request.startAsync();
            if (delayMs > 0) asyncContext.setTimeout(delayMs);
            asyncContext.addListener(new DoSTimeoutAsyncListener());
            return;
          }
      }
    }

    if (LOG.isDebugEnabled()) LOG.debug("Throttling {}", request);

    // Throttle the request.
    boolean accepted = false;
    try {
      // Check if we can afford to accept another request at this time.
      accepted = _passes.tryAcquire(getMaxWaitMs(), TimeUnit.MILLISECONDS);
      if (!accepted) {
        // We were not accepted, so either we suspend to wait,
        // or if we were woken up we insist or we fail.
        Boolean throttled = (Boolean) request.getAttribute(__THROTTLED);
        long throttleMs = getThrottleMs();
        if (throttled != Boolean.TRUE && throttleMs > 0) {
          int priority = getPriority(request, tracker);
          request.setAttribute(__THROTTLED, Boolean.TRUE);
          if (isInsertHeaders()) response.addHeader("DoSFilter", "throttled");
          AsyncContext asyncContext = request.startAsync();
          request.setAttribute(_suspended, Boolean.TRUE);
          if (throttleMs > 0) asyncContext.setTimeout(throttleMs);
          asyncContext.addListener(_listeners[priority]);
          _queues[priority].add(asyncContext);
          if (LOG.isDebugEnabled()) LOG.debug("Throttled {}, {}ms", request, throttleMs);
          return;
        }

        Boolean resumed = (Boolean) request.getAttribute(_resumed);
        if (resumed == Boolean.TRUE) {
          // We were resumed, we wait for the next pass.
          _passes.acquire();
          accepted = true;
        }
      }

      // If we were accepted (either immediately or after throttle)...
      if (accepted) {
        // ...call the chain.
        if (LOG.isDebugEnabled()) LOG.debug("Allowing {}", request);
        doFilterChain(filterChain, request, response);
      } else {
        // ...otherwise fail the request.
        if (LOG.isDebugEnabled()) LOG.debug("Rejecting {}", request);
        if (isInsertHeaders()) response.addHeader("DoSFilter", "unavailable");
        response.sendError(getTooManyCode());
      }
    } catch (InterruptedException e) {
      LOG.ignore(e);
      response.sendError(getTooManyCode());
    } finally {
      if (accepted) {
        try {
          // Wake up the next highest priority request.
          for (int p = _queues.length - 1; p >= 0; --p) {
            AsyncContext asyncContext = _queues[p].poll();
            if (asyncContext != null) {
              ServletRequest candidate = asyncContext.getRequest();
              Boolean suspended = (Boolean) candidate.getAttribute(_suspended);
              if (suspended == Boolean.TRUE) {
                if (LOG.isDebugEnabled()) LOG.debug("Resuming {}", request);
                candidate.setAttribute(_resumed, Boolean.TRUE);
                asyncContext.dispatch();
                break;
              }
            }
          }
        } finally {
          _passes.release();
        }
      }
    }
  }
Esempio n. 2
0
 /**
  * Get priority for this request, based on user type
  *
  * @param request the current request
  * @param tracker the rate tracker for this request
  * @return the priority for this request
  */
 protected int getPriority(HttpServletRequest request, RateTracker tracker) {
   if (extractUserId(request) != null) return USER_AUTH;
   if (tracker != null) return tracker.getType();
   return USER_UNKNOWN;
 }
Esempio n. 3
0
 public void sessionDidActivate(HttpSessionEvent se) {
   RateTracker tracker = (RateTracker) se.getSession().getAttribute(__TRACKER);
   if (tracker != null) _rateTrackers.put(tracker.getId(), tracker);
 }