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