001 /*
002 * Copyright 2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2016 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 */
021 package com.unboundid.ldap.listener;
022
023
024
025 import java.util.List;
026
027 import com.unboundid.ldap.protocol.AbandonRequestProtocolOp;
028 import com.unboundid.ldap.protocol.AddRequestProtocolOp;
029 import com.unboundid.ldap.protocol.BindRequestProtocolOp;
030 import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
031 import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
032 import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
033 import com.unboundid.ldap.protocol.LDAPMessage;
034 import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
035 import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
036 import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
037 import com.unboundid.ldap.sdk.Control;
038 import com.unboundid.ldap.sdk.LDAPException;
039 import com.unboundid.util.FixedRateBarrier;
040 import com.unboundid.util.NotMutable;
041 import com.unboundid.util.ThreadSafety;
042 import com.unboundid.util.ThreadSafetyLevel;
043 import 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)
055 public 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 }