File Coverage

blib/lib/Net/Server/Mail/SMTP/Prefork.pm
Criterion Covered Total %
statement 24 67 35.8
branch 0 20 0.0
condition 0 18 0.0
subroutine 8 15 53.3
pod 0 5 0.0
total 32 125 25.6


line stmt bran cond sub pod time code
1             package Net::Server::Mail::SMTP::Prefork;
2 1     1   1000 use 5.008005;
  1         4  
  1         43  
3 1     1   5 use strict;
  1         3  
  1         49  
4 1     1   16 use warnings;
  1         2  
  1         29  
5 1     1   5 use Carp;
  1         2  
  1         81  
6 1     1   1038 use IO::Socket::INET;
  1         33115  
  1         9  
7 1     1   1920 use Parallel::Prefork;
  1         9229  
  1         13  
8 1     1   1181 use Net::Server::Mail::SMTP;
  1         20472  
  1         52  
9 1     1   16 use Socket qw(IPPROTO_TCP TCP_NODELAY);
  1         2  
  1         1082  
10              
11             our $VERSION = "0.01";
12              
13             sub new {
14 0     0 0   my ($class, %args) = @_;
15              
16 0   0       my $self = bless {
      0        
      0        
17             host => $args{host} || 0,
18             port => $args{port} || 25,
19             max_workers => $args{max_workers} || 10,
20             };
21              
22 0           $self;
23             }
24              
25             sub setup_listener {
26 0     0 0   my $self = shift;
27              
28 0 0 0       $self->{listen_sock} ||= IO::Socket::INET->new(
29             Listen => SOMAXCONN,
30             LocalPort => $self->{port},
31             LocalAddr => $self->{host},
32             Proto => 'tcp',
33             ReuseAddr => 1,
34             ) or die "failed to listen to port $self->{port}:$!";
35              
36 0 0         if ($^O eq 'linux') {
37 0 0         setsockopt($self->{listen_sock}, IPPROTO_TCP, 9, 1)
38             and $self->{_using_defer_accept} = 1;
39             }
40             }
41              
42             sub accept_loop {
43 0     0 0   my ($self, $max_reqs_per_child) = @_;
44              
45 0           my $proc_req_count = 0;
46              
47 0   0       while (! defined $max_reqs_per_child || $proc_req_count < $max_reqs_per_child) {
48 0 0         if (my $conn = $self->{listen_sock}->accept) {
49 0           $self->{_is_deferred_accept} = $self->{_using_defer_accept};
50 0 0         $conn->blocking(0)
51             or die "failed to set socket to nonblocking mode:$!";
52 0 0         $conn->setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
53             or die "setsockopt(TCP_NODELAY) failed:$!";
54 0           $proc_req_count++;
55 0           my $smtp = $self->_prepare_smtp($conn);
56 0           $smtp->process;
57 0           $conn->close;
58             }
59             }
60             }
61              
62             sub set_callback {
63 0     0 0   my ($self, $name, $code, $context) = @_;
64 0 0 0       confess('bad callback() invocation')
65             unless defined $code && ref $code eq 'CODE';
66 0           $self->{callback}->{$name} = [$code, $context];
67             }
68              
69             sub run {
70 0     0 0   my ($self) = @_;
71 0           $self->setup_listener();
72 0 0         if ($self->{max_workers} != 0) {
73             # use Parallel::Prefork
74 0           my %pm_args = (
75             max_workers => $self->{max_workers},
76             trap_signals => {
77             TERM => 'TERM',
78             HUP => 'TERM',
79             },
80             );
81 0           my $pm = Parallel::Prefork->new(\%pm_args);
82 0           while ($pm->signal_received !~ /^(TERM|USR1)$/) {
83 0 0         $pm->start and next;
84 0           $self->accept_loop();
85 0           $pm->finish;
86             }
87             } else {
88             # run directly, mainly for debugging
89 0     0     local $SIG{TERM} = sub { exit 0; };
  0            
90 0           while (1) {
91 0           $self->accept_loop();
92             }
93             }
94             }
95              
96             sub _prepare_smtp {
97 0     0     my ($self, $conn) = @_;
98              
99 0           my $smtp = Net::Server::Mail::SMTP->new('socket' => $conn);
100 0 0 0       if ($self->{callback} && ref $self->{callback}) {
101 0           for my $name (keys %{$self->{callback}}) {
  0            
102 0           my ($code, $context) = @{$self->{callback}->{$name}};
  0            
103 0           $smtp->set_callback($name, $code, $context);
104             }
105             }
106              
107 0           return $smtp;
108             }
109              
110              
111             1;
112             __END__