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:
parent
88b030e64c
commit
c108979483
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user