Расширения ISAPI
Анализ пары "Заголовок-Значение"
После получения заголовка ALL_HTTP из функции GetServerVariable (см. листинг 5.4) для отображения содержимого в XML необходимо реализовать обработку строки. Заголовки разделяются символами новой строки и возврата каретки. Двоеточие (":") разделяет имя заголовка и его значение. GetHeaderValuePair инициализирует поиск в указанной позиции и возвращает имя и соответствующее ему значение для данного заголовка, а также позицию, в которой прерван поиск заголовков. GetHeaderValuePair единовременно осуществляет поиск одного значения заголовка.
Как видно из листинга 5.6 функция GetHeaderValuePair осуществляет поиск символа ":" в HTTP-заголовке, начиная с позиции nStart в строке sHeader. Значение nStart – это счетчик (начинается с нуля). Если символ " :" не найден, функция завершает работу с возвращением значения "ложь". При обнаружении символа " :" (т.е. заголовок существует) в sHeader продолжается поиск новой строки, начиная с позиции, в которой обнаружен данный символ. При поиске используется функция find строки Standard Template Library (STL) с символом новой строки \n в качестве аргумента и с указанием в качестве начальной позиции символа " :". Позиция новой строки становится конечной позицией, возвращаемой указателем pnEnd, посредством чего вызывающей функции становится известно, в каком месте остановлен поиск. С помощью всех параметров позиции, выявленных в процессе вызовов функции поиска строки sHeader, имя заголовка и значение извлекаются в место расположения памяти, связанное с указателями psName и psValue.
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Name: GetHeaderValuePair
In: sHeader - string HTTP Header
nStart - integer search start location
psName - pointer to name of header that is being sought
psValue - pointer to string that will be filled if
value found
pnEnd - pointer to integer of the final search position
Out: bool true returned if the header was found,
false returned otherwise
Purpose:
Searches through the HTTP header passed in for a
header value. Returns data about the search
parameters if found or not.
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
bool GetHeaderValuePair(const string &sHeader,
const int &nStart,
string *psName,
string *psValue,
int *pnEnd)
{
const string sColon(":");
//determine if header is a post header
string::size_type idxColonPosition = nStart;
//start looking at beginning
idxColonPosition = sHeader.find(sColon, idxColonPosition);
if (idxColonPosition == string::npos)//no more headers found
return false;//this is failure
//get the name
psName->assign(sHeader.substr
(nStart, idxColonPosition - nStart));
//find next newline
string::size_type idxNewLine;
idxNewLine = sHeader.find('\n', idxColonPosition);
//get the end even if it means not found
*pnEnd = idxNewLine;
if (idxNewLine == string::npos) //a newline was not found
return true;//not a failure - might be the last header
//get the value
//adjust colon position so we do not assign colon in value
idxColonPosition = idxColonPosition +1;
psValue->assign(sHeader.substr(idxColonPosition,
idxNewLine - idxColonPosition));
return true;
Листинг
5.6.
Source Code for Function GetHeaderValuePair
Построение остальных элементов XML
После обработки функции HttpExtensionProc значения заголовка ALL_HTTP остальные серверные переменные обрабатываются с помощью функции GetECBElement (см. листинг 5.7). Каждая серверная переменная, передаваемая в GetECBElement, извлекается с помощью функции GetServerVariable и записывается в элемент XML, присоединяемый к строке, на которую указывает psElement. Указатель psElement указывает на документ XML, конструируемый в HttpExtensionProc.
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Name:GetECBElement
In: pECB - Pointer to the extension control block for the
purposes of calling the GetServerVariable function.
sName - string name of the server variable that is
being sought.
psElement - string pointer to XML document being built that
will be updated with the name and value for the
server variable extracted from the extension
control block.
Out: nothing returned but the string psElement points to
will be updated.
Purpose:
appends a string of an XML element to the string psElement
points to. The XML element that is created is in the form of
<Server Variable Name>Server Variable Value</Server Variable Name>
for example:
<GATEWAY_INTERFACE>CGI/1.1</GATEWAY_INTERFACE> + newline
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void GetECBElement( EXTENSION_CONTROL_BLOCK *pECB,
const string &sName,
string *psElement)
{
TCHAR szTempBuffer[BUFFER_LENGTH];
DWORD dwBufferSize = BUFFER_LENGTH;
//get the server variable value
if (pECB->GetServerVariable( pECB->ConnID,
(LPSTR)sName.c_str(),
szTempBuffer,
&dwBufferSize))
{
//build the XML element and
//add it to the XML document passed in
psElement->append(string(XML_L) + sName + string(XML_R));
psElement->append(ValidateValue((string)szTempBuffer));
psElement->append(string(XML_L_END) + sName + string(XML_R) +
string(NEW_LINE));
}
}
Листинг
5.7.
Function GetECBElement
Функция ValidateValue проверяет, что специальные символы указаны с помощью альтернативных комбинаций символов. Функция применяется к строке, перед тем как строке присваивается статус значения элемента. Для подтверждения значения атрибута можно применять ValidateValue. XML не разрешает использование определенных специальных символов в позиции значения, если они не представлены в альтернативном виде. Как видно из листинга 5.8, символы, используемые для реализации XML-структуры: " = ", " ?" и " ?" – заменяются альтернативными эквивалентами.
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Name: ValidateValue
In: Constant reference to a string variable sValue. sValue is the
value being checked to see if it has a character requiring
escaping
Out: returns a string with the escaped characters in place
Purpose:
blindly replaces all special characters
with the escape sequence character so that XML will
be valid.
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
string ValidateValue(const string &sValue)
{
string sReturn;
sReturn = sValue;
FindAndReplace(&sReturn, &string("&"),&string("&"));
FindAndReplace(&sReturn, &string("="),&string("="));
FindAndReplace(&sReturn, &string("<"),&string("<"));
FindAndReplace(&sReturn, &string(">"),&string(">"));
FindAndReplace(&sReturn, &string("'"),&string("'"));
FindAndReplace(&sReturn, &string("\""),&string("""));
return sReturn;
}
Листинг
5.8.
Function ValidateValue
Функция FindAndReplace представляет собой утилиту для замещения всех вхождений строки. В расширении ISAPI SEUX она является идеальным механизмом для замещения одной фразы внутри строки другой фразой. Аргументы представляют собой указатели на строки:
- изменяемая строка (контейнер);
- строка, которую нужно заменить внутри контейнера (цель);
- строка, заменяющая цель в контейнере (замещение).
Строка STL содержит функции find и replace, используемые функцией FindAndReplace для поиска контейнера всех вхождений цели (см. листинг 5.9). Каждый раз при обнаружении цели в контейнере происходит ее замена, и начинается новый поиск. По завершении работы функции FindAndReplace контейнер обновляется замещениями, если таковые имеются.
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Name: FindAndReplace
In: psContainer - pointer to a string that will be searched
and edited if a value is discovered.
psTarget - pointer to a string that is being sought for
replacement.
psReplacement - pointer to a string that will replace the
the string pointed to in psTarget.
Out: nothing - but psContainer will be changed
Purpose:
searches string psContainer pointer for the string that
psTarget points to and replaces it with the string that
psReplacement points to.
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void FindAndReplace(string *psContainer,
string *psTarget,
string *psReplacement)
{
string::size_type idx;
idx = psContainer->find(*psTarget);
while (idx != string::npos)//an instance was found
{
//are we at the end of the string
if (psContainer->size() == idx)
{
*psContainer += *psReplacement;
break;
}
else
{
psContainer->replace
(idx, psTarget->size() , *psReplacement);
//advance beyond the current character
idx += psReplacement->size();
}
//look for next occurance
idx = psContainer->find(*psTarget, idx);
}
}
Листинг
5.9.
Функция GetElement работает аналогично функции GetECBElement ; она вызывается из функции HttpExtensionProc для конкатенации элементов из свойств ECB. Свойства извлекаются из ECB, после чего передаются вместе со своими именами и документом XML в функцию GetElement. GetElement размещает свойство ECB и соответствующее значение в XML документе и конкатенирует его с указателем документа XML, переданным функции GetElement. Осуществляется запрос следующих свойств:
- lpszLogData. Буфер размера HSE_LOG_BUFFER_LEN, используемый для размещения информации, добавляемой в файл журнала для данной транзакции HTTP-запроса.
- lpszMethod. Строковое значение используемого метода HTTP, например, GET, PUT или HEAD.
- lpszQueryString. Строковое значение символов в секции дополнительной информации адреса URL, исключая символ " ?". Аналогично значению переменной сервера QUERY_STRING.
- lpszPathInfo. Строковое значение секции URL, находящейся между библиотекой DLL расширения ISAPI и началом секции дополнительной информации URL. Обычно не содержит данных, если запрашивающее ПО не разместило в этом месте определенное значение.
- lpszContentType. Строковое значение типа содержимого отправленных по HTTP данных. Аналогично значению серверной переменной CONTENT_TYPE.
Когда функция HttpExtensionProc завершает получение содержимого всех возможных серверных переменных и свойств ECB, в документе XML указываются закрывающие тегов XML, и он передается функции SendResponse. SendResponse направляет запрашивающей программе строковое значение, переданное в функцию. Заголовок типа содержимого передается запрашивающему клиенту с помощью функции ECB ServerSupportFunction (см. листинг 5.10). Передаваемый заголовок представляется константой BASIC_HEADER, эквивалентной следующей строке: Content-type: text/html\r\n\r\n. За заголовками HTTP следуют два символа возврата каретки и новой строки, поскольку возвращаемые данные представляют собой текст. Можно указать и XML, однако если в запрашивающем браузере в XML зарегистрированы типы Multipurpose Internet Mail Extensions (MIME), то для отображения XML откроется зарегистрированная программа.
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Name: SendResponse
In: pECB - pointer to the extension control block
sValue - string reference to the value to be
written to the HTTP response
Out: nothing
Purpose:
writes the intended value to the HTTP response
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
void SendResponse(EXTENSION_CONTROL_BLOCK *pECB, string &sValue)
{
TCHAR szTempBuffer[BUFFER_LENGTH];
DWORD dwBufferSize = BUFFER_LENGTH;
// set content-type header
strcpy(szTempBuffer, BASIC_HEADER);
DWORD dwHeaderSize = strlen(szTempBuffer);
pECB->ServerSupportFunction(pECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER,
NULL,
&dwHeaderSize,
(LPDWORD) szTempBuffer);
//write value to http response
DWORD dwLength=sValue.length();
pECB->WriteClient( pECB->ConnID,
(PVOID)sValue.c_str(),
&dwLength,
HSE_IO_SYNC);
}
Листинг
5.10.
Function SendResponse
После отправки заголовка возврата отправляется содержимое с помощью функции WriteClient. Как показано в следующем примере, при передаче данных клиенту отправляется идентификатор соединения ConnID. Имеющееся значение, полученное из текущего экземпляра указателя, использовано в листинге 5.10. Содержимое передается функцией WriteClient с помощью пустого указателя в параметре Buffer. Содержимое, на которое ссылается указатель Buffer, должно равняться количеству байт, передаваемому клиенту, и указываться в параметре lpdwBytes. По завершении вызова lpdwBytes содержит количество переданных байт, если запись не осуществлялась асинхронно. Значение параметра dwSync определяет способ передачи данных клиенту. В листинге 5.10 с помощью макроса HSE_IO_SYNC указывается значение 0х00000001, т.е. запись выполняется синхронно, и пространство памяти, на которое ссылается указатель lpdwBytes, обновится по завершении WriteClient количеством байт, переданным клиенту. Если в макросе HSE_IO_ASYNC представлено значение 0х00000002, то данные, отправляемые клиенту, и функция обратной связи зафиксируют события передачи информации клиенту. Асинхронное использование функции WriteClient требует объявления функции обратной связи, а также отправки функцией ServerSupportFunction значения HSE_REQ_IO_COMPLETION для установки с клиентом транзакции асинхронной записи.
Функция WriteClient является членом ECB. Может показаться странным, что используемое описание ECB передается функции. Поскольку приложение IIS включает несколько нитей, в любой момент времени может потребоваться несколько экземпляров ECB, и вероятно выполнение записи в экземпляре ECB в другой экземпляр ECB. Ниже приведен пример WriteClient:
BOOL WriteClient( HCONN ConnID, LPVOID Buffer, LPDWORD lpdwBytes, DWORD dwSync );