| 35 | | 'search_post_execute' => \&log_search, |
| 36 | | 'search_post_render' => \&cache_out, |
| | 35 | 'MT::App::Search::search_post_execute' => \&log_search, |
| | 36 | 'MT::App::Search::search_post_render' => \&cache_out, |
| | 37 | 'MT::App::Search::prepare_throttle' => \&_default_throttle, |
| | 38 | 'MT::App::Search::take_down' => \&_default_takedown, |
| | 781 | # throttling related methods |
| | 782 | sub throttle_control { |
| | 783 | my $app = shift; |
| | 784 | my ( $messages ) = @_; |
| | 785 | my $result; |
| | 786 | $app->run_callbacks( 'prepare_throttle', $app, \$result, $messages ); |
| | 787 | $result; |
| | 788 | } |
| | 789 | |
| | 790 | sub throttle_response { |
| | 791 | my $app = shift; |
| | 792 | my ( $messages ) = @_; |
| | 793 | my $tmpl = $app->param('Template') || ''; |
| | 794 | if ($tmpl eq 'feed') { |
| | 795 | $app->response_code(503); |
| | 796 | $app->set_header('Retry-After' => $app->config('ThrottleSeconds')); |
| | 797 | $app->send_http_header("text/plain"); |
| | 798 | $app->{no_print_body} = 1; |
| | 799 | } |
| | 800 | my $msg = $messages && @$messages |
| | 801 | ? join '; ', @$messages |
| | 802 | : $app->translate('Throttled'); |
| | 803 | return $app->error($msg); |
| | 804 | } |
| | 805 | |
| | 806 | sub _default_throttle { |
| | 807 | my ( $cb, $app, $result, $messages ) = @_; |
| | 808 | |
| | 809 | # Don't bother if a callback proiritized higher |
| | 810 | # set up its throttle already |
| | 811 | return $$result if defined $$result; |
| | 812 | |
| | 813 | ## Get login information if user is logged in (via cookie). |
| | 814 | ## If no login cookie, this fails silently, and that's fine. |
| | 815 | ($app->{user}) = $app->login; |
| | 816 | |
| | 817 | ## Don't throttle MT registered users |
| | 818 | if ( $app->{user} && $app->{user}->type == MT::Author::AUTHOR() ) { |
| | 819 | $$result = 1; |
| | 820 | return 1; |
| | 821 | } |
| | 822 | |
| | 823 | my $ip = $app->remote_ip; |
| | 824 | my $whitelist = $app->config->SearchThrottleIPWhitelist; |
| | 825 | if ($whitelist) { |
| | 826 | # check for $ip in $whitelist |
| | 827 | my @list = split /(\s*[,;]\s*|\s+)/, $whitelist; |
| | 828 | foreach (@list) { |
| | 829 | next unless $_ =~ m/^\d{1,3}(\.\d{0,3}){0,3}$/; |
| | 830 | if (($ip eq $_) || ($ip =~ m/^\Q$_\E/)) { |
| | 831 | $$result = 1; |
| | 832 | return 1; |
| | 833 | } |
| | 834 | } |
| | 835 | } |
| | 836 | |
| | 837 | unless ( $^O eq 'Win32' ) { |
| | 838 | # Use SIGALRM to stop processing in 5 seconds for each request |
| | 839 | $SIG{ALRM} = sub { $app->errtrans('Throttled'); die; }; |
| | 840 | $app->{__have_throttle} = 1; |
| | 841 | alarm($app->config->SearchThrottleSeconds); |
| | 842 | $$result = 1; |
| | 843 | } |
| | 844 | 1; |
| | 845 | } |
| | 846 | |
| | 847 | sub _default_takedown { |
| | 848 | my ( $cb, $app ) = @_; |
| | 849 | alarm(0) if $app->{__have_throttle}; |
| | 850 | 1; |
| | 851 | } |
| | 852 | |
| | 900 | =item prepare_throttle |
| | 901 | |
| | 902 | callback($cb, $app, \$result, \@messages); |
| | 903 | |
| | 904 | Called right before the beginning of the search processing. |
| | 905 | Each callback should see if certain condition is met, and |
| | 906 | set 0 to $$result if the request should be throttled. |
| | 907 | |
| | 908 | There can be more than one throttling method set up. |
| | 909 | Callbacks are called in order of priority set up when add_callback |
| | 910 | was called. Each callback should start its own code by something like |
| | 911 | below, to prevent itself overwriting throttle set up in the callback |
| | 912 | whose priority is higher than itself. |
| | 913 | |
| | 914 | return $$result if defined $$result; |
| | 915 | |