Сценарии JavaScript в активных страницах Web

Счетчик посещений на базе cookie и программы CGI


Последний пример, приведенный в этой главе, показывает основные приемы работы с cookie в программах CGI и сценарии JavaScript, вставляемом в тело динамически формируемого документа HTML.

Внешний вид исходного документа HTML, вызывающего программы CGI, показан на рис. 7.10.

Рис. 7.10. Документ HTML, вызывающий программу CGI

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

Если нажать на кнопку с надписью Go to page, программа GCI создаст документ HTML, предварительно проанализировав заголовок пришедшего к ней запроса на предмет наличия в нем информации о cookie. Кнопка Remove All Cookies предназначена для вызова программы CGI с целью удаления cookie.

В том случае, когда этой информации нет, программа CGI динамически сформирует документ HTML, добавив к его заголовку HTTP заголовок Set-Cookie (рис. 7.11).

Рис. 7.11. Документ HTML, создаваемый программой CGI при первом посещении

При последующих посещениях cookie уже определен, и наша программа CGI получает его значение, интерпретируя это значение как счетчик посещений. Затем она увеличивает значение счетчика на единицу, и записывает заголовок Set-Cookie в заголовок HTTP создаваемого документа HTML с новым значением cookie.

Значение счетчика посещений отображается в теле документа (рис. 7.12).

Рис. 7.12. Документ HTML, создаваемый программой CGI при третьем посещении



Далее программа CGI вставляет в текст этого документа сценарий JavaScript, расположенный в отдельном файле. Данный сценарий определяет значение cookie своими средствами и отображает его вместе со значением счетчика посещений в многострочном окне редактирования.

Исходный документ HTML представлен в листинге 7.7.

Листинг 7.7. Файл chapter7/AgainCGI/AgainCGI.html

<HTML>

  <HEAD>

    <TITLE>Cookies demo</TITLE>

  </HEAD>

  <BODY BGCOLOR=white>

    <H1>Visit our page!</H1>

    <FORM METHOD=POST ACTION="http://frolov/scripts/again.exe?go">


      <P><INPUT TYPE="submit" VALUE="Go to page">

    </FORM>   

    <FORM METHOD=POST ACTION="http://frolov/scripts/again.exe?clear">

      <P><INPUT TYPE="submit" VALUE="Remove All Cookies">

    </FORM>   

  </BODY>

</HTML>

В этом документе определены две формы.

Первая форма предназначена для вызова программы CGI с параметром go, а вторая - с параметром clear.

Исходный текст программы CGI, использованной в нашем примере, вы найдете в листинге 7.8.

Листинг 7.8. Файл chapter7/AgainCGI/Again.c

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

// Расширение CGI, предназначенное для

// работы с cookie

//

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

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

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

//         или

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

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

#include <windows.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

// Прототипы функций, определенных в нашей программе

char *findCookie(char * szName);

void insertHTML(char * pszFileName, char * pszBuf);

void main(int argc, char *argv[])

