Разработка собственного модуля C\C++ для IIS 7.0

Майк Володарский

Введение

СЛУЖБЫ IIS 7.0 и более поздних версий позволяют расширять сервер с помощью модулей, разработанных двумя способами:

  • Использование управляемого кода и API расширяемости сервера ASP.NET
  • Использование машинного кода и API расширения собственного сервера IIS

В отличие от предыдущих версий IIS, большинство сценариев расширяемости сервера не требуют разработки собственного кода (C++) и могут быть размещены с помощью управляемого кода и ASP.NET API. Использование ASP.NET для расширения сервера позволяет значительно сократить время разработки и воспользоваться широкими возможностями ASP.NET и платформа .NET Framework. Дополнительные сведения о расширении СЛУЖБ IIS с помощью ASP.NET см. в статье Разработка модуля IIS с помощью .NET.

СЛУЖБЫ IIS также предоставляют (C++) собственный API сервера ядра, который заменяет API фильтра и расширения ISAPI из предыдущих выпусков IIS. Если у вас есть определенные требования, требующие разработки машинного кода, или вы хотите преобразовать существующие собственные компоненты ISAPI, воспользуйтесь преимуществами этого API для создания серверных компонентов. Новый API собственного сервера поддерживает объектно-ориентированную разработку с интуитивно понятной объектной моделью, обеспечивает больший контроль над обработкой запросов и использует более простые конструктивные шаблоны для создания надежного кода.

В этом пошаговом руководстве рассматриваются следующие задачи:

  • Разработка собственного модуля с помощью собственного API сервера (C++)
  • Развертывание собственного модуля на сервере

Чтобы скомпилировать модуль, необходимо установить пакет SDK для платформы, содержащий файлы заголовков IIS. Последняя версия пакета SDK для платформы Windows Vista доступна здесь.

Чтобы использовать пакет SDK для платформы в Visual Studio 2005, необходимо зарегистрировать пакет SDK. После установки пакета SDK сделайте это в разделе Запуск > программ > Microsoft Windows SDK > регистрации > Visual Studio Регистрация каталогов пакета SDK для Windows в Visual Studio.

Исходный код этого модуля доступен в примере собственного модуля Visual Studio IIS7.

Разработка собственного модуля

В этой задаче мы рассмотрим разработку собственного модуля с помощью нового собственного API сервера (C++). Собственный модуль — это библиотека DLL Windows, содержащая следующие компоненты:

  • Экспортируемая функция RegisterModule . Эта функция отвечает за создание фабрики модулей и регистрацию модуля для одного или нескольких событий сервера.
  • Реализация класса модуля, наследуемого от базового класса CHttpModule . Этот класс предоставляет main функциональные возможности модуля.
  • Реализация класса фабрики модуля, реализующего интерфейс IHttpModuleFactory . Класс отвечает за создание экземпляров модуля.

Примечание

В некоторых случаях можно также реализовать интерфейс IGlobalModule , чтобы расширить некоторые функциональные возможности сервера, не связанные с обработкой запросов. Это расширенный раздел, который не рассматривается в этом пошаговом руководстве.

Собственный модуль имеет следующий жизненный цикл:

  1. При запуске серверного рабочего процесса он загрузит библиотеку DLL, содержащую модуль, и вызовет экспортированную функцию RegisterModule . В этой функции вы:

    а. Создайте фабрику модулей.
    b. Зарегистрируйте фабрику модулей для событий конвейера запросов, которые реализует модуль.

  2. При поступлении запроса сервер:

    а. Создает экземпляр класса модуля, используя указанную вами фабрику.
    b. Вызывает соответствующий метод обработчика событий в экземпляре модуля для каждого события запроса, для которых вы зарегистрировали.
    c. Удаляет экземпляр модуля в конце обработки запроса.

Теперь, чтобы создать его.

Полный исходный код модуля доступен в примере собственного модуля Visual Studio IIS7. Приведенные ниже действия являются наиболее важными для разработки модуля и не включают в себя поддержку обработки кода и ошибок.

Реализуйте функцию RegisterModule , которую сервер вызывает при загрузке библиотеки DLL модуля. Его сигнатура и остальная часть собственного API определяются в файле заголовка httpserv.h, который является частью пакета SDK для платформы (если у вас нет пакета SDK для платформы, см. общие сведения о его получении):

main.cpp:

HRESULT        
__stdcall        
RegisterModule(        
    DWORD                           dwServerVersion,    
    IHttpModuleRegistrationInfo *   pModuleInfo,
    IHttpServer *                   pHttpServer            
)
{
   // step 1: save the IHttpServer and the module context id for future use 
    g_pModuleContext = pModuleInfo->GetId();
    g_pHttpServer = pHttpServer;

    // step 2: create the module factory 
    pFactory = new CMyHttpModuleFactory();

    // step 3: register for server events 
    hr = pModuleInfo->SetRequestNotifications( pFactory, 
                                              RQ_ACQUIRE_REQUEST_STATE,
                                               0 );            
}

