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

Как это устроено

В 23 томе “Библиотеки системного программиста”, который называется “Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets” мы описали основы технологий, которые широко используются в сети Internet. Если вы никогда раньше не работали в этой сети и не имеете о ней ни малейшего представления, указанный том может послужить для вас отправной точкой. В книге, которую вы сейчас читаете, есть все необходимое для создания сервера Web или размещения своей информации на сервере Web поставщика услуг Internet, поэтому предварительное знакомство с 23 томом “Библиотеки системного программиста” не обязательно, хотя и желательно.
Если же вы знакомы с технологиями Internet, то можете пропустить первую главу и сразу перейти ко второй, где мы рассказываем о создании статических документов HTML.

Создание документов HTML

В этой главе мы займемся изготовлением простейших статических документов на языке HTML. Научившись создавать такие документы, вы сможете разместить их на сервере WWW поставщика услуг Internet или даже создать свой виртуальный сервер WWW на сервере этого поставщика.
Язык HTML в своем развитии прошел несколько версий. Заметим, что после версий 1.0 и 2.0 в марте 1995 года был подготовлен черновой вариант версии 3.0, в которой по сравнению с версией 2.0 было много добавлений. Не углубляясь пока в детали, отметим, что основные производители навигаторов (фирмы Netscape и Microsoft) разработали свои расширения языка HTML, которые оказались несовместимыми между собой. В настоящее время ситуация осталась прежней, хотя совместимость навигаторов Netscape Navigator версии 3.0 и Microsoft Internet Explorer версии 3.0 несколько улучшилась.
На смену неудачному, так и не реализованному стандарту языка HTML версии 3.0, был разработан стандарт версии 3.2. В разработке этого стандарта участвовали такие фирмы как Netscape Communications Corporation, Microsoft, IBM, Novell, SoftQuad, Sun Microsystems и Spyglass. Возможно, что такой представительный состав разработчиков стандарта положительно повлияет на совместимость навигаторов.

Таблицы в документах HTML

Очень часто приходится размещать в документе HTML табличные данные. Это можно сделать двумя способами.
Первый способ заключается в том, что вы оформляете текст таблицы шрифтом с фиксированной шириной символов, а для выравнивания колонок используете символы пробела. Этот способ будет работать, однако он едва ли позволит вам создать таблицу, которая будет выглядеть красиво.
Второй способ предполагает использование операторов языка HTML, специально предназначенных для создания таблиц. Эти операторы открывают перед вами широкие возможности: вы можете использовать рамки вокруг всех или только некоторых ячеек и строк таблицы, создавать таблицы, имеющие заголовки и подписи, размещать в ячейках таблицы не только текст, но и графику или произвольные объекты, использовать в качестве фона для ячеек растровые графические изображения.
Заметим, что таблицы можно использовать в документах HTML не только для отображения табличных данных, но и для решения других задач, например, для размещения различных объектов. Например, в ячейках таблицы могут располагаться графические изображения, ссылки и так далее.
Для ячейки таблицы можно указать цветовой фон и отступ, что позволяет изображать текст на цветном фоне с отбивкой. Этот прием часто используется в Internet, так как значительно улучшает внешний вид страницы.

Графика, анимация, видео и звук

В этой главе мы расскажем о том, как оживить страницы вашего сервера WWW, разместив на них графические изображения, видеофрагменты, анимацию, а также звук, - словом мультимедийную информацию.
Для подготовки мультимедийной информации вам могут потребоваться специальные приложения, такие как графические редакторы, средства ввода и обработки звука. В качестве примера мы можем назвать такие графические редакторы как Corel Draw и Adobe Photoshop.
Не исключено, что вам придется приобрести видеокамеру, адаптер для ввода видеосигнала от видеомагнитофона или видеокамеры, а также редактор файлов с видеофрагментами. В 15 томе “Библиотеки системного программиста”, который называется “Мультимедиа для Windows”, мы рассмотрели основные средства и методики, пользуясь которыми вы сможете самостоятельно создавать небольшие видеофрагменты в формате файлов AVI.
Если вы не обладаете художественными талантами, необходимыми для создания графических изображений или видеофрагментов, можно их заказать у художника либо приобрести отдельно. В любом случае вам нужно позаботиться о соблюдении авторских прав на всю информацию, которую вы собираетесь опубликовать на вашем сервере WWW, а также на все приложения, которые вы установите как расширение сервера (программы CGI или расширения типа ISAPI).

Ссылки в документах HTML

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

Фреймы

Большинство документов HTML не помещается на одной странице, поэтому для их просмотра пользователю приходится сдвигать документ в окне навигатора. Кроме того, для навигации необходимо искать ссылки на другие документы, которые могут быть расположены в любом, самом неожиданном месте страницы.
Вы сможете сделать свой сервер намного удобнее для просмотра, если предоставите пользователю многооконный интерфейс, реализованный при помощи фреймов. В этом случае окно навигатора разделяется на несколько окон (фреймов), в каждом из которых отображается содержимое различных документов HTML (рис. 6.1). Таким образом, можно работать одновременно с несколькими документами.
Рис. 6.1. Страница нашего сервера WWW, реализованная с использованием фреймов
Наша страница состоит из трех фреймов. Первый фрейм расположен в верхней части окна и служит для отображение логотипа. Второй фрейм предназначен для отображения ссылок на различные разделы сервера, представляющие собой ни что иное, как обычные документы HTML. И, наконец, третий фрейм имеет самые большие размеры и предназначен для просмотра документов.
Мы настроили параметры фрейма оглавления и фрейма просмотра таким образом, что пользователь может менять их горизонтальный размер, передвигая мышью вертикальную разделительную линию, расположенную между фреймами. Если содержимое документа не помещается внутри фрейма, в правой части соответствующего окна появляется полоса просмотра.
Пользуясь ссылками в окне фрейма оглавления, вы можете загружать в фрейм просмотра различные документы, которые, в свою очередь, также могут иметь ссылки. Разумеется, мы не претендуем на то, что расположение и назначение фреймов выбраны на нашем сервере оптимальным образом, однако вы можете создать для своего сервера любое количество фреймов и расположить их как угодно.
Заметим, что далеко не все навигаторы способны работать с фреймами. Поэтому несмотря на то что фреймы придают страницам сервера более привлекательный вид и облегчают работу пользователя, их можно встретить далеко не на всех серверах WWW. В качестве альтернативы фреймам обычно применяют таблицы.

Приложения CGI

До настоящего момента мы рассказывали вам о том, как разместить в документах HTML различную статическую информацию. Статическую в том смысле, что она не изменяется во времени без вмешательства администратора сервера WWW. Если сервер WWW содержит только статические документы HTML, то такой сервер мы будем называть статическим, или пассивным.
Вне всякого сомнения, пассивные серверы очень нужны, так как далеко не всякая информация изменяется динамически. Однако есть приложения, в которых нужны серверы, не просто отображающие мультимедийную информацию, но и способные вести диалог с пользователем в интерактивном режиме, выполнять обращения к базам данных, а также выполнять другую аналогичную работу. Например, если даже информация по своему характеру является статической, но ее объем чрезвычайно большой, поиск по гипертекстовым ссылкам может отнять слишком много времени. Было бы удобнее в этом случае организовать запросный режим для поиска по ключевым словам.
Серверы, которые ведут диалог с удаленным пользователем или выполняют обработку данных пользователя, мы будем называть активными.
Наиболее известный способ создания активных серверов WWW заключается в использовании так называемых приложений CGI. В отечественной литературе, посвященной серверам WWW, часто встречается транслитерация “CGI-скрипты”, которая произошла от оригинального термина CGI Scripts.
Что кроется за аббревиатурой CGI?
CGI - это стандартный шлюзовой интерфейс (Common Gateway Interface) для запуска внешних программ под управлением сервера WWW. Соответственно, приложениями CGI называются программы, которые, пользуясь этим интерфейсом, получают через протокол HTTP информацию от удаленного пользователя, обрабатывают ее, и возвращают результат обработки обратно в виде ссылки на уже существующий документ HTML или другой объект (например, графическое изображение) или в виде документа HTML, созданного динамически.
Передача информации от удаленного пользователя приложению CGI обычно выполняется следующим образом.

В документе HTML, который создается для ввода информации, предназначенной для обработки, размещается форма ввода. Эта форма состоит из необходимых органов управления: полей редактирования текстовой информации, переключателей, списков и так далее. Больше всего форма ввода похожа на привычные вам диалоговые панели операционной системы Microsoft Windows. Каждому органу управления присваивается произвольное имя. Кроме того, в этой форме должна быть кнопка, которую следует нажать после заполнения формы.
Когда пользователь заполняет форму и нажимает указанную кнопку, данные передаются приложению CGI, путь к которому задается в заголовке формы. Это приложение получает через протокол HTTP данные из полей формы в виде пар значений “имя поля/значение”.
После обработки полученных данных приложение CGI создает документ HTML, и записывает его в стандартное устройство вывода stdout. Этот документ автоматически передается удаленному пользователю.
Все описанные процедуры мы рассмотрим в этой главе более подробно. Кроме того, мы приведем исходные тексты различных приложений CGI. Заметим, что возможны и другие сценарии работы с программами CGI.
Так как приложение CGI является ни чем иным, как программой, вы должны оттранслировать ее для той операционной системы, под управлением которой работает ваш сервер WWW. В некоторых случаях вы можете найти более удобным создавать программы CGI с использованием специально предназначенных для этого интерпретаторов, таких как Perl, или языка пакетных заданий. Например, вы можете создать программу CGI для сервера Microsoft Information Server как обычный пакетный файл *.bat. В нашей книге мы сконцентрируемся на использовании для создания программ CGI мобильного языка программирования C. Транслятор этого языка вы можете найти в любой операционной системе.

Приложения ISAPI

В этой главе мы расскажем о приложениях ISAPI, дополняющих возможности сервера Microsoft Information Server. Все эти приложения можно разделить на две группы: расширения ISAPI и фильтры ISAPI.
Расширения ISAPI по своему назначению напоминают уже изученные вами программы CGI. Однако в отличие от последних эти расширения выполнены в виде библиотек динамической компоновки DLL, что имеет ряд преимуществ. Так же как и программы CGI, расширения ISAPI получают данные от навигатора (например, из заполненной удаленным пользователем формы), обрабатывают их и посылают навигатору ответ. Однако вместо чтения содержимого переменных среды и стандартного потока ввода STDIN расширение ISAPI получает данные при помощи специально предназначенных для этого функций. Аналогично, вместо записи выходных данных в стандартный поток вывода расширение ISAPI вызывает специальную функцию.
Фильтры ISAPI также реализуются в виде библиотек динамической компоновки DLL, однако их назначение другое. Фильтры ISAPI способны контролировать весь поток данных, проходящий через сервер, на уровне протокола HTTP. Поэтому их можно применять для решения таких задач, как шифрование или перекодирование данных, компрессия информации, создания собственных процедур подключения пользователей к системе и аутентификации (проверки идентификации пользователей), а также для сбора статистической информации использования ресурсов сервера удаленными пользователями.

Установка и настройка сервера WWW