{

  char * pszQueryString;

  char * pszCookie;

  char * pszMyCookie;

  int nCount;

  char szBuf[4096];

  char szBuf1[20];

 

  // Заголовок для добавления cookie

  char szCookieHeader[] =

    "Set-Cookie: AgainCount=0;\r\nContent-type: text/html\r\n\r\n";

 

  // Заголовок для удаления cookie

  char szCookieRemoveHeader[] =

    "Set-Cookie: AgainCount=0;expires=Mon 03-May-1993 12:00:00 GMT;"

    "\r\nContent-type: text/html;\r\n\r\n";

  // Получаем параметр запуска CGI

  pszQueryString = getenv("QUERY_STRING");

  // Получаем строку Cookie из заголовка HTTP

  pszCookie = getenv("HTTP_COOKIE");

  // Посещение страницы

  if(!strcmp(pszQueryString, "go"))

  {



    // Если cookie не обнаружен, создаем его

    if(pszCookie == NULL)

    {

      // Выводим заголовок для создания cookie

      printf("Content-type: text/html\r\n");

      printf(szCookieHeader);

     

      // Выводим приглашение для первого посещения

      printf("<HTML><HEAD><TITLE>Cookie

demo</TITLE></HEAD><BODY>");

      printf("<H2>Welcome to our page!</H2>");

    }

    else

    {

      // Получаем значение параметра cookie

      // с именем AgainCount

      pszMyCookie = findCookie("AgainCount");

      if(pszMyCookie != NULL)

      {

        // Преобразуем это значение в число и увеличиваем

        // на единицу при каждом посещении

        nCount = atoi(pszMyCookie);

        nCount++;

        sprintf(szBuf1, "%d", nCount);

        // Выводим заголовок для обновления cookie

        printf("Content-type: text/html\r\n");

        strcpy(szBuf, "Set-Cookie: AgainCount=");

        strcat(szBuf, szBuf1);

        strcat(szBuf, ";\r\nContent-type: text/html\r\n\r\n");

        printf(szBuf);

        // Выводим приглашение для повторных посещений

        printf("<H2>Welcome to our page AGAIN!</H2>");

       

        // Выводим счетчик посещений

        printf("<P>Access count: %s",szBuf1);

        // Вставляем документ HTML с текстом сценария

        // JavaScript, который тоже работает с cookie

        insertHTML("script.ht", szBuf);

        printf(szBuf);

      }

    }

  }

  // Удаление cookie

  else if(!strcmp(pszQueryString, "clear"))

  {

    // Выводим заголовок для удаления cookie

    printf("Content-type: text/html\r\n");

    printf(szCookieRemoveHeader);

    // Выводим сообщение об успешном удалении cookie

    printf("<HTML><HEAD><TITLE>Cookie demo</TITLE></HEAD><BODY>");



    printf("<P>Cookie Removed");

  }

  printf("</BODY></HTML>");

}

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

// findCookie

// Получение значение параметра cookie по его

// имени

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

char *findCookie(char * szName)

{

  char * pszCookie;

  char * pszBegin;

  char * pszEnd;

  char szBuf[4096];

  // Получаем текстовую строку cookie

  pszCookie = getenv("HTTP_COOKIE");

  if(pszCookie != NULL)

  {

    // Копируем ее в рабочий буфер

    strcpy(szBuf, pszCookie);

    // Ищем в строке имя параметра

    pszBegin = strstr(szBuf, szName);

    if(pszBegin == NULL)

      return NULL;

   

    else

    {

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

      pszBegin += strlen(szName) + 1;

      // Ищем символ ; и заменяем его на

      // двоичный нуль

      pszEnd = strstr(pszBegin, ";");

      if(pszEnd != NULL)

        *pszEnd = 0;

      // Возвращаем значение параметра

      return pszBegin;

    }

  }

}

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

// insertHTML

// Вставка в буфер содержимого текстового файла

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

void insertHTML(char * pszFileName, char * pszBuf)

{

  HFILE hSrcFile;

  DWORD dwFileSize;

  // Открываем файл

  hSrcFile = _lopen(pszFileName, OF_READ);

  // Определяем его длину

  dwFileSize = _llseek(hSrcFile, 0, 2);

 

  // Устанавливаем указатель на начало файла

  _llseek(hSrcFile, 0, 0);

  // Читаем файл в буфер

  _hread(hSrcFile, pszBuf, dwFileSize);

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

  pszBuf[dwFileSize] = '\0';

  // Закрываем файл

  _lclose(hSrcFile);

}

В переменной szCookieHeader мы подготовили заголовок Set-Cookie, предназначенный для создания параметра cookie с именем AgainCount:

char szCookieHeader[] =

 "Set-Cookie: AgainCount=0;\r\nContent-type: text/html\r\n\r\n";



Начальное значение этого параметра равно нулю.

Заголовок, хранящийся в переменной szCookieRemoveHeader, предназначен для удаления cookie:

char szCookieRemoveHeader[] =

  "Set-Cookie: AgainCount=0;expires=Mon 03-May-1993 12:00:00 GMT;\r\nContent-type: text/html;\r\n\r\n";

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

Сразу после запуска программа CGI получает значение переменных среды QUERY_STRING и HTTP_COOKIE:

pszQueryString = getenv("QUERY_STRING");

pszCookie = getenv("HTTP_COOKIE");

В первой из них хранится параметр запуска программы CGI, а во второй - строка cookie (если она определена).

Далее наша программа анализирует параметр запуска.

Если программа вызвана с параметром go, она проверяет переменную pszCookie. В эту переменную функция getenv записывает строку cookie или значение NULL, если cookie не определено.

При первом посещении cookie еще нет, поэтому наша программа добавляет к заголовку HTTP формируемого документа заголовок Set-Cookie:

printf("Content-type: text/html\r\n");

printf(szCookieHeader);

Затем программа выводит приглашение для первого посещения и завершает свою работу.

В том случае, если в принятом запросе уже имеется информация о cookie, программа CGI извлекает значение параметра cookie с именем AgainCount, вызывая для этого функцию findCookie:

pszMyCookie = findCookie("AgainCount");

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

Полученная строка преобразуется в численное значение при помощи функции atoi, после чего это значение увеличивается на единицу, преобразуется обратно в тестовую строку и записывается в буфер szBuf1:

nCount = atoi(pszMyCookie);

nCount++;

sprintf(szBuf1, "%d", nCount);

На следующем этапе программа формирует заголовок Set-Cookie с новым значением параметра AgainCount:

printf("Content-type: text/html\r\n");

strcpy(szBuf, "Set-Cookie: AgainCount=");



strcat(szBuf, szBuf1);

strcat(szBuf, ";\r\nContent-type: text/html\r\n\r\n");

printf(szBuf);

Этот заголовок вместе с заголовком Content-type записывается в создаваемый документ HTML.

Далее после вывода приглашения для повторного посещения страницы программа CGI записывает в документ новое значение счетчика посещений:

printf("<P>Access count: %s",szBuf1);

И, наконец, перед завершением своей работы программа вставляет в текст документа HTML файл со сценарием JavaScript, вызывая для этого функцию insertHTML:

insertHTML("script.ht", szBuf);

Эта функция определена в нашей программе, как и функция findCookie.

Когда программа CGI вызывается для удаления cookie с параметром clear, она выводит специально предназначенный для этого заголовок с просроченной датой:

printf("Content-type: text/html\r\n");

printf(szCookieRemoveHeader);

Теперь мы кратко расскажем о работе функции findCookie.

Получив текстовую строку cookie при помощи функции getenv, эта функция копирует строку в рабочий буфер, который можно редактировать (напомним, что содержимое буфера, полученного от функции getenv, изменять нельзя).

Далее, вызывая функцию strstr, мы ищем в рабочем буфере имя нужного нам параметра cookie. Если это имя найдено, то мы пропускаем символ равенства, ищем символ разделителя ‘;’ и заменяем его на двоичный нуль. После выполнения всех этих действий наша функция возвращает адрес искомой строки со значением нужного нам параметра cookie.

Функция insertHTML просто открывает файл, имя которого передается ей в качестве параметра, читает его содержимое в оперативную память, и затем в буфер, адрес которого передается через второй параметр.

Текст сценария, вставляемого функцией insertHTML в динамически формируемый документ HTML, представлен в листинге 7.9.

Листинг 7.9. Файл chapter7/AgainCGI/script.ht

<HR>

<P>Cookie information from JavaScript:

<FORM NAME="TestForm">

  <P><TEXTAREA NAME="Comment"



    ROWS="3" COLS="25">

  </TEXTAREA>

</FORM>   

<SCRIPT LANGUAGE="JavaScript">

<!--

function findCookie(szName)

{

  var i = 0;

  var nStartPosition = 0;

  var nEndPosition = 0; 

  var szCookieString = document.cookie; 

  var szTemp = "";

  while (i <= szCookieString.length)

  {

    nStartPosition = i;

    nEndPosition = nStartPosition + szName.length;

    if(szCookieString.substring(nStartPosition,nEndPosition) == szName)

    {

      nStartPosition = nEndPosition + 1;

      nEndPosition = document.cookie.indexOf(";",nStartPosition);

      if(nEndPosition < nStartPosition)

        nEndPosition = document.cookie.length;

      szTemp = document.cookie.substring(nStartPosition,nEndPosition); 

      return unescape(szTemp);

      break;   

    }

    i++; 

  }

  return "";

}

var szMyText="";

szMyText = findCookie("AgainCount");

if(szMyText != "")

{

  TestForm.Comment.value =

   "Cookie: " + document.cookie + "\nAccess count: " + szMyText;

}

// -->

</SCRIPT>

С функцией findCookie вы уже знакомы. Она предназначена для получения значения параметра cookie по его имени.

После завершения загрузки документа HTML наш сценарий при помощи этой функции получает текущее значение параметра cookie с именем AgainCount, установленное программой CGI:

var szMyText="";

szMyText = findCookie("AgainCount");

Далее это значение добавляется к полной строке cookie и отображается в многострочном поле редактирования:

TestForm.Comment.value =

 "Cookie: " + document.cookie + "\nAccess count: " + szMyText;

Форма, содержащая поле редактирования, определена в начале вставляемого файла сценария JavaScript.


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