Introduction
Here's something that I'd like to share with you. I was developing a website and wanted to limit the number of operations per second that a client is allowed to post. For example, if a visitor posts a comment within 10 seconds of his previous post, it's very unlikely that the post contributed by the person is something useful. Most probably, that post was somehow automated.
Solution
The class that I have written is actually very simple to use. For example if you have an event handler that implements the post of a comment, your code may look something like this:
private void Submit_click(object sender, EventArgs args)
{
if(Page.IsValid)
{
}
}
Now, to add our flood gate, change the code so that it looks something like this:
private static FloodGate _submitFlooding;
private void Submit_click(object sender, EventArgs args)
{
if(Page.IsValid)
{
if(_submitFlooding == null)
{
_submitFlooding = new FloodGate("Post Comment",
TimeSpan.FromSeconds(10))
}
_submitFlooding.Assert();
}
}
First, I declare a static reference to a FloodGate
class. This makes sure that my floodgate is kept in memory across all the requests that are made to this application.
When somebody clicks on the Submit button, an instance of a FloodGate
is assigned. It takes two parameters: the name of the gate, and the minimum delay between each operation.
Whenever somebody (with a certain client IP address) clicks the button within 10 seconds, a FloodGateException
is thrown, thereby effectively aborting the operation.
To make it more user friendly, you can catch the exception and show a message box.
The code
It works as follows:
- The class maintains a
Hashtable
of client IP's.
- Whenever the
Assert()
method is called, a check is performed to see if the current client IP (by using HttpContext.Current.Request.UserHostAddress
) is already on the list.
- If it is, the
Hashtable
entries contain the DateTime
of the last operation. If that date is no longer than n seconds apart (configured by the constructor), a FloodGateException
is thrown.
- If it isn't (or sufficient time has elapsed), the
Hashtable
is updated.
If you're interested in seeing the entire code, it is available for download at the top of the page.
The cool thing about this solution is that it is easily reusable in both code-behind and business classes, and that you can declare multiple floodgates for different operations. One of the downsides is that a reference to System.Web
is required.
Improvements
The first thing that comes to mind: Clean up IP addresses that have dates older than the minimum waiting time. This will save precious memory.