The RegisterModule

В RegisterModule необходимо выполнить три основные задачи:

Сохранение глобального состояния

Мы будем хранить экземпляр глобального сервера и идентификатор контекста модуля для последующего использования в глобальных переменных. Хотя в этом примере эти сведения не используются, многие модули считают полезным сохранить и использовать позже во время обработки запроса. Интерфейс IHttpServer предоставляет доступ ко многим функциям сервера, таким как открытие файлов и доступ к кэшу. Идентификатор контекста модуля используется для связывания состояния настраиваемого модуля с несколькими объектами сервера, такими как запрос и приложение.

Создание фабрики модулей

Далее в этом пошаговом руководстве мы реализуем класс фабрики CMyHttpModuleFactory. Эта фабрика отвечает за производство экземпляров нашего модуля для каждого запроса.

Регистрация фабрики модулей для событий обработки требуемых запросов

Регистрация выполняется с помощью метода SetRequestNotificatons , который предписывает серверу создать экземпляр модуля для каждого запроса, используя указанную фабрику; и , чтобы вызвать соответствующие обработчики событий для каждого из указанных этапов обработки запроса.

В этом случае нас интересует только этап RQ_ACQUIRE_REQUEST_STATE. Полный список этапов, составляющих конвейер обработки запросов, определен в httpserv.h:

#define RQ_BEGIN_REQUEST               0x00000001 // request is beginning 
#define RQ_AUTHENTICATE_REQUEST        0x00000002 // request is being authenticated             
#define RQ_AUTHORIZE_REQUEST           0x00000004 // request is being authorized 
#define RQ_RESOLVE_REQUEST_CACHE       0x00000008 // satisfy request from cache 
#define RQ_MAP_REQUEST_HANDLER         0x00000010 // map handler for request 
#define RQ_ACQUIRE_REQUEST_STATE       0x00000020 // acquire request state 
#define RQ_PRE_EXECUTE_REQUEST_HANDLER 0x00000040 // pre-execute handler 
#define RQ_EXECUTE_REQUEST_HANDLER     0x00000080 // execute handler 
#define RQ_RELEASE_REQUEST_STATE       0x00000100 // release request state 
#define RQ_UPDATE_REQUEST_CACHE        0x00000200 // update cache 
#define RQ_LOG_REQUEST                 0x00000400 // log request 
#define RQ_END_REQUEST                 0x00000800 // end request

Кроме того, вы можете подписаться на несколько недетерминированных событий, которые могут возникать во время обработки запроса из-за действий, выполняемых другими модулями, таких как очистка ответа клиенту:

#define RQ_CUSTOM_NOTIFICATION         0x10000000 // custom notification 
#define RQ_SEND_RESPONSE               0x20000000 // send response 
#define RQ_READ_ENTITY                 0x40000000 // read entity 
#define RQ_MAP_PATH                    0x80000000 // map a url to a physical path

Чтобы реализация RegisterModule была доступна серверу, необходимо экспортировать ее. Используйте . DEF-файл, содержащий ключевое слово EXPORTS для экспорта функции RegisterModule.

Затем реализуйте класс фабрики модуля:

mymodulefactory.h:

class CMyHttpModuleFactory : public IHttpModuleFactory
{
public:
    virtual HRESULT GetHttpModule(
        OUT CHttpModule            **ppModule, 
        IN IModuleAllocator        *
    )
            
    {
    }

   virtual void Terminate()
    {
    }

};

Фабрика модулей реализует интерфейс IHttpModuleFactory и служит для создания экземпляров модуля при каждом запросе.

Сервер вызывает метод GetHttpModule в начале каждого запроса, чтобы получить экземпляр модуля, который будет использоваться для этого запроса. Реализация просто возвращает новый экземпляр класса модуля CMyHttpModule, который мы реализуем далее. Как мы видим вкратце, это позволяет нам легко хранить состояние запроса, не беспокоясь о потокобезопасности, так как сервер всегда создает и использует новый экземпляр модуля для каждого запроса.

Более сложные реализации фабрики могут решить использовать одноэлементный шаблон вместо создания нового экземпляра каждый раз или использовать предоставленный интерфейс IModuleAllocator для выделения памяти модуля в пуле запросов. Эти расширенные шаблоны не рассматриваются в этом пошаговом руководстве.

Метод Terminate вызывается сервером, когда рабочий процесс завершает работу для выполнения окончательной очистки модуля. Если вы инициализируете глобальное состояние в RegisterModule, реализуйте его очистку в этом методе.

Реализация класса Module

Этот класс отвечает за предоставление main функциональных возможностей модуля во время одного или нескольких событий сервера:

myhttpmodule.h:

class CMyHttpModule : public CHttpModule
{
public:
    REQUEST_NOTIFICATION_STATUS
    OnAcquireRequestState(
        IN IHttpContext *                       pHttpContext,
        IN OUT IHttpEventProvider *             pProvider
    );
};

