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}