Сервер Web своими руками. Язык HTML, приложения CGI и ISAPI

       

Приложение ISFORM


Приложение ISFORM демонстрирует способ получения и обработки данных, полученных от формы, расширением ISAPI. Аналогичные действия выполняла программа CGI с именем CONTROLS, описанная нами в предыдущей главе.

Исходный текст документа HTML, содержащий форму, представлен в листинге 8.4. Эта форма уже использовалась нами ранее в предыдущей главе (рис. 7.2), поэтому мы не будем показывать ее внешний вид снова для экономии места.

Листинг 8.4. Файл chap8\isform\isform.htm

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">

<HTML>

<HEAD>

 <TITLE>Органы управления в формах</TITLE>

</HEAD>

<BODY BGCOLOR=#FFFFFF>

<FORM METHOD=POST ACTION="http://frolov/scripts/isform.dll?Param1|Param2|Param3">

  <TABLE>

    <TR>

      <TD VALIGN=TOP>Текстовое поле TEXT</TD>



      <TD><INPUT TYPE=text NAME="text1" VALUE="Sample of text1" SIZE=30></TD>

    </TR>

    <TR>

      <TD VALIGN=TOP>Текстовое поле PASSWORD</TD>

      <TD><INPUT TYPE=password NAME="pwd" VALUE="Sample of password"></TD>

    </TR>

    <TR>

      <TD VALIGN=TOP>Текстовое поле TEXTAREA</TD>

      <TD><TEXTAREA NAME="text2" ROWS=4 COLS=30>Sample of text</TEXTAREA></TD>

    </TR>

    <TR>

      <TD VALIGN=TOP>Переключатели CHECKBOX</TD>

      <TD>

        <INPUT TYPE=CHECKBOX NAME="chk1" VALUE="on" CHECKED>Первый<BR>

        <INPUT TYPE=CHECKBOX NAME="chk2" VALUE="on">Второй<BR>

        <INPUT TYPE=CHECKBOX NAME="chk3" VALUE="on" CHECKED>Третий<BR>

      </TD>

    </TR>

    <TR>

      <TD VALIGN=TOP>Переключатели RADIO</TD>

      <TD>

        <INPUT TYPE=RADIO NAME="rad" VALUE="on1" CHECKED>Первый<BR>


        <INPUT TYPE=RADIO NAME="rad" VALUE="on2">Второй<BR>

        <INPUT TYPE=RADIO NAME="rad" VALUE="on3">Третий<BR>

      </TD>

    </TR>

    <TR>

      <TD VALIGN=TOP>Список</TD>

      <TD>

        <SELECT NAME="sel" SIZE="1">

          <OPTION Value="First Option">First Option</OPTION>

          <OPTION Value="Second Option">Second Option</OPTION>

          <OPTION Value="None">None Selected</OPTION>

        </SELECT>

      </TD>

    </TR>

    <TR>

      <TD VALIGN=TOP>Скрытый орган управления</TD>

      <TD><INPUT TYPE=HIDDEN NAME="hid" VALUE="Hidden"></TD>

    </TR>

  </TABLE>

<BR><INPUT TYPE=submit VALUE="Send">&nbsp;

<INPUT TYPE=reset VALUE="Reset">

<P><INPUT TYPE=IMAGE SRC="send.gif" BORDER=0>

</FORM>

</BODY>

</HTML>

Вызов расширения ISAPI выполняется в форме с помощью параметра ACTION оператора <FORM>, как это показано ниже:

ACTION="http://frolov/scripts/isform.dll?Param1|Param2|Param3">

После разделительного символа “?” расширению передается строка параметров Param1|Param2|Param3.

Результат обработки формы показан на рис. 8.2.



Рис. 8.2. Результат обработки формы расширением ISAPI с именем isform.dll

Обратите внимание, что поля TotalBytes и Available содержат одинаковые значения. Следовательно, все принятые данные поместились в буфере предварительной загрузки. И это не удивительно - форма передала всего 127 байт данных.

Исходный текст расширения isform.dll показан в листинге 8.5.