Класс модуля наследуется от базового класса CHttpModule , который определяет метод обработчика событий для каждого из событий сервера, рассмотренных ранее. Когда конвейер обработки запросов выполняет каждое событие, он вызывает связанный метод обработчика событий для каждого экземпляра модуля, зарегистрированного для этого события.

Каждый метод обработчика событий имеет следующую сигнатуру:

REQUEST_NOTIFICATION_STATUS
    OnEvent(
        IN IHttpContext *                       pHttpContext,
        IN OUT IHttpEventProvider *             pProvider
    );

Интерфейс IHttpContext предоставляет доступ к объекту контекста запроса, который можно использовать для выполнения задач обработки запросов, таких как проверка запроса и управление ответом.

Интерфейс IHttpEventProvider заменяется более конкретным интерфейсом для каждого события, предоставляющего определенные функциональные возможности модуля. Например, обработчик событий OnAuthenticateRequest получает интерфейс IAuthenticationProvider , который позволяет модулю задать пользователя, прошедшего проверку подлинности.

Возврат каждого метода обработчика событий является одним из значений перечисления REQUEST_NOTIFICATION_STATUS. Необходимо вернуть RQ_NOTIFICATION_CONTINUE, если модуль успешно выполнил задачу; выполнение конвейера должно продолжиться.

Если произошел сбой и вы хотите прервать обработку запроса с ошибкой, необходимо задать состояние ошибки и вернуть RQ_NOTIFICATION_FINISH_REQUEST. Возврат RQ_NOTIFICATION_PENDING позволяет выполнять работу асинхронно и отпустить поток обработки запроса, чтобы его можно было повторно использовать для другого запроса. В этой статье не рассматривается асинхронное выполнение.

Наш класс модуля переопределяет метод обработчика событий OnAcquireRequestState. Чтобы обеспечить функциональность на любом из этапов конвейера, класс модуля должен переопределить соответствующий метод обработчика событий. Если вы регистрируетесь для получения события в RegisterModule, но не переопределяете соответствующий метод обработчика событий в классе модуля, модуль завершится ошибкой во время выполнения (и запустит утверждение времени отладки при компиляции в режиме отладки). Будьте внимательны и убедитесь, что сигнатура метода переопределяющего метода в точности эквивалентна методу базового класса класса CHttpModule , который вы переопределяете.

Компиляция модуля

Помните, что для компиляции требуется пакет SDK для платформы. Дополнительные сведения о получении и включении ссылки на него см. в разделе Введение .

Развертывание собственного модуля

После компиляции модуля его необходимо развернуть на сервере. Скомпилируйте модуль, а затем скопируйте IIS7NativeModule.dll (и файл символов отладки IIS7NativeModule.pdb при необходимости) в любое расположение на компьютере с IIS.

Собственные модули, в отличие от управляемых модулей, которые можно добавить непосредственно в приложение, необходимо сначала установить на сервере. Для этого требуются права администратора.

Чтобы установить собственный модуль, можно воспользоваться несколькими вариантами:

  • Использование программы командной строки APPCMD.EXE
    APPCMD упрощает установку модуля. Перейдите в раздел Запуск>программ>Стандартные, щелкните правой кнопкой мыши командную строку и выберите Запуск от имени администратора. В окне командной строки выполните следующие действия:
    %systemroot%\system32\inetsrv\appcmd.exe install module /name:MyModule /image:[FULL\_PATH\_TO\_DLL]
    Где [FULL_PATH_TO_DLL] — это полный путь к скомпилированной библиотеке DLL, содержащей только что созданный модуль.
  • Использование средства администрирования IIS
    Это позволяет добавить модуль с помощью графического пользовательского интерфейса. Перейдите к запуску>, введите inetmgr и нажмите клавишу ВВОД. Подключитесь к localhost, найдите задачу Модули и дважды щелкните ее, чтобы открыть ее. Затем щелкните задачу Add a Native Module (Добавить собственный модуль) в области справа.
  • Установка модуля вручную
    Установите модуль вручную, добавив его <в раздел конфигурации system.webServer>/<globalModules> в applicationHost.config файле конфигурации, и добавьте ссылку на него в <раздел конфигурации system.webServer>/<modules> в том же файле, чтобы включить его. Для установки модуля рекомендуется использовать один из двух предыдущих вариантов, а не изменять конфигурацию напрямую.

Задача завершена— мы завершили настройку нового собственного модуля.

Итоги

В этом пошаговом руководстве вы узнали, как разрабатывать и развертывать пользовательский собственный модуль с помощью новых собственных API расширения (C++). Дополнительные сведения о собственных (C++) серверных API см. в обзоре разработки машинного кода.

Дополнительные сведения о расширении СЛУЖБ IIS с помощью управляемого кода и платформы .NET см. в статье Разработка модуля IIS с помощью .NET. Дополнительные сведения об управлении модулями IIS см. в техническом документе с общими сведениями о модулях.