7. Применение cookie

Возможно, изучая настройки браузера вы сталкивались с таким понятием, как cookie.

Что это такое?

Скажем сразу, что с кулинарным искусством это связано мало, хотя некоторую аналогию все же можно провести. Говоря кратко, cookie представляет собой свойство документа HTML. Данные cookie физически хранятся локально на компьютере пользователя, загрузившего к себе этот документ, в виде специального системного файла. С помощью cookie пользователь может настроить, или “приготовить” по собственному вкусу документ HTML, если для него предусмотрена такая настройка.

Проще всего представить себе cookie как набор строковых параметров, каждый из которых имеет имя и значение. Сценарий JavaScript может создавать cookie для документа HTML, определяя в нем произвольное количество параметров и задавая для них произвольные значения. После создания такой набор параметров становится принадлежностью данного конкретного документа HTML и может быть проанализирован, изменен или удален сценарием JavaScript.

Как мы уже говорили, основное, для чего нужно cookie - это для того, чтобы дать пользователю возможность настроить под свои потребности интерфейс активных документов HTML. Эти настройки могут анализироваться или не анализироваться сервером Web, но в любом случае они хранятся у пользователя. Настройки, разумеется, пропадут, если пользователь, скажем, отформатирует свой жесткий диск. После этого параметры настраиваемого документа HTML придется задавать заново.

Конечно, задачу индивидуальной настройки параметров страниц можно было бы решить и другими способами, например, при помощи расширений сервера Web, таких как программы CGI или приложения ISAPI. Для этого на сервере Web должна быть установлена база данных, хранящая настройки для всех зарегистрированных в ней пользователей.

В этом случае расширение сервера Web может динамически создавать настраиваемые документы HTML, используя для определения внешнего вида страниц параметры, хранящиеся в базе данных.

Наличие базы данных настройки пользователей усложняет сопровождение сервера Web, и потому неприемлемо во многих случаях. Например, если ваш сервер Web виртуальный и расположен на дисках сервера какого-либо провайдера Internet, создание базы данных может превратиться в трудно преодолимую проблему.

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

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

Среди других применений cookie можно отметить сетевые игры. Вы можете, например, хранить в cookie текущее состояние игры или другие параметры.

Выполнение основных операций с cookie

Рассмотрим основные операции с cookie, такие как создание cookie, получение и изменение значений параметров cookies, а также удаление cookie.

Создание cookie

Существуют два способа создание cookie, первый из которых используется расширениями сервера Web, а второй - сценариями JavaScript. Мы рассмотрим оба этих способа.

Первый способ: создание cookie расширением сервера Web

Для того чтобы создать cookie первым способом, расширение сервера Web обычно добавляет в заголовок HTTP динамически создаваемого документа HTML поле с именем Set-Cookie. В этом поле определяются имена и значения параметров cookie.

Когда расширение сервера Web вызывается из документа HTML, имеющего cookie, параметры cookie предаются этому расширению через поле Cookie заголовка HTTP и могут быть проанализированы.

Заголовок HTTP, предназначенный для создания cookie, выглядит следующим образом:

Set-Cookie: Имя=Значение; expires=Дата_GMT; 
path=Адрес_URL; domain=Домен; secure

Описание отдельных полей заголовка Set-Cookie приведено ниже:

Поле Описание
Имя Произвольное имя параметра, определенного в cookie. Здесь вы можете использовать любую строку, лишь бы в ней не было пробелов, запятых и двоеточий. В том случае, когда имя должно содержать перечисленные выше символы, используйте кодировку URL
Значение Текстовая строка значений параметров. В этой строке не должно быть пробелов, запятых и двоеточий, поэтому вы должны использовать для нее кодировку URL
expires Дата автоматического удаления cookie по Гринвичу. Если эта дата не указана, а параметр expires отсутствует, cookie будет удалено сразу после того, как браузер закончит сеанс связи с сервером Web
domain Доменная часть адреса URL, для которой действует данный cookie. Если этот параметр не указан, то по умолчанию используется доменный адрес URL документа HTML, где был установлен cookie
path Часть адреса URL, задающая путь к документу HTML, для которой действует данный cookie. Если этот параметр не указан, то по умолчанию используется адрес URL документа HTML, где был установлен cookie
secure Если указано это поле, данные cookie необходимо предавать только с использованием защищенного протокола SSL. Такой протокол используется серверами HTTPS

Все поля, кроме первых двух (Имя и Значение), необязательны.

Дата должна быть записана в формате День_недели, ДД-Мес-ГГ ЧЧ:ММ:СС GMT, где:

·       День_недели - английское трехбуквенное сокращение названия дня недели (например, Mon);

·       ДД - номер дня недели;

·       Мес - английское трехбуквенное сокращение названия месяца (например, Jun);

