Revision 39 (by ahitrov@rambler.ru, 2010/07/27 17:47:40) |
Добавлен content-type =~ rss для обработки масоном
|
package Contenido::Apache;
# ----------------------------------------------------------------------------
# Здесь будут храниться все хэндлеры Apache...
# ----------------------------------------------------------------------------
use strict;
use warnings;
use locale;
use vars qw($VERSION);
$VERSION = '7.0';
use Apache::Constants;
use Apache::Request;
use Contenido::Globals;
use Contenido::Project;
use Contenido::Request;
use Contenido::State;
use Contenido::Keeper;
use Contenido::Object;
use Contenido::User;
use Contenido::Document;
use Contenido::Section;
use Contenido::Link;
use Contenido::Init;
use Contenido::Logger;
# ----------------------------------------------------------------------------
# Хэндлер, который запускается при старте каждого нового дочернего процесса
# web-сервера.
# ----------------------------------------------------------------------------
sub child_init {
my $r = shift;
$log->info("Обрабатываем рождение дочернего процесса Apache") if $DEBUG;
my $project_keeper_module;
eval {
$project_keeper_module=$state->project().'::Keeper';
$keeper = $project_keeper_module->new($state);
};
if ($@) {
$log->error("Не могу инициализировать $project_keeper_module из-за: $@");
die;
}
for my $plugin ($state->project, split(/\s+/, $state->plugins)) {
my $class = $plugin.'::Apache';
eval { $class->child_init($r); };
if ( $@ ) {
$log->error("Не могу выполнить метод child_init плагина $plugin ($class) по причине '$@'");
}
}
return OK;
}
# ----------------------------------------------------------------------------
# Хэндлер, который запускается в самом начале обработки запроса пользователя.
# ----------------------------------------------------------------------------
sub request_init {
my $r = shift;
$r = ref($r) eq 'Apache::Request' ? $r : Apache::Request->instance($r);
my $URI = $r->uri;
my $ARGS = $r->args;
$log->info("Начало обработки запроса ".$URI.($ARGS ? "?$ARGS":'')) if $DEBUG;
$state->_refresh_();
$request = Contenido::Request->new($state);
if ($DEBUG) {
$request->{_start} = Time::HiRes::time();
$Contenido::Globals::DB_TIME = 0;
$Contenido::Globals::CORE_TIME = 0;
$Contenido::Globals::DB_COUNT = 0;
$Contenido::Globals::RPC_TIME = 0;
}
if (ref($r)) {
my %headers = $r->headers_in();
my $ip = $headers{'X-Real-IP'} ? $headers{'X-Real-IP'} : $r->connection->remote_ip();
my @ips = split(/\s*,\s*/, $ip);
$ip = $ips[ $#ips ];
$request->set_properties (
'uri' => $URI||'',
'query' => $r->args()||'',
'ip' => $ip||'',
'user' => $r->connection->user()||'',
'http_host' => $ENV{HTTP_HOST},
'r' => $r,
)
}
$project->restore($keeper);
for my $plugin ($state->project, split(/\s+/, $state->plugins)) {
my $class = $plugin.'::Apache';
eval { $class->request_init($r); };
if ( $@ ) {
$log->error("Не могу выполнить метод request_init плагина $plugin ($class) по причине '$@'");
}
}
return OK;
}
sub is_valid_request {
my $r = shift;
if ($r->uri =~ /^(?:\/i\/|\/images\/|\/binary\/)/ or ($r->content_type && !($r->content_type =~ m#(?:^text/|javascript|json|^httpd/unix-directory)#i || $r->content_type =~ /rss/i)) ) {
return 0;
} else {
return 1;
}
}
#завершение запроса (отрабатывает ПОСЛЕ отдачи ответа... так что можно буз ущерба интерактивности тут всяие полезности повызывать
sub cleanup {
my $r = shift;
#сброс глобальных переменных запроса
$user = undef;
$session = undef;
return Apache::Constants::DECLINED unless Contenido::Apache::is_valid_request($r);
#установка отложенных данных в memcached
if ($state->{memcached_enable} and $request->{_to_memcache}) {
while ( my ($key, $values) = each(%{$request->{_to_memcache}}) ) {
my ($value, $expire, $mode) = @$values;
if (ref($key) or !$key) {
$log->warning("bad key value in set ($key)");
next;
}
$mode ||= 'set';
if ($mode eq 'add') {
$keeper->{MEMD}->add($key, $value, $expire);
} elsif ($mode eq 'delete') {
$keeper->{MEMD}->delete($key);
#по умолчанию set
} else {
$keeper->{MEMD}->set($key, $value, $expire);
}
}
}
for my $plugin ($state->project, split(/\s+/, $state->plugins)) {
my $class = $plugin.'::Apache';
next unless ($class->can('cleanup'));
eval { $class->cleanup($r); };
if ( $@ ) {
$log->error("Не могу выполнить метод request_init плагина $plugin ($class) по причине '$@'");
}
}
# отсоединяемся от базы проекта и плагинов
# (если не используются постоянные соденинения)
# также сбрасываем потенциально зависшие транзакционные соединения
unless ($state->db_type eq 'none') {
$keeper->shutdown unless $state->db_keepalive;
$keeper->{_connect_ok} = 0;
$keeper->t_shutdown(1);
}
for (split /\s+/, $state->plugins) {
next unless $keeper->{$_};
next if $state->{$_}->db_type eq 'none';
$keeper->{$_}->shutdown unless $state->{$_}->db_keepalive;
$keeper->{$_}->{_connect_ok} = 0;
$keeper->{$_}->t_shutdown(1);
}
if ($DEBUG) {
my $finish = Time::HiRes::time();
my $time = int(10000*($finish-$request->{_start}))/10;
my $db_time = int(10000*($Contenido::Globals::DB_TIME)/10);
my $core_time = int(10000*($Contenido::Globals::CORE_TIME)/10);
my $rpc_time = int(10000*($Contenido::Globals::RPC_TIME)/10);
$log->info("DEBUG: $$ ".__PACKAGE__." ".scalar(localtime())." ".($r->uri || '').($r->args ? '?'.$r->args : '')." worked $time ms, database time $db_time ms, rpc time $rpc_time ms, core time: $core_time ms, db requests count: $Contenido::Globals::DB_COUNT");
}
$request = undef;
return OK;
}
# ----------------------------------------------------------------------------
# Хэндлер, который запускается при окончании работы дочернего процесса
# web-сервера...
# ----------------------------------------------------------------------------
sub child_exit {
my $r = shift;
for my $plugin ($state->project, split(/\s+/, $state->plugins)) {
my $class = $plugin.'::Apache';
eval { $class->child_exit($r); };
if ( $@ ) {
$log->error("Не могу выполнить метод child_exit плагина $plugin ($class) по причине '$@'");
}
}
unless ($state->db_type eq 'none') {
$keeper->shutdown;
}
$log->info("Обрабатываем смерть дочернего процесса Apache.") if ($DEBUG);
return OK;
}
# ----------------------------------------------------------------------------
# Хэндлер для аутентификации.
# - Если это делается для редакторского интерфейса, то группа
# пользователей должна быть группой 1 (редакторы)
# ----------------------------------------------------------------------------
sub authentication {
my $r = shift;
return Apache::Constants::DECLINED unless Contenido::Apache::is_valid_request($r);
return FORBIDDEN if $state->db_type eq 'none';
my ($res, $sent_pw) = $r->get_basic_auth_pw();
return $res if $res != OK;
my $username = $r->connection->user();
$keeper->db_connect unless $keeper->is_connected;
$user = $keeper->get_user_by_login($username);
$keeper->shutdown unless $state->db_keepalive;
if (ref($user) && ($user->login() eq $username) && ($user->passwd() eq $sent_pw) && $user->passwd() && $user->status == 1) {
return OK;
} else {
$log->warning("Попытка авторизации с неверной парой логин/пароль ($username, $sent_pw)");
$r->note_basic_auth_failure();
return AUTH_REQUIRED;
}
}
1;