line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
###################################################################### |
2
|
|
|
|
|
|
|
# TCP listener on a given port |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# Copyright 2004, Danga Interactive, Inc. |
5
|
|
|
|
|
|
|
# Copyright 2005-2007, Six Apart, Ltd. |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package Perlbal::TCPListener; |
9
|
22
|
|
|
22
|
|
140
|
use strict; |
|
22
|
|
|
|
|
54
|
|
|
22
|
|
|
|
|
942
|
|
10
|
22
|
|
|
22
|
|
137
|
use warnings; |
|
22
|
|
|
|
|
546
|
|
|
22
|
|
|
|
|
1035
|
|
11
|
22
|
|
|
22
|
|
339
|
no warnings qw(deprecated); |
|
22
|
|
|
|
|
357
|
|
|
22
|
|
|
|
|
952
|
|
12
|
|
|
|
|
|
|
|
13
|
22
|
|
|
22
|
|
127
|
use base "Perlbal::Socket"; |
|
22
|
|
|
|
|
47
|
|
|
22
|
|
|
|
|
6031
|
|
14
|
22
|
|
|
|
|
216
|
use fields ('service', |
15
|
|
|
|
|
|
|
'hostport', |
16
|
|
|
|
|
|
|
'sslopts', |
17
|
|
|
|
|
|
|
'v6', # bool: IPv6 libraries are available |
18
|
22
|
|
|
22
|
|
141
|
); |
|
22
|
|
|
|
|
44
|
|
19
|
22
|
|
|
22
|
|
1801
|
use Socket qw(IPPROTO_TCP SOL_SOCKET SO_SNDBUF); |
|
22
|
|
|
|
|
43
|
|
|
22
|
|
|
|
|
2845
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
BEGIN { |
22
|
22
|
|
|
22
|
|
69
|
eval { require Perlbal::SocketSSL }; |
|
22
|
|
|
|
|
19837
|
|
23
|
22
|
|
|
|
|
29238
|
if (Perlbal::DEBUG > 0 && $@) { warn "SSL support failed on load: $@\n" } |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# TCPListener |
27
|
|
|
|
|
|
|
sub new { |
28
|
39
|
|
|
39
|
1
|
94
|
my Perlbal::TCPListener $self = shift; |
29
|
39
|
|
|
|
|
104
|
my ($hostport, $service, $opts) = @_; |
30
|
|
|
|
|
|
|
|
31
|
39
|
50
|
|
|
|
210
|
$self = fields::new($self) unless ref $self; |
32
|
39
|
|
50
|
|
|
14582
|
$opts ||= {}; |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Were ipv4 or ipv6 explicitly mentioned by syntax? |
35
|
39
|
|
|
|
|
82
|
my $force_v4 = 0; |
36
|
39
|
|
|
|
|
69
|
my $force_v6 = 0; |
37
|
|
|
|
|
|
|
|
38
|
39
|
|
|
|
|
71
|
my @args; |
39
|
39
|
50
|
|
|
|
457
|
if ($hostport =~ /^\d+$/) { |
|
|
50
|
|
|
|
|
|
40
|
0
|
|
|
|
|
0
|
@args = ('LocalPort' => $hostport); |
41
|
|
|
|
|
|
|
} elsif ($hostport =~ /^\d+\.\d+\.\d+\.\d+:/) { |
42
|
39
|
|
|
|
|
62
|
$force_v4 = 1; |
43
|
39
|
|
|
|
|
129
|
@args = ('LocalAddr' => $hostport); |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
39
|
|
|
|
|
84
|
my $v6_errors = ""; |
47
|
|
|
|
|
|
|
|
48
|
39
|
|
|
|
|
66
|
my $can_v6 = 0; |
49
|
39
|
50
|
|
|
|
156
|
if (!$force_v4) { |
50
|
0
|
|
|
|
|
0
|
eval "use Danga::Socket 1.61; 1; "; |
51
|
0
|
0
|
|
|
|
0
|
if ($@) { |
|
|
0
|
|
|
|
|
|
52
|
0
|
|
|
|
|
0
|
$v6_errors = "Danga::Socket 1.61 required for IPv6 support."; |
53
|
0
|
|
|
|
|
0
|
} elsif (!eval { require IO::Socket::INET6; 1 }) { |
|
0
|
|
|
|
|
0
|
|
54
|
0
|
|
|
|
|
0
|
$v6_errors = "IO::Socket::INET6 required for IPv6 support."; |
55
|
|
|
|
|
|
|
} else { |
56
|
0
|
|
|
|
|
0
|
$can_v6 = 1; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
39
|
50
|
|
|
|
134
|
my $socket_class = $can_v6 ? "IO::Socket::INET6" : "IO::Socket::INET"; |
61
|
39
|
|
|
|
|
101
|
$self->{v6} = $can_v6; |
62
|
|
|
|
|
|
|
|
63
|
39
|
|
|
|
|
678
|
my $sock = $socket_class->new( |
64
|
|
|
|
|
|
|
@args, |
65
|
|
|
|
|
|
|
Proto => IPPROTO_TCP, |
66
|
|
|
|
|
|
|
Listen => 1024, |
67
|
|
|
|
|
|
|
ReuseAddr => 1, |
68
|
|
|
|
|
|
|
); |
69
|
|
|
|
|
|
|
|
70
|
39
|
50
|
0
|
|
|
16856
|
return Perlbal::error("Error creating listening socket: " . ($@ || $!)) |
71
|
|
|
|
|
|
|
unless $sock; |
72
|
|
|
|
|
|
|
|
73
|
39
|
50
|
|
|
|
208
|
if ($^O eq 'MSWin32') { |
74
|
|
|
|
|
|
|
# On Windows, we have to do this a bit differently. |
75
|
|
|
|
|
|
|
# IO::Socket should really do this for us, but whatever. |
76
|
0
|
|
|
|
|
0
|
my $do = 1; |
77
|
0
|
0
|
|
|
|
0
|
ioctl($sock, 0x8004667E, \$do) or return Perlbal::error("Unable to make listener on $hostport non-blocking: $!"); |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
else { |
80
|
|
|
|
|
|
|
# IO::Socket::INET's Blocking => 0 just doesn't seem to work |
81
|
|
|
|
|
|
|
# on lots of perls. who knows why. |
82
|
39
|
50
|
|
|
|
621
|
IO::Handle::blocking($sock, 0) or return Perlbal::error("Unable to make listener on $hostport non-blocking: $!"); |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
39
|
|
|
|
|
398
|
$self->SUPER::new($sock); |
86
|
39
|
|
|
|
|
88
|
$self->{service} = $service; |
87
|
39
|
|
|
|
|
93
|
$self->{hostport} = $hostport; |
88
|
39
|
|
|
|
|
91
|
$self->{sslopts} = $opts->{ssl}; |
89
|
39
|
|
|
|
|
1813
|
$self->watch_read(1); |
90
|
39
|
|
|
|
|
5394
|
return $self; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# TCPListener: accepts a new client connection |
94
|
|
|
|
|
|
|
sub event_read { |
95
|
89
|
|
|
89
|
1
|
5827458
|
my Perlbal::TCPListener $self = shift; |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# accept as many connections as we can |
98
|
89
|
|
|
|
|
1987
|
while (my ($psock, $peeraddr) = $self->{sock}->accept) { |
99
|
89
|
|
|
|
|
27136
|
IO::Handle::blocking($psock, 0); |
100
|
|
|
|
|
|
|
|
101
|
89
|
50
|
|
|
|
1764
|
if (my $sndbuf = $self->{service}->{client_sndbuf_size}) { |
102
|
0
|
|
|
|
|
0
|
my $rv = setsockopt($psock, SOL_SOCKET, SO_SNDBUF, pack("L", $sndbuf)); |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
89
|
|
|
|
|
174
|
if (Perlbal::DEBUG >= 1) { |
106
|
|
|
|
|
|
|
my ($pport, $pipr) = $self->{v6} ? |
107
|
|
|
|
|
|
|
Socket6::unpack_sockaddr_in6($peeraddr) : |
108
|
|
|
|
|
|
|
Socket::sockaddr_in($peeraddr); |
109
|
|
|
|
|
|
|
my $pip = $self->{v6} ? |
110
|
|
|
|
|
|
|
"[" . Socket6::inet_ntop(Socket6::AF_INET6(), $pipr) . "]" : |
111
|
|
|
|
|
|
|
Socket::inet_ntoa($pipr); |
112
|
|
|
|
|
|
|
print "Got new conn: $psock ($pip:$pport) for " . $self->{service}->role . "\n"; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# SSL promotion if necessary |
116
|
89
|
50
|
|
|
|
471
|
if ($self->{sslopts}) { |
117
|
|
|
|
|
|
|
# try to upgrade to SSL, this does no IO it just re-blesses |
118
|
|
|
|
|
|
|
# and prepares the SSL engine for handling us later |
119
|
0
|
|
|
|
|
0
|
Perlbal::SocketSSL2->start_SSL( |
120
|
|
|
|
|
|
|
$psock, |
121
|
|
|
|
|
|
|
SSL_server => 1, |
122
|
|
|
|
|
|
|
SSL_startHandshake => 0, |
123
|
0
|
|
|
|
|
0
|
%{ $self->{sslopts} }, |
124
|
|
|
|
|
|
|
); |
125
|
0
|
|
|
|
|
0
|
print " .. socket upgraded to SSL!\n" if Perlbal::DEBUG >= 1; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# safety checking to ensure we got upgraded |
128
|
0
|
0
|
|
|
|
0
|
return $psock->close |
129
|
|
|
|
|
|
|
unless ref $psock eq 'Perlbal::SocketSSL2'; |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# class into new package and run with it |
132
|
0
|
|
|
|
|
0
|
my $sslsock = new Perlbal::SocketSSL($psock, $self); |
133
|
0
|
|
|
|
|
0
|
$sslsock->try_accept; |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# all done from our point of view |
136
|
0
|
|
|
|
|
0
|
next; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# puts this socket into the right class |
140
|
89
|
|
|
|
|
658
|
$self->class_new_socket($psock); |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub class_new_socket { |
145
|
89
|
|
|
89
|
0
|
235
|
my Perlbal::TCPListener $self = shift; |
146
|
89
|
|
|
|
|
212
|
my $psock = shift; |
147
|
|
|
|
|
|
|
|
148
|
89
|
|
|
|
|
911
|
my $service_role = $self->{service}->role; |
149
|
89
|
100
|
|
|
|
768
|
if ($service_role eq "reverse_proxy") { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
150
|
25
|
|
|
|
|
357
|
return Perlbal::ClientProxy->new($self->{service}, $psock); |
151
|
|
|
|
|
|
|
} elsif ($service_role eq "management") { |
152
|
17
|
|
|
|
|
236
|
return Perlbal::ClientManage->new($self->{service}, $psock); |
153
|
|
|
|
|
|
|
} elsif ($service_role eq "web_server") { |
154
|
42
|
|
|
|
|
533
|
return Perlbal::ClientHTTP->new($self->{service}, $psock); |
155
|
|
|
|
|
|
|
} elsif ($service_role eq "selector") { |
156
|
|
|
|
|
|
|
# will be cast to a more specific class later... |
157
|
5
|
|
|
|
|
70
|
return Perlbal::ClientHTTPBase->new($self->{service}, $psock, $self->{service}); |
158
|
|
|
|
|
|
|
} elsif (my $creator = Perlbal::Service::get_role_creator($service_role)) { |
159
|
|
|
|
|
|
|
# was defined by a plugin, so we want to return one of these |
160
|
0
|
|
|
|
|
|
return $creator->($self->{service}, $psock); |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub as_string { |
165
|
0
|
|
|
0
|
1
|
|
my Perlbal::TCPListener $self = shift; |
166
|
0
|
|
|
|
|
|
my $ret = $self->SUPER::as_string; |
167
|
0
|
|
|
|
|
|
my Perlbal::Service $svc = $self->{service}; |
168
|
0
|
|
|
|
|
|
$ret .= ": listening on $self->{hostport} for service '$svc->{name}'"; |
169
|
0
|
|
|
|
|
|
return $ret; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub as_string_html { |
173
|
0
|
|
|
0
|
0
|
|
my Perlbal::TCPListener $self = shift; |
174
|
0
|
|
|
|
|
|
my $ret = $self->SUPER::as_string_html; |
175
|
0
|
|
|
|
|
|
my Perlbal::Service $svc = $self->{service}; |
176
|
0
|
|
|
|
|
|
$ret .= ": listening on $self->{hostport} for service $svc->{name}"; |
177
|
0
|
|
|
|
|
|
return $ret; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub die_gracefully { |
181
|
|
|
|
|
|
|
# die off so we stop waiting for new connections |
182
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
183
|
0
|
|
|
|
|
|
$self->close('graceful_death'); |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
1; |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# Local Variables: |
190
|
|
|
|
|
|
|
# mode: perl |
191
|
|
|
|
|
|
|
# c-basic-indent: 4 |
192
|
|
|
|
|
|
|
# indent-tabs-mode: nil |
193
|
|
|
|
|
|
|
# End: |