简体   繁体   中英

Can I Terminate HTTP transaction on server WITHOUT sending response to client?

I'm writing a public-facing transaction processor. Naturally, we run on https:// and the payload carries all relevant detail so we'll only process legitimate transactions. However, as a public interface, any number of nefarious actors will no doubt be throwing shade at my server if for no other reason than to just be annoying.

When I detect such a request, is there anyway I can terminate processing at my end - not going to waste time on the transaction - but NOT send a response to the client? Basically, I'd like to force the nefarious clients into a timeout situation so that, if nothing else, it diminishes their capacity to annoy my server.

Here's the code:

public class Webhook : IHttpModule
{
    /// <summary>
    /// You will need to configure this module in the Web.config file of your
    /// web and register it with IIS before being able to use it. For more information
    /// see the following link: http://go.microsoft.com/?linkid=8101007
    /// </summary>

    private bool m_sslRequired = false;

    #region IHttpModule Members
    <snip...>
    #endregion

    private void OnBeginRequest(object sender, EventArgs e)
    {
        WriteTrace("Begin OnBeginRequest");
        HttpContext ctx = HttpContext.Current;

        try
        {
            string processor = ctx.Request.Params["p"];
            if (processor != null && processor != "")
            {
                PluginProcessor(processor, ctx);
            }
        }
        catch (Exception ex)
        {
            ctx.Response.StatusCode = 500;
            ctx.Response.Write("ERROR");
        }

        ctx.ApplicationInstance.CompleteRequest();

        WriteTrace("End OnBeginRequest");
    }

    private void PluginProcessor(string processor, HttpContext ctx)
    {
        string pluginSpec = AppConfig.GetAppSetting(processor.Trim().ToLower());

        if (pluginSpec != "")
        {
            IWebhookProcessor proc = CreateProcessor(pluginSpec, ctx);
            proc.Process(ctx);
        }
    }

    private IWebhookProcessor CreateProcessor(string Processor, HttpContext ctx)
    {
        string assembly;
        string typeName;

        typeName = Processor.Substring(0, Processor.IndexOf(",")).Trim();
        assembly = Path.Combine(ctx.Request.PhysicalApplicationPath, "bin", Processor.Substring(Processor.IndexOf(",") + 1).Trim());

        var obj = Activator.CreateInstanceFrom(assembly, typeName);

        return (Interfaces.IWebhookProcessor)obj.Unwrap();
    }
}

So if the request doesn't map to a transaction handler, I'd like to 'hang' the client, but not in a way which will tie up resources on the server.

Thanks for your advice!

I think the best thing you can do is use HttpRequest.Abort() , which doesn't leave the client hanging, but it does immediately sever the TCP connection. Even the docs say it is for this kind of scenario:

You might use this method in response to an attack by a malicious HTTP client.

You would use it like this:

ctx.Request.Abort();

In a browser, you see a "connection reset" error.

Another option is to send back an unexpected HTTP status, like 400, or my personal favourite, 418 .

Update: If you reaaallly want to make the client wait, you could implement your own HttpModule so that you can make an asynchronous BeginRequest event and then use Task.Delay() .

The HttpModule class would look something like this:

public class AsyncHttpModule : IHttpModule {
    public void Dispose() { }

    public void Init(HttpApplication app) {
        var wrapper = new EventHandlerTaskAsyncHelper(DoAsyncWork);
        app.AddOnBeginRequestAsync(wrapper.BeginEventHandler, wrapper.EndEventHandler);
    }

    private async Task DoAsyncWork(object sender, EventArgs e) {
        var app = (HttpApplication) sender;
        var ctx = app.Context;

        if (shouldDie) { //whatever your criteria is
            await Task.Delay(60000); //wait for one minute
            ctx.Request.Abort(); //kill the connection without replying
        }
    }
}

Then add the module in your web.config (replace the namespace with your app's namespace):

<system.webServer>
  <modules>
    <add name="AsyncHttpModule" type="MyNamespace.AsyncHttpModule" />
  </modules>
</system.webServer>

Since this is asynchronous, it is not holding up a thread while it waits. Other requests that come in will use the same thread (I tested this).

However, it is still keeping the request context in memory, because the request is still in progress. So if they hit you with 1000+ requests, all of those 1000+ requests are held in memory for 60 seconds. Whereas if you just use HttpRequest.Abort() right away, those get removed from memory right away.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM