ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
19.5. Повышение эффективности сценариев CGIПроблемаОт частых вызовов вашего сценария CGI снижается производительность сервера. Вы хотите уменьшить нагрузку, связанную с работой сценария.РешениеИспользуйте модуль mod_perl Web-сервера Apache и включите в файл httpd.conf следующую секцию:Alias /perl/ /real/path/to/perl/scripts/ PerlModule Apache::Registry PerlModule CGI PerlSendHeader On КомментарийМодуль mod_perl Web-сервера Apache позволяет писать код Perl, который может выполняться па любой стадии обработки запроса. Вы можете написать своп собственные процедуры регистрации и аутентификации, определить виртуальные хосты и их конфигурацию и написать собственные обработчики для некоторых типов запросов.Приведенный выше фрагмент сообщает, что URL, начинающиеся с /perl/, в действительности находятся в /real/path/to/perl/scripts и обрабатываются Apache:: Registry. В результате они будут выполняться в среде CGI. Строка PerlModule CGI выполняет предварительную загрузку модуля CGI, a PerlSendHandler On позволяет большинству сценариев CGI работать с mod_perl. /perl/ работает аналогично /cgi-bin/. Чтобы суффикс .perl являлся признаком сценариев CGI mod_perl, подобно тому, как суффикс .cgi является признаком обычных сценариев CGI, включите в конфигурационный файл Apache следующий фрагмент: PerlHandler Apache::Registry Options ExecCGI Поскольку интерпретатор Perl, выполняющий сценарий CGI, не выгружается из памяти при завершении сценария (что обычно происходит, когда Web-сервер выполняет сценарий как отдельную программу), не следует полагаться на то, что при запуске программы глобальные переменные имеют неопределенные значения. Флаг -w и use strict проверяют многие недостатки в сценариях такого рода. Существуют и другие потенциальные ловушки - обращайтесь к странице руководства mod__perl_traps. Не беспокойтесь о том, насколько снизится быстродействие Web-сервера от предварительной загрузки всех сценариев. Все равно когда-нибудь придется загружать их в память; желательно, чтобы это произошло до того, как Apache начнет плодить потомков. В этом случае каждый сценарий будет находиться в памяти в единственном экземпляре, поскольку в любой современной операционной системе потомки используют общие страницы памяти. Иначе говоря, предварительная загрузка только на первый взгляд увеличивает расходы памяти - на самом деле она их уменьшает! По адресу http://www.perl.com/CPAN-local/modules/by-modules/Netscape/nsapi_ perl-0.24.tar.gz имеется интерфейс к серверу Netscape, который также повышает производительность за счет отказа от порождения новых процессов. > Смотри также -------------------------------
19.6. Выполнение команд без обращений к командному интерпретаторуПроблемаПользовательский ввод должен использоваться как часть команды, но вы не хотите, чтобы пользователь заставлял командный интерпретатор выполнять другие команды или обращаться к другим файлам. Если просто вызвать функцию system или '. . . ' с одним аргументом (командной строкой), то для выполнения может быть использован командный интерпретатор, а это небезопасно.РешениеВ отличие от одноаргументной версии, списковый вариант функции system надежно защищен от обращений к командному интерпретатору. Если аргументы команды содержат пользовательский ввод от формы, никогда не используйте вызовы вида:system("command $input @files"); # НЕНАДЕЖНО Воспользуйтесь следующей записью: system("command", $input, (Sfiles); # НАДЕЖНЕЕ КомментарийПоскольку Perl разрабатывался как "язык-клей", в нем легко запустить другую программу - в некоторых ситуациях даже слишком легко. Если вы просто пытаетесь выполнить команду оболочки без сохранения ее вывода, вызвать system в многоаргументной версии достаточно просто. Но что делать, если вы используете команду в '. . . ' или она является аргументом функции open? Возникают серьезные трудности, поскольку эти конструкции в отличие. от system не позволяют передавать несколько аргументов. Возможное решение - вручную создавать процессы с помощью fork и ехес. Работы прибавится, но, по крайней мере, непредвиденные обращения к командному интерпретатору не будут портить вам настроение.Обратные апострофы используются в сценариях CGI лишь в том случае, если передаваемые аргументы генерируются внутри самой программы: chomp($now = 'date'); Но если команда в обратных апострофах содержит пользовательский ввод - например: @output = 'grep $input (nifties)'; приходится действовать намного осторожнее. die "cannot fork: $!" unless defined ($pid = open(SAFE_KID, "|-")); if ($pid == 0) { exec('grep', $input, Ofiles) or die "can't exec grep: $!"; } else { @output = close SAFE_KID; # $? содержит информацию состояния } Такое решение работает, поскольку ехес, как и system, допускает форму вызова, свободную от обращений к командному интерпретатору. При передаче списка интерпретатор не используется, что исключает возможные побочные эффекты. При выполнении команды функцией open также потребуется немного потрудиться. Начнем с открытия функцией open конвейера для чтения. Вместо ненадежного кода: open(KID_TO"READ, "$program $options @args |"); # НЕНАДЕЖНО используется более сложный, но безопасный код: # Добавить обработку ошибок die "cannot fork: $!" unless defined($pid = open(KID_TO_READ, "-!")); if ($pid) { # Родитель while ( # Сделать что-то интересное } close(KID_TO_READ) or warn "kid exited $?"; } else { # Потомок # Переконфигурировать, затем exec($prograni, @options, @iargs) or die "can't exec program: $!"; } Безопасный конвейерный вызов open существует и для записи. Непадежный вызов: open(KID_TO_WRITE, "|$program $options @args"); # НЕНАДЕЖНО заменяется более сложным, но безопасным кодом: $pid = open(KID_TO_WRITE, "|-"); die "cannot fork: $!" unless defined($pid = open(KID_TO_WRITE, "|-")); $SIG{ALRM} = sub { die "whoops, $program pipe broke" }; if ($pid) { # Родитель for (@data) { print KID_TO_WRITE $_ } close(KID_TO_WRITE) or warn "kid exited $?"; } else { # Потомок # Переконфигурировать, затем exec($program, @options, @args) or die "can't exec program: $!": } Там, где комментарий гласит "Переконфигурировать", предпринимаются дополнительные меры безопасности. Вы находитесь в порожденном процессе, и вносимые изменения не распространяются на родителя. Можно изменить переменные окружения, сбросить временный идентификатор пользователя или группы, сменить каталог или маску umask и т. д. Разумеется, все это не поможет в ситуации, когда вызов system запускает программу с другим идентификатором пользователя. Например, почтовая программа sendmail является setuid-программой, часто запускаемой из сценариев CGI. Вы должны хорошо понимать риск, связанный с запуском sendmail или любой другой setuid-программы. > Смотри также --------------------------------
19.7. Форматирование списков и таблиц средствами HTMLПроблемаТребуется сгенерировать несколько списков и таблиц. Нужны вспомогательные функции, которые бы упростили вашу работу.РешениеМодуль CGI содержит вспомогательные функции HTML, которые получают ссылку на массив и автоматически применяются к каждому элементу массива:print ol( li([ qw(red blue green)]) );
print ul( li({ -TYPE => "disc" }, \@names) ); КомментарийСвойство дистрибутивности функций CGI.pm, генерирующих HTML-код, заметно упрощает процесс генерации списков и таблиц. При передаче простой строки эти функции просто выдают HTML-код для данной строки. Но при передаче ссылки на массив они применяются ко всем строкам. print li("alpha");print И( [ "alpha", "omega"] ); Вспомогательные функции для списков загружаются при использовании тега : standard, но для получения вспомогательных функций для работы с таблицами придется явно запросить : html3. Кроме того, возникает конфликт между тегом , которому должна соответствовать (функция tr(), и встроенным оператором Perl tr///. Следовательно, для построения строк таблицы следует использовать функцию Тr(). Следующий пример генерирует таблицу HTML по хэшу массивов. Ключи хэша содержат заголовки строк, а массивы значений - столбцы. use CGI qw(:standard :html3); %hash = ( "Wisconsin" => [ "Superlor", "Lake Geneva", "Madison" ], "Colorado" => [ "Denver", "Fort Collins", "Boulder" ], "Texas" => [ "Piano", "Austin", "Fort Stockton" ], "California" => [ "Sebastopol", "Santa Rosa", "Berkeley" ], ); $\ = "\n": print " TABLE> CAPTION>Cities I Have Known"; print Tr(th [qw(State Cities)]); for $k (sort keys %hash) { print Tr(th($k), td( [ sort @{$hash{$k}} ] )); } print " /TABLE>"; Генерируется следующий текст: TABLE> Cities I Have Known State Cities California Berkeley Santa Rosa Sebastopol Colorado Boulder Denver Fort Collins Texas Austin Fort Stockton PIano Wisconsin Lake Geneva Madison Superlor /TABLE> Те же результаты можно получить всего одной командой print, хотя это несколько сложнее, поскольку вам придется создавать неявный цикл с помощью тар. Следующая команда print выдает результат, идентичный приведенному выше: print table caption('Cities I have Known'), Tr(th [qw(State Cities)]), map { Tr(th($_), td( [ sort @{$hash{$_}} ] )) } sort keys %hash; Эти функции особенно удобны при форматировании результатов запроса к базе данных, как показано в примере 19.3 (см. главу 14 "Базы данных"). Пример 19.3. salcheck #!/usr/bin/perl # salcheck - проверка жалованья use DBI; use CGI qw(:standard :html3); $limit = param("LIMIT"); print header(), start_html("Salary Query"), h1("Search"), start_form(), p(Enter minimum salary", textfield("LIMIT")), submitO, end_form(); if (defined $limit) { $dbh = DBI->connect("dbi:mysql:somedb:server.host.dom:3306", "username", "password") or die "Connecting: $DBI::errstr"; $sth = $dbh->prepare("SELECT name,salary FROM employees WHERE salary > $limit") or die "Preparing: ", $dbh->errstr; $sth->execute or die "Executing: ", $sth->errstr; print h1("Results"), " |