001/*
002 * Copyright 2016-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-2017 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.listener;
022
023
024
025import java.util.List;
026
027import com.unboundid.ldap.protocol.AbandonRequestProtocolOp;
028import com.unboundid.ldap.protocol.AddRequestProtocolOp;
029import com.unboundid.ldap.protocol.BindRequestProtocolOp;
030import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
031import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
032import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
033import com.unboundid.ldap.protocol.LDAPMessage;
034import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
035import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
036import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
037import com.unboundid.ldap.sdk.Control;
038import com.unboundid.ldap.sdk.LDAPException;
039import com.unboundid.util.FixedRateBarrier;
040import com.unboundid.util.NotMutable;
041import com.unboundid.util.ThreadSafety;
042import com.unboundid.util.ThreadSafetyLevel;
043import com.unboundid.util.Validator;
044
045
046
047/**
048 * This class provides an implementation of an LDAP listener request handler
049 * that can be used to apply rate limiting to client requests.  It uses one or
050 * more {@link FixedRateBarrier} instances to enforce the rate limiting, and
051 * provides the ability to control rate limiting on a per-operation-type basis.
052 */
053@NotMutable()
054@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
055public final class RateLimiterRequestHandler
056       extends LDAPListenerRequestHandler
057{
058  // The rate limiters that will be used for each type of operation.
059  private final FixedRateBarrier abandonRateLimiter;
060  private final FixedRateBarrier addRateLimiter;
061  private final FixedRateBarrier bindRateLimiter;
062  private final FixedRateBarrier compareRateLimiter;
063  private final FixedRateBarrier deleteRateLimiter;
064  private final FixedRateBarrier extendedRateLimiter;
065  private final FixedRateBarrier modifyRateLimiter;
066  private final FixedRateBarrier modifyDNRateLimiter;
067  private final FixedRateBarrier searchRateLimiter;
068
069  // The downstream request handler that will be used to process the requests
070  // after any appropriate rate limiting has been performed.
071  private final LDAPListenerRequestHandler downstreamRequestHandler;
072
073
074
075  /**
076   * Creates a new rate limiter request handler that will limit the rate of
077   * operations to the specified maximum number per second.  The rate limiting
078   * will be enforced for all types of operations except abandon and unbind.
079   * No rate limiting will be enforced for abandon or unbind operations.
080   *
081   * @param  downstreamRequestHandler  The downstream request handler that will
082   *                                   be used to actually process the requests
083   *                                   after any appropriate rate limiting has
084   *                                   been performed.  It must not be
085   *                                   {@code null}.
086   * @param  maxPerSecond              The maximum number of operations that
087   *                                   will be allowed per second, across all
088   *                                   types of operations except abandon and
089   *                                   unbind.  It must be greater than zero.
090   */
091  public RateLimiterRequestHandler(
092              final LDAPListenerRequestHandler downstreamRequestHandler,
093              final int maxPerSecond)
094  {
095    Validator.ensureNotNull(downstreamRequestHandler);
096    Validator.ensureTrue(maxPerSecond > 0);
097
098    this.downstreamRequestHandler = downstreamRequestHandler;
099
100    final FixedRateBarrier rateLimiter =
101         new FixedRateBarrier(1000L, maxPerSecond);
102
103    abandonRateLimiter  = null;
104    addRateLimiter      = rateLimiter;
105    bindRateLimiter     = rateLimiter;
106    compareRateLimiter  = rateLimiter;
107    deleteRateLimiter   = rateLimiter;
108    extendedRateLimiter = rateLimiter;
109    modifyRateLimiter   = rateLimiter;
110    modifyDNRateLimiter = rateLimiter;
111    searchRateLimiter   = rateLimiter;
112  }
113
114
115
116  /**
117   * Creates a new rate limiter request handler that will use the provided
118   * {@link FixedRateBarrier} to perform rate limiting for all types of
119   * operations except abandon and unbind.  No rate limiting will be enforced
120   * for abandon or unbind operations.
121   *
122   * @param  downstreamRequestHandler  The downstream request handler that will
123   *                                   be used to actually process the requests
124   *                                   after any appropriate rate limiting has
125   *                                   been performed.  It must not be
126   *                                   {@code null}.
127   * @param  rateLimiter               The fixed-rate barrier that will be used
128   *                                   to achieve the rate limiting for all
129   *                                   types of operations except abandon and
130   *                                   unbind.  It may be {@code null} if no
131   *                                   rate limiting should be performed for any
132   *                                   operation types.
133   */
134  public RateLimiterRequestHandler(
135              final LDAPListenerRequestHandler downstreamRequestHandler,
136              final FixedRateBarrier rateLimiter)
137  {
138    this(downstreamRequestHandler, null, rateLimiter, rateLimiter, rateLimiter,
139         rateLimiter, rateLimiter, rateLimiter, rateLimiter, rateLimiter);
140  }
141
142
143
144  /**
145   * Creates a new rate limiter request handler that can use the provided
146   * {@link FixedRateBarrier} instances to perform rate limiting for different
147   * types of operations.  The same barrier instance can be provided for
148   * multiple operation types if performance for those operations should be
149   * limited in aggregate rather than individually (e.g., if you don't want the
150   * total combined rate of search and modify operations to exceed a given
151   * threshold, then you could provide the same barrier instance for the
152   * {@code modifyRateLimiter} and {@code searchRateLimiter} arguments).
153   *
154   * @param  downstreamRequestHandler  The downstream request handler that will
155   *                                   be used to actually process the requests
156   *                                   after any appropriate rate limiting has
157   *                                   been performed.  It must not be
158   *                                   {@code null}.
159   * @param  abandonRateLimiter        The fixed-rate barrier to use when
160   *                                   processing abandon operations.  It may be
161   *                                   {@code null} if no rate limiting should
162   *                                   be enforced for abandon operations.
163   * @param  addRateLimiter            The fixed-rate barrier to use when
164   *                                   processing add operations.  It may be
165   *                                   {@code null} if no rate limiting should
166   *                                   be enforced for add operations.
167   * @param  bindRateLimiter           The fixed-rate barrier to use when
168   *                                   processing bind operations.  It may be
169   *                                   {@code null} if no rate limiting should
170   *                                   be enforced for bind operations.
171   * @param  compareRateLimiter        The fixed-rate barrier to use when
172   *                                   processing compare operations.  It may be
173   *                                   {@code null} if no rate limiting should
174   *                                   be enforced for compare operations.
175   * @param  deleteRateLimiter         The fixed-rate barrier to use when
176   *                                   processing delete operations.  It may be
177   *                                   {@code null} if no rate limiting should
178   *                                   be enforced for delete operations.
179   * @param  extendedRateLimiter       The fixed-rate barrier to use when
180   *                                   processing extended operations.  It may
181   *                                   be {@code null} if no rate limiting
182   *                                   should be enforced for extended
183   *                                   operations.
184   * @param  modifyRateLimiter         The fixed-rate barrier to use when
185   *                                   processing modify operations.  It may be
186   *                                   {@code null} if no rate limiting should
187   *                                   be enforced for modify operations.
188   * @param  modifyDNRateLimiter       The fixed-rate barrier to use when
189   *                                   processing modify DN operations.  It may
190   *                                   be {@code null} if no rate limiting
191   *                                   should be enforced for modify DN
192   *                                   operations.
193   * @param  searchRateLimiter         The fixed-rate barrier to use when
194   *                                   processing search operations.  It may be
195   *                                   {@code null} if no rate limiting should
196   *                                   be enforced for search operations.
197   */
198  public RateLimiterRequestHandler(
199              final LDAPListenerRequestHandler downstreamRequestHandler,
200              final FixedRateBarrier abandonRateLimiter,
201              final FixedRateBarrier addRateLimiter,
202              final FixedRateBarrier bindRateLimiter,
203              final FixedRateBarrier compareRateLimiter,
204              final FixedRateBarrier deleteRateLimiter,
205              final FixedRateBarrier extendedRateLimiter,
206              final FixedRateBarrier modifyRateLimiter,
207              final FixedRateBarrier modifyDNRateLimiter,
208              final FixedRateBarrier searchRateLimiter)
209  {
210    Validator.ensureNotNull(downstreamRequestHandler);
211
212    this.downstreamRequestHandler = downstreamRequestHandler;
213    this.abandonRateLimiter       = abandonRateLimiter;
214    this.addRateLimiter           = addRateLimiter;
215    this.bindRateLimiter          = bindRateLimiter;
216    this.compareRateLimiter       = compareRateLimiter;
217    this.deleteRateLimiter        = deleteRateLimiter;
218    this.extendedRateLimiter      = extendedRateLimiter;
219    this.modifyRateLimiter        = modifyRateLimiter;
220    this.modifyDNRateLimiter      = modifyDNRateLimiter;
221    this.searchRateLimiter        = searchRateLimiter;
222  }
223
224
225
226  /**
227   * {@inheritDoc}
228   */
229  @Override()
230  public RateLimiterRequestHandler newInstance(
231              final LDAPListenerClientConnection connection)
232         throws LDAPException
233  {
234    return new RateLimiterRequestHandler(
235         downstreamRequestHandler.newInstance(connection), abandonRateLimiter,
236         addRateLimiter, bindRateLimiter, compareRateLimiter, deleteRateLimiter,
237         extendedRateLimiter, modifyRateLimiter, modifyDNRateLimiter,
238         searchRateLimiter);
239  }
240
241
242
243  /**
244   * {@inheritDoc}
245   */
246  @Override()
247  public void processAbandonRequest(final int messageID,
248                                    final AbandonRequestProtocolOp request,
249                                    final List<Control> controls)
250  {
251    if (abandonRateLimiter != null)
252    {
253      abandonRateLimiter.await();
254    }
255
256    downstreamRequestHandler.processAbandonRequest(messageID, request,
257         controls);
258  }
259
260
261
262  /**
263   * {@inheritDoc}
264   */
265  @Override()
266  public LDAPMessage processAddRequest(final int messageID,
267                                       final AddRequestProtocolOp request,
268                                       final List<Control> controls)
269  {
270    if (addRateLimiter != null)
271    {
272      addRateLimiter.await();
273    }
274
275    return downstreamRequestHandler.processAddRequest(messageID, request,
276         controls);
277  }
278
279
280
281  /**
282   * {@inheritDoc}
283   */
284  @Override()
285  public LDAPMessage processBindRequest(final int messageID,
286                                        final BindRequestProtocolOp request,
287                                        final List<Control> controls)
288  {
289    if (bindRateLimiter != null)
290    {
291      bindRateLimiter.await();
292    }
293
294    return downstreamRequestHandler.processBindRequest(messageID, request,
295         controls);
296  }
297
298
299
300  /**
301   * {@inheritDoc}
302   */
303  @Override()
304  public LDAPMessage processCompareRequest(final int messageID,
305                          final CompareRequestProtocolOp request,
306                          final List<Control> controls)
307  {
308    if (compareRateLimiter != null)
309    {
310      compareRateLimiter.await();
311    }
312
313    return downstreamRequestHandler.processCompareRequest(messageID, request,
314         controls);
315  }
316
317
318
319  /**
320   * {@inheritDoc}
321   */
322  @Override()
323  public LDAPMessage processDeleteRequest(final int messageID,
324                                          final DeleteRequestProtocolOp request,
325                                          final List<Control> controls)
326  {
327    if (deleteRateLimiter != null)
328    {
329      deleteRateLimiter.await();
330    }
331
332    return downstreamRequestHandler.processDeleteRequest(messageID, request,
333         controls);
334  }
335
336
337
338  /**
339   * {@inheritDoc}
340   */
341  @Override()
342  public LDAPMessage processExtendedRequest(final int messageID,
343                          final ExtendedRequestProtocolOp request,
344                          final List<Control> controls)
345  {
346    if (extendedRateLimiter != null)
347    {
348      extendedRateLimiter.await();
349    }
350
351    return downstreamRequestHandler.processExtendedRequest(messageID, request,
352         controls);
353  }
354
355
356
357  /**
358   * {@inheritDoc}
359   */
360  @Override()
361  public LDAPMessage processModifyRequest(final int messageID,
362                                          final ModifyRequestProtocolOp request,
363                                          final List<Control> controls)
364  {
365    if (modifyRateLimiter != null)
366    {
367      modifyRateLimiter.await();
368    }
369
370    return downstreamRequestHandler.processModifyRequest(messageID, request,
371         controls);
372  }
373
374
375
376  /**
377   * {@inheritDoc}
378   */
379  @Override()
380  public LDAPMessage processModifyDNRequest(final int messageID,
381                          final ModifyDNRequestProtocolOp request,
382                          final List<Control> controls)
383  {
384    if (modifyDNRateLimiter != null)
385    {
386      modifyDNRateLimiter.await();
387    }
388
389    return downstreamRequestHandler.processModifyDNRequest(messageID, request,
390         controls);
391  }
392
393
394
395  /**
396   * {@inheritDoc}
397   */
398  @Override()
399  public LDAPMessage processSearchRequest(final int messageID,
400                                          final SearchRequestProtocolOp request,
401                                          final List<Control> controls)
402  {
403    if (searchRateLimiter != null)
404    {
405      searchRateLimiter.await();
406    }
407
408    return downstreamRequestHandler.processSearchRequest(messageID, request,
409         controls);
410  }
411}