·       ГГ - две последние цифры года;

·       ЧЧ - часы;

·       ММ - минуты;

·       СС - секунды

Например, дата может быть указана так:

Mon, 07-Jun-93 14:45:00 GMT

Сделаем небольшое замечание относительно полей domain и path, определяющих условие, при котором выполняется установка cookie.

Когда браузер загружает документ HTML с сервера Web и среди заголовков HTTP этого документа присутствует заголовок Set-Cookie, он проверяет возможность установки cookie. В процессе проверки анализируется адрес URL, откуда был загружен этот документ, а также содержимое полей domain и path.

Если эти поля не указаны, то по умолчанию считаются, что они соответствуют адресу URL, по которому находится загруженный документ HTML. В этом случае выполняется установка cookie.

В том случае, когда указано поле domain, установка cookie выполняется только тогда, когда документ был загружен с сервера Web, принадлежащего данному домену.

С помощью параметра path можно установить ограничение на адреса URL в рамках домена, для которых выполняется установка cookie. При этом значение “/” соответствует всем адресам данного домена.

Одновременно сервер Web может создать несколько параметров cookie, включив в заголовок документа HTML несколько заголовков Set-Cookie.

Второй способ: создание cookie в сценарии JavaScript

Второй способ предполагает использование свойства document.cookie. Это свойство мы упомянули, рассказывая о свойствах и методах объекта document, создаваемого для документа HTML, загруженного в окно браузера.

В общем виде сценарий JavaScript может создать cookie следующим образом:

document.cookie = 
  "Имя=Значение; 

Здесь мы просто записываем в свойство cookie объекта document текстовую строку, определяющую cookie.

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

В качестве примера приведем исходный текст функции addCookie, которую мы будем использовать в своих сценариях для создания cookie:

function addCookie(szName,szValue,dtDaysExpires) 
{
   var dtExpires = new Date();
   var dtExpiryDate = "";

   dtExpires.setTime(dtExpires.getTime() + 
     dtDaysExpires * 24 * 60 * 60 * 1000);

   dtExpiryDate = dtExpires.toGMTString();

   document.cookie = 
    szName + "=" + szValue + "; expires=" + dtExpiryDate;
}

Функция addCookie получает три параметра.

Через параметр szName передается имя параметра, хранящегося в cookie. Параметр szValue определяет значение этого параметра cookie. Что же касается последнего параметра с именем dtDaysExpires, то он задает интервал времени по отношению к моменту создания cookie, когда этот cookie необходимо удалить.

Самое сложное в функции addCookie - это определение даты удаления cookie и преобразование этой даты в формат GMT. Данная задача решается следующим образом.

Прежде всего функция addCookie создает объект класса Date с помощью ключевого слова new:

var dtExpires = new Date();

Записанная таким образом в переменную dtExpires дата соответствует моменту вызова функции addCookie.

Далее с помощью метода getTime функция addCookie определяет текущую дату в миллисекундах и прибавляет к результату значение параметра dtDaysExpires, полученное функцией, умноженное на константу (24 * 60 * 60 * 1000):

dtExpires.getTime() + dtDaysExpires * 24 * 60 * 60 * 1000

Константа представляет собой количество часов в сутках, умноженное на количество минут в часе, затем на количество секунд в минуте, и, наконец, на количество миллисекунд в секунде.

Результат вычислений записывается при помощи метода setTime в переменную даты dtExpires. Теперь здесь хранится дата автоматического уничтожения cookie браузером. Осталось лишь преобразовать эту дату в формат GMT.

Такое преобразование нетрудно сделать с помощью специально предназначенного для этого метода toGMTString, возвращающего текстовую строку в нужном нам формате:

dtExpiryDate = dtExpires.toGMTString();

Теперь нам остается только сформировать текстовую строку определения cookie и записать ее в свойство document.cookie:

document.cookie = 
  szName + "=" + szValue + "; expires=" + dtExpiryDate;

На этом создание cookie завершено.

Теперь, когда в вашем распоряжении есть функция addCookie, создание cookie представляет собой очень простую задачу. Например, в следующей строке мы создаем cookie с именем Count, значением 0, причем через 10 дней браузер автоматически удалит этот cookie:

addCookie("Count","0",10);

При необходимости использования других параметров cookie, таких как path или domain, вы можете немного дополнить текст функции addCookie. С этой задачей вы легко справитесь самостоятельно.

Получение значения cookie

Итак, вы научились создавать cookie в сценариях JavaScript. Теперь решим другую задачу - определение значения параметров cookie.

Эта задача сводится к простому сканированию текстовой строки, полученной следующим образом:

var szCookieString = document.cookie;  

В этой строке вам нужно найти подстроку “Имя=Значение;”, а затем извлечь полученное значение.

Для облегчения этой работы вы можете воспользоваться функцией findCookie. Исходный текст этой функции приведен ниже:

function findCookie(szName) 
{
  var i = 0;
  var nStartPosition = 0;
  var nEndPosition = 0;  
  var szCookieString = document.cookie;  

  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;

      return document.cookie.substring( 
          nStartPosition,nEndPosition);  
      break;    
    }
    i++;  
  }
  return "";
}

После извлечения строки из свойства document.cookie и записи этой строки в переменную szCookieString функция findCookie организует цикл по всем символам этой строки. Условием завершения цикла является просмотр всех szCookieString.length символов.

Сравнивая имя параметра с подстрокой, извлеченной из строки szCookieString при помощи метода substring, функция findCookie пытается найти нужный параметр. Если такая попытка оказывается успешной, функция findCookie пропускает символ присваивания, извлекая значение параметра, ограниченное символом точка с запятой. Это значение возвращается функцией findCookie.

Если же поиск оказывается неудачным, функция findCookie возвращает пустую строку.

Как пользоваться функцией findCookie?

Во-первых, с помощью этой функции вы можете проверить, установлен ли для данного документа cookie с заданным именем:

if(findCookie("Visit") == "")
{
  // cookie с именем Visit установлен
  . . .
}
else
{
  // cookie с именем Visit не установлен
  . . .
}

Для того чтобы записать в текстовую переменную значение параметра cookie с заданным именем, вы должны сделать следующее:

var szVisitValue = findCookie("Visit");

Как видите, пользоваться функцией findCookie достаточно просто.

Изменение значения параметра cookie

Для изменения значения параметра cookie с заданным именем вы можете просто вызвать функцию addCookie еще раз:

addCookie("Count","0",10);
// Значение параметра Count равно 0
  . . .
addCookie("Count","5",10);
// Значение параметра Count равно 5

Здесь мы вначале установили для параметра Count значение 0, а затем изменили это значение на 5.

Удаление cookie

Самый простой способ удалить cookie - установить для него такое время автоматического удаления, которое уже прошло. Для этого нужно получить текущую дату, уменьшить ее, например, на одну микросекунду, а затем изменить значение document.cookie.

Все это делает функция removeCookie:

function removeCookie(szName) 
{
  var dtExpires = new Date();
  dtExpires.setTime(dtExpires.getTime() - 1);

  var szValue = findCookie(szName);

  document.cookie = szName + "=" + szValue +
    "; expires=" + dtExpires.toGMTString();
}

В последней строке этой функции мы указали такое значение параметра expires, которое вызывает немедленное удаление cookies браузером.

Ограничения на использование cookie

На использование cookie накладываются определенные ограничения, которые мы перечислили ниже:

·       всего может быть создано не более чем 300 cookie;

·       каждый cookie не может превышать по своему размеру 4 Кбайт;

·       для каждого домена может быть создано не более 20 cookie

Если указанные значения будут превышены, браузер может удалить самые старые cookie или обрезать значения параметров cookie.

Несколько примеров использования cookie

В этом разделе на примере конкретных сценариев JavaScript мы покажем, как можно использовать cookies для решения различных практических задач.

Фиксация повторных посещений страницы

В нашем первом примере документ HTML содержит форму с двумя кнопками (рис. 7.1).

Рис. 7.1. Кнопки для перехода к динамически создаваемому документу HTML и для удаления cookie

Если нажать на кнопку Go to page, сценарий JavaScript создаст новый документ HTML. Внешний вид этого документа зависит от того, сколько раз пользователь нажимал на эту кнопку.

Кнопка Remove All Cookies предназначена для удаления cookie, созданного в нашем документе HTML.

Когда вы нажимаете на кнопку Go to page в первый раз, cookie еще не создано. При этом создается документ HTML, внешний вид которого показан на рис. 7.2.

Рис. 7.2. Внешний вид созданного динамически документа HTML при первом посещении

Во второй и последующий разы внешний вид документа изменяется (рис. 7.3).

Рис. 7.3. Внешний вид созданного динамически документа HTML при третьем посещении

Теперь здесь виден новый заголовок, а также содержимое параметров cookie с именами Visit и Count.

При каждом новом посещении значение параметра Count будет увеличиваться на единицу. Если же в документе, показанном на рис. 7.1, нажать кнопку Remove All Cookies, подсчет посещений начнется заново.

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

Листинг 7.1. Файл chapter7/Again/Again.html

