Опубликован: 24.11.2006 | Доступ: свободный | Студентов: 678 / 23 | Оценка: 4.46 / 4.54 | Длительность: 17:18:00
Лекция 5:

Расширения ISAPI

Извлечение информации из IIS

ECB обычно используется для извлечения информации о запросе HTTP и экземпляре сервера IIS, посредством чего расширение ISAPI выполняет определенные программные действия на основе события запроса. В коде листинга 5.3, взятого из расширения ISAPI SEUX (Простое расширение с использованием XML), функция HttpExtensionProc выполняет следующие задачи.

  • Построение документа XML со множеством общих серверных переменных, получаемых из функции GetServerVariable.
  • Добавление свойств ECB в документ XML.
  • Возврат документа XML запрашивающей стороне.
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Name:HttpExtensionProc

In:        pECB - pointer to the Extension control block structure

Out:    DWORD - HSE status code 

Purpose:
    main entry point for HTTP request

    the possible return codes are: 
    HSE_STATUS_SUCCESS    - everything worked great
    HSE_STATUS_SUCCESS_AND_KEEP_CONN - same as HSE_STATUS_SUCCESS 
                                        since IIS 4
    HSE_STATUS_PENDING - wait until effort completed 
    HSE_STATUS_ERROR   - sends a 500 error code
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
{   
    string sDoc;

    //start our XML document
    sDoc = string(HEAD) + string(NEW_LINE) + string(XML_L) + 
                string(MAIN_ELEMENT_NAME) + string(XML_R) + 
                string(NEW_LINE);

    //START THE ECBServerVariable VARIABLES
    sDoc += string(XML_L) + 
        string("ECBServerVariable") + string(XML_R) + 
        string(NEW_LINE);
    //GET the ALL_HTTP
    sDoc += string(XML_L) + string("ALL_HTTP");
    GetALLHTTPHeader(pECB, &sDoc);    
    sDoc += string(XML_R_END) + 
        string(NEW_LINE);//end the first main element

    GetECBElement(pECB, string("AUTH_TYPE"), &sDoc);
    GetECBElement(pECB, string("APPL_MD_PATH"), &sDoc);
    GetECBElement(pECB, string("APPL_PHYSICAL_PATH"), &sDoc);
    GetECBElement(pECB, string("CONTENT_LENGTH"), &sDoc);
    GetECBElement(pECB, string("CONTENT_TYPE"), &sDoc);
    GetECBElement(pECB, string("GATEWAY_INTERFACE"), &sDoc);
    GetECBElement(pECB, string("HTTP_ACCEPT"), &sDoc);
    GetECBElement(pECB, string("HTTPS"), &sDoc);
    GetECBElement(pECB, string("HTTP_AUTHORIZATION"), &sDoc);
    GetECBElement(pECB, string("LOGON_USER"), &sDoc);
    GetECBElement(pECB, string("AUTH_PASSWORD"), &sDoc);
    GetECBElement(pECB, string("AUTH_TYPE"), &sDoc);
    GetECBElement(pECB, string("AUTH_USER"), &sDoc);
    GetECBElement(pECB, string("APPL_PHYSICAL_PATH"), &sDoc);
    GetECBElement(pECB, string("INSTANCE_ID"), &sDoc);
    GetECBElement(pECB, string("INSTANCE_META_PATH"), &sDoc);
    GetECBElement(pECB, string("PATH_INFO"), &sDoc);
    GetECBElement(pECB, string("PATH_TRANSLATED"), &sDoc);
    GetECBElement(pECB, string("QUERY_STRING"), &sDoc);
    GetECBElement(pECB, string("REMOTE_ADDR"), &sDoc);
    GetECBElement(pECB, string("REMOTE_HOST"), &sDoc);
    GetECBElement(pECB, string("REMOTE_USER"), &sDoc);
    GetECBElement(pECB, string("REQUEST_METHOD"), &sDoc);
    GetECBElement(pECB, string("SCRIPT_NAME"), &sDoc);
    GetECBElement(pECB, string("SERVER_NAME"), &sDoc);
    GetECBElement(pECB, string("SERVER_PORT"), &sDoc);
    GetECBElement(pECB, string("SERVER_PORT_SECURE"), &sDoc);
    GetECBElement(pECB, string("SERVER_PROTOCOL"), &sDoc);
    GetECBElement(pECB, string("SERVER_SOFTWARE"), &sDoc);
    GetECBElement(pECB, string("URL"), &sDoc);

    //End THE ECBServerVariable VARIABLES
    sDoc += string(XML_L_END) + 
        string("ECBServerVariable") + string(XML_R) + 
        string(NEW_LINE);

    //START THE ECBProperties
    sDoc += string(XML_L) + 
        string("ECBProperties") + string(XML_R) + 
        string(NEW_LINE);
    
    GetElement(string("lpszLogData"),
               string(pECB->lpszLogData),&sDoc);
    GetElement(string("lpszMethod"),
               string(pECB->lpszMethod),&sDoc);
    GetElement(string("lpszQueryString"),
               string(pECB->lpszQueryString),&sDoc);
    GetElement(string("lpszPathInfo"),
               string(pECB->lpszPathInfo),&sDoc);
    GetElement(string("lpszContentType"),
               string(pECB->lpszContentType),&sDoc);

    //end THE ECBProperties
    sDoc += string(XML_L_END) + 
        string("ECBProperties") + string(XML_R) + 
        string(NEW_LINE);

       //end our XML document
    sDoc += string(XML_L_END) + 
        string(MAIN_ELEMENT_NAME) + string(XML_R) + 
        string(NEW_LINE);

    //write it!
    SendResponse(pECB,sDoc);

   return HSE_STATUS_SUCCESS;
}
Листинг 5.3. HttpExtensionProc Function Building XML Document in SEUX ISAPI Extension

