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}