<HTML>
  <HEAD>
    <TITLE>Cookies demo</TITLE>
    <SCRIPT LANGUAGE="JavaScript">
    <!--
    function addCookie(szName,szValue,dtDaysExpires) 
    {
      var dtExpires = new Date();
      var dtExpiryDate = "";

      dtExpires.setTime(dtExpires.getTime() + 
        dtDaysExpires * 24 * 60 * 60 * 1000);

      dtExpiryDate = dtExpires.toGMTString();

      document.cookie = 
        szName + "=" + szValue + "; expires=" + 
        dtExpiryDate;
    }

    function findCookie(szName) 
    {
      var i = 0;
      var nStartPosition = 0;
      var nEndPosition = 0;  
      var szCookieString = document.cookie;  

      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;

          return document.cookie.substring( nStartPosition,nEndPosition);  
          break;    
        }
        i++;  
      }
      return "";
    }

    function removeCookie(szName) 
    {
      var dtExpires = new Date();
      dtExpires.setTime(dtExpires.getTime() - 1);

      var szValue = findCookie(szName);

      document.cookie = szName + "=" + szValue +
        "; expires=" + dtExpires.toGMTString();
    }

    function btnClick()
    {
      if(findCookie("Visit") == "")
      {
        addCookie("Visit","Alexandr_Frolov",10);
        addCookie("Count","0",10);

        document.write("<H2>You are welcome!</H2>");     
      }
      else
      {
        var szCnt = findCookie("Count");
        var i=0;

        if(szCnt != "")
        {
          i = szCnt;
          i++;
          szCnt = i.toString();
  
          addCookie("Count",szCnt,10);
        }

        document.write("<H2>You are welcome AGAIN!</H2>");     
        document.write(document.cookie);     
      }
    }
    // -->
    </SCRIPT>
  </HEAD>
  <BODY BGCOLOR=white>
    <H1>Visit our page!</H1>
    <FORM NAME="TestForm">
      <P><INPUT TYPE="button" VALUE="Go to page"
      onClick="btnClick();">
      <P><INPUT TYPE="button" VALUE="Remove All Cookies"
      onClick="removeCookie('Count');removeCookie('Visit')">
    </FORM>    
  </BODY>
</HTML>

Функции addCookie, findCookie и removeCookie, определенные в этом документе, вам уже знакомы. Они предназначены, соответственно, для создания cookie, извлечения значения заданного параметра cookie и удаления cookie.

Функция btnClick вызывается, когда пользователь нажимает в форме кнопку с надписью Go to page.

Прежде всего эта функция ищет параметр cookie с именем Visit. Если такой параметр не найден, считается, что страница посещается в первый раз. В этом случае функция btnClick создает параметры cookie с именами Visit и Count, а затем формирует текст документа HTML с приглашением:

addCookie("Visit","Alexandr_Frolov",10);
addCookie("Count","0",10);
document.write("<H2>You are welcome!</H2>");

В том случае, когда пользователь посещает страницу повторно, параметр cookie с именем Visit уже существует. В этом случае функция btnClick пытается найти параметр с именем Count и получить его значение:

var szCnt = findCookie("Count");

Это значение затем увеличивается на единицу и записывается обратно в параметр cookie с именем Count:

i = szCnt;
i++;
szCnt = i.toString();
addCookie("Count",szCnt,10);

Завершая свою работу, функция btnClick записывает приглашение для повторно посетивших страницу пользователей и отображает содержимое свойства document.cookie:

document.write("<H2>You are welcome AGAIN!</H2>");     
document.write(document.cookie);     

Обработчик события onClick кнопки с надписью Remove All Cookies вызывает функцию removeCookie для параметров cookie с именами Count и Visit, удаляя их:

<INPUT TYPE="button" VALUE="Remove All Cookies"
onClick="removeCookie('Count');removeCookie('Visit')">

Записная книжка Cookies Notepad

В следующем примере мы применили cookie для хранения произвольного текста, набранного пользователем в многострочном окне редактирования (рис. 7.4).

Рис. 7.4. Документ с записной книжкой Cookies Notepad

При первой загрузке документа HTML с записной книжкой окно редактирования остается пустым. Вы можете набрать здесь любой текст и записать его в cookie, нажав кнопку Store text. Если теперь закрыть документ HTML и открыть его вновь, набранный вами ранее текст появится в окне редактирования.

Для того чтобы удалить текст и cookie, достаточно нажать кнопку Clear text.

Исходный текст документа HTML с записной книжкой Cookies Notepad представлен в листинге 7.2.

Листинг 7.2. Файл chapter7/Notebook/Notebook.html

