Is ReleaseRequestState called multiple times for each Request received by IIS?

坚强是说给别人听的谎言 提交于 2020-01-15 14:16:24

问题


I am writing an HttpModule in VS2010/ASP.NET 4.0 for use with IIS 7. The module is going to enforce query string security by encrypting query strings.

I would like this module to be completely independent of as well as transparent to the website, so that the website has no knowledge of the fact that query string encryption is being employed. This will ensure firstly that pages/controls don't have to care about this issue. Secondly, it would enable query string encryption for Production environments and disable it for non-Production ones (by removing the HTTP module from the Web.config).

I have designed the HttpModule to get plugged into IIS via the Web.config by:

<configuration>
    <system.web>
        <httpModules>
            <add name="QueryStringSecurityModule" type="MyHttpModules.QueryStringSecurityModule"/>
        </httpModules>
    </system.web>
</configuration>

The module itself looks like this:

public class QueryStringSecurityModule : IHttpModule
{
    public virtual void Init(HttpApplication application)
    {
        application.BeginRequest += HandleBeginRequest;
        application.EndRequest += HandleEndRequest;
        application.ReleaseRequestState += HandleReleaseRequestState;
    }

    public virtual void Dispose()
    {
    }

    private void HandleBeginRequest(object sender, EventArgs e)
    {
        // TODO : Decrypt the query string here and pass it on to the application
    }

    private void HandleEndRequest(object sender, EventArgs e)
    {
        // TODO : Twiddle thumbs
    }

    private void HandleReleaseRequestState(object sender, EventArgs e)
    {
        var response = HttpContext.Current.Response;

        if (response.ContentType == "text/html")
        {
            response.Filter = new QueryStringSecurityStream(response.Filter);
        }
    }
}

There is a class QueryStringSecurityStream which is used to fiddle with the HTML output in the Response, and secure all tags by replacing the query strings therein with encrypted ones.

public QueryStringSecurityStream : Stream
{
    public QueryStringSecurityStream(Stream stream)
        : base()
    {
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        var html = Encoding.Default.GetString(buffer, offset, count).ReplaceHRefsWithSecureHRefs();
        var bytes = Encoding.Default.GetBytes(html);

        this.stream.Write(bytes, 0, bytes.Length);
    }
}

The magic happens, or is supposed to happen, in the ReplaceHRefsWithSecureHRefs() extension method.

This method expects the entire HTML. It will go through it with a fine-toothed comb (i.e., using a Regex), find all the anchor tags, take out their href attributes, replace any query strings in the href value with encrypted versions and return the HTML. This HTML will then be written out to the Response stream.

So far so good. All of this falls over because I suspect that ReleaseRequestState is raised multiple times for individual requests. That is to say, that there are multiple calls to ReleaseRequestState as a result of a single call to BeginRequest.

What I am looking for is:

  1. Confirmation that my hunch is correct. I have asked Mr. Google and Mr. MSDN but haven't found anything definitive. I seem to remember hitting something similar while debugging WSDL from an ASMX web service running in IIS 6. In that case I solved the issue by caching the incoming byte stream till I have valid XML and then writing it all out after modifying it.

  2. The right way to handle this sort of scenario. You can take this to either mean specifically the single BeginRequest/multiple ReleaseRequestState calls issue or query string encryption generally.

Ladies and Gentlemen. Start your engines. Let the answers roll in.

Update:

I read this article on MSDN on request life-cycle

I have solved this issue for myself by creating a buffer to store response content across multiple calls to ReleaseRequestState. On every call, I check for the existence of a </html> tag and writes out the content buffered up to that point after modification (in my case encrypting the query strings in the <a> tags).

So:

  1. Declare a StringBuilder as a private field member in the QueryStringSecurityModule class (in my case a StringBuilder to serve as a buffer for response content).
  2. Initialize the field at BeginRequest (in my case, allocate a StringBuilder).
  3. Finalize the field at EndRequest (in my case set it to null, though I have read that EndRequest doesn't always fire)
  4. Buffer bytes being sent to Write in the custom filter till we find a closing html tag, at which point we modify buffer contents and write them out to the output stream.

Would anybody like to comment on this approach?

来源:https://stackoverflow.com/questions/6059094/is-releaserequeststate-called-multiple-times-for-each-request-received-by-iis

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!