Примечание. Исходный код SEUX доступен на веб-сайте автора книги (см. введение).

Построение XML для представления значений серверных переменных

Первой задачей (см. листинг 5.3) является открытие документа XML посредством присоединения некоторых констант, представляющих собой части документа XML. Построение XML происходит вручную, и константы объявляются для общих частей документа XML, так как они используются во многих местах. Документ XML размещается в строковой переменной с именем sDoc. Ниже приведены константы XML:

//xml parts
    const char* MAIN_ELEMENT_NAME = "HTTPRequestRaw";
    const char* QUOTE        ="\"";
    const char* XML_L        ="<";
    const char* XML_R        =">";
    const char* XML_L_END    ="</";
    const char* XML_R_END    ="/>";
    const char* NEW_LINE    ="\n";
    const char* HEAD = "<?xml version=\"1.0\"?>";

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

После инициализации документа XML начинается работа по размещению в элементах XML всех серверных переменных. Создаваемый документ XML имеет родительский элемент HTTPRequestRaw. Внутри HTTPRequestRaw содержатся два дочерних элемента с ECBServerVariable и ECBProperties. Для каждого значения серверной переменной, запрашиваемого из ECB, будет создаваться дочерний элемент для элемента ECBServerVariable, независимо от получения значения соответствующей серверной переменной. Для каждого запрошенного свойства ECB создается дочерний элемент под элементом ECBProperties, которое содержит значение независимо от получения значения свойства. При вызове расширения ISAPI SEUX с помощью IE 6.0 отобразится документ XML (см. рис. 5.12). В других версиях браузера XML может отобразится по-другому или не отобразиться вовсе.

IE 6.0, отображающий документ XML серверных переменных из расширения SEUX ISAPI

увеличить изображение
Рис. 5.12. IE 6.0, отображающий документ XML серверных переменных из расширения SEUX ISAPI

Специальный случай серверной переменной ALL_HTTP