<HTML>
  <HEAD>
    <TITLE>Cookies demo</TITLE>
    <SCRIPT LANGUAGE="JavaScript">
    <!--
    function addCookie(szName,szValue,dtDaysExpires) 
    {
      var dtExpires = new Date();
      var dtExpiryDate = "";

      dtExpires.setTime(dtExpires.getTime() + dtDaysExpires * 24 * 60 * 60 * 1000);
      dtExpiryDate = dtExpires.toGMTString();

      document.cookie = szName + "=" + escape(szValue) + "; expires=" + dtExpiryDate;
    }

    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 "";
    }

    function removeCookie(szName) 
    {
      var dtExpires = new Date();
      dtExpires.setTime(dtExpires.getTime() - 1);
      var szValue = findCookie(szName);
      document.cookie = szName + "=" + szValue +
        "; expires=" + dtExpires.toGMTString();
    }

    function btnClick()
    {
      addCookie("MyText",TestForm.Comment.value,10);
    }

    // -->
    </SCRIPT>
  </HEAD>

  <BODY BGCOLOR=white>
    <H1>Cookies Notepad</H1>
    <FORM NAME="TestForm">
      <P><TEXTAREA NAME="Comment"
        ROWS="5" COLS="25" WRAP="physical">
      </TEXTAREA>

      <P><INPUT TYPE="button" VALUE="Store text"
      onClick="btnClick();">

      <INPUT TYPE="button" VALUE="Clear text"
      onClick="removeCookie('MyText');TestForm.Comment.value=''">
    </FORM>    

    <SCRIPT LANGUAGE="JavaScript">
    <!--
      var szMyText="";
      szMyText = findCookie("MyText");

      if(szMyText != "")
      {
        TestForm.Comment.value = szMyText;
      }
    // -->
    </SCRIPT>
  </BODY>
</HTML>

Функция addCookie, использованная нами в этом документе, имеет одну особенность: перед записью текстовой строки в параметр cookie она выполняет ее кодировку в формате URL, вызывая для этого функцию escape:

document.cookie = szName + "=" + escape(szValue) + "; expires=" + dtExpiryDate;

Это необходимо по той причине, что введенный в окне редактирования текст может содержать пробелы и любые другие символы.

Аналогичные изменения мы внесли и в функцию findCookie. Эта функция возвращает значение, перекодированное в обычный текст функцией unescape, выполняющей действия, обратные по отношению к функции escape:

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

Когда пользователь нажимает кнопку Store text, вызывается функция btnClick:

function btnClick()
{
  addCookie("MyText",TestForm.Comment.value,10);
}

Эта функция просто записывает в параметр cookie с именем MyText текстовую строку, извлеченную из многострочного поля редактирования TestForm.Comment.value.

При удалении текста кнопкой Clear text вызывается функция removeCookie, удаляющая параметр cookie с именем 'MyText, а также записывается пустая строка в окно многострочного редактирования:

<INPUT TYPE="button" VALUE="Clear text"
  onClick = "removeCookie('MyText'); TestForm.Comment.value=''">

В самом конце тела документа HTML находится небольшой фрагмент сценария JavaScript, запускающийся сразу после загрузки этого документа:

var szMyText="";
szMyText = findCookie("MyText");

if(szMyText != "")
{
  TestForm.Comment.value = szMyText;
}

Этот фрагмент пытается получить значение параметра cookie с именем MyText. Если это ему удается и функция findCookie возвращает непустую строку, полученная строка записывается в окно многострочного поля редактирования TestForm.Comment.value.

В результате сразу после загрузки документа это окно оказывается заполненным текстом, сохраненным в cookie.

Вы можете посмотреть системный файл, хранящий данные cookie. Для этого откройте каталог Temporary Internet Files, расположенный в системном каталоге Microsoft Windows 95 или Microsoft Windows NT. Там должен быть файл и именем Notebook\. Вы можете скопировать этот файл, например, на поверхность рабочего стола и открыть для просмотра любым текстовым редактором. Вы увидите примерно это:

MyText
This%20is%20sample%20text.%0D%0A%u042D%u0442%u043E%20%u0442%u0435%u043A%u0441%u0442%2C%20%u043A%u043E%u0442%u043E%u0440
%u044B%u0439%20%u044F%20%u043D%u0430%u0431%u0440%u0430%u043B%20%u0434
%u043B%u044F%20%u043F%u0440%u0438%u043C%u0435%u0440%u0430.%0D%0A%0D%0A%u0410%u043B%u0435%u043A%u0441%u0430
%u043D%u0434%u0440%20%u0424%u0440%u043E%u043B%u043E%u0432. ~~local~~/E:\JavaScript\Source\chapter7\Notebook\ 0 642302464 29173566 2120102016 29171554 *

В самом начале файла видно имя MyText параметра cookie. На следующих строках до строки ~~local~~ расположено значение параметра MyText, соответствующее тексту, показанному на рис. 7.4.

Вслед за строкой ~~local~~ идет локальный адрес URL документа и другие параметры cookies, такие как дата, по достижению которой браузер удалит cookie.

Если удалить файл Notebook\ и затем открыть документ HTML, многострочное окно редактирования будет пустым. Удалив этот файл, мы удалим и расположенный в нем cookie.

Настройка параметров документа HTML

Третий пример демонстрирует, как можно использовать cookie для настройки пользователем параметров документа HTML.