Для тех, кто решил создать свой собственный сервер WWW или FTP в сети Internet или корпоративной сети Intranet, в этой главе мы расскажем об установке и настройке серверов WWW, созданных Microsoft:
  • Microsoft Peer WebServices для операционной системы Microsoft Windows NT Workstation версии 4.0;

  • Personal Web Server для операционной системы Microsoft Windows 95

  • Научившись устанавливать эти серверы, вы самостоятельно справитесь с установкой сервера Microsoft Information Server для операционной системы Microsoft Windows NT Server версий 3.51 и 4.0;
    Все перечисленные выше серверы бесплатны для легальных владельцев соответствующих версий операционной системы Windows.
    Какой сервер из перечисленных выше следует выбрать?
    Если вы создаете крупный сервер WWW в сети Internet или Intranet, с которым будут работать десятки пользователей, вам подойдет платформа Microsoft Windows NT. Владельцы версии 3.51 этой операционной системы могут загрузить сервер Microsoft Information Server из сервера WWW корпорации Microsoft. Если же у вас имеется операционная система Microsoft Windows NT Server версии 4.0, то в ее составе уже есть сервер Microsoft Information Server.
    Для небольших серверов, с которыми будут работать одновременно всего несколько пользователей, больше подойдет сервер на базе операционной системы Microsoft Windows NT Workstation версии 4.0 и сервера Microsoft Peer WebServices. Заметим, что лицензионное соглашение не позволяет подключаться большому числу пользователей к серверу на базе Microsoft Windows NT Workstation.
    Если ресурсы компьютера, на котором должен работать сервер WWW недостаточны для операционной системы Windows NT, вы можете создать сервер WWW на базе Windows 95 и сервера Personal Web Server.
    Заметим, что серверы Microsoft Peer WebServices и Microsoft Personal Web Server очень удобно использовать для отладки расширений, таких как приложения ISAPI или программы CGI.

    Администрирование сервера Microsoft Peer Web Services

    Как мы уже говорили, для администрирования сервера Microsoft Peer Web Services вы можете использовать либо технологию, основанную на HTML, либо запустить приложение Microsoft Internet Service Manager (рис.9.24).
    Администрирование сервера Microsoft Peer Web Services
    Рис. 9.24. Главное окно приложения Microsoft Internet Service Manager
    Сразу после установки сервера Microsoft Peer Web Services в окне приложения Microsoft Internet Service Manager вы увидите состояние сервисов, запущенных на компьютере. По умолчанию автоматически запускается сервис WWW, а сервисы FTP и Gopher находятся в остановленном состоянии.
    Для запуска или остановки нужного вам сервиса достаточно выделить этот сервис в столбце Computer и затем нажать одну из трех кнопок управления, расположенных на инструментальной линейке:

    Кнопка
    Операция над сервисом
    Администрирование сервера Microsoft Peer Web Services
    Запуск после остановки или временной остановки
    Администрирование сервера Microsoft Peer Web Services
    Остановка
    Администрирование сервера Microsoft Peer Web Services
    Временная остановка

    Вместо кнопок вы также можете воспользоваться строками Start Service, Stop Service или Pause Service, выбрав их из меню Properties.
    Остановка сервиса WWW может вам потребоваться, например, при замене программных файлов расширения сервера ISAPI.
    Заметим, что если в вашей сети имеется несколько серверов, таких как Microsoft Peer Web Services или Microsoft Internet Information Server, вы сможете управлять ими со своей рабочей станции дистанционно с помощью приложения Microsoft Internet Service Manager. Для добавления сервера в список, отображаемый в главном окне этого приложения вы можете выбрать строку Connect to Server из меню Properties или строку Find All Servers. В первом случае вам придется указать имя подключаемого сервера вручную.
    Для настройки параметров сервиса вы должны сделать двойной щелчок левой клавишей мыши по имени компьютера напротив нужного сервиса. При этом на экране появится блокнот настроек. Для сервиса WWW внешний вид этого блокнота представлен на рис. 9.25.
    Администрирование сервера Microsoft Peer Web Services
    Рис. 9.25. Страница Service блокнота настроек параметров сервиса WWW
    В поле TCP Port по умолчанию указан стандартный номер порта для работы с протоколом HTTP. При необходимости вы можете использовать другой номер порта, однако при этом следует сделать соответствующие изменения в настройках навигатора. Изменение номера порта имеет смысл делать только для корпоративных сетей.

    Обратите внимание на поля группы Anonymous Logon. По умолчанию программа установки сервера Microsoft Peer Web Services создает в базе данных пользователей идентификатор пользователя вида IUSR_xxxx, где xxxx - имя компьютера. Для пользователя с этим идентификатором программа установки автоматически генерирует пароль. Все удаленные пользователи, которые работают с вашим сервером WWW, подключаются к вашей системе с этим идентификатором и обладают правами доступа, установленными для пользователя с идентификатором IUSR_xxxx.

    При необходимости вы можете изменить права доступа к каталогам для пользователя IUSR_xxxx, пользуясь для этого стандартными средствами операционной системы Microsoft Windows NT. Можно даже изменить имя пользователя IUSR_xxxx или пароль. Это нужно сделать в двух местах - в базе данных пользователей Microsoft Windows NT и в диалоговой панели, показанной на рис. 9.25.

    С помощью страницы Directories блокнота настроек параметров сервиса WWW, показанной на рис. 9.26, вы можете изменить список каталогов, в которых хранятся объекты сервера, доступные пользователям.

    Администрирование сервера Microsoft Peer Web Services

    Рис. 9.26. Страница Directories блокнота настроек параметров сервиса WWW

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

    С помощью кнопки Add можно добавить в список новый каталог, а с помощью кнопки Remove - удалить каталог из списка.

    Кнопка Edit Properties позволяет изменить параметры каталога. Если нажать на эту кнопку, на экране появится диалоговая панель Directory Properties (рис. 9.27).

    Администрирование сервера Microsoft Peer Web Services

    Рис. 9.27. Диалоговая панель Directory Properties

    Многое в этой диалоговой панели вам уже знакомо по серверу Microsoft Personal Web Service для Windows 95. В частности, в поле Virtual Directory вы можете задать для выбранного каталога имя соответствующего ему виртуального каталога. Например, на рис. 9.27 физическому каталогу e:\drweb\dll соответствует виртуальный каталог с именем /drweb.

    В группе органов управления Account Information вы можете указать имя пользователя и пароль для доступа к данному каталогу.


    Включив переключатель Virtual Server вы можете разместить на своем сервере еще один, виртуальный, сервер WWW. Для этого к сетевому адаптеру необходимо привязать еще один адрес IP, указав его в поле Virtual Server IP Address. Виртуальные серверы позволяют создать на одном физическом сервере несколько логических серверов, возможно, принадлежащих разным организациям и имеющих собственные адреса IP. Каждый такой адрес может быть отображен в доменной базе адресов на отдельное доменное имя, в результате чего создается полная иллюзия наличия нескольких отдельных серверов.

    Заметим, что виртуальные серверы создаются обычно на серверах крупных поставщиков услуг Internet.

    Последняя страница блокнота настроек параметров сервиса WWW называется Logging и представлена на рис. 9.28.

    Администрирование сервера Microsoft Peer Web Services

    Рис. 9.28. Страница Logging блокнота настроек параметров сервиса WWW

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

    Если сервер WWW используется совместно с базой данных, вы можете включить переключатель log to SQL/ODBC Database и выполнить протоколирование операций, выполняемых над этой базой данных.

    Администрирование

    Для администрирования сервера Personal Web Server разработчики из Microsoft не стали создавать отдельное приложение. Вместо этого они использовали технологию, основанную на применении расширений ISAPI самого сервера Personal Web Server.
    Откройте блокнот Personal Web Server Properties на странице Administration, как это было описано в предыдущем разделе, и нажмите кнопку Administration. В результате будет запущен навигатор Microsoft Internet Explorer, в окне которого появится главная страница системы администрирования (рис. 9.11).
    Администрирование
    Рис. 9.11. Главная страница системы администрирования сервера Personal Web Server
    Если сделать двойной щелчок левой клавишей мыши по ссылке WWW Administration, в окне навигатора Microsoft Internet Explorer появится документ HTML с нарисованным блокнотом (рис. 9.12). С помощью страниц этого блокнота вы можете настраивать различные параметры сервера.
    Администрирование
    Рис. 9.12. Страница Service блокнота настройки параметров сервера в документе HTML
    В полях Connection timeout и Maximum Connections вы можете задать, соответственно, время ожидания соединения (в секундах) и максимальное количество соединений, которые могут быть установлены с сервером.
    Переключатели, расположенные в поле Password Authentication позволяют выбрать режим аутентификации, то есть проверки идентификатора пользователя. Если включен переключатель Basic, пароли передаются по сети в незашифрованном виде, поэтому они могут быть легко перехвачены злоумышленниками. По умолчанию используется режим передачи зашифрованных паролей (переключатель Windows NT Challenge/Response). Этот режим работает только в том случае, если ваш сервер подключен к локальной сети, в которой есть домен Microsoft Windows NT.
    Если включен переключатель Allow anonymous, к серверу может подключиться пользователь с любым идентификатором (анонимный пользователь). Такой режим установлен по умолчанию.
    В поле Comment вы можете вставить короткую строку описания сервера.
    Страница Directories блокнота настройки параметров сервера, показанная на рис. 9.13, позволяет выполнить настройку путей к каталогам, в которых расположены документы HTML и программные расширения сервера.

    Администрирование

    Рис. 9.13. Страница Directories блокнота настройки параметров сервера

    Каталог C:\WebShare\wwwroot является корневым каталогом сервера Personal Web Server, в который вы должны поместить свои файлы документов HTML. Эти файлы могут лежать как в самом корневом каталоге, так и в его подкаталогах.

    Программные расширения сервера располагаются в каталоге C:\WebShare\scripts, который создается по умолчанию при установке сервера. В этот каталог вы должны записать свои программы CGI или приложения ISAPI.

    Каталог C:\Program Files\WebSvr содержит подкаталоги Docs, Htmla и Htmlascr. В первом из них находится документация на сервер Personal Web Server в виде документов HTML. Каталоги Htmla и Htmlascr содержат, соответственно, документы HTML с графическими изображениями и расширения сервера ISAPI, которые используются для администрирования сервера.

    Теперь о переключателях Enable Default Document и Directory Browsing Allowed.

    Если включен переключатель Enable Default Document (а он включен по умолчанию), пользователь может не указывать навигатору имя документа HTML, ограничившись только заданием адреса URL каталога. В этом случае в окно навигатора будет загружен документ, имя файла которого указано в поле Default Document.

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

    А что произойдет, если переключатель Enable Default Document выключен, а пользователь не задал имя файла, ограничившись только адресом каталога?

    Это зависит от состояния переключателя Directory Browsing Allowed.

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

    Администрирование

    Рис. 9.14. Список файлов в окне навигатора

    Возможно, у вас есть причины скрывать от пользователей содержимое каталогов. В этом случае вам следует отключить переключатель Directory Browsing Allowed, одновременно предусмотрев для каждого каталога документ, который будет отображаться по умолчанию.


    Страница Directories блокнота настройки параметров сервера Personal Web Server позволяет вам редактировать список каталогов, доступных удаленным пользователям. Например, вы можете добавить в этот список новые каталоги, удалить имеющиеся или изменить параметры каталогов.

    Для редактирования параметров каталогов вы должны сделать щелчок левой клавишей мыши по ссылке Edit, расположенной справа от нужного вам каталога. В результате в окне навигатора появится документ, позволяющий изменить параметры указанного вами каталога (рис. 9.15). Добавление нового каталога выполняется при помощи аналогичного документа, вызываемого по ссылке Add.

    Администрирование

    Рис. 9.15. Документ HTML, позволяющий изменить параметры выбранного каталога

    В поле Directory вы можете указать путь к физическому каталогу. Если нажать кнопку Browse, у вас появится возможность выбрать каталог в окне навигатора.

    Переключатель Home Directory позволяет указать, что данный каталог является главным (домашним) каталогом сервера и должен отображаться по умолчанию.

    Переключатель Virtual Directory используется для создания так называемого виртуального каталога. На рис. 9.15 для корневого каталога указано имя виртуального каталога, состоящее из символа “/”.

    Зачем нужен виртуальный каталог?

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

    С помощью переключателей группы Access вы можете определить вид доступа, разрешенного удаленным пользователям к данному каталогу. Для каталогов, содержащих обычные документы HTML разрешается доступ на чтение (включается переключатель Read) и запрещается доступ на выполнение программ (выключается переключатель Execute). Для каталогов, в которых записаны программы CGI или приложения ISAPI, наоборот, запрещается доступ на чтение и разрешается доступ на выполнение.

    Страница Logging блокнота настройки параметров сервера Personal Web Server позволяет управлять процессом протоколирования событий (рис. 9.16). 


    Администрирование

    Рис. 9.16. Страница Logging блокнота настройки параметров сервера Personal Web Server

    Протоколирование выполняется только в том случае, если включен переключатель Enable logging. Если включить переключатель Automatically open new log, то можно выбрать интервал, с которым будет создаваться новый файл журнала. Вы можете указать, что журнал должен создаваться ежедневно (переключатель Daily), еженедельно (переключатель Weekly), ежемесячно (переключатель Monthly) или когда файл журнала достигнет заданных размеров (переключатель When the file size reaches).

    В поле Log file directory указан путь к файлу журнала. Имя этого файла - Inetserver_event.log.

    Ниже мы привели фрагмент содержимого файла журнала:

    154.101.200.255, -, 26.01.97, 10:05:19, W3SVC, FROLOV, 154.101.200.255, 406, 278, 1379, 200, 0, GET, /Default.htm, -,

    154.101.200.255, -, 26.01.97, 10:05:23, W3SVC, FROLOV, 154.101.200.255, 3884, 350, 255, 200, 0, GET, /scripts/counter.dll, 9605071636,

    154.101.200.255, -, 26.01.97, 10:05:44, W3SVC, FROLOV, 154.101.200.255, 290, 293, 3660, 200, 0, GET, /htmla/htmla.htm, -,

    . . .

    154.101.200.255, -, 26.01.97, 10:05:48, W3SVC, FROLOV, 154.101.200.255, 1138, 349, 1277, 200, 0, GET, /htmla/bolt.gif, -,

    154.101.200.255, -, 26.01.97, 10:05:52, W3SVC, FROLOV, 154.101.200.255, 4300, 355, 2445, 200, 0, GET, /HtmlaScripts/htmla.dll, http/serv,

    . . .

    В этом файле через запятую приведена следующая информация о выполненной операции:

  • адрес IP компьютера удаленного пользователя;


  • имя удаленного пользователя или символ прочерка, если имя недоступно;


  • дата выполнения операции;


  • время выполнения операции;


  • название сервиса (W3SVC - сервис WWW, MSFTPSVC - сервис FTP и так далее);


  • сетевое имя сервера;


  • адрес IP сервера;


  • продолжительность операции;


  • количество принятых байт данных;


  • количество переданных байт данных;


  • код завершения операции;


  • имя выполненной операции;


  • файл или другой объект, участвующий в операции;


  • дополнительные параметры для объекта


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

    Анимация

    Как мы уже говорили, формат GIF позволяет хранить в одном файле несколько графических изображений. При отображении такого файла навигаторы могут показывать эти изображения по очереди, как кадры обычного кинофильма. Подготовив достаточное количество кадров, вы можете создать небольшой фильм (или мультфильм) и разместить его в документе HTML.

    Аннотация

    Книга представляет собой практическое руководство по созданию серверов Web в глобальной сети Internet или в глобальных корпоративных сетях Intranet. Здесь вы найдете все, что вам потребуется для создания собственных документов HTML, как статических, так и динамических.
    Отдельная глава посвящена установке и настройке серверов Microsoft Information Server и Microsoft Peer WebServices в среде операционной системы Windows NT, а также сервера Microsoft Personal Web Server для операционной системы Windows 95.
    Мы научим вас создавать расширения серверов Web на базе интерфейсов CGI и ISAPI, что необходимо для конструирования интерактивных документов HTML.

    Автоматическая перезагрузка документа

    Если какой-либо документ периодически обновляется на сервере, вы можете сообщить навигатору, что этот документ надо время от времени перезагружать. Такая перезагрузка выполняется с использованием методики, которая называется client pull, что можно перевести как запрос данных со стороны клиента (то есть навигатора).
    Включив в заголовок документа следующую строку, вы можете заставить навигатор автоматически перезагружать документ HTML каждые три секунды:


    Автоматическая загрузка другого документа

    С помощью оператора можно решить такую задачу, как автоматическая загрузка заданного документа HTML через определенный интервал времени.
    Для чего это может потребоваться?
    Например, на главной странице вашего сервера может находиться приветственное сообщение. Вы можете сделать так, что через некоторое время после появления этого сообщения навигатор автоматически загрузит другой документ, например, содержащий меню.
    В приведенном ниже фрагменте через 15 секунд после загрузки текущего документа будет загружен документ с адресом http://www.my.ru/hello1.htm:


    Бегущая строка

    С помощью оператора , который используется в паре с оператором , можно разместить на экране бегущую строку, например:
    Бегущая строка…Бегущая строка…
    Бегущая строка может быть использована для привлечения внимания пользователя, однако не злоупотребляйте этой возможностью - неуместная бегущая строка может раздражать пользователя и он покинет вашу страницу, так и не прочитав на ней что-нибудь важное…

    Благодарности

    Авторы выражают благодарность сотруднику фирмы Microsoft АО Юрию Тумашко за предоставленное в наше распоряжение программное обеспечение.
    В работе над книгой нам помогли сотрудники фирмы Interactive Products Inc. Максим Синев и Сергей Ноженко, у которых мы консультировались по различным вопросам, связанным с языком HTML и с разработкой программных расширений ISAPI.
    Отдельную благодарность мы выражаем генеральному директору АО “ДиалогНаука” Антимонову Сергею Григорьевичу и его заместителю Лященко Юрию Павловичу за возможность размещения информации о наших книгах на сервере Web по адресу http://www.dials.ccas.ru/frolov, а также за возможность доступа к сети Internet через сервер АО “ДиалогНаука”.
    Мы благодарим корректора Кустова В. С. и сотрудников издательского отдела АО “Диалог-МИФИ” Голубева О. А., Голубева А. О., Дмитриеву Н. В., Виноградову Е. К., Кузьминову О. А.

    Что лучше - GET или POST

    Метод GET обычно используется для обработки небольших форм, так как навигаторы накладывают ограничения для размера данных, передаваемых через переменную среды QUERY_STRING.
    В этом отношении метод POST является более предпочтительным, так как не накладывает на размер передаваемых данных никаких ограничений. Только метод POST пригоден для передачи файлов из локального компьютера через навигатор в сервер WWW.

    Что такое сеть Internet

    Как вы, наверное, знаете, глобальная сеть Internet объединяет миллионы компьютеров, расположенные в разных странах по всему свету. Большинство таких компьютеров - это рабочие станции конечных пользователей, подключенные к сети Internet через модем и обычную телефонную линию. Другие компьютеры выполняют функцию серверов и служат хранилищами информации в сети. Серверы соединяют между собой, как правило, высокоскоростными и дорогостоящими каналами связи, например, каналами ISDN, оптоволоконными или спутниковыми каналами.
    Конечные пользователи обычно подключаются к одному из серверов, расположенных в том же городе, что и они сами. При этом соединение через телефонную сеть получается локальным, а не междугородним, что стоит существенно дешевле. Что же касается соединения серверов между собой, то каналы связи, которые для этого используются, арендуются владельцами серверов, поэтому конечным пользователям не нужно за них платить.
    На рис. 1.1 мы воспроизвели схему основных компонент глобальной сети Internet, взяв ее из упомянутого выше 23 тома “Библиотеки системного программиста”.
    Что такое сеть Internet
    Рис. 1.1. Основные компоненты глобальной сети Internet
    Из этой схемы видно, что существуют различные возможности для подключения компьютеров к сети Internet.
    Обычные “одиночные” пользователи подключаются к сети через модем. Пользователи локальной сети могут также подключаться к Internet через свои модемы, однако чаще такое подключение выполняется через сервер, расположенный в этой локальной сети. Сервер может быть подключен к Internet через модем, адаптер линии ISDN, оптоволоконную линию связи или спутниковую линию.
    Заметим, что не существует никакого администратора сети Internet. Никакая организация не отвечает за работоспособность этой сети в целом, так как фактически сеть состоит из большого количества самостоятельных сетей, отдельных серверов и рабочих станций, принадлежащим различным фирмам и частным лицам. Тем не менее, сеть Internet работает, и достаточно надежно для того, чтобы ее можно было использовать в коммерческих целях.
    Какова скорость передачи данных в сети Internet?
    Разная. Здесь все зависит от пропускной способности и загруженности каналов связи, от производительности и загруженности серверов. Если вы приобрели современный модем со скоростью передачи данных 33600 бит/с, то совсем не обязательно, что вы будете получать информацию из сети Internet именно с этой скоростью. Некоторые серверы так загружены, что способны отдавать данные со скоростью не более 100-200 байт/с, и никакой даже самый дорогой модем тут не поможет. Однако в среднем может быть достигнута скорость 1-2 Кбайт/с, что вполне достаточно для многих приложений.

    Цвет в документах HTML

    Цветные страницы выглядят намного привлекательнее серых или черно-белых. Язык HTML позволяет вам указывать цвет фона, текста, полей ссылок на другие документы HTML и так далее.
    Для выделения заголовков и отдельных слов в тексте вы можете использовать цвет. При этом, однако, не следует увлекаться, так как стремление использования максимально возможного количества цветов приведет к тому, что ваши страницы будут выглядеть слишком пестрыми. Кроме того, на внешний вид страницы влияет цветовое разрешение, установленное в компьютере пользователя. Если пользователь работает с видеоадаптером VGA (а такое еще встречается, особенно среди пользователей старых и блокнотных компьютеров), он не сможет воспринять всю цветовую гамму многоцветных документов HTML, рассчитанных на режимы с высоким цветовым разрешением. Поэтому используйте цвет только там, где он действительно необходим для улучшения внешнего вида страницы.

    Цвет в других элементах документа HTML

    Многие операторы документа HTML позволяют задавать цвет определяемых ими элементов. Это ссылки на другие страницы HTML, таблицы, рамки, разделительные линии и так далее. Как правило, во всех операторах для указания цвета используется только что описанный нами параметр COLOR. По мере изучения языка HTML вы научитесь задавать цвет всех элементов документа HTML.

    Физическое форматирование символов

    В языке HTML определены операторы так называемого физического форматирования символов текста. Эти операторы определяют внешний вид символов явным образом.
    Наряду с операторами физического форматирования существуют операторы логического форматирования, которые мы опишем в следующем разделе. Эти операторы предназначены для указания семантического (смыслового) назначения оформляемого текста. Анализируя операторы логического форматирования, навигатор самостоятельно подбирает для этого текста шрифтовое оформление.
    Ниже мы перечислили операторы физического форматирования:

    Оператор
    Описание
    ,
    Выделение жирным шрифтом
    ,
    Выделение наклонным шрифтом
    ,
    Выделение подчеркиванием
    ,
    Выделение перечеркиванием
    ,
    Оформление шрифтом с фиксированной шириной букв
    ,
    Текст с крупным размером букв
    ,
    Текст с малым размером букв
    ,
    Мигающий текст
    ,
    Подстрочный индекс
    ,
    Надстрочный индекс


    Формат GIF

    Формат GIF был разработан пользователями сети CompuServe и в настоящее время стал очень популярен. Несмотря на то что изображения, хранящиеся в этом формате, не могут иметь более 256 цветов, формат GIF обладает возможностями, которые делают его незаменимым в целом ряде случаев.
    Во-первых, в формате GIF используются алгоритмы сжатия изображения без потерь качества. Поэтому этот формат как нельзя лучше подходит для хранения копий экранов или штриховых рисунков, содержащих большие однотонные площади.
    Во-вторых, вы можете существенно уменьшить размер графического файла, записав в него сокращенную цветовую палитру вместо полной 256-цветной.
    В-третьих, размещая в документах HTML специальным образом подготовленные изображения GIF, вы можете получить эффект прозрачности. Части изображения могут быть прозрачными, поэтому видимая форма изображения может отличаться от квадратной или прямоугольной.
    В-четвертых, формат GIF и только формат GIF позволяет создавать анимационные графические изображения, оживляющие внешний вид документа HTML.
    И, наконец, в пятых, формат GIF позволяет создавать файлы с чересстрочным (interlaced) изображением. Что это такое?
    Строки чересстрочных изображений GIF располагаются не подряд, а по очереди. Вначале в этом файле находятся строки, номера которых кратны восьми, затем - четырем и так далее. Чересстрочное изображение проявляется на экране навигатора постепенно, увеличивая свою четкость по мере получения новых порций данных. Это дает пользователю возможность оценить содержимое графического изображения до момента его полной загрузки. Если изображение вам не нужно, вы можете отказаться от его загрузки до полного завершения процесса и тем самым сэкономить время.
    Как устроен файл GIF?
    Файл в формате GIF версии 89a состоит из блоков нескольких типов, два из которых являются обязательными, а остальные - дополнительными. Обязательные блоки - это блоки заголовка и блоки изображения. Дополнительно файл GIF может содержать блоки комментария, текстовые и управляющие блоки, а также блоки данных, содержащие произвольную информацию.

    В блоке заголовка расположена палитра и сведения о размерах области, в которой желательно отображать содержимое файла. Что же касается палитры, то в файле GIF может быть определена одна глобальная палитра, которая используется для всех графических изображений, записанных в файле, или несколько локальных палитр для отдельных изображений.

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

    В блок комментария вы можете записать текст, описывающий изображение, или сведения о правах на изображение. Содержимое блока комментария не появится на экране во время отображения файла GIF, однако его можно увидеть непосредственно в файле, например, с помощью программы просмотра содержимого файла, встроенного в оболочку Norton Commander.

    Что же касается текстовых блоков, то расположенный в них текст будет выведен на экран поверх изображения. При подготовке текстового блока вы можете указать цвет и расположение текста.

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

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

    Формат JPEG

    Хотя исторически формат GIF появился раньше, сначала мы расскажем о формате JPEG. Этот формат имеет особенности, делающие привлекательным его использование в документах HTML, особенно для отображения многоцветных фотографий.
    Прежде всего, формат JPEG допускает сжатие графической информации с потерями. Подготавливая файл в формате JPEG, вы можете указать допустимый процент потери качества. Чем больше этот процент, тем меньший объем дискового пространства потребуется для хранения файла и, что самое главное, тем меньшее время потребуется удаленному пользователю для загрузки изображения.
    Минимизация размера графического изображения без существенных потерь качества - очень важная процедура. Дело здесь в том, что в среднем информация передается через сеть Internet со скоростью примерно 1 Кбайт в секунду, поэтому на загрузку больших изображений может уйти слишком много времени. Поэтому даже и не думайте о том, чтобы разместить в документе HTML цветную фотографию, занимающую на диске 1 Мбайт - посчитайте сами, сколько времени эта фотография будет передаваться пользователю.
    Реальные объемы изображений, которые можно использовать, лежат в пределах от нескольких Кбайт до нескольких десятков Кбайт. Если же существует принципиальная необходимость размещения в документе HTML графического изображения высокого разрешения, занимающего много места, вы можете создать в документе ссылку на это изображение в виде небольшой пиктограммы. О том как это сделать, мы расскажем позже.
    Другое преимущество формата JPEG заключается в том, что он позволяет хранить изображения с высоким цветовым разрешением (например, содержащие 16 млн. цветов).
    Есть ли у формата JPEG недостатки?
    Конечно есть, а как же без них.
    Наиболее заметный недостаток алгоритма сжатия с потерей качества сказывается при попытке сжать изображение с большим количеством четких контуров, например, копий экрана или штриховых рисунков. Даже при небольших потерях качества контуры размываются, что может оказаться совершенно неприемлемо. Поэтому мы рекомендуем применять формат JPEG только для хранения фотографий или рисунков с плавными переходами цветов и яркостей.

    Функция GetExtensionVersion

    Насколько проста реализация функции GetExtensionVersion вы можете судить по следующему фрагменту кода, взятому нами из исходных текстов приложения FILEUPL (это приложение будет полностью рассмотрено позже):
    // =============================================================
    // Функция GetExtensionVersion
    // =============================================================
    BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVersion)
    {
      pVersion->dwExtensionVersion =
        MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR);
      lstrcpyn(pVersion->lpszExtensionDesc,
        "Remote File Upload", HSE_MAX_EXT_DLL_NAME_LEN);
      return TRUE;
    }
    При вызове функции GetExtensionVersion передается указатель на структуру типа HSE_VERSION_INFO. Эта структура и указатель на нее LPHSE_VERSION_INFO определены в файле httpext.h следующим образом:
    #define   HSE_MAX_EXT_DLL_NAME_LEN  256
    typedef struct _HSE_VERSION_INFO
    {
      DWORD dwExtensionVersion;
      CHAR  lpszExtensionDesc[HSE_MAX_EXT_DLL_NAME_LEN];
    } HSE_VERSION_INFO, *LPHSE_VERSION_INFO;
    Константы HSE_VERSION_MINOR и HSE_VERSION_MAJOR указывают текущую версию интерфейса расширения ISAPI и также определены в файле httpext.h:
    #define HSE_VERSION_MAJOR 2 // верхний номер версии
    #define HSE_VERSION_MINOR 0 // нижний номер версии

    Функция GetServerVariable

    Прототип функции GetServerVariable определен в структуре EXTENSION_CONTROL_BLOCK, описанной нами ранее:
    BOOL (WINAPI * GetServerVariable)(HCONN hConn,
         LPSTR lpszVariableName, LPVOID lpvBuffer, LPDWORD lpdwSize);
    Через параметр hConn вы должны передать этой функции идентификатор канала, полученный через поле ConnID структуры EXTENSION_CONTROL_BLOCK.
    Параметр lpszVariableName должен содержать указатель на строку имени переменной, содержимое которой необходимо получить. Это содержимое будет записано функцией в буфер, адрес которого передается через параметр lpvBuffer, а размер - через параметр lpdwSize.
    Ниже мы перечислили возможные значения строк, передаваемых через параметр lpszVariableName:
  • AUTH_TYPE

  • Переменная среды AUTH_TYPE содержит тип идентификации, который применяется сервером.
  • HTTP_ACCEPT

  • В этой переменной перечислены типы данных MIME, которые могут быть приняты навигатором от сервера WWW.
  • CONTENT_LENGTH

  • Количество байт данных, которые расширение ISAPI должно получить от навигатора.
  • CONTENT_TYPE

  • Тип данных, присланных навигатором.
  • PATH_INFO

  • Путь к виртуальному каталогу, в котором находится библиотека DLL расширения ISAPI.
  • PATH_TRANSLATED

  • Физический путь к библиотеки DLL расширения ISAPI.
  • QUERY_STRING

  • Строка параметров, указанная в форме или операторе ссылки . Эта строка указывается после адреса URL библиотеки DLL расширения ISAPI вслед за разделительным символом “?”.
  • REMOTE_ADDR

  • Адрес IP узла, на котором работает навигатор удаленного пользователя.
  • REMOTE_HOST

  • Доменное имя узла, на котором работает навигатор удаленного пользователя. Если эта информация недоступна (например, для узла не определен доменный адрес), то вместо доменного имени указывается адрес IP, как в переменной REMOTE_ADDR.
  • REMOTE_USER

  • Имя пользователя, которое используется навигатором для аутентификации.
  • UNMAPPED_REMOTE_USER

  • Имя пользователя до обработки фильтром ISAPI, которое используется навигатором для аутентификации.
  • REQUEST_METHOD

  • Метод доступа, который используется для передачи данных от навигатора серверу WWW.
  • SCRIPT_NAME


  • В эту переменную записывается путь к виртуальному каталогу и имя библиотеки DLL расширения ISAPI. Анализируя эту переменную, расширение ISAPI может определить путь к своему загрузочному файлу.

  • SERVER_NAME


  • Доменное имя сервера WWW или адрес IP сервера WWW, если доменное имя недоступно или не определено.

  • SERVER_PROTOCOL


  • Имя и версия протокола, который применяется для выполнения запроса к расширению ISAPI.

  • SERVER_PORT


  • Номер порта, на котором навигатор посылает запросы серверу WWW.

  • SERVER_PORT_SECURE


  • Если обработка запроса выполняется через защищенный порт, в этой строке записано значение 1, а если через незащищенный - значение 0.

  • SERVER_SOFTWARE


  • Название и версия программного обеспечения сервера WWW. Версия следует после названия и отделяется от последнего символом “/”.

  • ALL_HTTP


  • Строка, закрытая двоичным нулем, в которую записаны значения всех переменных, имеющих отношение к протоколу HTTP. Это, например, такие переменные как HTTP_ACCEPT, HTTP_CONNECTION, HTTP_USER_AGENT и так далее.

    Извлекать содержимое отдельных переменных ваша программа должна самостоятельно. При этом следует учесть, что названия переменных отделены от их значений символом двоеточия “:”, а поля переменных разделены символом перевода строки.

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

    В случае успешного завершения функция GetServerVariable возвращает значение TRUE, а при возникновении ошибки - значение FALSE. Код ошибки можно определить с помощью функции GetLastError, вызвав ее сразу после функции GetServerVariable. Эта функция может вернуть в данном случае следующие коды ошибок:

    Код ошибки

    Описание

    ERROR_INVALID_INDEX

    Неправильное имя переменной, передаваемой через параметр lpszVariableName

    ERROR_INVALID_PARAMETER

    Неправильное значение параметра hConn

    ERROR_INSUFFICIENT_BUFFER

    Буфер, адрес которого указан с помощью параметра lpvBuffer, слишком мал. Необходимый размер буфера записывается по адресу, который был передан функции через параметр lpdwSize

    ERROR_MORE_DATA

    Буфер, адрес которого указан с помощью параметра lpvBuffer, слишком мал. В результате данные были прочитаны частично, причем размер буфера, необходимый для чтения всех данных, неизвестен

    ERROR_NO_DATA

    Данные не были получены

    Ниже мы привели пример использования функции GetServerVariable для получения содержимого переменной с именем ALL_HTTP в буфер szTempBuf.

    CHAR  szTempBuf[4096];

    DWORD dwSize;

    dwSize = 4096;

      lpECB->GetServerVariable(lpECB->ConnID,

        (LPSTR)"ALL_HTTP", (LPVOID)szTempBuf, &dwSize);

      strcat(szBuff, szTempBuf);

    Функция HttpExtensionProc

    Теперь рассмотрим вторую функцию, которую должна экспортировать библиотека DLL расширения ISAPI. Она называется HttpExtensionProc и имеет следующий прототип:
    DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB);
    Функция HttpExtensionProc получает единственный параметр - указатель на структуру типа EXTENSION_CONTROL_BLOCK, определенную в файле httpext.h:
    typedef struct _EXTENSION_CONTROL_BLOCK 
    {
      DWORD cbSize;    // размер структуры в байтах
      DWORD dwVersion; // версия спецификации ISAPI
      HCONN ConnID;    // идентификатор канала
      DWORD dwHttpStatusCode; // код состояния HTTP
      CHAR  lpszLogData[HSE_LOG_BUFFER_LEN]; // текстовая строка,
            // закрытая двоичным нулем, в которой находится информация
            // протоколирования, специфичная для данного расширения
      LPSTR lpszMethod;         // переменная REQUEST_METHOD
      LPSTR lpszQueryString;    // переменная QUERY_STRING
      LPSTR lpszPathInfo;       // переменная PATH_INFO
      LPSTR lpszPathTranslated; // переменная PATH_TRANSLATED
      DWORD  cbTotalBytes;    // полный размер данных, полученных от
                              // навигатора
      DWORD  cbAvailable;     // размер доступного блока данных
      LPBYTE lpbData;         // указатель на доступный блок данных
                              // размером cbAvailable байт
      LPSTR  lpszContentType; // тип принятых данных
      // Функция GetServerVariable для получения значения переменных
      BOOL (WINAPI * GetServerVariable)(HCONN hConn,
         LPSTR lpszVariableName, LPVOID lpvBuffer, LPDWORD lpdwSize);
      // Функция WriteClient для посылки данных удаленному пользователю
      BOOL (WINAPI * WriteClient)(HCONN ConnID,
         LPVOID Buffer, LPDWORD lpdwBytes, DWORD dwReserved);
      // Функция ReadClient для получения данных от удаленного
      // пользователя
      BOOL (WINAPI * ReadClient) (HCONN ConnID,
         LPVOID lpvBuffer, LPDWORD lpdwSize);
      // Вспомогательная функция ServerSupportFunction
      // для выполнения различных операций

      BOOL (WINAPI * ServerSupportFunction)(HCONN hConn,

         DWORD dwHSERRequest, LPVOID lpvBuffer,

         LPDWORD lpdwSize, LPDWORD lpdwDataType);

    } EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;

    Рассмотрим отдельные поля этой структуры.

  • cbSize


  • В самом начале структуры EXTENSION_CONTROL_BLOCK находится поле cbSize, в которое при вызове расширения сервер записывает размер структуры в байтах.

  • dwVersion


  • Поле dwVersion содержит номер версии расширения ISAPI. Верхнее и нижнее значения номера версии можно получить, соответственно, при помощи макрокоманд HIWORD и LOWORD.

  • ConnID


  • В поле ConnID сервер записывает идентификатор канала, созданного для расширения. Это поле вы не должны изменять.

  • dwHttpStatusCode


  • Поле dwHttpStatusCode должно заполняться расширением ISAPI. Вы должны записать сюда результат завершения операции (код состояния транзации). В случае успеха в это поле записывается значение 200 (как указано в спецификации HTTP).

  • lpszLogData


  • Поле lpszLogData предназначено для записи сообщения о выполнении транзакции в журнал сервера WWW. Это сообщение должно быть в виде текстовой строки, закрытой нулем. Размер строки в байтах не должен превышать значения HSE_LOG_BUFFER_LEN.

  • lpszMethod


  • Поле lpszMethod заполняется сервером и содержит название метода передачи данных от удаленного пользователя серверу в виде текстовой строки, закрытой двоичным нулем. Расширения ISAPI используют те же самые методы, что и программы CGI - метод GET и метод POST. Проводя аналогию с программами CGI дальше, скажем, что поле lpszMethod эквивалентно переменной среды с именем REQUEST_METHOD, создаваемой для программы CGI.

  • lpszQueryString


  • Аналогично, поле lpszQueryString соответствует переменной среды с именем QUERY_STRING. В это поле записываются данные, принятые от удаленного пользователя методом GET.

  • lpszPathInfo


  • В поле lpszPathInfo записывается виртуальный путь к программному файлу библиотеки DLL расширения ISAPI. Напомним, что аналогичная информация для программ CGI передавалась через переменную среды с именем PATH_INFO.

  • lpszPathTranslated



  • Это поле содержит физический путь к программному файлу библиотеки DLL расширения ISAPI. Оно соответствует переменной среды с именем PATH_TRANSLATED, создаваемой для программ CGI.

  • cbTotalBytes


  • В поле cbTotalBytes записывается общее количество байт данных, которое необходимо получить от удаленного пользователя. Часть этих данных (размером не более 48 Кбайт) считывается сервером автоматически и становится доступной сразу после того как функция HttpExtensionProc получит управление. Остальные данные необходимо дочитать в цикле при помощи функции ReadClient, о которой мы еще будем говорить.

  • cbAvailable


  • В поле cbAvailable записывается размер блока данных, полученных от удаленного пользователя автоматически. Как мы только что сказали, размер этого блока не может превышать 48 Кбайт. Этого, однако, вполне достаточно для обработки данных, полученных от форм обычного размера.

  • lpbData


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

  • lpszContentType


  • Поле lpszContentType содержит тип принятых данных, например, text/html.

  • GetServerVariable


  • Помимо полей данных, структура EXTENSION_CONTROL_BLOCK содержит указатели на функции. С помощью этих функций расширение ISAPI может выполнять различные операции, такие как прием данных от удаленного пользователя.

    Поле GetServerVariable содержит указатель на функцию, с помощью которой расширение ISAPI может получить информацию, которая доступна программам CGI через переменные среды, описанные нами в предыдущей главе этой книги.

  • WriteClient


  • В поле WriteClient находится адрес функции, которую расширение ISAPI должно использовать для посылки данных удаленному пользователю. Таким образом, вместо того чтобы записывать данные в стандартный поток вывода STDOUT, как это делает программа CGI, приложение ISAPI посылает данные с помощью функции WriteClient.

  • ReadClient


  • С помощью функции, адрес которой передается в поле ReadClient, приложение может дочитать дополнительные данные, не поместившиеся в буфер предварительного чтения, имеющий адрес lpbData и размер, не превышающий 48 Кбайт. Аналогичную операцию приема данных от пользователя выполняет программа CGI в случае применения метода передачи данных POST. Отличие заключается в том, что программа CGI получает данные через стандартный поток ввода STDIN, а расширение ISAPI берет эти данные из буфера предварительного чтения и при необходимости дочитывает данные функцией ReadClient.

  • ServerSupportFunction


  • С помощью функции, адрес которой передается через поле ServerSupportFunction, расширение ISAPI может выполнять различные действия, такие как посылка стандартного заголовка протокола HTTP и некоторые другие.

    При успешном завершении функция HttpExtensionProc должна вернуть значение HSE_STATUS_SUCCESS, а при ошибке - значение HSE_STATUS_ERROR. Соответствующие константы определены в файле httpext.h.

    Функция ReadClient

    Прототип функции ReadClient находится в определении структуры EXTENSION_CONTROL_BLOCK и выглядит следующим образом:
    BOOL (WINAPI * ReadClient) (HCONN ConnID,
         LPVOID lpvBuffer, LPDWORD lpdwSize);
    Через параметр hConn этой функции надо передать идентификатор канала, полученный через поле ConnID структуры EXTENSION_CONTROL_BLOCK.
    Функция ReadClient читает данные в буфер, адрес которого передается через параметр lpvBuffer, а размер - через параметр lpdwSize. В случае успеха функция возвращает значение TRUE, а при ошибке - значение FALSE. Код ошибки можно получить при помощи функции GetLastError.
    Работа с функцией ReadClient имеет некоторые особенности.
    Когда расширение ISAPI получает управление, через структуру типа EXTENSION_CONTROL_BLOCK передается адрес предварительно прочитанного блока данных, полученного от удаленного пользователя. Как вы знаете, адрес и размер этого блока данных указаны, соответственно, в полях lpbData и cbAvailable структуры EXTENSION_CONTROL_BLOCK.
    Однако размер предварительно прочитанных данных не может превышать 48 Кбайт. Если все данные не поместились в буфер предварительного чтения, их необходимо дочитать функцией ReadClient. При этом следует использовать следующий алгоритм.
    Прежде всего следует сравнить размер предварительно считанных данных с полным размером данных, которые нужно считать (этот размер передается в поле cbTotalBytes структуры EXTENSION_CONTROL_BLOCK).
    Если все данные уже были считаны предварительно, функцию ReadClient вызывать не нужно. В том случае, когда значение, передаваемое через поле cbTotalBytes, превышает значение cbAvailable, вы должны воспользоваться функцией ReadClient для того чтобы прочесть (cbTotalBytes - cbAvailable) байт данных от пользователя.
    Заметим, что функция ReadClient не будет читать заново данные, предварительно прочитанные в буфер lpbData. Она прочитает только оставшиеся данные, причем не исключено, что для чтения оставшихся данных эту функцию придется вызывать в цикле несколько раз. Причина этого заключается в том, что функция ReadClient не обязательно сможет прочитать все оставшиеся данные за один прием.
    После успешного завершения чтения функция ReadClient записывает размер прочитанного блока данных в переменную, адрес которой передается через параметр lpdwSize. Если при первом вызове значение этого размера меньше величины (cbTotalBytes - cbAvailable), вы должны вызвать функцию ReadClient еще один или несколько раз для чтения оставшихся данных.
    Пример использования функции ReadClient вы найдете в разделе “Приложение FILEUPL”. Там мы привели исходные тексты расширения ISAPI, позволяющее использовать сервер WWW довольно необычным способом - для получения файлов от удаленных пользователей и записи их на диск сервера. Так как размеры передаваемых файлов могут быть значительны, приложение FILEUPL вызывает функцию ReadClient в цикле.

    Функция ServerSupportFunction

    Прототип функции ServerSupportFunction, определенный в структуре типа EXTENSION_CONTROL_BLOCK, приведен ниже:
    BOOL (WINAPI * ServerSupportFunction)(HCONN hConn,
         DWORD dwHSERRequest, LPVOID lpvBuffer,
         LPDWORD lpdwSize, LPDWORD lpdwDataType);
    Через параметр hConn функции ServerSupportFunction передается идентификатор канала, полученный через поле ConnID структуры EXTENSION_CONTROL_BLOCK.
    С помощью параметра dwHSERRequest вы можете задать один из нескольких кодов запроса, определяющих операцию, выполняемую этой функцией.
    Через параметр lpvBuffer передается размер буфера, который используется при выполнении операции. Размер этого буфера должен быть записан в переменной типа DWORD, адрес которой передается через параметр lpdwSize. После выполнения операции передачи данных в эту переменную будет записан размер успешно переданного блока данных.
    Параметр lpdwDataType используется для указания дополнительной строки заголовка или дополнительных данных, которые будут добавлены к заголовку, передаваемому удаленному пользователю. Если для параметра lpdwDataType указать значение NULL (что допустимо), к заголовку будут добавлены символы конца строки “\r\n”.
    Какие операции можно выполнять при помощи функции ServerSupportFunction?
    Ниже мы привели список возможных значений параметра dwHSERRequest, определяющего код выполняемой операции:
  • HSE_REQ_SEND_RESPONSE_HEADER

  • Эта операция предназначена для посылки удаленному пользователю стандартного заголовка HTTP. При необходимости добавления других заголовков следует воспользоваться параметром lpdwDataType. В качестве дополнительного заголовка вы можете указать любую строку, закрытую символами конца строки “\r\n” и двоичным нулем.
    Если ваше расширение ISAPI динамически формирует документ HTML и посылает его пользователю, то ей не нужно вызывать функцию WriteClient. Все необходимые для этого действия можно сделать при помощи одной только функции ServerSupportFunction. Ниже мы показали фрагмент кода, в котором эта функция используется для посылки документа HTML, подготовленного заранее в буфере szBuff:

    CHAR  szBuff[4096];

    wsprintf(szBuff,

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

        " Simple ISAPI Extension\n"

        "

    Hello from ISAPI Extension!

    \n");

    strcat(szBuff, "

    Заголовок документа

    "); 

    strcat(szBuff, "
    ");

    strcat(szBuff, ""); 

    lpECB->ServerSupportFunction(lpECB->ConnID,

        HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff);

    Заметим, однако, что функция ServerSupportFunction  не позволяет посылать двоичные данные. Для посылки двоичных данных вы обязательно должны использовать функцию WriteClient.

  • HSE_REQ_SEND_URL


  • Используя операцию HSE_REQ_SEND_URL, расширение ISAPI может послать удаленному пользователю данные, заданные адресом URL, как будто бы эти данные были запрошены непосредственно пользователем по этому адресу URL. Такая возможность удобна для посылки либо динамически созданных данных, либо для посылки предварительно подготовленных данных.

    Адрес URL должен быть указан в виде текстовой строки, закрытой двоичным нулем, через параметр lpvBuffer. Размер строки вы должны указать в параметре lpdwSize. Что же касается параметра lpdwDataType, то при выполнении данной операции этот параметр игнорируется.

  • HSE_REQ_SEND_URL_REDIRECT_RESP


  • Посылка сообщения с номером 302 (URL Redirect). Адрес URL указывается аналогично тому, как это делается при выполнении операции HSE_REQ_SEND_URL.

  • HSE_REQ_MAP_URL_TO_PATH


  • Преобразование логического адреса URL в физический. Адрес логического пути передается через параметр lpvBuffer. По этому же адресу записывается результат преобразования. Размер буфера при вызове функции задается как обычно с помощью параметра lpdwSize. После преобразования переменная типа DWORD, адрес которой указан параметром lpdwSize, будет содержать длину строки результата преобразования.

  • HSE_REQ_DONE_WITH_SESSION


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

    Функция WriteCilent

    Прототип функции WriteClient, взятый из определения структуры EXTENSION_CONTROL_BLOCK, приведен ниже:
    BOOL (WINAPI * WriteClient)(HCONN ConnID,
         LPVOID Buffer, LPDWORD lpdwBytes, DWORD dwReserved);
    Через параметр hConn функции WriteClient передается идентификатор канала, полученный через поле ConnID структуры EXTENSION_CONTROL_BLOCK.
    Функция WriteClient посылает удаленному пользователю данные из буфера Buffer, причем размер передаваемого блока данных должен быть записан в переменную типа DWORD, адрес которой передается через параметр lpdwBytes. Параметр dwReserved зарезервирован для дальнейших расширений возможностей функции.
    В случае успеха функция возвращает значение TRUE, а при ошибке - значение FALSE. Код ошибки можно получить при помощи функции GetLastError.
    Заметим, что после посылки данных функция WriteClient записывает в переменную, адрес которой был ей передан через параметр lpdwBytes, количество успешно переданных байт данных. В отличие от функции ReadClient, функция WriteClient посылает данные за один прием, поэтому нет необходимости вызывать ее в цикле. Если же эта функция смогла передать только часть данных, то это означает, что произошла ошибка.

    Использование графики в ссылках

    Ссылки в виде текстовых строк удобны, так как после посещения соответствующих страниц они меняют свой цвет. Однако более привлекательно выглядят ссылки, оформленные с использованием графических изображений.
    Для выполнения такого оформления у вас есть две возможности.
    Во-первых, вы можете вставить между операторами
    и графическое изображение небольшого размера. При этом оно будет использовано как чувствительная область. Если сделать щелчок левой клавишей мыши по этой области, произойдет загрузка объекта, указанного в ссылке.
    Во-вторых, для организации ссылок можно применить так называемые сегментированные графические изображения (image maps). Такие изображения разделяются на несколько чувствительных областей, с каждой из которых при помощи отдельного текстового файла связывается та или иная ссылка.
    Рассмотрим обе эти возможности.

    Изменение цвета фона документа HTML

    По умолчанию навигаторы отображают фон документов HTML серым цветом. С помощью параметра BGCOLOR оператора вы можете изменить цвет фона документа HTML, значительно улучшив его внешний вид:

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

    Изменение цвета текста

    Цвет текста легко изменить в операторе , задавая параметр COLOR. В приведенном ниже фрагменте документа текстовая строка отображается красным цветом:
    Красный цвет
    Здесь мы использовали символическое обозначение цвета.
    Заметим, что в спецификации языка HTML версии 3.2 допускается определение цвета в операторе , задающем параметры базового шрифта. Цвет определяется при помощи параметра COLOR, как и в операторе . К сожалению, определение цвета базового шрифта не работает в навигаторе Netscape Navigator версий 2.0 и 3.0. В этом можно убедиться на примере документа HTML, исходный текст которого приведен в листинге 2.5.
    Листинг 2.5. Файл chap2\color.htm


     
        XYZ Incorporation
     
     
       
        Красный цвет
        Серебряный цвет
     

    Если просмотреть этот документ в среде навигатора Microsoft Internet Explorer, строка “Красный цвет” будет действительно отображаться красным цветом на желтом фоне. Навигатор Netscape Navigator покажет эту строку черным цветом.

    Изменение начертания шрифта

    Очень часто для выделения отдельных предложений, слов или символов используется изменение начертания шрифта. При этом можно указывать наклонный, утолщенный, подчеркнутый или перечеркнутый шрифт, шрифт с малым или большим размером символов, шрифт с мигающими символами, шрифт для отображения надстрочных или подстрочных индексов и так далее. В ряде случаев вы можете указать даже название шрифта, такое например, как Arial или Times New Roman. Однако, как мы уже говорили, нельзя рассчитывать, что навигатор пользователя сможет отобразить текст указанным вами шрифтом - этот шрифт может отсутствовать в системе пользователя.

    Изменение размера шрифта

    Шрифт увеличенного размера применяется, как правило, для выделения заголовков или важных слов в тексте. Второстепенная информация для экономии места часто оформляется шрифтом уменьшенного размера.
    В наших первых документах HTML мы не указывали размер шрифта, поэтому использовался тот размер, который был установлен по умолчанию при настройке навигатора. Как устанавливается этот размер?
    Документы HTML обычно не содержат ссылок на название конкретного шрифта, такое, например, как Arial или Times New Roman. К тому же, в документах HTML не указывается точный размер шрифта в пунктах. Причина этого заключается в том, что разработчик документа HTML не может знать, какие шрифты используются для просмотра документа.
    Пользователи могут работать в операционной системе Microsoft Windows, IBM OS/2 Warp или на компьютере Macintosh, они могут иметь различные наборы шрифтов. Поэтому нет смысла указывать название шрифта - скорее всего у пользователя шрифт с таким названием просто не установлен. В результате страница будет оформлена другим шрифтом, что может очень сильно изменить ее внешний вид.
    Нельзя также указывать точный размер шрифта, так как разработчик документа HTML не знает, какое разрешение имеет экран пользователя. Если указать слишком крупный шрифт, в окне навигатора поместится очень мало информации. Если же размер шрифта окажется слишком мал, текст будет трудно прочитать.
    Поэтому все навигаторы дают пользователю возможность выбрать для отображения нормального текста два шрифта из числа установленных в системе, указав размер букв. Первый шрифт имеет фиксированную ширину символов и используется для отображения такой информации, как листинги программ, а второй - это шрифт с переменной шириной букв (пропорциональный шрифт). В нашей книге шрифт с фиксированной шириной букв вы можете увидеть в листингах исходных текстов документов HTML и программ, весь остальной текст оформлен с использованием пропорционального шрифта.
    Что же касается операторов языка HTML, управляющих размером шрифта, то они позволяют указать относительное изменение высоты символов, а не абсолютные значения.

    Языки программирования Java, JavaScript и VBScript

    Язык программирования Java был разработан фирмой Sun как платформно-независимый интерпретируемый объектно-ориентированныя язык. Этот язык был разработан на основе языка программирования С++.
    Вы можете создать программы Java и разместить ссылки на них в документах HTML. Такие программы являются подмножеством программ Java и  называются аплетами (applets).
    Программы Java, расположенные на сервере WWW, также как и органы управления ActiveX обладают большими возможностями по обработке и отображению данных, однако они более безопасны, так как не могут выполнять запись в локальные файлы. Кроме того, эти программы проще в разработке по сравнению с органами управления ActiveX и потому доступны более широкому кругу разработчиков.
    Для создания программ Java вам нужны такие средства разработки, как Java Development Kit (JDK) от фирмы Sun, SDK-Java или Visual J++ от фирмы Microsoft.
    Исходный текст программ, составленных на языке программирования JavaScript или VBScript вставляется непосредственно в документ HTML, поэтому для их разработки не нужны специальные средства (исключая, возможно, отладчик). Интерпретатор языка JavaScript и VBScript встроен непосредственно в навигатор Microsoft Internet Explorer (навигатор Netscape Navigator не работает с языком VBScript).
    В дальнейшем мы планируем выпустить книгу, посвященную языкам программирования Java и JavaScript, получающими все большую популярность.

    Как изменить внешний вид таблицы

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

         

         

       

       

         

         

       

       

         

         

       

       

         

         

       

       

         

         

       

     
    , определяющими внешний вид строк и столбцов таблицы.

    Как работает сервер WWW

    Сервер WWW хранит в своих каталогах текстовые файлы страниц, которые посылаются удаленным пользователям, а также все другие файлы, на которые есть ссылки в страницах (например, файлы с графическими изображениями).
    Текстовые файлы страниц готовятся с использованием специального языка разметки гипертекста HTML (Hyper Text Markup Language). Этот язык будет предметом подробного изучения в нашей книге. Напомним также, что введение в язык HTML вы можете найти в 23 томе “Библиотеки системного программиста”, который называется “Глобальные сети компьютеров. Практическое введение в Internet, e-mail, FTP, WWW и HTML, программирование для Windows Sockets”.

    Как связаться с авторами

    Полную информацию о всех наших книгах серий “Библиотека системного программиста” и “Персональный компьютер. Шаг за шагом”, а также дискеты к книгам, статьи и другую информацию вы можете найти в сети Internet на серверах Web по следующим адресам:
    http://www.glasnet.ru/~frolov
    http://www.dials.ccas.ru/frolov
    Вы можете передать нам свои замечания и предложения по содержанию этой и других наших книг через электронную почту по адресам:
    frolov@glas.apc.org
    frolov.alexandr@usa.net
    Если электронная почта вам недоступна, присылайте ваши отзывы в АО “Диалог-МИФИ” по адресу:
    115409, Москва, ул. Москворечье, 31, корп. 2,
    тел. 324-43-77
    Приносим свои извинения за то что не можем ответить на каждое письмо. Мы также не занимаемся продажей и рассылкой книг, дискет и исходных текстов к нашим книгам. По этому вопросу обращайтесь непосредственно в издательство “Диалог-МИФИ”.

    Как выбрать формат графического файла

    Подготовка и размещение графических изображений в документах HTML - это большое искусство. Однако помимо художественного вкуса, от вас потребуются знание различных технических тонкостей, от правильного учета которых во многих случаях зависит многое.
    Прежде всего вы должны выбрать формат файла, в котором будет храниться графическое изображение. Несмотря на то что сегодня существуют десятки различных графических форматов, практически для оформления документов HTML используются только два. Первый формат - это формат обмена графикой GIF (Graphics Interchange Format), второй - JPEG. Рассмотрим особенности этих форматов, не углубляясь в несущественные для нас технические подробности.

    Какой формат лучше

    Современные навигаторы позволяют работать с форматами GIF, JPEG, PCX, BMP и XBM, однако форматы PCX и BMP не обладают развитыми средствами компрессии, поэтому они практически не используются. Формат XBM родился в мире операционной системы UNIX с оболочкой X-Windows и применяется только для хранения черно-белых изображений. При размещении в документе HTML графического изображения вы должны будете сделать выбор между форматами GIF и JPEG.
    Если вы размещаете в документе HTML копию экрана или другое аналогичное изображение (например, изображение окна приложения или диалоговой панели), выбор сделать легко. В этом случае вам придется использовать формат GIF, так как сжатие с потерей качества приведет к неудовлетворительному результату.
    Аналогично, если требуется получить эффект прозрачности или анимации, выбора тоже нет - только формат GIF позволит вам создать необходимые изображения.
    Труднее всего сделать выбор для фотографических изображений или для рисунков с плавными изменениями цветов, так как трудно заранее сказать, какой из форматов позволит достичь минимального размера файла при приемлемом качестве изображения. Возможно, что вам придется провести эксперименты, выбирая различные степени потери качества для формата JPEG. При этом не исключено, что формат GIF даст лучшие результаты, особенно если учесть возможность использования сокращенной палитры.

    Логическое форматирование символов

    Как мы уже говорили, операторы логического форматирования предназначены для указания семантического (смыслового) назначения оформляемого текста. Они определяют не конкретный способ оформления, а указывают навигатору тип информации, подлежащей выделению. Способ выделения выбирается навигатором.
    Ниже мы привели список операторов логического форматирования символов с кратким описанием.

    Оператор
    Описание
    ,
    Цитата
    ,
    Текст, имеющий особое значение
    ,
    Сильное выделение текста
    ,
    Текст, введенный пользователем
    ,
    Листинг программы
    ,
    Последовательность литералов
    ,
    Имя переменной

    Комментарий

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

    Локальные ссылки внутри документа

    Если длина документа HTML велика, имеет смысл организовать ссылки на его отдельные логически самостоятельные части, расположив их, например, в начале документа. Такие ссылки мы будем называть локальными ссылками в документе.
    Пусть вы создаете документ с именем book.htm, который состоит из нескольких глав. В начале документа вы желаете разместить оглавление, содержащее ссылки на отдельные главы.
    Прежде всего вы должны определить в начале каждой главы локальные метки. Для этого следует использовать оператор с параметром NAME, как это показано ниже:

    Первая глава


    . . .
    Содержимое главы
    . . .

    Вторая глава


    . . .
    Содержимое главы
    . . .

    Третья глава


    . . .
    Содержимое главы
    . . .

    Четвертая глава


    . . .
    Содержимое главы
    . . .
    Здесь для заголовков глав определяются метки с именами Chapter1, Chapter2, Chapter3 и Chapter4.
    Ссылки на созданные таким образом локальные метки выполняются также с помощью оператора , имеющего параметр HREF:
    Первая глава
    Вторая глава
    Третья глава
    Четвертая глава
    Когда пользователь сделает щелчок по локальной ссылке, текущий документ будет сдвинут в окне навигатора по вертикали таким образом, что строка, на которую была сделана ссылка, окажется в верхней части этого окна. Таким образом пользователь, загрузив документ, может легко перейти к просмотру любой его части.
    Заметим, однако, что загрузка большого документа может потребовать много времени. Поэтому следует рассмотреть возможность разделения документа на несколько файлов HTML с организацией оглавления в виде отдельного документа, содержащего только ссылки.

    В листинге 5. 1 мы привели исходный текст документа HTML, в котором созданы две локальные ссылки на две таблицы, имеющие различное оформление.

    Листинг 5.1. Файл chap5\local.htm





     

        Локальные ссылки в документе HTML

     

     

        Таблица с двойным заголовком

        Таблица с двойным заголовком и подписью

       

    Таблица с двойным заголовком



       

         

           

           

           

           

           

       
    Табл. 1.1. Таблица с двойным заголовком
    Заголовок для всех столбцов
    Стобец 1 и 2Столбец 3
    000001002
    010011012
    100101102


       

    Таблица с двойным заголовком и подписью



       

         

         

         

         

         

         

        
    Табл. 1.2. Таблица с двойным заголовком
    Заголовок для всех столбцов
    Стобец 1 и 2Столбец 3
    000001002
    010011012
    100101102


     



    Метод GET

    Метод GET предполагает передачу данных программе CGI через переменные среды (environment variables). Это те самые переменные среды, которые устанавливаются в операционной системе MS-DOS командой SET.
    Сервер WWW создает для программы CGI довольно много переменных среды. Имена и назначение всех этих переменных вы узнаете позже, а пока мы расскажем только о самых необходимых.
    Прежде всего, метод GET предполагает использование переменной среды с именем QUERY_STRING. Именно сюда попадают данные из полей формы. Эти данные находятся в следующем формате:
    “Имя1=Значение1&Имя2=Значение2&Имя3=Значение3”
    Здесь в качестве имен используются значения параметров NAME, задающих имена полей формы. Вместо значений подставляются данные из соответствующих полей. Сканируя содержимое текстовой строки переменной среды QUERY_STRING, программа CGI может найти в ней имя любого нужного поля и соответствующее этому имени значение. Заметим, что никакие данные от выключенных переключателей не передаются, поэтому не следует думать, что в полученной строке вы обязательно встретите имена всех полей, расположенных в форме.
    Адрес заданной строки переменной среды в программе, составленной на C, легко получить с помощью функции getenv:
    char * szQueryString;
    szQueryString = getenv("QUERY_STRING");
    Заметим, что если вы собираетесь модифицировать строку переменной среды, то ее следует скопировать во внутренний буфер. Операционная система сервера WWW может не допустить прямого редактирования блока памяти, содержащего переменные среды.
    Строка, передаваемая в переменной среды QUERY_STRING, закодирована с использованием так называемой кодировки URL. В этой кодировке все символы пробелов заменяются на символы “+”. Кроме того, для представления кодов управляющих и некоторых других символов используется последовательность символов вида “%xx”, где символы “xx” представляют собой шестнадцатеричный код символа в виде двух символов ASCII. В нашей книге вы найдете исходные тексты функций, предназначенные для перекодирования информации, полученной из формы.

    Метод POST

    При использовании метода POST программа CGI получает данные из формы через стандартный поток ввода STDIN. Если программа CGI составлена на языке программирования C, то для получения данных она может воспользоваться такими функциями, как fread или scanf.
    Что же касается количества байт данных, которые нужно считать из стандартного потока ввода, то эта информация передается программе CGI через переменную среды с именем CONTENT_LENGTH.
    Ниже мы привели фрагмент кода для определения размера информации для ввода через стандартный поток STDIN:
    int Size;
    Size = atoi(getenv("CONTENT_LENGTH"));
    Входные данные могут быть затем получены, например, следующим образом:
    char szBuf[8196];
    fread(szBuf, Size, 1, stdin);
    Разумеется, буфер для чтения данных можно заказывать и динамически, для чего следует воспользоваться такой функцией, как malloc.
    Если в операторе
    не указан параметр ENCTYPE (тип MIME передаваемых данных) или этот параметр имеет значение application/x-www-form-urlencoded, данные, полученные через стандартный поток ввода, закодированы в кодировке URL. Перед использованием вы должны их раскодировать соответствующим образом.

    Многослойные заголовки

    Строки или столбца таблицы могут иметь несколько заголовков, расположенных слоями. В листинге 3.6 мы привели исходный текст документа HTML с двойным заголовком столбцов.
    Листинг 3.6. Файл chap3\tableh4.htm


     
        Таблицы с заголовком
     
     
       
         
           
         
         
           
           
         
         
           
         
         
           
         
         
           
         
       
    Заголовок для всех столбцов
    Стобец 1 и 2Столбец 3
    000001002
    010011012
    100101102

     

    Первый заголовок в этом документе относится ко всем столбцам. Второй заголовок расположен под первым. В нем используется объединение соседних ячеек для первого и второго столбца. Внешний вид получившейся таблицы показан на рис. 3.6.
    Многослойные заголовки
    Рис. 3.6. Таблица с двойным заголовком

    Настройка сервера Microsoft Personal Web Server

    Вы можете в большинстве случаев использовать значения многих параметров сервера Microsoft Personal Web Server, выбранных по умолчанию после установки. Однако не исключено, что вам придется выполнить дополнительную настройку сервера.
    Настроить параметры сервера можно при помощи приложения Personal Web Server, пиктограмма которого появляется после установки сервера в папке Control Panel. Если сделать по этой пиктограмме двойной щелчок левой клавишей мыши, на экране появится блокнот Personal Web Server Properties. На рис. 9.4 показана страница General этого блокнота.
    Настройка сервера Microsoft Personal Web Server
    Рис. 9.4. Страница General блокнота Personal Web Server Properties
    В верхней части страницы General отображается адрес вашего сервера в сети Internet или в сети Intranet (если ваша корпоративная сеть не подключена к Internet).
    В поле Default Home Page отображается физический путь к главной (домашней) странице вашего сервера. По умолчанию эта страница находится в файле с именем Default.htm, который создается в каталоге C:\WebShare\wwwroot. Если нажать кнопку Display Home Page, будет запущен навигатор Microsoft Internet Explorer, в котором вы увидите содержимое главной страницы сервера (рис. 9.5).
    Нажав на кнопку More Details, вы сможете ознакомиться с документацией для сервера Personal Web Server, которая также отобразится в окне навигатора Microsoft Internet Explorer.
    Настройка сервера Microsoft Personal Web Server
    Рис. 9.5. Содержимое главной страницы сервера Microsoft Personal Web Server
    Исходный текст соответствующего документа HTML представлен ниже:




    <br>        My Home Page<br>





           (Your Home Page goes here)





    Welcome to my web server running on Windows 95.








    Here are my interests.



  • Movies

  • Reading




    Here are some useful links:



  • Microsoft




    This page has been accessed Access counter times.








    Brought to you by the

    Personal Web Server from

    Microsoft.









    В нижней части страницы находится счетчик посещений, реализованный в виде расширения ISAPI. Файл соответствующе библиотеки DLL называется counter.dll.

    При создании собственного сервера можно взять документ Default.htm за основу. При этом вы можете разместить на своей странице счетчик посещений counter.dll, сделанный Microsoft.

    Вернемся к страницам блокнота Personal Web Server Properties.

    Откройте страницу Startup, показанную на рис. 9.6.

    Настройка сервера Microsoft Personal Web Server

    Рис. 9.6. Страница Startup блокнота Personal Web Server Properties

    С помощью кнопок Start и Stop можно, соответственно, запустить или остановить сервер Personal Web Server.

    Если включить в группе Options переключатель Run the server automatically at startup, сервер Personal Web Server будет запускаться автоматически после загрузки операционной системы. Этот режим выбран по умолчанию.

    Для удобства управления сервером вы также можете включить переключатель Show the Web server icon on the taskbar. При этом в правом углу на панели задач, расположенной в нижней части рабочего стола Windows 95, появится пиктограмма сервера Personal Web Server.


    Если сделать по этой пиктограмме двойной щелчок левой клавишей мыши, на экране появится блокнот Personal Web Server Properties. С помощью правой клавиши мыши вы можете вызвать на экран контекстное меню, состоящее из строк Administer, Home Page и Properties.

    При выборе строки Administer будет запущен навигатор Microsoft Internet Explorer, в окне которого появится документ HTML, предназначенный для администрирования сервера. Процесс администрирования будет рассмотрен позже.

    Если выбрать строку Home Page, также будет запущен навигатор Microsoft Internet Explorer. В его окне вы увидите главную страницу вашего сервера. Сразу после установки она будет иметь вид, показанный на рис. 9.5.

    Выбор строки Properties приведет к появлению на экране блокнота Personal Web Server Properties.

    На рис. 9.7 показана страница Administration блокнота Personal Web Server Properties.

    Настройка сервера Microsoft Personal Web Server

    Рис. 9.7. Страница Administration блокнота Personal Web Server Properties

    На этой странице есть всего одна кнопка с названием Administration. Если нажать на эту кнопку, в окне навигатора Microsoft Internet Explorer появится документ HTML, предназначенный для администрирования сервера.

    И, наконец, последняя страница блокнота Personal Web Server Properties, которая называется Services, показана на рис. 9.8.

    Настройка сервера Microsoft Personal Web Server

    Рис. 9.8. Страница Services блокнота Personal Web Server Properties

    В поле Services находится список сервисов, предоставляемых сервером Personal Web Server. По умолчанию сразу после установки работает сервис HTTP, который реализует сервер WWW. Нажав кнопку Stop, вы можете при необходимости остановить этот сервис. С помощью кнопки Start работа сервиса может быть возобновлена.

    Для чего вам может потребоваться останавливать сервис HTTP?

    Например, если вы заменяете библиотеку DLL, содержащую расширение сервера ISAPI, то перед выполнением такой замены следует остановить сервис HTTP, а затем, после замены файла библиотеки, возобновить его работу.

    Сервер Microsoft Personal Web Server может работать не только как сервер WWW, но и как сервер FTP. По умолчанию сразу после установки сервис FTP отключен, однако его можно включить на странице Services, показанной на рис. 9.8. Для этого следует выбрать в списке строку FTP и нажать кнопку Start.


    Кнопка Properties, расположенная на странице Services блокнота Personal Web Server Properties, позволяет настроить параметры сервиса HTTP или FTP. Выбрав в списке сервис HTTP и нажав эту кнопку, вы увидите диалоговую панель HTTP Properties, показанную на рис. 9.9.

    Настройка сервера Microsoft Personal Web Server

    Рис. 9.9. Диалоговая панель HTTP Properties

    С помощью переключателей группы Startup Options вы можете выбрать автоматический или ручной режим запуска сервиса HTTP. Для этого нужно включить, соответственно, переключатель Automatic или Manual. По умолчанию сервис HTTP запускается автоматически, однако вы можете выбрать ручной режим, если сервер WWW вам нужен лишь эпизодически (например, вы его используете только для отладки программ CGI или ISAPI).

    В поле The Web server home root directory показан физический путь к корневому каталогу, в котором хранятся ваши документы HTML. При необходимости вы можете выбрать в качестве корневого другой каталог, нажав кнопку Change Home Root. Процедура настройки каталогов будет описана ниже в разделе “Администрирование”.

    Аналогично, в поле The Web server default home page указан физический путь к документу HTML, который выполняет роль главной страницы вашего сервера. Нажав кнопку Change Home Page, вы можете изменить имя или расположение главной страницы.

    Диалоговая панель FTP Properties, предназначенная для настройки параметров сервера FTP, показана на рис. 9.10.

    Настройка сервера Microsoft Personal Web Server 

    Рис. 9.10. Диалоговая панель FTP Properties

    По умолчанию для сервиса FTP установлен ручной режим запуска, поэтому сразу после установки в группе Startup Options включен переключатель Manual. Если вы планируете использовать сервис FTP постоянно наряду с сервисом HTTP, имеет смысл выбрать автоматический режим запуска, включив переключатель Automatic.

    Путь к корневому каталогу, в котором находятся все файлы и подкаталоги, доступные через протокол FTP, отображается в поле The FTP Server Home Root Directory. Вы можете изменить этот путь, нажав кнопку Change FTP Home Root.

    Неупорядоченные списки

    Для создания неупорядоченного списка предназначен оператор , который используется в паре с оператором , закрывающем список. Каждая строка в списке отмечается оператором
  • , как это показано ниже:

     
  • Первая строка
     
  • Вторая строка
     
  • Третья строка

    Если документ со списком предназначен для просмотра навигатором Netscape Navigator, вы можете добавить в оператор
  • параметр TYPE. Этот параметр определяет внешний вид символа, который используется для выделения строки в списке, и может иметь следующие значения:

    Значение оператора TYPE
    Символ, который используется для выделения
    DISK
    Круглая жирная точка
    CIRCLE
    Окружность
    SQUARE
    Маленький черный квадрат

    К сожалению, навигатор Microsoft Internet Explorer игнорирует параметры команды LI, используя для выделения только жирную точку.
    В листинге 2.13 мы привели исходный текст документа HTML, в котором есть два неупорядоченных списка. Первый из них обычный, а во втором для выделения каждой строки используются различные символы.
    Листинг 2.13. Файл chap2\list.htm


     
        Ненумерованные списки
     
     
       
         
  • Первая строка
         
  • Вторая строка
         
  • Третья строка
       
       
         
  • Первая строка
         
  • Вторая строка
         
  • Третья строка
       
     

    Внешний вид этого документа при его просмотре в навигаторе Netscape Navigator показан на рис. 2.14.
    Неупорядоченные списки
    Рис. 2.14. Использование неупорядоченных списков
    Если просматривать этот документ в навигаторе Microsoft Internet Explorer, то нижний список не будет ничем отличаться от верхнего.
    Для создания неупорядоченных списков вы также можете использовать вместо оператора оператор (вместе с оператором ). Элементы таких списков должны состоять из одиночных строк (наподобие строк меню).
    Списки, созданные оператором , выглядят более компактно, чем списки, сделанные при помощи оператора .

    Нумерованные списки

    Для создания нумерованных списков предназначен оператор , который необходимо использовать вместе с оператором . Нумерованный список создается аналогично только что рассмотренному неупорядоченному списку. Каждая строка в списке должна быть отмечена оператором
  • .
    В операторе можно указывать перечисленные ниже параметры:

    Параметр
    Описание
    START
    Начальный номер для списка
    TYPE
    Тип нумерации:
    A              заглавными буквами
    a              прописными буквами
    I               заглавными римскими цифрами
    i               прописными римскими цифрами
    1              арабскими цифрами
    COMPACT
    Список должен отображаться компактно с уменьшением расстояния между строками

    В листинге 2.14 мы привели исходный текст документа HTML, содержащего два списка. Первый из них использует нумерацию арабскими цифрами, принятую по умолчанию. Для второго указана нумерация прописными буквами.
    Листинг 2.14. Файл chap2\listol.htm


     
        Нумерованные списки
     
     
       
         
  • Первая строка
         
  • Вторая строка
         
  • Третья строка
       
       

           
    1. Первая строка
           
    2. Вторая строка
           
    3. Третья строка
         
       

      На рис. 2.15 мы привели исходный вид этого документа.
      Нумерованные списки
      Рис. 2.15. Нумерованные списки
      Заметим, что если бы во втором списке был указан параметр START со значением, например, c, строки списка были бы пронумерованы символами c, d и e.

      Объединение ячеек таблицы

      Для объединения ячеек таблицы, расположенных в одной и той же строке, вы можете воспользоваться параметром COLSPAN. Аналогично, для объединения ячеек, расположенных в одной и той же строке, используется параметр ROWSPAN.
      В документе HTML, исходный текст которого приведен в листинге 3.4, мы показали, как можно использовать объединение ячеек для создания общего заголовка к двум столбцам таблицы.
      Листинг 3.4. Файл chap3\tableh2.htm


       
          Таблицы с заголовком
       
       
         
           
             
             
           
           
           
           
         
      Столбец 1Столбец 2 и 3
      000001002
      010011012
      100101102

       

      Здесь параметр COLSPAN указан в операторе
  • , определяющем вторую ячейку заголовка, и имеет значение, равное двум. Внешний вид таблицы, которая получится в результате, показан на рис. 3.4.
    Объединение ячеек таблицы
    Рис. 3.4. Таблица с общим заголовком для двух столбцов
    Аналогично вы можете создать общий заголовок для строк таблицы, воспользовавшись параметром ROWSPAN. Исходный текст документа HTML, в котором имеется таблица с общим заголовком для двух строк, показан в листинге 3.5.
    Листинг 3.5. Файл chap3\tableh3.htm


     
        Таблицы с заголовком
     
     
       
         
           
           
         
         
           
         
          
           
           
         
       
    Строка 1 и 2000001002
    010011012
    Строка 3100101102

     

    Внешний вид соответствующей таблицы мы привели на рис. 3.5.
    Объединение ячеек таблицы
    Рис. 3.5. Таблица с общим заголовком для двух строк

    Обозначение цвета

    Цвет отдельных элементов документа HTML определяется соответствующими параметрами операторов, создающих эти элементы. В качестве значения операторам необходимо передавать либо численное значение отдельных компонент цвета, либо символическое название цвета.
    Численное значение указывается в виде #RRGGBB, где RR определяет содержание красной компоненты цвета, GG - зеленой, а BB - голубой.
    Содержание компонент может изменяться в диапазоне от 0 до FF (шестнадцатеричное значение). Например, значение #FF0000 соответствует чистому красному цвету, а значение #00FF00 - чистому зеленому цвету.
    Если вы ограничите себя только шестнадцатью цветами, допустимыми в режиме VGA, то сможете указывать цвет символически. Ниже мы привели список символических названий цвета, которые можно указывать в языке HTML:

    Символическое название
    Цвет
    Численное значение
    Aqua
    морская волна
    #00FFFF
    Black
    черный
    #000000
    Blue
    голубой
    #0000FF
    Fuchsia
    малиновый
    #FF00FF
    Gray
    серый
    #808080
    Green
    зеленый
    #008000
    Lime
    ярко-зеленый
    #00FF00
    Maroon
    темно-красный
    #800000
    Navy
    темно-синий
    #000080
    Olive
    оливковый
    #808000
    Purple
    пурпурный
    #800080
    Red
    красный
    #FF0000
    Silver
    серебряный
    #C0C0C0
    Teal
    темная морская волна
    #008080
    White
    белый
    #FFFFFF
    Yellow
    желтый
    #FFFF00

    К сожалению, символическое определение цвета не работает, если пользователь будет просматривать документ HTML навигатором Netscape Navigator версии 2.0. Навигатор Netscape Navigator версии 3.0, а также навигатор Microsoft Internet Explorer способен работать с символическим определением цвета.

    Общие таблицы стилей для нескольких документов HTML

    Наиболее интересное применение таблиц стилей - это их использование для оформления многих, возможно, даже всех документов HTML, расположенных на вашем сервере. При этом выполняется подключение (linking) файлов таблиц стилей к документам HTML. Имя файла таблицы стилей должно иметь вид *.css.
    Изменяя содержимое файлов таблиц стилей, вы сможете без прямого редактирования изменить внешний вид всех документов HTML, к которым подключены эти файлы.
    Заметим, что общие таблицы стилей могут использоваться одновременно не только на одном, но и на нескольких серверах WWW, так как можно подключать файлы таблиц стилей, указывая их стандартный адрес URL. Это позволяет централизованно создавать и изменять корпоративные таблицы стилей.

    Оператор

    Оператор используется в паре с оператором и предназначен для выделения фрагмента текста жирным шрифтом. Вот пример его использования:
    Выделение символов жирным шрифтом

    Оператор

    Оператор предназначен для изменения базового размера шрифта, его начертания и цвета. Параметры, определяющие начертание шрифта и цвет символов, мы рассмотрим немного позже. Что же касается базового размера шрифта, то он задаются параметром SIZE:
    Размер шрифта равен 3
    В этом примере базовый размер шрифта устанавливается равным 3, однако не следует думать, что здесь имеется в виду высота символов, равная 3 пунктам. Пользователь может изменить фактический размер шрифта, поэтому значение 3 следует рассматривать как относительное. По умолчанию навигаторы Microsoft Internet Explorer и Netscape Navigator используют для шрифта с размером 3 высоту символов, равную 12 пунктам.
    Таблица соответствия базового размера шрифта, указанного в операторе , и отображаемая навигатором Microsoft Internet Explorer показана ниже:

    Значение параметра SIZE
    Размер символов в пикселах
    1
    9
    2
    10
    3
    12
    4
    14
    5
    18
    6
    24
    7
    36

    Заметьте также, что параметр SIZE изменяет размер шрифта нелинейно. Если, например, размер символов для значений этого параметра 1 и 2 отличаются друг от друга на единицу, то для значений 6 и 7 разница составит 12 пикселов.

    Оператор

    Оператор , который используется в паре с оператором , позволяет выделить текст миганием. Эта иллюминация, однако, будет видна только в навигаторе Netscape Navigator. Что же касается навигатора Microsoft Internet Explorer, то он игнорирует оператор . Впрочем, для создания динамических страниц имеются и другие, более мощные средства, которые мы рассмотрим позже в отдельной главе.
    Используя выделение текста миганием, будьте умереннее. Лучше всего сделать так, чтобы в документе мигало только одно слово или предложение, которое действительно необходимо выделить. В противном случае мигание будет действовать на пользователя раздражающе и он захочет поскорее покинуть вашу страницу.

    Оператор

    С помощью оператора вы можете снабдить таблицу подписью, расположенной сверху над таблицей или снизу под таблицей.
    Для оператора
    в стандарте языка HTML версии 3.2 определен параметр ALIGN. Этот параметр может иметь значения TOP или BOTTOM. В первом случае подпись будет расположена над таблицей, а во втором - под таблицей.
    Ниже мы привели пример использования оператора
    :

     
     
     
     
    Табл. 1. Таблица значений
    000001002
    010011012
    100101102

    Навигатор Microsoft Internet Explorer использует иную систему определения расположения подписи таблицы. Дополнительно к параметру ALIGN здесь используется параметр VALIGN.
    С помощью параметра VALIGN, который может иметь значения TOP или BOTTOM, вы можете изменять расположение подписи по вертикали - над или под таблицей.
    Параметр ALIGN в трактовке навигатора Microsoft Internet Explorer может принимать значения LEFT или RIGHT и определяет выравнивание подписи по горизонтали.
    Например, в следующем фрагменте исходного текста документа HTML создается таблица с подписью, которая расположена над таблицей и выровнена по правой границе:

     
     
     
     
    Табл. 2. Таблица значений
    000001002
    010011012
    100101102

    Способ оформления подписей таблиц, предлагаемый фирмой Microsoft, имеет больше возможностей, однако этот способ несовместим с навигатором Netscape Navigator. Это ограничивает его применение.

    Оператор

    С помощью оператора вы можете оформит в тексте цитату. Обычно цитата выделяется наклонным шрифтом. Оператор должен использоваться в паре с оператором .

    Оператор

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

    Оператор

    С помощью оператора вы можете оформить текст, имеющий особое значение. Название этого оператора произошло от слова emphasis, которое можно перевести как ударение, подчеркивание или особое значение. Такой текст обычно выделяется с использованием наклонного шрифта.

    Оператор

    С помощью оператора с параметром SIZE вы можете изменить текущий размер шрифта относительно базового значения, заданного оператором .
    В качестве значения для параметра SIZE вы можете указывать значения со знаками + или -. Например, в следующем примере первая строка имеет размер 3, а вторая - 6:
    Размер шрифта равен 3
    Размер шрифта равен 3+3=6
    В листинге 2.4 мы привели пример документа HTML, в котором использованы описанные ранее операторы, предназначенные для изменения размера шрифта.
    Листинг 1.4. Файл chap2\size.htm


     
        XYZ Incorporation
     
     
       
        Добро пожаловать в мир серверов WWW!
       
        Образец нормального шрифта
       
       
        Образец шрифта уменьшенной высоты
       

        Размер шрифта равен 3
        Размер шрифта равен 3+3=6
        Размер шрифта равен 3-1=2
     

    На рис. 2.6 показан внешний вид этого документа в окне навигатора Microsoft Internet Explorer.
    Оператор <FONT…>
    Рис. 2.6. Изменение размера шрифта

    Оператор

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

    Параметр
    Описание
    TYPE
    Тип органа управления. В зависимости от значения этого параметра будут создаваться различные органы управления (кнопки, переключатели и так далее)
    NAME
    Имя органа управления. Это имя посылается программе обработки формы и используется для определения состояния органа управления (для переключателей) или получения других данных (например, для получения строки, введенной в текстовом поле)
    VALUE
    Начальное состояние или начальное значение для органа управления. Используется для инициализации органа управления при начальном отображении формы
    CHECKED
    Этот параметр используется для установки начального значения переключателей
    SIZE
    Ширина поля для ввода текстовой информации в символах. По умолчанию поле имеет ширину 20 символов
    MAXLENGTH
    Максимальное количество символов, которое можно ввести в поле редактирования текстовой информации. По умолчанию такое ограничение отсутствует
    ALIGN
    Выравнивание текста, расположенного около формы
    SRC
    Адрес URL графического изображения, если оно используется в органе управления

    Параметр TYPE определяет тип создаваемого органа управления и может иметь следующие значения:

    Значение параметра TYPE
    Тип органа управления
    TEXT
    Однострочное поле для ввода текстовой информации. Размер этого поля определяется параметрами SIZE и MAXLENGTH
    TEXTAREA
    Многострочное поле для ввода текстовой информации. Размер поля также определяется параметрами SIZE и MAXLENGTH
    PASSWORD
    Этот орган управления предназначен для ввода такой информации, как пароли. Он аналогичен органу управления типа TEXT, но отличается тем, что текст, введенный пользователем, не отображается на экране
    CHECKBOX
    Переключатель типа Check Box. Предназначен для использования в наборе независимых друг от друга переключателей или отдельно
    RADIO
    Переключатель для группы зависимых переключателей. Используется для выбора одного значения из нескольких
    FILE
    Орган управления для выбора и передачи файла. Это значение используется по-разному навигаторами Microsoft Internet Explorer и Netscape Navigator
    BUTTON
    Кнопка с заданной надписью
    SUBMIT
    Кнопка, которая предназначена для посылки данных из заполненной формы серверу WWW. Надпись на этой кнопке также можно задавать
    RESET
    С помощью этой кнопки пользователь может сбросить содержимое полей ввода и состояние переключателей в их начальные значения, заданные операторами VALUE
    IMAGE
    Для посылки данных из формы в сервер вы можете использовать не только кнопку типа SUBMIT, но и произвольное графическое изображение, заданное параметром SRC. Соответствующий графический орган управления имеет тип IMAGE
    HIDDEN
    Скрытое поле, которое не отображается. Содержимое этого поля посылается серверу и может быть проанализировано


    Оператор

    Оператор предназначен для выделения текста шрифтом с фиксированной шириной символов. Это может быть, например, образец команд, введенных пользователем. Навигатор Microsoft Internet Explorer версии 3.0 отображает такой текст жирным шрифтом с фиксированной шириной символов, а навигатор Netscape Navigator использует фиксированный нежирный шрифт.

    Оператор

    Оператор обеспечивает для навигатора дополнительную информацию о документе HTML. С помощью этого оператора вы можете отменить кэширование документа, заставить навигатор выполнять загрузку документа HTML периодически с заданным интервалом времени. Можно сделать так, чтобы навигатор через заданное время после загрузки документа HTML автоматически загрузил другой документ HTML.
    Рассмотрим параметры оператора .

    Параметр
    Описание
    CONTENT
    Содержимое дополнительной информации, которая связана с заданным именем или ответным заголовком протокола HTTP. Может использоваться вместе с параметром URL, для определения даты и времени, когда необходимо выполнить повторную загрузку документа HTML
    CHARSET
    Этот параметр определяет набор символов, который используется в данном документе HTML. Параметр CHARSET указывается в составе оператора CONTENT
    HTTP-EQUIV
    Параметр HTTP-EQUIV выполняет привязку к ответному заголовку HTTP. Эта информация может использоваться различными навигаторами по-разному
    NAME
    Имя дополнительной информации
    URL
    Адрес URL документа HTML. Параметр URL указывается в составе оператора CONTENT

    Ниже мы привели несколько примеров использования оператора  .

    Оператор

    С помощью оператора обычно выделяют отдельные слова или последовательность литеральных символов. Такой текст отображается  нежирным шрифтом с фиксированной шириной символов.

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

    Для записи строк в список здесь используется оператор

    Switches CHECKBOX

            One

            Two

            Nothing

         
    Action:

            Virus Checking

            Spell Checking

            Translate

         
    Select Uploaded File:
    List

           

         
    Hidden Control



     










    Здесь вам нужно обратить внимание на параметры оператора
    , с помощью которого в документе HTML создается форма:



    Параметр ENCTYPE задает тип кодировки передаваемых данных как multipart/form-data. Метод передачи данных указан как POST, а в параметре ACTION находится адрес URL файла библиотеки DLL нашего расширения ISAPI.

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



      Select Uploaded File:

     



    Здесь указан тип поля FILE и имя поля fupload.

    Внешний вид формы, содержащий орган управления для выбора файла, показан на рис. 8.5.

    Приложение FILEUPL

    Рис. 8.5. Форма, позволяющая выбирать файл для передачи серверу WWW

    На этом рисунке в поле Select Uploaded File уже выбран файл C:\UT\800.COM. Если нажать на кнопку Browse, на экране появится диалоговая панель File Upload, показанная на рис. 8.6.

    Приложение FILEUPL

    Рис. 8.6. Диалоговая панель File Upload, с помощью которой можно выбрать файл для передачи серверу WWW

    Теперь если выбрать файл и нажать кнопку Send, файл и данные из других полей формы будут переданы расширению ISAPI fileupl.dll. Расширение запишет принятые данные без какой-либо обработки в файл и возвратит пользователю сообщение (в виде динамически созданного документа HTML) об успешном завершении пересылки файла, показанное на рис. 8.7.

    Приложение FILEUPL

    Рис. 8.7. Сообщение об успешном завершении передача файла

    Рассмотрим исходные тексты расширения, приведенные в листинге 8.8.

    Листинг 8.8. Файл chap8\fileupl\fileupl.c

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

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

    // Загрузка файла из локального компьютера

    // на диск удаленного сервера WWW

    // из документа HTML

    //

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

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


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

    //         или

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

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

    #include

    #include

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

    LPVOID ReadClientMIME(EXTENSION_CONTROL_BLOCK *lpECB,

                          int *nStatus);

    BOOL GetMIMEBoundary(LPVOID lpDataMIME, LPSTR lpBuffer,

                         DWORD dwBufSize);

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

    // Функция GetExtensionVersion

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

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

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

    BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)

    {

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

      pVer->dwExtensionVersion =

        MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR );

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

      lstrcpyn(pVer->lpszExtensionDesc,

        "Remote File Upload", HSE_MAX_EXT_DLL_NAME_LEN);

      return TRUE;

    }

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

    // Функция HttpExtensionProc

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

    DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *lpECB)

    {

      CHAR   szBuff[4096];

      HANDLE hOutFile;

      DWORD  dwWritten;

      LPVOID lpDataMIME;

      int    nStatus;

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

      lpECB->dwHttpStatusCode = 0;

      // Получаем данные от навигатора в кодировке MIME

      lpDataMIME = ReadClientMIME(lpECB, &nStatus);

      if(lpDataMIME != NULL)

      {

        // Создаем файл для записи принятых данных

        hOutFile = CreateFile("e:\\InetPub\\scripts\\uploaded.dat",

          GENERIC_WRITE, FILE_SHARE_READ, NULL,

          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

            

        if(hOutFile != INVALID_HANDLE_VALUE)

        {

          // Выполняем запись данных в файл

          WriteFile(hOutFile, lpDataMIME,


            lpECB->cbTotalBytes, &dwWritten, NULL);

              

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

          CloseHandle(hOutFile);

        }

         

        // Освобождаем память, заказанную для принятых

        // данных функцией ReadClientMIME

        LocalFree(lpDataMIME);

      }

     

      // Создаем документ HTML с сообщением об

      // успешной загрузке файла

      wsprintf(szBuff,

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

        " Remote File Upload\n"

        "

    Make your file upload!

    \n"

        "
    \n");

      strcat(szBuff, "Upload finished");

      strcat(szBuff, ""); 

      // Отправляем созданный документ HTML

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

        HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff))

      {

        return HSE_STATUS_ERROR;

      }

      lpECB->dwHttpStatusCode = 200;

      return HSE_STATUS_SUCCESS;

    }

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

    // Функция ReadClientMIME

    // Получение данных в кодировке MIME

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

    LPVOID ReadClientMIME(

           EXTENSION_CONTROL_BLOCK *lpECB,

           int *nStatus)

    {

      DWORD cbReaded;

      DWORD nBufferPos;

      DWORD nBytesToCopy;

      LPVOID lpTemp = NULL;

     

      // Код завершения

      *nStatus = 0;

      // Определяем, есть ли данные для чтения

      if(lpECB->cbTotalBytes != 0)

      {

        // Заказываем буфер памяти для чтения принятых данных

        if(!(lpTemp = (LPVOID)LocalAlloc(LPTR,

          lpECB->cbTotalBytes)))

        {

          // Если памяти не хватает, завершаем работу

          // с установкой кода ошибки и возвращаем

          // значение NULL

          *nStatus = HSE_STATUS_ERROR;

          return NULL;

        }

        // Копируем в буфер предварительно считанные данные

        memcpy(lpTemp, lpECB->lpbData, lpECB->cbAvailable);


       

        // Устанавливаем указатель текущей позиции

        // в буфере после скопированных данных

        nBufferPos = lpECB->cbAvailable;

     

        // Определяем, сколько данных нужно считать

        // дополнительно с помощью функции ReadClient

        nBytesToCopy = lpECB->cbTotalBytes - lpECB->cbAvailable;

            

        // Если не все данные находятся в буфере предварительного

        // чтения, запускаем цикл копирования оставшихся данных

        if(nBytesToCopy > 0)

        {

          while(1)

          {

            // Читаем очередную порцию данных в текущую

            // позицию буфера

            lpECB->ReadClient(lpECB->ConnID,

               (LPVOID)((LPSTR)lpTemp + nBufferPos), &cbReaded);

                 

            // Уменьшаем содержимое переменной nBytesToCopy,

            // в которой находится размер непрочитанных данных

            nBytesToCopy -= cbReaded;

            // Продвигаем указатель текущей позиции в буфере

            // на количество прочитанных байт данных

            nBufferPos   += cbReaded;

                 

            // Когда копировать больше нечего, прерываем цикл

            if(nBytesToCopy <= 0l)

              break;

          }

        }

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

        // адрес буфера с прочитанными данными

        return lpTemp;

      }

     

      // Если данных для чтения нет, завершаем

      // работу с установкой кода ошибки

      else

      {

        *nStatus = HSE_STATUS_ERROR;

        // В случае ошибки вместо адреса буфера

        // с прочитанными данными возвращается

        // значение NULL

        return NULL;

      }

    }

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

    // Функция GetMIMEBoundary

    // Поиск разделителя в буфере.

    // Параметры:

    //   lpDataMIME - адрес буфера с данными MIME

    //   lpBuffer   - адрес буфера для записи разделителя

    //   dwBufSize  - размер буфера с данными MIME

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

    BOOL GetMIMEBoundary(LPVOID lpDataMIME, LPSTR lpBuffer, DWORD dwBufSize)


    {

      LPSTR lpCurrent;

      DWORD dwOffset;

      BOOL  fFound;

     

      // Устанавливаем признак успешного поиска

      fFound = TRUE;

      // Ищем конец первой строки

      for(lpCurrent = lpDataMIME,

        dwOffset = 0;;lpCurrent++, dwOffset++)

      {

        // Сравниваем с концом строки

        if(!memcmp(lpCurrent,"\r\n",2))

          break;

       

        // Если достигнут конец буфера,

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

        // и прерываем работу цикла

        if(dwOffset >= dwBufSize)

        {

          fFound = FALSE;

          break;

        }

        // Копируем очередной символ разделителя

        *(lpBuffer + dwOffset) = *lpCurrent;

      }

     

      // Если разделитель найден, закрываем строку

      // разделителя двоичным нулем

      if(fFound)

        *lpBuffer = '\0'; 

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

      // неуспешного поиска

      return fFound;

    }

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

    Листинг 8.9. Файл chap8\fileupl\fileupl.def

    LIBRARY            fileupl

    DESCRIPTION  'File Upload DLL'

    EXPORTS

        GetExtensionVersion

        HttpExtensionProc

    Рассмотрим функции нашего расширения ISAPI.

    Функция GetExtensionVersion не имеет никаких особенностей.

    Функция HttpExtensionProc в начале своей работы вызывает функцию ReadClientMIME, определенную в нашем приложении. Эта функция заказывает динамически блок памяти, достаточный для размещения принимаемых от удаленного пользователя данных, записывает в этот блок принятые данные и возвращает указатель на заказанный блок памяти. После использования вы должны освободить блок памяти функцией LocalFree.

    Если данные были приняты успешно, функция HttpExtensionProc создает файл, в который будут записаны принятые данные:

    hOutFile = CreateFile("e:\\InetPub\\scripts\\uploaded.dat",

          GENERIC_WRITE, FILE_SHARE_READ, NULL,

          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

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


    Для создания файла мы использовали функцию CreateFile, описанную нами в 26 томе “Библиотеки системного программиста”, который называется “Программирование для Windows NT. Часть 1”. Примеры использования этой и других функций, предназначенных для работы с файлами, вы можете найти в 27 томе этой же серии, который называется “Программирование для Windows NT. Часть 2”.

    Запись принятых данных в файл выполняется за один вызов функции WriteFile:

    WriteFile(hOutFile, lpDataMIME,

       lpECB->cbTotalBytes, &dwWritten, NULL);

    В качестве размера блока данных здесь указывается содержимое поля cbTotalBytes структуры типа EXTENSION_CONTROL_BLOCK. После выполнения записи файл закрывается функцией CloseHandle. Блок памяти, полученный от функции ReadClientMIME, освобождается при помощи функции LocalFree.

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

    Теперь займемся функцией ReadClientMIME.

    В качестве первого параметра эта функция получает указатель на блок EXTENSION_CONTROL_BLOCK, передаваемый функции HttpExtensionProc  через единственный параметр. Второй параметр nStatus используется для передачи результата работы функции ReadClientMIME вызывающей программе.

    В самом начале своей работы функция ReadClientMIME анализирует содержимое поля cbTotalBytes структуры EXTENSION_CONTROL_BLOCK, в котором находится размер принимаемых данных. Если данных для чтения нет, функция ReadClientMIME передает код ошибки HSE_STATUS_ERROR и возвращает вызывающей программе значение NULL.

    Если все нормально и данные для чтения есть, функция ReadClientMIME с помощью функции LocalAlloc заказывает блок памяти размером lpECB?>cbTotalBytes байт. Описание этой функции вы найдете в только что упомянутом 26 томе “Библиотеки системного программиста”.

    После этого начинается процесс копирования данных в полученный буфер.

    Вначале функция ReadClientMIME копирует блок данных из буфера предварительного чтения, вызывая для этого функцию memcpy:


    memcpy(lpTemp, lpECB->lpbData, lpECB->cbAvailable);

    Напомним, что размер этого буфера не превышает 48 Кбайт. Буфер предварительного чтения располагается по адресу lpECB->lpbData и в нем доступно для чтения lpECB->cbAvailable байт данных.

    Так как размеры передаваемого файла могут легко превысить предел 48 Кбайт, необходимо организовать цикл чтения остальных данных с помощью функции ReadClient.

    Перед запуском такого цикла мы устанавливаем указатель текущей позиции в буфере lpTemp на конец блока данных, скопированных из буфера предварительного чтения:

    nBufferPos = lpECB->cbAvailable;

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

    nBytesToCopy = lpECB->cbTotalBytes - lpECB->cbAvailable;

    После проверки значения nBytesToCopy (оно должно быть больше нуля) мы запускаем цикл чтения дополнительных данных:

    while(1)

    {

      // Читаем очередную порцию данных в текущую

      // позицию буфера

      lpECB->ReadClient(lpECB->ConnID,

        (LPVOID)((LPSTR)lpTemp + nBufferPos), &cbReaded);

                 

      // Уменьшаем содержимое переменной nBytesToCopy,

      // в которой находится размер непрочитанных данных

      nBytesToCopy -= cbReaded;

      // Продвигаем указатель текущей позиции в буфере

      // на количество прочитанных байт данных

      nBufferPos   += cbReaded;

                 

      // Когда копировать больше нечего, прерываем цикл

      if(nBytesToCopy <= 0l)

        break;

    }

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

    Функция ReadClient не обязательно прочитает за один прием все входные данные. Размер прочитанного ей блока данных записывается в переменную cbReaded.


    Далее в цикле уменьшается содержимое переменной nBytesToCopy, хранящей количество еще не прочитанных данных. После этого указатель текущей позиции nBufferPos в буфере lpTemp продвигается вперед на количество прочитанных байт cbReaded.

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

    Теперь о функции GetMIMEBoundary, которая определена в файле исходных текстов, но нигде не используется. Эта функция предназначена для получения строки разделителя блоков принятого файла.

    Как мы уже говорили, принятый файл имеет формат MIME. Полное описание этого формата вы найдете в документах RFC2045, RFC2046, RFC2047, RFC2048 и RFC2049. Первый из этих документов называется “Multipurpose Internet Mail Extensions”. Однако для работы с принятым файлом вы можете обойтись без полной спецификации MIME.

    Ниже мы привели в сокращенном виде содержимое файла e:\\InetPub\\scripts\\uploaded.dat после приема файла известного драйвера 800.com, предназначенного для работы с нестандартными форматами дискет. Помимо этого файла в принятых данных есть содержимое всех полей формы:

    -----------------------------264872619131689

    Content-Disposition: form-data; name="text1"

    Sample of text1

    -----------------------------264872619131689

    Content-Disposition: form-data; name="pwd"

    Sample of password

    -----------------------------264872619131689

    Content-Disposition: form-data; name="text2"

    Sample of text

    -----------------------------264872619131689

    Content-Disposition: form-data; name="chk1"

    on

    -----------------------------264872619131689

    Content-Disposition: form-data; name="chk3"

    on

    -----------------------------264872619131689

    Content-Disposition: form-data; name="rad"

    on1

    -----------------------------264872619131689

    Content-Disposition: form-data; name="fupload"; filename="C:\UT\800.com"


    йtf

    *“*t*—*—_

    Ђьv_Ђъv_ъ.я.__V3цЋЮ‹тѓжЂь_sd_SRД_x

    ‹ыѕI№

    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

    Здесь располагаются двоичные данные принятого файла.

    Мы сократили листинг, выкинув из него часть двоичных данных

    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

    ђЗ___ffЗ___ !&Ђ>__Tt____ѕ5cї__№_

    юЛЃщ   't_юЛГґ_І

    . Vc&ўW°й‹

    _ЊИ-

    -----------------------------264872619131689

    Content-Disposition: form-data; name="sel"

    First Option

    -----------------------------264872619131689

    Content-Disposition: form-data; name="hid"

    Hidden

    -----------------------------264872619131689—

    Как видите, формат этого файла относительно несложен. Он состоит из отдельных секций, разделенных текстовой строкой. Строка состоит из символов “-“ и числа, которое каждый раз изменяется. При посылке файла навигатор сам выбирает разделитель. При помощи функции GetMIMEBoundary ваша программа может скопировать разделитель в отдельный буфер и использовать его при поиске нужной секции в принятых данных.

    Каков дальнейший алгоритм получения данных из полей формы, а также принятого файла?

    Он несложен и вы сможете реализовать его самостоятельно. Сканируя секции с использованием разделителя, вы можете в каждой секции искать строку “name=<ИмяПоля>”, где ИмяПоля - это имя поля, данные из которого вам нужно получить. Для сканирования лучше не пользоваться функцией strstr, так как она рассчитана только на символьные данные, а в секциях, содержащих файлы, присутствуют двоичные данные. Найдя нужное вам поле, вы можете извлечь его содержимое и записать его в память или файл на диске.

    Сделаем еще одно замечание, касающееся мультизадачного режима работы расширений ISAPI.

    Так как для повышения производительности расширение ISAPI загружается в адресное пространство сервера Microsoft Information Server в единственном экземпляре, оно работает в мультизадачном режиме. Это означает, что при обращении к критичным ресурсам вы должны использовать средства синхронизации задач, описанные нами в 26 томе “Библиотеки системного программиста”.


    В частности, расширение fileupl.dll выполняет запись в файл, а эта операция является критической, так как всем пользователям предлагается записывать свои данные только в один файл. Чтобы избежать взаимных коллизий, можно предложить простейшее средство синхронизации - критические секции.

    Перед началом записи в файл расширение ISAPI должно войти в критическую секцию, а после использования - выйти из нее. В этом случае пользователи будут работать с файлом по очереди. Для работы с критическими секциями предназначены функции InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection, DeleteCriticalSection.

    Критическая секция должна быть создана в момент инициализации библиотеки DLL расширения ISAPI, поэтому вызов функции InitializeCriticalSection должен быть размещен в функции DllMain. Удаление критической секции можно выполнить в обработчике функции TerminateExtension, которая вызывается перед удалением расширения из адресного пространства сервера WWW.

    Подробную информацию о работе с библиотеками DLL в среде операционной системы Microsoft Windows NT  вы найдете в 27 томе “Библиотеки системного программиста”.

    Приложение ISFORM

    Приложение ISFORM демонстрирует способ получения и обработки данных, полученных от формы, расширением ISAPI. Аналогичные действия выполняла программа CGI с именем CONTROLS, описанная нами в предыдущей главе.
    Исходный текст документа HTML, содержащий форму, представлен в листинге 8.4. Эта форма уже использовалась нами ранее в предыдущей главе (рис. 7.2), поэтому мы не будем показывать ее внешний вид снова для экономии места.
    Листинг 8.4. Файл chap8\isform\isform.htm



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



     
       
         
         
       
       
         
         
       
       
         
         
       
       
         
         
       
       
         
         

       

       

         

         

       

       

         

         

       

     
    Текстовое поле TEXT
    Текстовое поле PASSWORD
    Текстовое поле TEXTAREA
    Переключатели CHECKBOX
            Первый

            Второй

            Третий

         
    Переключатели RADIO
            Первый


            Второй

            Третий

         
    Список

           

         
    Скрытый орган управления



     











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

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

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

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

    Приложение ISFORM

    Рис. 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

    #include

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

    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"

        " Simple ISAPI Extension\n"

        "

    Information from ECB

    \n");

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

      wsprintf(szTempBuf, "Extension Version: %d.%d",

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

      strcat(szBuff, szTempBuf);


     

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

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

      strcat(szBuff, szTempBuf);

     

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

      wsprintf(szTempBuf, "
    QueryString: %s",

        lpECB->lpszQueryString);

      strcat(szBuff, szTempBuf);

     

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

      wsprintf(szTempBuf, "
    PathTranslated: %s",

        lpECB->lpszPathTranslated);

      strcat(szBuff, szTempBuf);

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

      wsprintf(szTempBuf, "
    TotalBytes: %d",

        lpECB->cbTotalBytes);

      strcat(szBuff, szTempBuf);

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

      wsprintf(szTempBuf, "
    Available: %d",

        lpECB->cbAvailable);

      strcat(szBuff, szTempBuf);

      // Тип данных

      wsprintf(szTempBuf, "
    ContentType: %s",

        lpECB->lpszContentType);

      strcat(szBuff, szTempBuf);

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

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

     

      strcat(szBuff, "

    Принятые данные

    ");

      strcat(szBuff, szTempBuf);

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

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

      DecodeStr(szTempBuf);

      strcat(szBuff, "

    Данные после перекодировки

    ");

      strcat(szBuff, szTempBuf);

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

      strcat(szBuff, "

    Список значений полей

    ");

      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, "
    ");

            szParam = szPtr + 1;


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

              break;

          }

          else

            break;

      }

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

      strcat(szBuff, ""); 

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

      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

    Приложение ISHELLO

    В качестве нашего первого расширения ISAPI мы предлагаем приложение ISHELLO, выполняющее простейшие функции.
    Вызов расширения ishello.dll выполняется из формы, исходный текст которой приведен в листинге 8.1.
    Листинг 8.1. Файл chap8\ishello\ishello.htm


     
        ISAPI Script Test
     
     
       

    Вызов расширения ISAPI


       
         
       
     

    Расширение вызывается в параметре ACTION оператора
    аналогично тому, как это делается для программ CGI.
    Расширение ishello.dll динамически создает документ HTML, представленный на рис. 8.1.
    Приложение ISHELLO
    Рис. 8.1. Документ HTML, созданный динамически расширением ishello.dll
    В верхней части этого документа отображается содержимое некоторых полей структуры EXTENSION_CONTROL_BLOCK, а в нижней в качетсве примера отображается содержимое переменной ALL_HTTP, полученное с помощью функции GetServerVariable.
    Исходный текст расширения ishello.dll представлен в листинге 8.2.
    Листинг 8.2. Файл chap8\ishello\ishello.c
    // ===============================================
    // Расширение ISAPI ishello.c
    // Пример простейшего расширения ISAPI
    //
    // (C) Фролов А.В., 1997
    // E-mail: frolov@glas.apc.org
    // WWW:    http://www.glasnet.ru/~frolov
    //         или
    //         http://www.dials.ccas.ru/frolov
    // ===============================================
    #include
    #include
    // =============================================================
    // Функция GetExtensionVersion
    // Запись версии интерфейса ISAPI и
    // строки описания расширения
    // =============================================================
    BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)

    {

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

      pVer->dwExtensionVersion =

        MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR );

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

      lstrcpyn(pVer->lpszExtensionDesc,

        "Simple 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"

        " Simple ISAPI Extension\n"

        "

    Hello from ISAPI Extension!

    \n");

      // Добавляем разделительную линию

      strcat(szBuff, "
    ");

     

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

      wsprintf(szTempBuf, "Extension Version: %d.%d",

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

      strcat(szBuff, szTempBuf);

     

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

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

      strcat(szBuff, szTempBuf);

     

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

      wsprintf(szTempBuf, "
    QueryString: %s",

        lpECB->lpszQueryString);

      strcat(szBuff, szTempBuf);

     

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

      wsprintf(szTempBuf, "
    PathTranslated: %s",

        lpECB->lpszPathTranslated);

      strcat(szBuff, szTempBuf);

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

      wsprintf(szTempBuf, "
    TotalBytes: %d",


        lpECB->cbTotalBytes);

      strcat(szBuff, szTempBuf);

      // Тип данных

      wsprintf(szTempBuf, "
    ContentType: %s",

        lpECB->lpszContentType);

      strcat(szBuff, szTempBuf);

      // Отображаем содержимое переменных сервера

      strcat(szBuff, "
    Server Variables:
    ");

      dwSize = 4096;

      lpECB->GetServerVariable(lpECB->ConnID,

        (LPSTR)"ALL_HTTP", (LPVOID)szTempBuf, &dwSize);

      strcat(szBuff, szTempBuf);

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

      strcat(szBuff, ""); 

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

      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;

    }

    Обратите внимание, что наряду с обычным для приложений Windows файлом windows.h мы включили в наш исходный текст файл httpext.h, в котором определены все необходимые константы, структуры данных и прототипы функций. Этот файл поставляется в составе Microsoft Visual C++ версии 4.2, а также в составе Internet SDK, который можно получить на сервере www.microsoft.com.

    В приложении определена функция GetExtensionVersion, которая уже была рассмотрена нами ранее. Эта функция с небольшими изменениями будет встречаться во всех наших примерах расширений ISAPI. Она записывает версию интерфейса ISAPI и текстовую строку описания расширения в поля структуры типа HSE_VERSION_INFO с именами dwExtensionVersion и lpszExtensionDesc, сответственно. Адрес структуры HSE_VERSION_INFO передается функции GetExtensionVersion через единственный параметр.

    Функция HttpExtensionProc использует буфер szBuff для подготовки динамически создаваемого документа HTML, который будет послан удаленному пользователю в результате работы нашего расширения. В качестве вспомогательного буфера применяется буфер szTempBuf.


    Прежде всего в буфер szBuff записывается заголовок HTTP и начальный фрагмент документа HTML, для чего используется функция wsprintf. Далее к буферу szBuff с помощью функции strcat будут добавляться другие строки документа. Например, разделительная линия добавляется так:

    strcat(szBuff, "
    ");

    После первой разделительной линии в документ добавляется несколько строк со значениями некоторых полей структуры типа EXTENSION_CONTROL_BLOCK. В следующем фрагменте кода добавляется строка версии интерфейса ISAPI:

    wsprintf(szTempBuf, "Extension Version: %d.%d",

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

    strcat(szBuff, szTempBuf);

    Далее в документ выводятся строка с названием метода передачи данных (поле lpszMethod), строка параметров запуска расширения ISAPI (поле lpszQueryString), физический путь к программному файлу библиотеки DLL расширения (поле lpszPathTranslated), полный размер данных, которые нужно прочитать (поле cbTotalBytes), а также тип данных (поле lpszContentType).

    После этого в документ снова выводится разделительная линия и отображается содержимое переменных сервера с префиксом имени HTTP, для чего используется рассмотренная ранее функция GetServerVariable.

    В завершении в документ записывается финальная строка:

    strcat(szBuff, "");

    Документ посылается удаленному пользователю функцией ServerSupportFunction, как это показано ниже:

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

        HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff))

    {

      return HSE_STATUS_ERROR;

    }

    Если при посылке данных произошла ошибка, расширение завершает свою работу с кодом HSE_STATUS_ERROR.

    В случае успеха в поле состояния dwHttpStatusCode записывается код 200, вслед за чем расширение завершает свою работу с кодом HSE_STATUS_SUCCESS.

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

    Листинг 8.3. Файл chap8\ishello\ishello.def

    LIBRARY            ishello

    DESCRIPTION  'Simple ISAPI DLL'

    EXPORTS

        GetExtensionVersion

        HttpExtensionProc

    Приложение Map This!

    Описанная выше методика подготовки сегментированных графических изображений не слишком удобна, так как вам необходимо вручную создавать карту чувствительных областей. Между тем вы можете бесплатно загрузить из Internet приложение Map This!, созданное специально для работы с сегментированной графикой. Ниже мы привели адрес сервера WWW, где вы можете получить самую свежую версию приложения Map This!:
    http://www.ecaetc.ohio-state.edu/tc/mt
    Главное окно этого приложения показано на рис. 5.2.
    Приложение Map This!
    Рис. 5.2. Главное окно приложения Map This!
    В приложение Map This! встроен редактор областей, позволяющий задавать чувствительные области визуально при помощи мыши. При этом вы просто рисуете границы областей поверх изображения.
    Органы управления, расположенные на инструментальной линейке, позволяют вам выбрать нужную форму области - прямоугольную, круглую или многоугольную.
    Помимо редактирования областей, приложение Map This! может автоматически создавать карту областей в одном из трех форматов, в частности, в формате, необходимом для включения карты в документ HTML.
    Работая со списком областей, вы можете указать для каждой области адрес URL объекта, на который при помощи этой области создается ссылка. Список областей можно редактировать, изменяя адрес ссылки, добавляя в список новые области или удаляя существующие.
    Ниже мы привели сокращенный образец документа HTML, который был создан приложением Map This! для изображения, входящего в комплект этого приложения:





















    Table



    . . .

     The lady in the pink shirt









    the left mountain

    . . .





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

    Пример документа HTML с формой

    Формы лучше изучать на конкретном примере. В этой главе мы создадим форму, содержащую почти все органы управления (рис. 7.2), и рассмотрим исходный текст соответствующего документа HTML.
    Пример документа HTML с формой
    Рис. 7.2. Форма в документе HTML с различными органами управления
    Этот исходный текст приведен в листинге 7.1.
    Листинг 7.1. Файл chap7\controls\controls.htm



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



     
       
         
         
       
       
         
         
       
       
         
         
       
       
         
         
       
       
         
         
       
       
         
         
       
       
         
         
       
     
    Текстовое поле TEXT
    Текстовое поле PASSWORD
    Текстовое поле TEXTAREA
    Переключатели CHECKBOX
            Первый

            Второй

            Третий

         
    Переключатели RADIO
            Первый

            Второй


            Третий

         
    Список
           
         
    Скрытый орган управления


     





    Оператор
    здесь имеет два параметра - METHOD и ACTION:

    Параметр METHOD имеет значение POST и задает способ передачи данных программе CGI через стандартный поток ввода.
    В параметре ACTION указан путь к загрузочному файлу программы CGI, которая находится в каталоге frolov-cgi сервера WWW с адресом http://www.someserver.ru.
    Заметим, что программы CGI (а также расширения сервера WWW в виде библиотек динамической компоновки DLL с интерфейсом ISAPI, которые мы рассмотрим в следующей главе) могут находиться не в любом каталоге сервера WWW, а только в таком, для которого разрешено выполнение программ. Если вы создаете виртуальный сервер WWW, который физически располагается у поставщика услуг Internet, возможно, вам придется получить разрешение на создание или использование такого каталога.
    Теперь займемся органами управления.
    Мы разместили все органы управления в таблице. В первой строке этой таблицы находится однострочное поле для ввода текста, которое вставлено в форму оператором и имеет тип TEXT:



    Имя поля указано в параметре NAME как “text1”. В качестве начального значения для поля параметром VALUE задана строка "Sample of text1". Эту строку можно будет редактировать после отображения формы. Мы также указали размер поля, равный 30 символам, для чего воспользовались параметром SIZE.
    Во второй строке таблицы также при помощи оператора определено поле для ввода пароля:

    При вводе символов в этом поле они не отображаются. Аналогично, не отображается и начальная строка, использованная для инициализации поля.
    Для ввода многострочного текста в третьей строке таблицы при помощи оператора
    Имя этого поля задано как “text2”. Поле имеет высоту, равную четырем строкам (параметр ROWS равен 4), и ширину, равную 30 символам (параметр COLS равен 30).
    В четвертой строке таблицы мы расположили группу из трех независимых переключателей типа CHECKBOX:

      Первый

      Второй

      Третий


    Каждый из этих переключателей имеет собственное имя, заданное параметром NAME. Оператор VALUE задает значение, которое будет послано в сервер WWW при включении переключателя. Кстати, если переключатель выключен, он не посылает в сервер WWW никаких данных.
    Ниже, в пятой строке, находится группа из трех переключателей типа RADIO с зависимой фиксацией:

      Первый


      Второй

      Третий


    Все переключатели, относящиеся к одной группе, должны называться одинаково. В нашем случае при помощи параметра NAME мы задали для всех трех переключателей имя “rad”.
    Первый переключатель включен по умолчанию, так как для него задан параметр CHECKED.
    Для того чтобы при анализе данных, полученных от формы, программа CGI могла определить, какой из переключателей, входящих в группу, был включен, мы задали для каждого переключателя свое значение параметра VALUE.
    Шестая строка таблицы содержит список, состоящий из трех строк. Этот список определен при помощи операторов
        
       
       
     

    Имя списка задано как “sel”, а высота его равна одной строке. Содержимое строк списка задается при помощи параметра VALUE соответствующих операторов