Reputation: 607
I have created an HttpModule
:
using System;
using System.Web;
public class TestModule : IHttpModule
{
public TestModule() { }
public String ModuleName { get { return "TestModule"; } }
public void Dispose() { }
public void Init(HttpApplication app)
{
app.BeginRequest += (new EventHandler(this.DoBeginRequest));
}
private void DoBeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
context.Response.Write("<pre>Request URL: " + context.Request.FilePath + "</pre>");
context.Response.Flush();
System.Diagnostics.Debug.WriteLine(context.Request.ToString());
}
}
Which is loaded like so:
<system.webServer>
<modules>
<add name="TestModule" type="TestModule"/>
</modules>
</system.webServer>
When i call this from a web browser, or from curl
, i get two log lines in the Output tab, and i see the following response:
<pre>Request URL: /example</pre><pre>Request URL: /example</pre>
Which indicates that it is the same Context each time. Why is this happening? There are a lot of fields in the Request
object, but i've not been able to spot any difference between them in the two calls. Is there some property i should be checking that gives a "stage" that i should only be responding to one of?
I have found a few related questions here on the site, but most of them seem to lean toward it being the web browser looking for another resource, like favicon.ico
, and that is not the case here.
MSDN seems a bit light on details for BeginRequest
, so i've not found a lot of help there so far.
I may be missing something obvious, this is my first encounter with .NET/IIS etc, i am a Java dev usually.
Update:
I've dumped all the public properties of Request
and this is the difference between the two:
Headers == Accept=*%2f*&Host=localhost%3a2017&User-Agent=curl%2f7.35.0
Headers == Content-Length=0&Accept=*%2f*&Host=localhost%3a2017&User-Agent=curl%2f7.35.0
Specifically, the Content-Length
header is getting set on the second call. This wasn't present in the request curl
made:
> GET /example HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:2017
> Accept: */*
Is this IIS trying to be helpful? After it's handled the whole request it knows how long the body is (0) and calls it again with that set?
Upvotes: 4
Views: 1966
Reputation: 46040
(Note: this solution may not be appropriate in every application)
Just call CompleteRequest() at the end of your event handler (be it BeginRequest, PostAuthorizeRequest, or something else).
This call finishes request processing and prevents other handlers from working, but if you have already handled it, most likely, you don't expect post-processing to take place, so no big deal.
Upvotes: 2
Reputation: 607
Ok, after much frustration i discovered that specific URLs did not suffer from this behaviour. URLs with an "extension" don't do it:
curl http://localhost:2017/test
> BeginRequest
> BeginRequest
curl http://localhost:2017/test.abc
> BeginRequest
curl http://localhost:2017/.a
> BeginRequest
Through some more digging online i've found references to a Handler called ExtensionlessUrl-Integrated-4.0
which is loaded by the main config file applicationhost.config
. I don't really know what this is doing, nor why it causes requests to be duplicated, but explicitly removing it in my own web.config
has solved the problem:
<system.webServer>
<handlers>
<remove name="ExtensionlessUrl-Integrated-4.0" />
</handlers>
<modules>
<add name="TestModule" type="TestModule"/>
</modules>
</system.webServer>
I must admit that i am a bit afraid of what other similar pitfalls i might encounter later on - applicationhost.config
loads a lot of other handlers and modules, any of which might be messing with stuff like this before my module is able to get at it. But that's a problem for another day...
Upvotes: 5
Reputation: 422
Using LogRequest event listener instead of BeginRequest should work for your purpose:
public void Init(HttpApplication app)
{
app.LogRequest += DoBeginRequest; // TODO: Rename your function
}
Yes, I see your behavior on very clean ASP.NET Web Application.
And it is not related to favicon.ico
additional request.
It may relate to multiple application instances, that IIS may create in application pool. And multiple instances of your module with multiple subscriptions to same event. It somewhere here...but I cannot prove none of these theories in my code for now.
Upvotes: 0