На рис. 7.5 показан документ HTML с двумя кнопками и переключателем, имеющим зависимую фиксацию.

Рис. 7.5. Главный документ HTML, при помощи которого можно выполнить настройку

Если нажать верхнюю кнопку, то в окне браузера появится документ HTML, созданный динамически сценарием JavaScript. В первый раз этот документ будет таким, как показано на рис. 7.6.

Рис. 7.6. Внешний вид документа при первом посещении

При помощи переключателей вы можете выбрать один из четырех цветов фона документа. Выбранное значение запоминается в cookie. Для того чтобы вернуться к цвету, принятому по умолчанию, в документе HTML, показанному на рис. 7.5, нужно нажать нижнюю кнопку.

При последующих посещениях внешний вид документа изменится (рис. 7.7).

Рис. 7.7. Внешний вид документа при третьем посещении

Его фон будет иметь такой цвет, какой был выбран при помощи переключателей.

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

Листинг 7.3. Файл chapter7/CustomPage/CustomPage.html

<HTML>
  <HEAD>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1251">
    <TITLE>Customize your page</TITLE>
    <SCRIPT LANGUAGE="JavaScript">
    <!--

    var szColor="White";

    function addCookie(szName,szValue,dtDaysExpires) 
    {
      var dtExpires = new Date();
      var dtExpiryDate = "";

      dtExpires.setTime(dtExpires.getTime() + dtDaysExpires * 24 * 60 * 60 * 1000);
      dtExpiryDate = dtExpires.toGMTString();

      document.cookie = szName + "=" + szValue + "; expires=" + dtExpiryDate;
    }

    function findCookie(szName) 
    {
      var i = 0;
      var nStartPosition = 0;
      var nEndPosition = 0;  
      var szCookieString = document.cookie;  

      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;

          return document.cookie.substring(nStartPosition, nEndPosition);  
          break;    
        }
        i++;  
      }
      return "";
    }

    function removeCookie(szName) 
    {
      var dtExpires = new Date();
      dtExpires.setTime(dtExpires.getTime() - 1);
      var szValue = findCookie(szName);
      document.cookie = szName + "=" + szValue +
        "; expires=" + dtExpires.toGMTString();
    }

    function btnGo()
    {
      if(findCookie("Count") == "")
      {
        addCookie("Count","0",10);
        addCookie("bgColor",szColor,10);

        document.write("<H2>Добро пожаловать!</H2>");     
        document.write("<P>Вы можете настроить цвет фона этой");
        document.write(" страницы при помощи переключателей,"); 
        document.write(" расположенных на главной странице.");     
        document.write("<P>Настройки будут использованы, когда вы");
        document.write(" посетите эту страницу в следующий раз.");
      }

      else
      {
        var szCnt = findCookie("Count");
        var i=0;

        if(szCnt != "")
        {
          i = szCnt;
          i++;
          szCnt = i.toString();
  
          addCookie("Count",szCnt,10);
        }

        document.write("<H2>Рады видеть вас снова!</H2>");     
        document.write("Вы посетили эту страницу в " + szCnt.bold() + " раз.");     
        document.bgColor=findCookie("bgColor");
      }
    }

    function chkRadio(form,value)
    {
      szColor = value;
      addCookie("bgColor",szColor,10);
    }

    function setDefault(form)
    {
      removeCookie('Count');
      szColor="White";
    }

    // -->
    </SCRIPT>
  </HEAD>
  <BODY BGCOLOR=white>
    <H1>Посетите вашу персональную страницу</H1>
    <FORM NAME="TestForm">
      <P>
      <INPUT TYPE="button" VALUE="Переход на страницу"
        onClick="btnGo();">
      <P><HR>
      <P>Настройка параметров персональной страницы

      <P><B>Цвет фона:</B>
      <P><INPUT TYPE="radio" NAME="Color" CHECKED VALUE="White"
        onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Белый

      <BR><INPUT TYPE="radio" NAME="Color" VALUE="Yellow"
        onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Желтый

      <BR><INPUT TYPE="radio" NAME="Color" VALUE="Lime"
        onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Зеленый

      <BR><INPUT TYPE="radio" NAME="Color" VALUE="Fuchsia"
        onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Малиновый

      <P>
      <INPUT TYPE="reset" VALUE="Параметры по умолчанию"
        onClick="setDefault(this.form);">
    </FORM>    
  </BODY>
</HTML>

Помимо функций addCookie, findCookie и removeCookie, предназначенных для работы с cookie, в сценарии определена переменная szColor, предназначенная для хранения выбранного пользователем цвета фона, а также функции btnGo, chkRadio и setDefault.

Функция btnGo прежде всего проверяет наличие параметра cookie с именем Count. Если такого параметра нет, сценарий считает, что пользователь просматривает этот документ в первый раз. В этом случае функция btnGo добавляет два параметра cookie с именами Count и bgColor:

addCookie("Count","0",10);
addCookie("bgColor",szColor,10);

Первый из них предназначен для хранения счетчика посещений, а второй - для хранения цвета фона.

Далее функция btnGo формирует документ HTML с приглашением для пользователя, просматривающего документ в первый раз, и завершает свою работу.

В том случае, когда сразу после вызова функция btnGo нашла параметр cookie с именем Count и получила его значение, это значение увеличивается на единицу и записывается обратно. Кроме того, оно отображается в тексте документа:

document.write("<H2>Рады видеть вас снова!</H2>");     
document.write("Вы посетили эту страницу в " +
  szCnt.bold()+" раз.");     

Затем функция btnGo устанавливает цвет фона сформированного документа HTML в соответствии со значением, извлеченным из параметра cookie с именем bgColor:

document.bgColor=findCookie("bgColor");

Функция chkRadio вызывается, когда пользователь включает один из переключателей выбора цвета:

function chkRadio(form,value)
{
  szColor = value;
  addCookie("bgColor",szColor,10);
}

Эта функция записывает значение выбранного цвета в переменную szColor, а также в параметр cookie с именем bgColor.

И, наконец, функция setDefault удаляет параметр cookie с именем Count и устанавливает в переменной szColor белый цвет фона, принятый по умолчанию:

function setDefault(form) 
{
  removeCookie('Count');
  szColor="White";
}

Эта функция вызывается, когда пользователь нажимает кнопку с надписью "Параметры по умолчанию":

<INPUT TYPE="reset" VALUE="Параметры по умолчанию"
  onClick="setDefault(this.form);">

Обратите внимание, что данная кнопка имеет тип reset. Когда пользователь нажимает на нее, в форме включается переключатель, задающий белый цвет фона. Это происходит потому, что указанный переключатель определен с параметром CHECKED, а кнопка типа reset устанавливает все органы управления формы в исходное состояние.

Получение cookie расширением сервера Web

В этом разделе мы приведем пример документа HTML, создающего cookie, а также исходные тексты расширения сервера ISAPI, отображающее на динамически создаваемой странице заголовки HTTP этого документа. Когда браузер создает cookie, расширение сервера получает заголовок HTTP_COOKIE и отображает его.

Наш документ аналогичен описанному ранее в разделе “Записная книжка Cookies Notepad” (рис. 7.8).

Рис. 7.8. Документ HTML, работающий с cookie и расширением сервера ISAPI

Дополнительно мы разместили в документе кнопку Send. С помощью этой кнопки содержимое окна редактирования и заголовок HTTP_COOKIES передается расширению ISAPI сервера Web.

Расширение ISAPI отображает содержимое cookie, как это показано на рис. 7.9.

Рис. 7.9. Результат работы расширения ISAPI

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

Листинг 7.4. Файл chapter7/NotebookISAPI/NotebookISAPI.html

<HTML>
  <HEAD>
    <TITLE>Cookies demo</TITLE>
    <SCRIPT LANGUAGE="JavaScript">
    <!--
    function addCookie(szName,szValue,dtDaysExpires) 
    {
      var dtExpires = new Date();
      var dtExpiryDate = "";

      dtExpires.setTime(dtExpires.getTime() + dtDaysExpires * 24 * 60 * 60 * 1000);
      dtExpiryDate = dtExpires.toGMTString();

      document.cookie = szName + "=" + escape(szValue) + "; 
expires=" + dtExpiryDate;
    }

    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 "";
    }

    function removeCookie(szName) 
    {
      var dtExpires = new Date();
      dtExpires.setTime(dtExpires.getTime() - 1);
      var szValue = findCookie(szName);
      document.cookie = szName + "=" + szValue +
        "; expires=" + dtExpires.toGMTString();
    }

    function btnClick()
    {
      addCookie("MyText",TestForm.Comment.value,10);
    }

    // -->
    </SCRIPT>
  </HEAD>

  <BODY BGCOLOR=white>
    <H1>Cookies Notepad</H1>
    <FORM NAME="TestForm" METHOD=POST ACTION="http://frolov/scripts/ishello.dll?">
      <P><TEXTAREA NAME="Comment"
        ROWS="5" COLS="25" WRAP="physical">
      </TEXTAREA>

      <P><INPUT TYPE="button" VALUE="Store text"
      onClick="btnClick();">

      <INPUT TYPE="button" VALUE="Clear text"
      onClick = 
        "removeCookie('MyText');TestForm.Comment.value=''">

      <P><INPUT TYPE=submit VALUE="Send">
    </FORM>    

    <SCRIPT LANGUAGE="JavaScript">
    <!--
      var szMyText="";
      szMyText = findCookie("MyText");

      if(szMyText != "")
      {
        TestForm.Comment.value = szMyText;
      }

    // -->
    </SCRIPT>
  </BODY>