Листинг 8.5. Файл chap8\isform\isform.c

// ===============================================

// Расширение ISAPI isform.c

// Обработка данных, полученных от формы,

// при помощи расширения ISAPI



//

// (C) Фролов А.В., 1997

// E-mail: frolov@glas.apc.org

// WWW:    http://www.glasnet.ru/~frolov

//         или

//         http://www.dials.ccas.ru/frolov

// ===============================================

#include <windows.h>

#include <httpext.h>

// Прототипы функций перекодировки

void DecodeStr(char *szString);

char DecodeHex(char *str);

// =============================================================

// Функция GetExtensionVersion

// Запись версии интерфейса ISAPI и

// строки описания расширения

// =============================================================

BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)

{

  // Записываем версию интерфейса ISAPI

  pVer->dwExtensionVersion =

    MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR );

  // Записываем строку описания расширения

  lstrcpyn(pVer->lpszExtensionDesc,

    "Form Parser ISAPI DLL", HSE_MAX_EXT_DLL_NAME_LEN);

  return TRUE;

}

// =============================================================

// Функция HttpExtensionProc

// =============================================================

DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *lpECB)

{

  CHAR  szBuff[4096];

  CHAR  szTempBuf[4096];

  char * szPtr;

  char * szParam;

 

  // Нулевой код состояния - признак успешного выполнения

  lpECB->dwHttpStatusCode = 0;

  // Записываем в буфер заголовок HTTP и начальный

  // фрагмент формируемого динамически документа HTML

  wsprintf(szBuff,

    "Content-Type: text/html\r\n\r\n"

    "<HTML><HEAD><TITLE>Simple ISAPI Extension</TITLE></HEAD>\n"

    "<BODY BGCOLOR=#FFFFFF><H2>Information from ECB</H2>\n");

  // Добавляем версию интерфейса ISAPI

  wsprintf(szTempBuf, "<P>Extension Version: %d.%d",

    HIWORD(lpECB->dwVersion), LOWORD(lpECB->dwVersion));

  strcat(szBuff, szTempBuf);



 

  // Название метода передачи данных

  wsprintf(szTempBuf, "<BR>Method: %s", lpECB->lpszMethod);

  strcat(szBuff, szTempBuf);

 

  // Строка параметров запуска расширения ISAPI

  wsprintf(szTempBuf, "<BR>QueryString: %s",

    lpECB->lpszQueryString);

  strcat(szBuff, szTempBuf);

 

  // Физический путь к программному файлу расширения ISAPI

  wsprintf(szTempBuf, "<BR>PathTranslated: %s",

    lpECB->lpszPathTranslated);

  strcat(szBuff, szTempBuf);

  // Полный размер данных, которые нужно получить

  wsprintf(szTempBuf, "<BR>TotalBytes: %d",

    lpECB->cbTotalBytes);

  strcat(szBuff, szTempBuf);

  // Сколько доступно предварительно прочитанных данных

  wsprintf(szTempBuf, "<BR>Available: %d",

    lpECB->cbAvailable);

  strcat(szBuff, szTempBuf);

  // Тип данных

  wsprintf(szTempBuf, "<BR>ContentType: %s",

    lpECB->lpszContentType);

  strcat(szBuff, szTempBuf);

  lstrcpyn(szTempBuf, lpECB->lpbData, lpECB->cbAvailable + 1);

  szTempBuf[lpECB->cbAvailable + 1] = '\0';

 

  strcat(szBuff, "<H2>Принятые данные</H2>");

  strcat(szBuff, szTempBuf);

  // Перекодируем данные и отображаем результат

  // перекодировки

  DecodeStr(szTempBuf);

  strcat(szBuff, "<H2>Данные после перекодировки</H2>");

  strcat(szBuff, szTempBuf);

  // Выводим в документ список значений полей формы

  strcat(szBuff, "<H2>Список значений полей</H2>");

  szTempBuf[lpECB->cbAvailable] = '&';

  szTempBuf[lpECB->cbAvailable + 1] = '\0';

 

  for(szParam = szTempBuf;;)

  {

      szPtr = strchr(szParam, '&');

      if(szPtr != NULL)

      {

        *szPtr = '\0';

        DecodeStr(szParam);

        strcat(szBuff, szParam);

        strcat(szBuff, "<BR>");

        szParam = szPtr + 1;



        if(szParam >= (szTempBuf + lpECB->cbAvailable))

          break;

      }

      else

        break;

  }

  // Конечный фрагмент документа HTML

  strcat(szBuff, "</BODY></HTML>"); 

  // Посылаем содержимое буфера удаленному пользователю

  if(!lpECB->ServerSupportFunction(lpECB->ConnID,

    HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL,

    (LPDWORD)szBuff))

  {

    // Если послать данные не удалось,

    // завершаем работу нашего расширения ISAPI

    // с кодом ошибки

    return HSE_STATUS_ERROR;

  }

  // Записываем код успешного завершения

  lpECB->dwHttpStatusCode = 200;

 

  // Возвращаем принак успешного завершения 

  return HSE_STATUS_SUCCESS;

}

