Вызывается ли ReleaseRequestState несколько раз для каждого запроса, полученного IIS? - PullRequest
1 голос
/ 19 мая 2011

Я пишу HttpModule в VS2010 / ASP.NET 4.0 для использования с IIS 7. Модуль будет обеспечивать безопасность строки запроса путем шифрования строк запроса.

Я бы хотел, чтобы этот модуль был полностью независимым и прозрачным для веб-сайта, чтобы веб-сайт не знал о том, что используется шифрование строки запроса. Это будет гарантировать, во-первых, что страницы / элементы управления не должны заботиться об этой проблеме. Во-вторых, он включит шифрование строки запроса для производственных сред и отключит его для непроизводственных (путем удаления модуля HTTP из Web.config).

Я разработал HttpModule для подключения к IIS через Web.config:

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

Сам модуль выглядит так:

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);
        }
    }
}

Существует класс QueryStringSecurityStream, который используется для управления выводом HTML в Response и защиты всех тегов, заменяя строки запроса на зашифрованные.

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);
    }
}

Волшебство происходит или должно произойти в методе расширения ReplaceHRefsWithSecureHRefs ().

Этот метод ожидает всего HTML. Он будет проходить через него с помощью зубчатого гребня (то есть с использованием регулярных выражений), находить все теги привязки, извлекать их атрибуты href, заменять любые строки запроса в значении href зашифрованными версиями и возвращать HTML. Этот HTML-код будет записан в поток ответа.

Пока все хорошо. Все это падает, потому что я подозреваю, что ReleaseRequestState вызывается несколько раз для отдельных запросов. То есть, существует несколько вызовов ReleaseRequestState в результате одного вызова BeginRequest.

Я ищу:

  1. Подтверждение, что моя догадка верна. Я спросил мистера Google и мистера MSDN, но не нашел ничего определенного. Кажется, я помню, что сталкивался с чем-то похожим при отладке WSDL из веб-службы ASMX, работающей в IIS 6. В этом случае я решил проблему, кэшируя входящий поток байтов, пока у меня не будет действительный XML, а затем выписал все это после изменения.

  2. Правильный способ справиться с подобным сценарием. Вы можете использовать это либо для обозначения конкретной проблемы BeginRequest / множественных вызовов ReleaseRequestState, либо для шифрования строки запроса в целом.

Дамы и господа. Запустите ваши двигатели. Пусть ответы накатятся.

Обновление:

Я прочитал эту статью о MSDN по запросу жизненного цикла

Я решил эту проблему для себя, создав буфер для хранения содержимого ответа при нескольких вызовах ReleaseRequestState. При каждом вызове я проверяю наличие тега </html> и записываю содержимое, буферизованное до этого момента после модификации (в моем случае шифрование строк запроса в тегах <a>).

Итак:

  1. Объявите StringBuilder как член частного поля в классе QueryStringSecurityModule (в моем случае StringBuilder в качестве буфера для содержимого ответа).
  2. Инициализируйте поле в BeginRequest (в моем случае выделите StringBuilder).
  3. Завершить поле в EndRequest (в моем случае установить его на ноль, хотя я читал, что EndRequest не всегда срабатывает)
  4. Буферные байты отправляются в Write в пользовательском фильтре, пока мы не найдем закрывающий HTML-тег, после чего мы изменяем содержимое буфера и записываем их в выходной поток.

Кто-нибудь хотел бы прокомментировать этот подход?

...