Структура расширения ISAPI очень проста. Библиотека DLL расширения должна экспортировать всего две функции с именами GetExtensionVersion и HttpExtensionProc. Первая из этих функций предназначена для того, чтобы расширение могло сообщить серверу версию спецификации, которой оно соответствует, и строку описания расширения. Функция HttpExtensionProc выполняет всю работу по передаче данных между расширением и сервером.
Дополнительно расширение ISAPI может экспортировать функцию TerminateExtension, которая вызывается сервером перед тем, как ненужное больше приложение ISAPI выгружается из памяти. Функция TerminateExtension должна освободить ресурсы, загруженные динамически при инициализации расширения ISAPI.
Насколько проста реализация функции GetExtensionVersion вы можете судить по следующему фрагменту кода, взятому нами из исходных текстов приложения FILEUPL (это приложение будет полностью рассмотрено позже):
При вызове функции GetExtensionVersion передается указатель на структуру типа HSE_VERSION_INFO. Эта структура и указатель на нее LPHSE_VERSION_INFO определены в файле httpext.h следующим образом:
Константы HSE_VERSION_MINOR и HSE_VERSION_MAJOR указывают текущую версию интерфейса расширения ISAPI и также определены в файле httpext.h:
#define HSE_VERSION_MAJOR 2 // верхний номер версии#define HSE_VERSION_MINOR 0 // нижний номер версии
Теперь рассмотрим вторую функцию, которую должна экспортировать библиотека DLL расширения ISAPI. Она называется HttpExtensionProc и имеет следующий прототип:
Функция HttpExtensionProc получает единственный параметр - указатель на структуру типа EXTENSION_CONTROL_BLOCK, определенную в файле httpext.h:
typedef struct _EXTENSION_CONTROL_BLOCK { DWORD cbSize; // размер структуры в байтах DWORD dwVersion; // версия спецификации ISAPI HCONN ConnID; // идентификатор канала DWORD dwHttpStatusCode; // код состояния HTTP CHAR lpszLogData[HSE_LOG_BUFFER_LEN]; // текстовая строка, // закрытая двоичным нулем, в которой находится информация // протоколирования, специфичная для данного расширения LPSTR lpszMethod; // переменная REQUEST_METHOD LPSTR lpszQueryString; // переменная QUERY_STRING LPSTR lpszPathInfo; // переменная PATH_INFO LPSTR lpszPathTranslated; // переменная PATH_TRANSLATED DWORD cbTotalBytes; // полный размер данных, полученных от // навигатора DWORD cbAvailable; // размер доступного блока данных LPBYTE lpbData; // указатель на доступный блок данных // размером cbAvailable байт LPSTR lpszContentType; // тип принятых данных // Функция GetServerVariable для получения значения переменных BOOL (WINAPI * GetServerVariable)(HCONN hConn, LPSTR lpszVariableName, LPVOID lpvBuffer, LPDWORD lpdwSize); // Функция WriteClient для посылки данных удаленному пользователю BOOL (WINAPI * WriteClient)(HCONN ConnID, LPVOID Buffer, LPDWORD lpdwBytes, DWORD dwReserved); // Функция ReadClient для получения данных от удаленного // пользователя BOOL (WINAPI * ReadClient) (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize); // Вспомогательная функция ServerSupportFunction // для выполнения различных операций BOOL (WINAPI * ServerSupportFunction)(HCONN hConn, DWORD dwHSERRequest, LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType); } EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;
Рассмотрим отдельные поля этой структуры.
· cbSize
В самом начале структуры EXTENSION_CONTROL_BLOCK находится поле cbSize, в которое при вызове расширения сервер записывает размер структуры в байтах.
· dwVersion
Поле dwVersion содержит номер версии расширения ISAPI. Верхнее и нижнее значения номера версии можно получить, соответственно, при помощи макрокоманд HIWORD и LOWORD.
· ConnID
В поле ConnID сервер записывает идентификатор канала, созданного для расширения. Это поле вы не должны изменять.
· dwHttpStatusCode
Поле dwHttpStatusCode должно заполняться расширением ISAPI. Вы должны записать сюда результат завершения операции (код состояния транзации). В случае успеха в это поле записывается значение 200 (как указано в спецификации HTTP).
· lpszLogData
Поле lpszLogData предназначено для записи сообщения о выполнении транзакции в журнал сервера WWW. Это сообщение должно быть в виде текстовой строки, закрытой нулем. Размер строки в байтах не должен превышать значения HSE_LOG_BUFFER_LEN.
· lpszMethod
Поле lpszMethod заполняется сервером и содержит название метода передачи данных от удаленного пользователя серверу в виде текстовой строки, закрытой двоичным нулем. Расширения ISAPI используют те же самые методы, что и программы CGI - метод GET и метод POST. Проводя аналогию с программами CGI дальше, скажем, что поле lpszMethod эквивалентно переменной среды с именем REQUEST_METHOD, создаваемой для программы CGI.
· lpszQueryString
Аналогично, поле lpszQueryString соответствует переменной среды с именем QUERY_STRING. В это поле записываются данные, принятые от удаленного пользователя методом GET.
· lpszPathInfo
В поле lpszPathInfo записывается виртуальный путь к программному файлу библиотеки DLL расширения ISAPI. Напомним, что аналогичная информация для программ CGI передавалась через переменную среды с именем PATH_INFO.
· lpszPathTranslated
Это поле содержит физический путь к программному файлу библиотеки DLL расширения ISAPI. Оно соответствует переменной среды с именем PATH_TRANSLATED, создаваемой для программ CGI.
· cbTotalBytes
В поле cbTotalBytes записывается общее количество байт данных, которое необходимо получить от удаленного пользователя. Часть этих данных (размером не более 48 Кбайт) считывается сервером автоматически и становится доступной сразу после того как функция HttpExtensionProc получит управление. Остальные данные необходимо дочитать в цикле при помощи функции ReadClient, о которой мы еще будем говорить.
· cbAvailable
В поле cbAvailable записывается размер блока данных, полученных от удаленного пользователя автоматически. Как мы только что сказали, размер этого блока не может превышать 48 Кбайт. Этого, однако, вполне достаточно для обработки данных, полученных от форм обычного размера.
· lpbData
Указатель на область памяти, в которую записан сервером полученный от удаленного пользователя блок данных размером cbAvailable байт.
· lpszContentType
Поле lpszContentType содержит тип принятых данных, например, text/html.
· GetServerVariable
Помимо полей данных, структура EXTENSION_CONTROL_BLOCK содержит указатели на функции. С помощью этих функций расширение ISAPI может выполнять различные операции, такие как прием данных от удаленного пользователя.
Поле GetServerVariable содержит указатель на функцию, с помощью которой расширение ISAPI может получить информацию, которая доступна программам CGI через переменные среды, описанные нами в предыдущей главе этой книги.
· WriteClient
В поле WriteClient находится адрес функции, которую расширение ISAPI должно использовать для посылки данных удаленному пользователю. Таким образом, вместо того чтобы записывать данные в стандартный поток вывода STDOUT, как это делает программа CGI, приложение ISAPI посылает данные с помощью функции WriteClient.
· ReadClient
С помощью функции, адрес которой передается в поле ReadClient, приложение может дочитать дополнительные данные, не поместившиеся в буфер предварительного чтения, имеющий адрес lpbData и размер, не превышающий 48 Кбайт. Аналогичную операцию приема данных от пользователя выполняет программа CGI в случае применения метода передачи данных POST. Отличие заключается в том, что программа CGI получает данные через стандартный поток ввода STDIN, а расширение ISAPI берет эти данные из буфера предварительного чтения и при необходимости дочитывает данные функцией ReadClient.
· ServerSupportFunction
С помощью функции, адрес которой передается через поле ServerSupportFunction, расширение ISAPI может выполнять различные действия, такие как посылка стандартного заголовка протокола HTTP и некоторые другие.
При успешном завершении функция HttpExtensionProc должна вернуть значение HSE_STATUS_SUCCESS, а при ошибке - значение HSE_STATUS_ERROR. Соответствующие константы определены в файле httpext.h.