// ------------------------------------------------

// Функция DecodeStr

// Раскодирование строки из кодировки URL

// ------------------------------------------------

void DecodeStr(char *szString)

{

  int src;

  int dst;

  char ch;

  // Цикл по строке

  for(src=0, dst=0; szString[src]; src++, dst++)

  {

    // Получаем очередной символ перекодируемой строки

    ch = szString[src];

    // Заменяем символ "+" на пробел

    ch = (ch == '+') ? ' ' : ch;

   

    // Сохраняем результат

    szString[dst] = ch;

   

    // Обработка шестнадцатеричных кодов вида "%xx"

    if(ch == '%')

    {

      // Выполняем преобразование строки "%xx"

      // в код символа

      szString[dst] = DecodeHex(&szString[src + 1]);

      src += 2;

    }

  }

 

  // Закрываем строку двоичным нулем

  szString[dst] = '\0';

}

// ------------------------------------------------

// Функция DecodeHex

// Раскодирование строки "%xx"

// ------------------------------------------------

char DecodeHex(char *str)

{

  char ch;

  // Обрабатываем старший разряд

  if(str[0] >= 'A')

    ch = ((str[0] & 0xdf) - 'A') + 10;

  else

    ch = str[0] - '0';



  // Сдвигаем его влево на 4 бита

  ch <<= 4;

  // Обрабатываем младший разряд и складываем

  // его со старшим

  if(str[1] >= 'A')

    ch += ((str[1] & 0xdf) - 'A') + 10;

  else

    ch += str[1] - '0';

  // Возвращаем результат перекодировки

  return ch;

}

Для перекодирования принятых данных из кодировки URL мы использовали здесь функции DecodeStr и DecodeHex, описанные нами в разделе “Программа CONTROLS” предыдущей главы.

Перед тем как выполнить перекодировку принятых данных, расширение копирует эти данные во временный буфер szTempBuf, и закрывает его двоичным нулем, превращая в строку. Такая операция допустима, если передаются только текстовые данные (а это как раз наш случай). После копирования адрес буфера передается функции DecodeStr:

lstrcpyn(szTempBuf, lpECB->lpbData, lpECB->cbAvailable + 1);

szTempBuf[lpECB->cbAvailable + 1] = '\0';

DecodeStr(szTempBuf);

Сканирование и вывод значений отдельных полей формы выполняется аналогично тому, как это делалось в программе CGI с именем CONTROLS. Однако в отличие от указанной программы, значения отдельных полей не выводятся в стандартный поток STDOUT, а дописываются в конец буфера szBuff функцией strcat.

Файл определения модуля библиотеки DLL приложения приведен в листинге 8.6.

Листинг 8.6. Файл chap8\isform\isform.def

LIBRARY            isform

DESCRIPTION  'Form Parser ISAPI DLL'

EXPORTS

    GetExtensionVersion

    HttpExtensionProc


Содержание раздела