5
0
mirror of git://git.proxmox.com/git/pve-common.git synced 2024-12-25 05:33:49 +03:00

speedup find_handler

Avoid to scan the same path multiple times. That way we can speedup things
by factor 5 (depends on patch length and method count).
This commit is contained in:
Dietmar Maurer 2014-04-24 13:49:43 +02:00
parent 88b030e64c
commit c108979483

View File

@ -13,6 +13,7 @@ use Storable qw(dclone);
my $method_registry = {};
my $method_by_name = {};
my $method_path_lookup = {};
our $AUTOLOAD; # it's a package global
@ -156,17 +157,68 @@ sub register_method {
my $match_re = [];
my $match_name = [];
foreach my $comp (split(/\/+/, $info->{path})) {
die "path compoment has zero length" if $comp eq '';
if ($comp =~ m/^\{(\w+)(:(.*))?\}$/) {
my $name = $1;
push @$match_re, $3 ? $3 : '\S+';
push @$match_name, $1;
my $errprefix;
my $method;
if ($info->{subclass}) {
$errprefix = "register subclass $info->{subclass} at ${self}/$info->{path} -";
$method = 'SUBCLASS';
} else {
push @$match_re, $comp;
$errprefix = "register method ${self}/$info->{path} -";
$info->{method} = 'GET' if !$info->{method};
$method = $info->{method};
}
$method_path_lookup->{$self} = {} if !defined($method_path_lookup->{$self});
my $path_lookup = $method_path_lookup->{$self};
die "$errprefix no path" if !defined($info->{path});
foreach my $comp (split(/\/+/, $info->{path})) {
die "$errprefix path compoment has zero length\n" if $comp eq '';
my ($name, $regex);
if ($comp =~ m/^\{(\w+)(:(.*))?\}$/) {
$name = $1;
$regex = $3 ? $3 : '\S+';
push @$match_re, $regex;
push @$match_name, $name;
} else {
$name = $comp;
push @$match_re, $name;
push @$match_name, undef;
}
if ($regex) {
$path_lookup->{regex} = {} if !defined($path_lookup->{regex});
my $old_name = $path_lookup->{regex}->{match_name};
die "$errprefix found changed regex match name\n"
if defined($old_name) && ($old_name ne $name);
my $old_re = $path_lookup->{regex}->{match_re};
die "$errprefix found changed regex\n"
if defined($old_re) && ($old_re ne $regex);
$path_lookup->{regex}->{match_name} = $name;
$path_lookup->{regex}->{match_re} = $regex;
die "$errprefix path match error - regex and fixed items\n"
if defined($path_lookup->{folders});
$path_lookup = $path_lookup->{regex};
} else {
$path_lookup->{folders}->{$name} = {} if !defined($path_lookup->{folders}->{$name});
die "$errprefix path match error - regex and fixed items\n"
if defined($path_lookup->{regex});
$path_lookup = $path_lookup->{folders}->{$name};
}
}
die "$errprefix duplicate method definition\n"
if defined($path_lookup->{$method});
$path_lookup->{$method} = $info;
$info->{match_re} = $match_re;
$info->{match_name} = $match_name;
@ -174,7 +226,7 @@ sub register_method {
$method_by_name->{$self} = {} if !defined($method_by_name->{$self});
if ($info->{name}) {
die "method '${self}::$info->{name}' already defined\n"
die "$errprefix method name already defined\n"
if defined($method_by_name->{$self}->{$info->{name}});
$method_by_name->{$self}->{$info->{name}} = $info;
@ -217,97 +269,68 @@ sub map_method_by_name {
return $info;
}
sub map_method {
my ($self, $stack, $method, $uri_param) = @_;
sub map_path_to_methods {
my ($class, $stack, $uri_param) = @_;
my $ma = $method_registry->{$self};
my $path_lookup = $method_path_lookup->{$class};
my $stacklen = scalar(@$stack);
while (my $comp = shift @$stack) {
return undef if !$path_lookup; # not registerd?
if ($path_lookup->{regex}) {
my $name = $path_lookup->{regex}->{match_name};
my $regex = $path_lookup->{regex}->{match_re};
#syslog ('info', "MAPTEST:$method:$self: " . join ('/', @$stack));
foreach my $info (@$ma) {
#syslog ('info', "TEST0 " . Dumper($info));
next if !($info->{subclass} || ($info->{method} eq $method));
my $regexlen = scalar(@{$info->{match_re}});
if ($info->{subclass}) {
next if $stacklen < $regexlen;
return undef if $comp !~ m/^($regex)$/;
$uri_param->{$name} = $1;
$path_lookup = $path_lookup->{regex};
} elsif ($path_lookup->{folders}) {
$path_lookup = $path_lookup->{folders}->{$comp};
} else {
next if $stacklen != $regexlen;
die "internal error";
}
#syslog ('info', "TEST1 " . Dumper($info));
return undef if !$path_lookup;
my $param = {};
my $i = 0;
for (; $i < $regexlen; $i++) {
my $comp = $stack->[$i];
my $re = $info->{match_re}->[$i];
#print "COMPARE $comp $info->{match_re}->[$i]\n";
my ($match) = $stack->[$i] =~ m/^($re)$/;
last if !defined($match);
if (my $name = $info->{match_name}->[$i]) {
$param->{$name} = $match;
}
}
next if $i != $regexlen;
#print "MATCH $info->{name}\n";
foreach my $p (keys %$param) {
$uri_param->{$p} = $param->{$p};
}
return $info;
}
}
sub __find_handler_full {
my ($class, $method, $stack, $uri_param, $pathmatchref) = @_;
my $info;
eval {
$info = $class->map_method($stack, $method, $uri_param);
};
syslog('err', $@) if $@;
return undef if !$info;
$$pathmatchref .= '/' . $info->{path};
if (my $subh = $info->{subclass}) {
my $matchlen = scalar(@{$info->{match_re}});
for (my $i = 0; $i < $matchlen; $i++) {
shift @$stack; # pop from stack
}
if (my $info = $path_lookup->{SUBCLASS}) {
$class = $info->{subclass};
my $fd = $info->{fragmentDelimiter};
if (defined($fd)) {
# we only support the empty string '' (match whole URI)
die "unsupported fragmentDelimiter '$fd'"
if $fd ne '';
$stack = [ join ('/', @$stack) ] if scalar(@$stack) > 1;
}
return $subh->__find_handler_full($method, $stack, $uri_param, $pathmatchref);
$path_lookup = $method_path_lookup->{$class};
}
}
return ($class, $info, $$pathmatchref);
};
return undef if !$path_lookup;
return ($class, $path_lookup);
}
sub find_handler {
my ($class, $method, $path, $uri_param) = @_;
my $stack = [ grep { length($_) > 0 } split('\/+' , $path)]; # skip empty fragments
my $pathmatch = '';
return $class->__find_handler_full($method, $stack, $uri_param, \$pathmatch);
my ($handler_class, $path_info);
eval {
($handler_class, $path_info) = $class->map_path_to_methods($stack, $uri_param);
};
my $err = $@;
syslog('err', $err) if $err;
return undef if !($handler_class && $path_info);
my $method_info = $path_info->{$method};
return undef if !$method_info;
return ($handler_class, $method_info);
}
sub handle {