</HTML>

В нем, по сравнению с документом из раздела “Записная книжка Cookies Notepad”, мы добавили параметр ACTION в оператор <FORM>, а также кнопку типа submit с надписью Send. С помощью этой кнопки данные из формы отправляются расширению ISAPI:

  . . .
<FORM NAME="TestForm" METHOD=POST ACTION="http://frolov/scripts/ishello.dll?">
  . . .
<INPUT TYPE=submit VALUE="Send">
. . .

Исходный текст расширения ISAPI представлен в листинге 7.5. Он сделан на базе примера, взятого из 29 тома нашей “Библиотеки системного программиста” (раздел “Приложение ISHELLO” восьмой главы).

Листинг 7.5. Файл chapter7/NotebookISAPI/ishello.c

// ===============================================
// Расширение ISAPI ishello.c
// Пример расширения ISAPI, отображающего 
// содержимое cookie
//
// (C) Фролов А.В., 1997, 1998
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//         или
//         http://www.dials.ccas.ru/frolov
// ===============================================

#include <windows.h>
#include <httpext.h>

// =========================================================
// Функция GetExtensionVersion
// Запись версии интерфейса ISAPI и
// строки описания расширения
// =========================================================
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
  // Записываем версию интерфейса ISAPI
  pVer->dwExtensionVersion = 
    MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR );

  // Записываем строку описания расширения
  lstrcpyn(pVer->lpszExtensionDesc,
    "Cookie show ISAPI DLL", HSE_MAX_EXT_DLL_NAME_LEN);

  return TRUE;
}

// =========================================================
// Функция HttpExtensionProc
// =========================================================
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *lpECB)
{
  CHAR  szBuff[4096];
  CHAR  szTempBuf[4096];
  
  DWORD  dwSize;

  // Нулевой код состояния - признак успешного выполнения
  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>Hello from ISAPI Extension!</H2>\n");

  // Добавляем разделительную линию
  strcat(szBuff, "<HR>");
 
  // Добавляем версию интерфейса 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>ContentType: %s", 
    lpECB->lpszContentType);
  strcat(szBuff, szTempBuf);

  // Отображаем содержимое COOKIE
  strcat(szBuff, "<HR><P><B>Cookie:</B><BR>");

  dwSize = 4096;
  lpECB->GetServerVariable(lpECB->ConnID,
    (LPSTR)"HTTP_COOKIE", (LPVOID)szTempBuf, &dwSize);
  strcat(szBuff, szTempBuf);

  // Конечный фрагмент документа 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;
}

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

Листинг 7.6. Файл chapter7/NotebookISAPI/ishello.def

LIBRARY	     ishello
DESCRIPTION  'Simple ISAPI DLL'
EXPORTS
    GetExtensionVersion
    HttpExtensionProc

Для извлечения значения cookie, предаваемого расширению через заголовки HTTP, мы использовали функцию GetServerVariable, указав ей в качестве второго параметра имя интересующей нас переменной HTTP_COOKIE:

lpECB->GetServerVariable(lpECB->ConnID,
    (LPSTR)"HTTP_COOKIE", (LPVOID)szTempBuf, &dwSize);

Полученное таким образом значение дописывается в буфер динамически создаваемого документа HTML. Этот буфер впоследствии будет отправлен клиенту при помощи функции ServerSupportFunction.

Счетчик посещений на базе 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.

Настройка браузера для работы с cookie

Хотя cookie не представляют собой никакой опасности для пользователей, не все пожелают, чтобы хранящаяся в них информация передавалась на сервер Web. В сочетании с другими полями заголовка HTTP данные cookie позволяют собирать определенную статистику о пользователях, а это нравится далеко не всем.

Настраивая браузер соответствующим образом, пользователи могут ограничить или вовсе отключить возможность работы с cookie. Вы должны это учитывать, если при создании активных документов HTML надеетесь на использование cookie.

Браузер Microsoft Internet Explorer версии 4.0 допускает установку трех режимов работы с cookies. Эти режимы указываются на странице Advanced блокнота Internet Options (рис. 7.13), доступного из меню View браузера.

Рис. 7.13. Установка режимов работы с cookie

По умолчанию включен переключатель Always accept cookies, разрешающий использование cookie.

Переключатель Disable all cookie use полностью запрещает использование cookie.

Если включить переключатель Prompt before accepting cookies, при попытке установить cookies на экране появится предупреждающее сообщение (рис. 7.14).

Рис. 7.14. Предупреждающее сообщение при попытке записи данных cookie

Нажав кнопку More Info, вы можете получить подробные сведения о данных cookie, которые планируется записать на диск вашего локального компьютера, а также просмотреть эти данные в поле Data.