DoSTracker.java (zxing-zxing-3.4.0) | : | DoSTracker.java (zxing-zxing-3.4.1) | ||
---|---|---|---|---|
skipping to change at line 19 | skipping to change at line 19 | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
package com.google.zxing.web; | package com.google.zxing.web; | |||
import java.lang.management.ManagementFactory; | ||||
import java.lang.management.OperatingSystemMXBean; | ||||
import java.util.Iterator; | import java.util.Iterator; | |||
import java.util.Map; | import java.util.Map; | |||
import java.util.Timer; | import java.util.Timer; | |||
import java.util.TimerTask; | import java.util.TimerTask; | |||
import java.util.concurrent.atomic.AtomicLong; | import java.util.concurrent.atomic.AtomicInteger; | |||
import java.util.logging.Logger; | import java.util.logging.Logger; | |||
/** | /** | |||
* Simple class which tracks a number of actions that happen per time and can fl ag when an action has | * Simple class which tracks a number of actions that happen per time and can fl ag when an action has | |||
* happened too frequently recently. This can be used for example to track and t emporarily block access | * happened too frequently recently. This can be used for example to track and t emporarily block access | |||
* from certain IPs or to certain hosts. | * from certain IPs or to certain hosts. | |||
*/ | */ | |||
final class DoSTracker { | final class DoSTracker { | |||
private static final Logger log = Logger.getLogger(DoSTracker.class.getName()) ; | private static final Logger log = Logger.getLogger(DoSTracker.class.getName()) ; | |||
private final long maxAccessesPerTime; | private volatile int maxAccessesPerTime; | |||
private final Map<String,AtomicLong> numRecentAccesses; | private final Map<String,AtomicInteger> numRecentAccesses; | |||
DoSTracker(Timer timer, final String name, final int maxAccessesPerTime, long | /** | |||
accessTimeMS, int maxEntries) { | * @param timer {@link Timer} to use for scheduling update tasks | |||
* @param name identifier for this tracker | ||||
* @param maxAccessesPerTime maximum number of accesses allowed from one sourc | ||||
e per {@code accessTimeMS} | ||||
* @param accessTimeMS interval in milliseconds over which up to {@code maxAcc | ||||
essesPerTime} accesses are allowed | ||||
* @param maxEntries maximum number of source entries to track before forgetti | ||||
ng least recent ones | ||||
* @param maxLoad if set, dynamically adjust {@code maxAccessesPerTime} downwa | ||||
rds when average load per core | ||||
* exceeds this value, and upwards when below this value | ||||
*/ | ||||
DoSTracker(Timer timer, | ||||
final String name, | ||||
final int maxAccessesPerTime, | ||||
long accessTimeMS, | ||||
int maxEntries, | ||||
Double maxLoad) { | ||||
this.maxAccessesPerTime = maxAccessesPerTime; | this.maxAccessesPerTime = maxAccessesPerTime; | |||
this.numRecentAccesses = new LRUMap<>(maxEntries); | this.numRecentAccesses = new LRUMap<>(maxEntries); | |||
timer.schedule(new TimerTask() { | timer.schedule(new TrackerTask(name, maxLoad), accessTimeMS, accessTimeMS); | |||
@Override | ||||
public void run() { | ||||
synchronized (numRecentAccesses) { | ||||
Iterator<Map.Entry<String,AtomicLong>> accessIt = numRecentAccesses.en | ||||
trySet().iterator(); | ||||
while (accessIt.hasNext()) { | ||||
Map.Entry<String,AtomicLong> entry = accessIt.next(); | ||||
AtomicLong count = entry.getValue(); | ||||
// If number of accesses is below the threshold, remove it entirely | ||||
if (count.get() <= maxAccessesPerTime) { | ||||
accessIt.remove(); | ||||
} else { | ||||
// Else it exceeded the max, so log it (again) | ||||
log.warning(name + ": Blocking " + entry.getKey() + " (" + count + | ||||
" outstanding)"); | ||||
// Reduce count of accesses held against the host | ||||
count.getAndAdd(-maxAccessesPerTime); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
}, accessTimeMS, accessTimeMS); | ||||
} | } | |||
boolean isBanned(String event) { | boolean isBanned(String event) { | |||
if (event == null) { | if (event == null) { | |||
return true; | return true; | |||
} | } | |||
AtomicLong count; | AtomicInteger count; | |||
synchronized (numRecentAccesses) { | synchronized (numRecentAccesses) { | |||
count = numRecentAccesses.get(event); | count = numRecentAccesses.get(event); | |||
if (count == null) { | if (count == null) { | |||
numRecentAccesses.put(event, new AtomicLong(1)); | numRecentAccesses.put(event, new AtomicInteger(1)); | |||
return false; | return false; | |||
} | } | |||
} | } | |||
return count.incrementAndGet() > maxAccessesPerTime; | return count.incrementAndGet() > maxAccessesPerTime; | |||
} | } | |||
private final class TrackerTask extends TimerTask { | ||||
private final String name; | ||||
private final Double maxLoad; | ||||
private TrackerTask(String name, Double maxLoad) { | ||||
this.name = name; | ||||
this.maxLoad = maxLoad; | ||||
} | ||||
@Override | ||||
public void run() { | ||||
// largest count <= maxAccessesPerTime | ||||
int maxAllowedCount = 1; | ||||
// smallest count > maxAccessesPerTime | ||||
int minDisallowedCount = Integer.MAX_VALUE; | ||||
int localMAPT = maxAccessesPerTime; | ||||
synchronized (numRecentAccesses) { | ||||
Iterator<Map.Entry<String,AtomicInteger>> accessIt = numRecentAccesses.e | ||||
ntrySet().iterator(); | ||||
while (accessIt.hasNext()) { | ||||
Map.Entry<String,AtomicInteger> entry = accessIt.next(); | ||||
AtomicInteger atomicCount = entry.getValue(); | ||||
int count = atomicCount.get(); | ||||
// If number of accesses is below the threshold, remove it entirely | ||||
if (count <= localMAPT) { | ||||
accessIt.remove(); | ||||
maxAllowedCount = Math.max(maxAllowedCount, count); | ||||
} else { | ||||
// Else it exceeded the max, so log it (again) | ||||
log.warning(name + ": Blocking " + entry.getKey() + " (" + count + " | ||||
outstanding)"); | ||||
// Reduce count of accesses held against the host | ||||
atomicCount.getAndAdd(-localMAPT); | ||||
minDisallowedCount = Math.min(minDisallowedCount, count); | ||||
} | ||||
} | ||||
} | ||||
if (maxLoad != null) { | ||||
OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBea | ||||
n(); | ||||
if (mxBean == null) { | ||||
log.warning("Could not obtain OperatingSystemMXBean; ignoring load"); | ||||
} else { | ||||
double loadAvg = mxBean.getSystemLoadAverage(); | ||||
if (loadAvg >= 0.0) { | ||||
int cores = mxBean.getAvailableProcessors(); | ||||
double loadRatio = loadAvg / cores; | ||||
log.info(name + ": Load ratio: " + loadRatio + " (" + loadAvg + '/' | ||||
+ cores + ") vs " + maxLoad); | ||||
maxAccessesPerTime = loadRatio > maxLoad ? | ||||
Math.min(maxAllowedCount, maxAccessesPerTime) : | ||||
Math.max(minDisallowedCount, maxAccessesPerTime); | ||||
log.info(name + ": New maxAccessesPerTime: " + maxAccessesPerTime); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
} | ||||
} | } | |||
End of changes. 8 change blocks. | ||||
31 lines changed or deleted | 88 lines changed or added |