Reputation: 256999
I want to insert a sleep (aka throttle, delay, tarpit, dwell) in an ASP.net application (imagine something like failed logon attempt escalating delay).
protected void Page_Load(object sender, EventArgs e)
{
Int32 sleepyTime = GetSleepyTime(Request);
if (sleepyTime > 0)
System.Threading.Thread.Sleep(sleepyTime);
//...Continue normal processing
}
I want all remaining processing to continue as normal; i just want the user agent to suffer.
The problem is that ASP.net uses a ThreadPool to handle requests. If i were to Sleep
for 5, 10, 30 seconds, i would be eating up a valuable limited resource.
I assume it needs to be something like:
protected void Page_Load(object sender, EventArgs e)
{
Int32 sleepyTime = GetSleepyTime(Request);
if (sleepyTime > 0)
ABetterKindOfSleep(sleepyTime);
//...Continue normal processing
}
private void ABetterKindOfSleep(int milliseconds)
{
await SleepAsync(milliseconds);
}
private async void SleepAsync(int milliseconds)
{
System.Threading.Thread.Sleep(milliseconds);
}
But never having written any async/await code, and not understanding the logic behind where the async
and await
go, or why, or even if it can be used to run async code: i don't know if it can be used to run async code.
Upvotes: 0
Views: 682
Reputation: 383
I also want to tarpit bot traffic attacking login endpoints, I'm worried that if I simply await that I will hit the max concurrent requests or run out of memory. I have yet to find a low overhead way of doing this with Windows and Asp.Net.
I like the way it is done here, where it changes the behaviour of the TCP/IP stack to shrink the window size and not ACK subsequent packets which makes the remote exponentially back off and only send a tiny amount of data.
I will likely add a few Linux VMs running HAProxy in-front to take advantage of its DDOS capabilities
Upvotes: 0
Reputation: 256999
It's easy enough.
First you create an IHttpModule
class:
class TarpitHttpModule : IHttpModule
{
}
And then you let IIS know about this module by registering it in web.config
:
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="Tarpit" type="TarpitHttpModule"/>
If you are Cassini, add it to:
<configuration>
<system.web>
<httpModules>
<add name="Tarpit" type="TarpitHttpModule"/>
Whenever an http request comes in, IIS will call your .Init
method. This is where you will register your async event handler using:
BeginRequest
event to the collection of asynchronous BeginRequest
event handlers for the current request.Code:
public void Init(HttpApplication application)
{
//This is the synchronous event handler; which we don't want
//application.BeginRequest += new EventHandler(this.Application_BeginRequest);
//EventHandlerTaskAsyncHelper requires .NET 4.5
//https://brockallen.com/2013/07/27/implementing-async-http-modules-in-asp-net-using-tpls-task-api/
// Archive: http://archive.is/Cdvle
//
//Normally you'd have to write a pair of methods:
// application.AddOnBeginRequestAsync(OnBegin, OnEnd);
//
//and then we'd have to write an OnBegin which returns IAsyncResult, and then OnEnd which takes the IAsyncResult.
//The modern way is to use Tasks, and use the IAsyncResult that a Task **is**.
//Fortunately the .NET team wrote a handy class that wraps up the boilerplate catching faults, etc,
//and created the EventHandlerTaskAsyncHelper class
var beginTaskHelper = new EventHandlerTaskAsyncHelper(BeginRequestAsync);
application.AddOnBeginRequestAsync(beginTaskHelper.BeginEventHandler, beginTaskHelper.EndEventHandler);
}
So now we have to supply the BeginRequestAsync asynchronous handler:
async Task BeginRequestAsync(object sender, EventArgs e)
{
var application = (HttpApplication)sender;
var context = application.Context;
// In reality i would use the context.Request to come up with a unique key
// for this user agent e.g.
String key = SHA256(UserHostAddress+UserAgent+AcceptTypes+UserLanguages).ToBase64();
// And use that as a cache key store information about this user agent
Object tarpitInfo = context.Cache.Get(agentIdentity);
if (ti == null)
return;
// But in this SO demo, i'm just going to unconditionally sleep
Boolean waitPerformed = await PerformDelay(context, tarpitInfo);
if (waitPerformed)
{
context.Response.StatusCode = 429;
context.Response.StatusDescription = "Too Many Requests";
context.Response.End();
return;
}
}
And then the work of sleeping:
async Task<Boolean> PerformDelay(HttpContext context, TarInfo ti)
{
int delayMs = 3000;
Task delay = Task.Delay(delayMs);
await delay;
return true;
}
Upvotes: 1
Reputation: 457147
The async
equivalent of Thread.Sleep
is await Task.Delay
:
if (sleepyTime > 0)
await Task.Delay(sleepyTime);
Note that this must be used in the context of an async
method, and there are limitations (particularly on WebForms) on where async
can be used. For more information, see my article on async
ASP.NET and the official tutorial on async
WebForms.
Upvotes: 1