Первой серверной переменной, для которой получается значение, является переменная ALL_HTTP, представляющая собой все заголовки HTTP, переданные в запросе HTTP. Результатом остальных запрошенных серверных переменных является число или строка, легко согласуемая с XML, однако ALL_HTTP возвращает все заголовки в одну и ту же серверную переменную.

Заголовки в значении, возвращаемом из ALL_HTTP, ограничены символами новой строки, поэтому требуется дополнительный анализ для размещения каждого заголовка в элементе XML в качестве атрибута. Функция GetALLHTTPHeader выполняет данную задачу с помощью функции ECB GetServerVariable (см. листинг 5.4).

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Name:GetALLHTTPHeader

In:    pECB - pointer to the Extension control block structure

    psElement - string pointer to XML document being built that 
                will be updated with the element for the ALL_HTTP
                server variable value.

Out:    nothing returned but the string psElement points to 
        will be updated.

Purpose:
    Updates the XML document string by adding an element 
    for the ALL_HTTP server variable. This variable contains 
    all of the http headers so some additional parsing must 
    take place on the headers to format them into XML. 

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void GetALLHTTPHeader(EXTENSION_CONTROL_BLOCK *pECB, 
                      string *psElement)
{
    TCHAR szTempBuffer[BUFFER_LENGTH];
    DWORD dwBufferSize = BUFFER_LENGTH;
    const string EQUAL("=");
    const string SPACE(" ");

    string sAllHeaders;    //used to cut up 'all headers' returned
    int nNewLinePos = 0;
    int nEndLinePos = 0;
    string sName;
    string sValue;


    //pull the whole HTTP header 
    if (pECB->GetServerVariable(    pECB->ConnID, 
                                    "ALL_HTTP", 
                                    szTempBuffer, 
                                    &dwBufferSize))
    {
       //if the whole http header was pulled then parse it
        sAllHeaders.assign(szTempBuffer);

        //get the name / value pairs
        while (GetHeaderValuePair(    sAllHeaders, 
                                    nNewLinePos, 
                                    &sName, 
                                    &sValue, 
                                    &nEndLinePos))
        {
            //add the attribute to the element
            psElement->append(    SPACE + sName + EQUAL + 
                                QUOTE + 
                                ValidateValue(sValue) + 
                                QUOTE); 

            //reset the newline to the last endline
            nNewLinePos = nEndLinePos;                    
            
            //dump the values
            sName.erase();
            sValue.erase();
        }
    }
}
Листинг 5.4. GetALLHTTPHeader Function Building XML Element in SEUX ISAPI Extension
Функция GetServerVariable

Функция GetServerVariable возвращает значение "истина" при успешном выполнении и значение "ложь" при возникновении ошибки, как показано в следующем примере:

BOOL WINAPI GetServerVariable(
  HCONN hConn,     
  LPSTR lpszVariableName,  
  LPVOID lpvBuffer,    
  LPDWORD lpdwSizeofBuffer  
);

Прототип GetServerVariable в данном случае требует передачи четырех параметров.

  • hConn. Поддержка соединения, полученная от ECB.
  • lpszVariableName. Строка с символом конца строки запрашиваемой серверной переменной.
  • lpvBuffer. Пустой указатель на буфер, который заполняется результирующим значением имени переменной и байтом конца строки.
  • lpdwSizeofBuffer. Указатель на значение DWORD, отражающее размер буфера.

При успешном выполнении функция GetServerVariable возвращает значение "истина". Указатель lpvBuffer указывает значение запрашиваемой серверной переменной, а lpdwSizeofBuffer – на новое значение DWORD, отражающее текущий размер значения, включая байт конца строки. При неудачном завершении работы функция GetServerValue возвращает значение "ложь". В этом случае вызывается функция GetLastError, которая возвращает значение DWORD, представляющее собой код ошибки. В таблице 5.2 показаны возможные ошибки функции GetServerVariable ; это константы, определяемыми во вспомогательном файле заголовка расширения ISAPI.

Олег Корсак
Олег Корсак
Латвия, Рига
Ренат Файзуллин
Ренат Файзуллин
Россия, Казань