суббота, 11 июня 2011 г.

Оптимизация взаимодействия веб-клиента и сервера - Динамический контент

Что касается раздачи динамического контента ("динамики"), хорошо известным методом является использование CGI-технологии:


CGI (от англ. Common Gateway Interface — «общий интерфейс шлюза») — стандарт интерфейса, используемого для связи внешней программы с веб-сервером. Программу, которая работает по такому интерфейсу совместно с веб-сервером, принято называть шлюзом, хотя многие предпочитают названия «скрипт» (сценарий) или «CGI-программа».

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


Промежуточным решением между CGI и серверами приложений можно назвать технологию FastCGI, позволяющую убрать часть ограничений CGI, но имеющую и свои серьезные проблемы. По моему мнению, стоит или ограничиться CGI, если хватает производительности, или работать с серверами приложений.


Часто CGI-приложения запускают с помощью веб-сервера, но на самом деле, это совсем не обязательно. Веб-сервер для CGI-программ всего лишь определяет значения переменных окружения согласно полученным заголовкам HTTP, хотя и само приложение может прочитать эти заголовки и обойтись без помощи веб-сервера. Далее я приведу примеры CGI-приложений как запускаемых напрямую, так и с помощью веб-сервера fnord-cgi (это уже знакомый нам fnord, скомпилированный с опцией поддержки CGI).


Разрабатывать веб-приложения можно на самых разных скриптовых языках, но я предпочитаю пользоваться языком Tcl, который с успехом применяется как для больших проектов (в том числе веб-проектов), так и для несложных утилит. Кроме прочих соображений, Tcl надежен и предложенные мною примеры можно запускать на общедоступном сервере. Если вы захотите переписать примеры на любом другом языке, это сделать несложно, но берегитесь уязвимостей, особенно при вызове внешних программ. В дополнение к примерам на Tcl я продемонстрирую написание CGI-программы и на unix shell.


Пример. Листинг директории (unix shell)


Для получения листинга директории можно поступить следующим образом: вывод утилиты ls с помощью unix shell отформатировать в виде, понятном для веб-клиентов. Главное достоинство этого метода в том, что он очень быстр - настолько, что зачастую не уступает встроенному в веб-сервер генератору индекса директории. Шелл-скрипт может выглядеть следующим образом:


$ tcpserver localhost 8888 echo "HTTP/1.0 200 OK
Content-type: text/plain

`ls`"

$ openload -t localhost:8888
URL: http://localhost:8888/
Clients: 1
------------------------
Status: 200
---- Headers -----------
Content-type: text/plain
---- Body    10 bytes --
hello.cgi
---- End ---------------

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


Пример. Вывод текущего времени (Tcl)


hello.cgi

#!/usr/bin/tclsh

 
puts "HTTP/1.0 200 OK"
puts "Content-type: text/html; charset=utf-8\n"
puts "<HTML><BODY>"
puts "<h1>Привет, мир! Сейчас [clock format [clock seconds]]</h1>"
puts "</BODY></HTML>"

Запустим почти так же, как и ранее. Утилита env здесь "прячет" от скрипта все переменные окружения - я это делаю из соображений безопасности, но это не является общепринятой практикой.


$ tcpserver localhost 8888 env -i ./hello.cgi

$ openload -t localhost:8888
URL: http://localhost:8888/
Clients: 1
------------------------
Status: 200
---- Headers -----------
Content-type: text/html; charset=utf-8
---- Body   104 bytes --
<HTML><BODY>
<h1>Привет, мир! Сейчас Tue Jul 13 23:29:13 MSD 2010</h1>
</BODY></HTML>
---- End ---------------

Откройте адрес http://localhost:8888 в своем браузере и вы увидите страничку текстом "Привет, мир!" и текущей датой.


Пример. AJAX и СУБД SQLite3 (Tcl)


Проиллюстрируем создание AJAX-сервиса на примере небольшого приложения, отображающего записи из базы данных: http://mobigroup.ru/service/jsontest/. Код доступен по ссылке jsontest. Для отображения результатов используется компактный и быстрый кроссбраузерный шаблонизатор на javascript (см. js-функцию supplant).


Пример. Конвертор трэков GPS (Tcl)


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


Для интерактивного приложения, взаимодействующего с пользователем, удобно использовать модуль ncgi из библиотеки tcllib, умеющий работать с HTTP-формами, cookie и загрузкой файлов. Образцы кода для работы можно найти по адресу ncgi.test. Модуль предполагает существование переменных окружения, список которых определяется стандартом для протокола CGI. Опять же, обратимся к минималистичному веб-серверу fnord-cgi для обработки CGI-скриптов.


HTML-страница для выбора файла исходного трэка:


index.html

<html>
<head> 
<meta content='text/html; charset=utf-8' http-equiv='content-type'></meta> 
</head> 
<body>
<h3>Конвертор трэков в формат KML</h3> 
<form enctype='multipart/form-data' method='POST' action='/track2kml.cgi'>
<input name='file' id='file' type='file' size='25''/>
<input name='upload' value='Преобразовать' id='upload' type='submit'/>
</form> 
</body> 
</html>

И CGI-скрипт пробразования трэка:


track2kml.cgi

package require ncgi
::ncgi::parse

puts "Content-type: text/html; charset=utf-8\n"

array set extensions {.gdb gdb .mps mapsource .plt ozi .gpx gpx .nmea nmea .txt nmea}
set filename [clock seconds].kml
set clientname [ncgi::importFile -client file]
# anti-XSS                                                                                                                   
set clientname [string map {& &amp; < &lt; > &gt; \" &quot;} $clientname] 
puts "
<html><body>"
if {[::ncgi::exists file]==1 && $clientname ne {} && 
  [info exists extensions([file ext $clientname])]==1} {
    set filetype $extensions([file ext $clientname])
} else {
    puts "<b>Укажите файл для конвертации в одном из форматов
[array names extensions]</b>"
    puts "<br><br><a href='javascript:history.go(-1)'>Назад</a>"
    exit
}
puts "<b>Загружен файл: $clientname</b><br>"
set inname [ncgi::importFile -server file]
set tmpname ${inname}.kml
catch {exec /usr/bin/gpsbabel -t -x simplify,error=0.002k -i $filetype \
  -o kml,points=0,trackdata=0,line_width=3,line_color=990000FF $inname $tmpname}
if {[file exists $tmpname]==1} {
  catch {file rename $tmpname $filename}
} else {
  puts "<b>Ошибка: не удалось выполнить конвертацию</b>"
}

if {[file exists $filename]==1} {
    puts "<a href='$filename'>Скачать файл KML</a><br>"
}
puts "<br><br><a href='javascript:history.go(-1)'>Назад</a>"
puts "</body></html>"

Запускаем сервис так:

tcpserver localhost 8888 env -i /usr/sbin/fnord-cgi .

Загружаем трэк, к примеру, с Garmin Oregon в формате gpx, а полученный трэк в формате kml можно открыть в Google Earth. Онлайн-версия этого сервиса также предоставляет ссылку для просмотра трэка на Google Maps и доступна по адресу http://mobigroup.ru/service/track2kml/, а ее код можно увидеть здесь track2kml. Более современным решением является создание AJAX-приложения, смотрите http://mobigroup.ru/service/track2kml-ajax/, и его код track2kml-ajax. В последнем случае мы полностью разделяем серверный код на Tcl и клиентский интерфейс на html/javascript, что очень облегчает дальнейшие поддержку и модификацию приложения, притом код серверной части заметно упрощается.

Комментариев нет:


(C) Alexey Pechnikov aka MBG, mobigroup.ru