Оптимизация взаимодействия веб-клиента и сервера - Динамический контент
Что касается раздачи динамического контента ("динамики"), хорошо известным методом является использование 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 {& & < < > > \" "} $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, что очень облегчает дальнейшие поддержку и модификацию приложения, притом код серверной части заметно упрощается.
Comments