line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
###################################################################### |
2
|
|
|
|
|
|
|
# HTTP connection to backend node |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# Copyright 2004, Danga Interactive, Inc. |
5
|
|
|
|
|
|
|
# Copyright 2005-2007, Six Apart, Ltd. |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package Perlbal::BackendHTTP; |
9
|
22
|
|
|
22
|
|
141
|
use strict; |
|
22
|
|
|
|
|
45
|
|
|
22
|
|
|
|
|
874
|
|
10
|
22
|
|
|
22
|
|
9741
|
use warnings; |
|
22
|
|
|
|
|
60
|
|
|
22
|
|
|
|
|
708
|
|
11
|
22
|
|
|
22
|
|
126
|
no warnings qw(deprecated); |
|
22
|
|
|
|
|
99
|
|
|
22
|
|
|
|
|
810
|
|
12
|
|
|
|
|
|
|
|
13
|
22
|
|
|
22
|
|
213
|
use base "Perlbal::Socket"; |
|
22
|
|
|
|
|
51
|
|
|
22
|
|
|
|
|
20442
|
|
14
|
22
|
|
|
|
|
683
|
use fields ('client', # Perlbal::ClientProxy connection, or undef |
15
|
|
|
|
|
|
|
'service', # Perlbal::Service |
16
|
|
|
|
|
|
|
'pool', # Perlbal::Pool; whatever pool we spawned from |
17
|
|
|
|
|
|
|
'ip', # IP scalar |
18
|
|
|
|
|
|
|
'port', # port scalar |
19
|
|
|
|
|
|
|
'ipport', # "$ip:$port" |
20
|
|
|
|
|
|
|
'reportto', # object; must implement reporter interface |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
'has_attention', # has been accepted by a webserver and |
23
|
|
|
|
|
|
|
# we know for sure we're not just talking |
24
|
|
|
|
|
|
|
# to the TCP stack |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
'waiting_options', # if true, we're waiting for an OPTIONS * |
27
|
|
|
|
|
|
|
# response to determine when we have attention |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
'disconnect_at', # time this connection will be disconnected, |
30
|
|
|
|
|
|
|
# if it's kept-alive and backend told us. |
31
|
|
|
|
|
|
|
# otherwise undef for unknown. |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# The following only apply when the backend server sends |
34
|
|
|
|
|
|
|
# a content-length header |
35
|
|
|
|
|
|
|
'content_length', # length of document being transferred |
36
|
|
|
|
|
|
|
'content_length_remain', # bytes remaining to be read |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
'use_count', # number of requests this backend's been used for |
39
|
|
|
|
|
|
|
'generation', # int; counts what generation we were spawned in |
40
|
|
|
|
|
|
|
'buffered_upload_mode', # bool; if on, we're doing a buffered upload transmit |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
'scratch' # for plugins |
43
|
22
|
|
|
22
|
|
192
|
); |
|
22
|
|
|
|
|
66
|
|
44
|
22
|
|
|
|
|
3694
|
use Socket qw(PF_INET IPPROTO_TCP SOCK_STREAM SOL_SOCKET SO_ERROR |
45
|
|
|
|
|
|
|
AF_UNIX PF_UNSPEC |
46
|
22
|
|
|
22
|
|
3390
|
); |
|
22
|
|
|
|
|
49
|
|
47
|
22
|
|
|
22
|
|
220
|
use IO::Handle; |
|
22
|
|
|
|
|
50
|
|
|
22
|
|
|
|
|
956
|
|
48
|
|
|
|
|
|
|
|
49
|
22
|
|
|
22
|
|
23920
|
use Perlbal::ClientProxy; |
|
22
|
|
|
|
|
80
|
|
|
22
|
|
|
|
|
1066
|
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# if this is made too big, (say, 128k), then perl does malloc instead |
52
|
|
|
|
|
|
|
# of using its slab cache. |
53
|
22
|
|
|
22
|
|
284
|
use constant BACKEND_READ_SIZE => 61440; # 60k, to fit in a 64k slab |
|
22
|
|
|
|
|
55
|
|
|
22
|
|
|
|
|
133620
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# keys set here when an endpoint is found to not support persistent |
56
|
|
|
|
|
|
|
# connections and/or the OPTIONS method |
57
|
|
|
|
|
|
|
our %NoVerify; # { "ip:port" => next-verify-time } |
58
|
|
|
|
|
|
|
our %NodeStats; # { "ip:port" => { ... } }; keep statistics about nodes |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# constructor for a backend connection takes a service (pool) that it's |
61
|
|
|
|
|
|
|
# for, and uses that service to get its backend IP/port, as well as the |
62
|
|
|
|
|
|
|
# client that will be using this backend connection. final parameter is |
63
|
|
|
|
|
|
|
# an options hashref that contains some options: |
64
|
|
|
|
|
|
|
# reportto => object obeying reportto interface |
65
|
|
|
|
|
|
|
sub new { |
66
|
23
|
|
|
23
|
1
|
58
|
my Perlbal::BackendHTTP $self = shift; |
67
|
23
|
|
|
|
|
75
|
my ($svc, $ip, $port, $opts) = @_; |
68
|
23
|
|
50
|
|
|
96
|
$opts ||= {}; |
69
|
|
|
|
|
|
|
|
70
|
23
|
|
|
|
|
49
|
my $sock; |
71
|
23
|
|
|
|
|
1256
|
socket $sock, PF_INET, SOCK_STREAM, IPPROTO_TCP; |
72
|
|
|
|
|
|
|
|
73
|
23
|
50
|
33
|
|
|
191
|
unless ($sock && defined fileno($sock)) { |
74
|
0
|
|
|
|
|
0
|
Perlbal::log('crit', "Error creating socket: $!"); |
75
|
0
|
|
|
|
|
0
|
return undef; |
76
|
|
|
|
|
|
|
} |
77
|
23
|
|
|
|
|
197
|
my $inet_aton = Socket::inet_aton($ip); |
78
|
23
|
50
|
|
|
|
107
|
unless ($inet_aton) { |
79
|
0
|
|
|
|
|
0
|
Perlbal::log('crit', "inet_aton failed creating socket for $ip"); |
80
|
0
|
|
|
|
|
0
|
return undef; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
23
|
|
|
|
|
359
|
IO::Handle::blocking($sock, 0); |
84
|
23
|
|
|
|
|
145
|
connect $sock, Socket::sockaddr_in($port, $inet_aton); |
85
|
|
|
|
|
|
|
|
86
|
23
|
50
|
|
|
|
23447
|
$self = fields::new($self) unless ref $self; |
87
|
23
|
|
|
|
|
11775
|
$self->SUPER::new($sock); |
88
|
|
|
|
|
|
|
|
89
|
23
|
|
|
|
|
81
|
Perlbal::objctor($self); |
90
|
|
|
|
|
|
|
|
91
|
23
|
|
|
|
|
65
|
$self->{ip} = $ip; # backend IP |
92
|
23
|
|
|
|
|
71
|
$self->{port} = $port; # backend port |
93
|
23
|
|
|
|
|
92
|
$self->{ipport} = "$ip:$port"; # often used as key |
94
|
23
|
|
|
|
|
59
|
$self->{service} = $svc; # the service we're serving for |
95
|
23
|
|
|
|
|
112
|
$self->{pool} = $opts->{pool}; # what pool we came from. |
96
|
23
|
|
66
|
|
|
184
|
$self->{reportto} = $opts->{reportto} || $svc; # reportto if specified |
97
|
23
|
|
|
|
|
128
|
$self->state("connecting"); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# mark another connection to this ip:port |
100
|
23
|
|
|
|
|
111
|
$NodeStats{$self->{ipport}}->{attempts}++; |
101
|
23
|
|
|
|
|
91
|
$NodeStats{$self->{ipport}}->{lastattempt} = $self->{create_time}; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# setup callback in case we get stuck in connecting land |
104
|
|
|
|
|
|
|
Perlbal::Socket::register_callback(15, sub { |
105
|
1
|
50
|
33
|
1
|
|
8
|
if ($self->state eq 'connecting' || $self->state eq 'verifying_backend') { |
106
|
|
|
|
|
|
|
# shouldn't still be connecting/verifying ~15 seconds after create |
107
|
0
|
|
|
|
|
0
|
$self->close('callback_timeout'); |
108
|
|
|
|
|
|
|
} |
109
|
1
|
|
|
|
|
4
|
return 0; |
110
|
23
|
|
|
|
|
237
|
}); |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# for header reading: |
113
|
23
|
|
|
|
|
120
|
$self->init; |
114
|
|
|
|
|
|
|
|
115
|
23
|
|
|
|
|
137
|
$self->watch_write(1); |
116
|
23
|
|
|
|
|
1447
|
return $self; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub init { |
120
|
23
|
|
|
23
|
0
|
49
|
my $self = shift; |
121
|
23
|
|
|
|
|
63
|
$self->{req_headers} = undef; |
122
|
23
|
|
|
|
|
60
|
$self->{res_headers} = undef; # defined w/ headers object once all headers in |
123
|
23
|
|
|
|
|
52
|
$self->{headers_string} = ""; # blank to start |
124
|
23
|
|
|
|
|
79
|
$self->{generation} = $self->{service}->{generation}; |
125
|
23
|
|
|
|
|
45
|
$self->{read_size} = 0; # total bytes read from client |
126
|
|
|
|
|
|
|
|
127
|
23
|
|
|
|
|
50
|
$self->{client} = undef; # Perlbal::ClientProxy object, initially empty |
128
|
|
|
|
|
|
|
# until we ask our service for one |
129
|
|
|
|
|
|
|
|
130
|
23
|
|
|
|
|
56
|
$self->{has_attention} = 0; |
131
|
23
|
|
|
|
|
50
|
$self->{use_count} = 0; |
132
|
23
|
|
|
|
|
57
|
$self->{buffered_upload_mode} = 0; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub new_process { |
137
|
0
|
|
|
0
|
0
|
0
|
my ($class, $svc, $prog) = @_; |
138
|
|
|
|
|
|
|
|
139
|
0
|
|
|
|
|
0
|
my ($psock, $csock); |
140
|
0
|
0
|
|
|
|
0
|
socketpair($csock, $psock, AF_UNIX, SOCK_STREAM, PF_UNSPEC) |
141
|
|
|
|
|
|
|
or die "socketpair: $!"; |
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
0
|
$csock->autoflush(1); |
144
|
0
|
|
|
|
|
0
|
$psock->autoflush(1); |
145
|
|
|
|
|
|
|
|
146
|
0
|
|
|
|
|
0
|
my $pid = fork; |
147
|
0
|
0
|
|
|
|
0
|
unless (defined $pid) { |
148
|
0
|
|
|
|
|
0
|
warn "fork failed: $!\n"; |
149
|
0
|
|
|
|
|
0
|
return undef; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# child process |
153
|
0
|
0
|
|
|
|
0
|
unless ($pid) { |
154
|
0
|
|
|
|
|
0
|
close(STDIN); |
155
|
0
|
|
|
|
|
0
|
close(STDOUT); |
156
|
|
|
|
|
|
|
#close(STDERR); |
157
|
0
|
|
|
|
|
0
|
open(STDIN, '<&', $psock); |
158
|
0
|
|
|
|
|
0
|
open(STDOUT, '>&', $psock); |
159
|
|
|
|
|
|
|
#open(STDERR, ">/dev/null"); |
160
|
0
|
|
|
|
|
0
|
exec $prog; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
0
|
|
|
|
|
0
|
close($psock); |
164
|
0
|
|
|
|
|
0
|
my $sock = $csock; |
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
0
|
my $self = fields::new($class); |
167
|
0
|
|
|
|
|
0
|
$self->SUPER::new($sock); |
168
|
0
|
|
|
|
|
0
|
Perlbal::objctor($self); |
169
|
|
|
|
|
|
|
|
170
|
0
|
|
|
|
|
0
|
$self->{ipport} = $prog; # often used as key |
171
|
0
|
|
|
|
|
0
|
$self->{service} = $svc; # the service we're serving for |
172
|
0
|
|
|
|
|
0
|
$self->{reportto} = $svc; # reportto interface (same as service) |
173
|
0
|
|
|
|
|
0
|
$self->state("connecting"); |
174
|
|
|
|
|
|
|
|
175
|
0
|
|
|
|
|
0
|
$self->init; |
176
|
0
|
|
|
|
|
0
|
$self->watch_write(1); |
177
|
0
|
|
|
|
|
0
|
return $self; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub close { |
181
|
18
|
|
|
18
|
1
|
39
|
my Perlbal::BackendHTTP $self = shift; |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# OSX Gives EPIPE on bad connects, and doesn't fail the connect |
184
|
|
|
|
|
|
|
# so lets treat EPIPE as a event_err so the logic there does |
185
|
|
|
|
|
|
|
# the right thing |
186
|
18
|
50
|
33
|
|
|
166
|
if (defined $_[0] && $_[0] eq 'EPIPE') { |
187
|
0
|
|
|
|
|
0
|
$self->event_err; |
188
|
0
|
|
|
|
|
0
|
return; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# don't close twice |
192
|
18
|
50
|
|
|
|
74
|
return if $self->{closed}; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# this closes the socket and sets our closed flag |
195
|
18
|
|
|
|
|
167
|
$self->SUPER::close(@_); |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# tell our client that we're gone |
198
|
18
|
100
|
|
|
|
1561
|
if (my $client = $self->{client}) { |
199
|
16
|
|
|
|
|
96
|
$client->backend(undef); |
200
|
16
|
|
|
|
|
42
|
$self->{client} = undef; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
# tell our owner that we're gone |
204
|
18
|
50
|
|
|
|
76
|
if (my $reportto = $self->{reportto}) { |
205
|
18
|
|
|
|
|
99
|
$reportto->note_backend_close($self); |
206
|
18
|
|
|
|
|
96
|
$self->{reportto} = undef; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# return our defined generation counter with no parameter, |
211
|
|
|
|
|
|
|
# or set our generation if given a parameter |
212
|
|
|
|
|
|
|
sub generation { |
213
|
254
|
|
|
254
|
0
|
455
|
my Perlbal::BackendHTTP $self = $_[0]; |
214
|
254
|
50
|
|
|
|
2288
|
return $self->{generation} unless $_[1]; |
215
|
0
|
|
|
|
|
0
|
return $self->{generation} = $_[1]; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# return what ip and port combination we're using |
219
|
|
|
|
|
|
|
sub ipport { |
220
|
0
|
|
|
0
|
0
|
0
|
my Perlbal::BackendHTTP $self = $_[0]; |
221
|
0
|
|
|
|
|
0
|
return $self->{ipport}; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# called to tell backend that the client has gone on to do something else now. |
225
|
|
|
|
|
|
|
sub forget_client { |
226
|
0
|
|
|
0
|
0
|
0
|
my Perlbal::BackendHTTP $self = $_[0]; |
227
|
0
|
|
|
|
|
0
|
$self->{client} = undef; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# called by service when it's got a client for us, or by ourselves |
231
|
|
|
|
|
|
|
# when we asked for a client. |
232
|
|
|
|
|
|
|
# returns true if client assignment was accepted. |
233
|
|
|
|
|
|
|
sub assign_client { |
234
|
135
|
|
|
135
|
0
|
316
|
my Perlbal::BackendHTTP $self = shift; |
235
|
135
|
|
|
|
|
257
|
my Perlbal::ClientProxy $client = shift; |
236
|
135
|
50
|
|
|
|
513
|
return 0 if $self->{client}; |
237
|
|
|
|
|
|
|
|
238
|
135
|
|
|
|
|
318
|
my $svc = $self->{service}; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# set our client, and the client's backend to us |
241
|
135
|
|
|
|
|
1398
|
$svc->mark_node_used($self->{ipport}); |
242
|
135
|
|
|
|
|
556
|
$self->{client} = $client; |
243
|
135
|
|
|
|
|
673
|
$self->state("sending_req"); |
244
|
135
|
|
|
|
|
764
|
$self->{client}->backend($self); |
245
|
|
|
|
|
|
|
|
246
|
135
|
|
|
|
|
1081
|
my Perlbal::HTTPHeaders $hds = $client->{req_headers}->clone; |
247
|
135
|
|
|
|
|
463
|
$self->{req_headers} = $hds; |
248
|
|
|
|
|
|
|
|
249
|
135
|
|
|
|
|
1241
|
my $client_ip = $client->peer_ip_string; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
# I think I've seen this be undef in practice. Double-check |
252
|
135
|
50
|
|
|
|
2166
|
unless ($client_ip) { |
253
|
0
|
|
|
|
|
0
|
warn "Undef client_ip ($client) in assign_client. Closing."; |
254
|
0
|
|
|
|
|
0
|
$client->close; |
255
|
0
|
|
|
|
|
0
|
return 0; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# Use HTTP/1.0 to backend (FIXME: use 1.1 and support chunking) |
259
|
135
|
|
|
|
|
654
|
$hds->set_version("1.0"); |
260
|
|
|
|
|
|
|
|
261
|
135
|
|
|
|
|
651
|
my $persist = $svc->{persist_backend}; |
262
|
|
|
|
|
|
|
|
263
|
135
|
100
|
|
|
|
815
|
$hds->header("Connection", $persist ? "keep-alive" : "close"); |
264
|
|
|
|
|
|
|
|
265
|
135
|
100
|
|
|
|
488
|
if ($svc->{enable_reproxy}) { |
266
|
24
|
|
|
|
|
80
|
$hds->header("X-Proxy-Capabilities", "reproxy-file"); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# decide whether we trust the upstream or not, to give us useful |
270
|
|
|
|
|
|
|
# forwarding info headers |
271
|
135
|
50
|
|
|
|
684
|
if ($svc->trusted_ip($client_ip)) { |
272
|
|
|
|
|
|
|
# yes, we trust our upstream, so just append our client's IP |
273
|
|
|
|
|
|
|
# to the existing list of forwarded IPs, if we're a blind proxy |
274
|
|
|
|
|
|
|
# then don't append our IP to the end of the list. |
275
|
0
|
0
|
|
|
|
0
|
unless ($svc->{blind_proxy}) { |
276
|
0
|
|
0
|
|
|
0
|
my @ips = split /,\s*/, ($hds->header("X-Forwarded-For") || ''); |
277
|
0
|
|
|
|
|
0
|
$hds->header("X-Forwarded-For", join ", ", @ips, $client_ip); |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
} else { |
280
|
|
|
|
|
|
|
# no, don't trust upstream (untrusted client), so remove all their |
281
|
|
|
|
|
|
|
# forwarding headers and tag their IP as the x-forwarded-for |
282
|
135
|
|
|
|
|
511
|
$hds->header("X-Forwarded-For", $client_ip); |
283
|
135
|
|
|
|
|
479
|
$hds->header("X-Host", undef); |
284
|
135
|
|
|
|
|
2421
|
$hds->header("X-Forwarded-Host", undef); |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
135
|
|
|
|
|
761
|
$self->tcp_cork(1); |
288
|
135
|
|
|
|
|
4961
|
$client->state('backend_req_sent'); |
289
|
|
|
|
|
|
|
|
290
|
135
|
|
|
|
|
489
|
$self->{content_length} = undef; |
291
|
135
|
|
|
|
|
556
|
$self->{content_length_remain} = undef; |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
# run hooks |
294
|
135
|
50
|
|
|
|
2012
|
return 1 if $svc->run_hook('backend_client_assigned', $self); |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# now cleanup the headers before we send to the backend |
297
|
135
|
50
|
|
|
|
870
|
$svc->munge_headers($hds) if $svc; |
298
|
|
|
|
|
|
|
|
299
|
135
|
|
|
|
|
781
|
$self->write($hds->to_string_ref); |
300
|
|
|
|
|
|
|
$self->write(sub { |
301
|
135
|
|
|
135
|
|
18014
|
$self->tcp_cork(0); |
302
|
135
|
50
|
|
|
|
98996
|
if (my $client = $self->{client}) { |
303
|
|
|
|
|
|
|
# start waiting on a reply |
304
|
135
|
|
|
|
|
892
|
$self->watch_read(1); |
305
|
135
|
|
|
|
|
27595
|
$self->state("wait_res"); |
306
|
135
|
|
|
|
|
1307
|
$client->state('wait_res'); |
307
|
135
|
|
|
|
|
2673
|
$client->backend_ready($self); |
308
|
|
|
|
|
|
|
} |
309
|
135
|
|
|
|
|
1600
|
}); |
310
|
|
|
|
|
|
|
|
311
|
135
|
|
|
|
|
1462
|
return 1; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# called by ClientProxy after we tell it our backend is ready and |
315
|
|
|
|
|
|
|
# it has an upload ready on disk |
316
|
|
|
|
|
|
|
sub invoke_buffered_upload_mode { |
317
|
29
|
|
|
29
|
0
|
63
|
my Perlbal::BackendHTTP $self = shift; |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# so, we're receiving a buffered upload, we need to go ahead and |
320
|
|
|
|
|
|
|
# start the buffered upload retransmission to backend process. we |
321
|
|
|
|
|
|
|
# have to turn watching for writes on, since that's what is doing |
322
|
|
|
|
|
|
|
# the triggering, NOT the normal client proxy watch for read |
323
|
29
|
|
|
|
|
82
|
$self->{buffered_upload_mode} = 1; |
324
|
29
|
|
|
|
|
142
|
$self->watch_write(1); |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# Backend |
328
|
|
|
|
|
|
|
sub event_write { |
329
|
113
|
|
|
113
|
1
|
191673
|
my Perlbal::BackendHTTP $self = shift; |
330
|
113
|
|
|
|
|
187
|
print "Backend $self is writeable!\n" if Perlbal::DEBUG >= 2; |
331
|
|
|
|
|
|
|
|
332
|
113
|
|
|
|
|
326
|
my $now = time(); |
333
|
113
|
50
|
33
|
|
|
685
|
delete $NoVerify{$self->{ipport}} if |
334
|
|
|
|
|
|
|
defined $NoVerify{$self->{ipport}} && |
335
|
|
|
|
|
|
|
$NoVerify{$self->{ipport}} < $now; |
336
|
|
|
|
|
|
|
|
337
|
113
|
100
|
66
|
|
|
652
|
if (! $self->{client} && $self->{state} eq "connecting") { |
338
|
|
|
|
|
|
|
# not interested in writes again until something else is |
339
|
23
|
|
|
|
|
98
|
$self->watch_write(0); |
340
|
23
|
|
|
|
|
1555
|
$NodeStats{$self->{ipport}}->{connects}++; |
341
|
23
|
|
|
|
|
146
|
$NodeStats{$self->{ipport}}->{lastconnect} = $now; |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
# OSX returns writeable even if the connect fails |
344
|
|
|
|
|
|
|
# so explicitly check for the error |
345
|
|
|
|
|
|
|
# TODO: make a smaller test case and show to the world |
346
|
23
|
100
|
|
|
|
292
|
if (my $error = unpack('i', getsockopt($self->{sock}, SOL_SOCKET, SO_ERROR))) { |
347
|
1
|
|
|
|
|
5
|
$self->event_err; |
348
|
1
|
|
|
|
|
3
|
return; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
22
|
100
|
66
|
|
|
274
|
if (defined $self->{service} && $self->{service}->{verify_backend} && |
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
352
|
|
|
|
|
|
|
!$self->{has_attention} && !defined $NoVerify{$self->{ipport}}) { |
353
|
|
|
|
|
|
|
|
354
|
1
|
50
|
|
|
|
8
|
return if $self->{service}->run_hook('backend_write_verify', $self); |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# the backend should be able to answer this incredibly quickly. |
357
|
1
|
|
|
|
|
11
|
$self->write("OPTIONS " . $self->{service}->{verify_backend_path} . " HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"); |
358
|
1
|
|
|
|
|
7
|
$self->watch_read(1); |
359
|
1
|
|
|
|
|
28
|
$self->{waiting_options} = 1; |
360
|
1
|
|
|
|
|
4
|
$self->{content_length_remain} = undef; |
361
|
1
|
|
|
|
|
5
|
$self->state("verifying_backend"); |
362
|
|
|
|
|
|
|
} else { |
363
|
|
|
|
|
|
|
# register our boredom (readiness for a client/request) |
364
|
21
|
|
|
|
|
87
|
$self->state("bored"); |
365
|
21
|
|
|
|
|
130
|
$self->{reportto}->register_boredom($self); |
366
|
|
|
|
|
|
|
} |
367
|
22
|
|
|
|
|
157
|
return; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# if we have a client, and we're currently doing a buffered upload |
371
|
|
|
|
|
|
|
# sendfile, then tell the client to continue sending us data |
372
|
90
|
100
|
66
|
|
|
674
|
if ($self->{client} && $self->{buffered_upload_mode}) { |
373
|
38
|
|
|
|
|
230
|
$self->{client}->continue_buffered_upload($self); |
374
|
38
|
|
|
|
|
415
|
return; |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
52
|
|
|
|
|
258
|
my $done = $self->write(undef); |
378
|
52
|
100
|
|
|
|
287
|
$self->watch_write(0) if $done; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
sub verify_success { |
382
|
1
|
|
|
1
|
0
|
2
|
my Perlbal::BackendHTTP $self = shift; |
383
|
1
|
|
|
|
|
2
|
$self->{waiting_options} = 0; |
384
|
1
|
|
|
|
|
2
|
$self->{has_attention} = 1; |
385
|
1
|
|
|
|
|
8
|
$NodeStats{$self->{ipport}}->{verifies}++; |
386
|
1
|
|
|
|
|
5
|
$self->next_request(1); # initial |
387
|
1
|
|
|
|
|
2
|
return; |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub verify_failure { |
391
|
0
|
|
|
0
|
0
|
0
|
my Perlbal::BackendHTTP $self = shift; |
392
|
0
|
|
|
|
|
0
|
$NoVerify{$self->{ipport}} = time() + 60; |
393
|
0
|
|
|
|
|
0
|
$self->{reportto}->note_bad_backend_connect($self); |
394
|
0
|
|
|
|
|
0
|
$self->close('no_keep_alive'); |
395
|
0
|
|
|
|
|
0
|
return; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub event_read_waiting_options { # : void |
399
|
1
|
|
|
1
|
0
|
4
|
my Perlbal::BackendHTTP $self = shift; |
400
|
|
|
|
|
|
|
|
401
|
1
|
50
|
|
|
|
14
|
if (defined $self->{service}) { |
402
|
1
|
50
|
|
|
|
10
|
return if $self->{service}->run_hook('backend_readable_verify', $self); |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
1
|
50
|
|
|
|
11
|
if ($self->{content_length_remain}) { |
|
|
50
|
|
|
|
|
|
406
|
|
|
|
|
|
|
# the HTTP/1.1 spec says OPTIONS responses can have content-lengths, |
407
|
|
|
|
|
|
|
# but the meaning of the response is reserved for a future spec. |
408
|
|
|
|
|
|
|
# this just gobbles it up for. |
409
|
0
|
|
|
|
|
0
|
my $bref = $self->read(BACKEND_READ_SIZE); |
410
|
0
|
0
|
|
|
|
0
|
return $self->verify_failure unless defined $bref; |
411
|
0
|
|
|
|
|
0
|
$self->{content_length_remain} -= length($$bref); |
412
|
|
|
|
|
|
|
} elsif (my $hd = $self->read_response_headers) { |
413
|
|
|
|
|
|
|
# see if we have keep alive support |
414
|
1
|
50
|
|
|
|
6
|
return $self->verify_failure unless $hd->res_keep_alive_options; |
415
|
1
|
|
|
|
|
4
|
$self->{content_length_remain} = $hd->header("Content-Length"); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
# if we've got the option response and read any response data |
419
|
|
|
|
|
|
|
# if present: |
420
|
1
|
50
|
33
|
|
|
9
|
if ($self->{res_headers} && ! $self->{content_length_remain}) { |
421
|
1
|
|
|
|
|
6
|
$self->verify_success; |
422
|
|
|
|
|
|
|
} |
423
|
1
|
|
|
|
|
6
|
return; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
sub handle_response { # : void |
427
|
150
|
|
|
150
|
0
|
380
|
my Perlbal::BackendHTTP $self = shift; |
428
|
150
|
|
|
|
|
366
|
my Perlbal::HTTPHeaders $hd = $self->{res_headers}; |
429
|
150
|
|
|
|
|
357
|
my Perlbal::ClientProxy $client = $self->{client}; |
430
|
|
|
|
|
|
|
|
431
|
150
|
|
|
|
|
195
|
print "BackendHTTP: handle_response\n" if Perlbal::DEBUG >= 2; |
432
|
|
|
|
|
|
|
|
433
|
150
|
|
|
|
|
966
|
my $res_code = $hd->response_code; |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
# keep a rolling window of the last 500 response codes |
436
|
150
|
|
100
|
|
|
1079
|
my $ref = ($NodeStats{$self->{ipport}}->{responsecodes} ||= []); |
437
|
150
|
|
|
|
|
384
|
push @$ref, $res_code; |
438
|
150
|
50
|
|
|
|
484
|
if (scalar(@$ref) > 500) { |
439
|
0
|
|
|
|
|
0
|
shift @$ref; |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
# call service response received function |
443
|
150
|
100
|
|
|
|
1376
|
return if $self->{reportto}->backend_response_received($self); |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# standard handling |
446
|
149
|
|
|
|
|
1385
|
$self->state("xfer_res"); |
447
|
149
|
|
|
|
|
1288
|
$client->state("xfer_res"); |
448
|
149
|
|
|
|
|
369
|
$self->{has_attention} = 1; |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
# RFC 2616, Sec 4.4: Messages MUST NOT include both a |
451
|
|
|
|
|
|
|
# Content-Length header field and a non-identity |
452
|
|
|
|
|
|
|
# transfer-coding. If the message does include a non- |
453
|
|
|
|
|
|
|
# identity transfer-coding, the Content-Length MUST be |
454
|
|
|
|
|
|
|
# ignored. |
455
|
149
|
|
|
|
|
1026
|
my $te = $hd->header("Transfer-Encoding"); |
456
|
149
|
50
|
33
|
|
|
633
|
if ($te && $te !~ /\bidentity\b/i) { |
457
|
0
|
|
|
|
|
0
|
$hd->header("Content-Length", undef); |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
149
|
|
|
|
|
454
|
my Perlbal::HTTPHeaders $rqhd = $self->{req_headers}; |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
# setup our content length so we know how much data to expect, in general |
463
|
|
|
|
|
|
|
# we want the content-length from the response, but if this was a head request |
464
|
|
|
|
|
|
|
# we know it's a 0 length message the client wants |
465
|
149
|
100
|
|
|
|
716
|
if ($rqhd->request_method eq 'HEAD') { |
466
|
2
|
|
|
|
|
8
|
$self->{content_length} = 0; |
467
|
|
|
|
|
|
|
} else { |
468
|
147
|
|
|
|
|
674
|
$self->{content_length} = $hd->content_length; |
469
|
|
|
|
|
|
|
} |
470
|
149
|
|
100
|
|
|
835
|
$self->{content_length_remain} = $self->{content_length} || 0; |
471
|
|
|
|
|
|
|
|
472
|
149
|
|
100
|
|
|
700
|
my $reproxy_cache_for = $hd->header('X-REPROXY-CACHE-FOR') || 0; |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# special cases: reproxying and retrying after server errors: |
475
|
149
|
100
|
66
|
|
|
547
|
if ((my $rep = $hd->header('X-REPROXY-FILE')) && $self->may_reproxy) { |
|
|
100
|
66
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
50
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
476
|
|
|
|
|
|
|
# make the client begin the async IO while we move on |
477
|
6
|
|
|
|
|
24
|
$self->next_request; |
478
|
6
|
|
|
|
|
33
|
$client->start_reproxy_file($rep, $hd); |
479
|
6
|
|
|
|
|
120
|
return; |
480
|
|
|
|
|
|
|
} elsif ((my $urls = $hd->header('X-REPROXY-URL')) && $self->may_reproxy) { |
481
|
13
|
|
|
|
|
48
|
$self->next_request; |
482
|
13
|
100
|
|
|
|
39
|
$self->{service}->add_to_reproxy_url_cache($rqhd, $hd) |
483
|
|
|
|
|
|
|
if $reproxy_cache_for; |
484
|
13
|
|
|
|
|
72
|
$client->start_reproxy_uri($hd, $urls); |
485
|
13
|
|
|
|
|
72
|
return; |
486
|
|
|
|
|
|
|
} elsif ((my $svcname = $hd->header('X-REPROXY-SERVICE')) && $self->may_reproxy) { |
487
|
0
|
|
|
|
|
0
|
$self->next_request; |
488
|
0
|
|
|
|
|
0
|
$self->{client} = undef; |
489
|
0
|
|
|
|
|
0
|
$client->start_reproxy_service($hd, $svcname); |
490
|
0
|
|
|
|
|
0
|
return; |
491
|
|
|
|
|
|
|
} elsif ($res_code == 500 && |
492
|
|
|
|
|
|
|
$rqhd->request_method =~ /^GET|HEAD$/ && |
493
|
|
|
|
|
|
|
$client->should_retry_after_500($self)) { |
494
|
|
|
|
|
|
|
# eh, 500 errors are rare. just close and don't spend effort reading |
495
|
|
|
|
|
|
|
# rest of body's error message to no client. |
496
|
0
|
|
|
|
|
0
|
$self->close; |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
# and tell the client to try again with a new backend |
499
|
0
|
|
|
|
|
0
|
$client->retry_after_500($self->{service}); |
500
|
0
|
|
|
|
|
0
|
return; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
# regular path: |
504
|
130
|
|
66
|
|
|
1268
|
my $res_source = $client->{primary_res_hdrs} || $hd; |
505
|
130
|
|
|
|
|
840
|
my $thd = $client->{res_headers} = $res_source->clone; |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
# if we had an alternate primary response header, make sure |
508
|
|
|
|
|
|
|
# we send the real content-length (from the reproxied URL) |
509
|
|
|
|
|
|
|
# and not the one the first server gave us |
510
|
130
|
100
|
|
|
|
809
|
if ($client->{primary_res_hdrs}) { |
511
|
15
|
|
|
|
|
57
|
$thd->header('Content-Length', $hd->header('Content-Length')); |
512
|
15
|
|
|
|
|
54
|
$thd->header('X-REPROXY-FILE', undef); |
513
|
15
|
|
|
|
|
53
|
$thd->header('X-REPROXY-URL', undef); |
514
|
15
|
|
|
|
|
48
|
$thd->header('X-REPROXY-EXPECTED-SIZE', undef); |
515
|
15
|
|
|
|
|
51
|
$thd->header('X-REPROXY-CACHE-FOR', undef); |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# also update the response code, in case of 206 partial content |
518
|
15
|
|
|
|
|
43
|
my $rescode = $hd->response_code; |
519
|
15
|
50
|
33
|
|
|
90
|
if ($rescode == 206 || $rescode == 416) { |
520
|
0
|
|
|
|
|
0
|
$thd->code($rescode); |
521
|
0
|
0
|
|
|
|
0
|
$thd->header('Accept-Ranges', $hd->header('Accept-Ranges')) if $hd->header('Accept-Ranges'); |
522
|
0
|
0
|
|
|
|
0
|
$thd->header('Content-Range', $hd->header('Content-Range')) if $hd->header('Content-Range'); |
523
|
|
|
|
|
|
|
} |
524
|
15
|
100
|
|
|
|
43
|
$thd->code(200) if $thd->response_code == 204; # upgrade HTTP No Content (204) to 200 OK. |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# setup_keepalive will set Connection: and Keep-Alive: headers for us |
528
|
|
|
|
|
|
|
# as well as setup our HTTP version appropriately |
529
|
130
|
|
|
|
|
942
|
$client->setup_keepalive($thd); |
530
|
|
|
|
|
|
|
|
531
|
130
|
100
|
|
|
|
1425
|
my $svc = ref $self->{service} eq 'Perlbal::Service' ? $self->{service} : $client->{service}; |
532
|
130
|
|
|
|
|
727
|
$svc->run_hook('modify_response_headers', $self, $client); |
533
|
|
|
|
|
|
|
|
534
|
130
|
|
|
|
|
220
|
print " writing response headers to client\n" if Perlbal::DEBUG >= 3; |
535
|
130
|
|
|
|
|
569
|
$client->write($thd->to_string_ref); |
536
|
|
|
|
|
|
|
|
537
|
130
|
|
|
|
|
229
|
print(" content_length=", (defined $self->{content_length} ? $self->{content_length} : "(undef)"), |
538
|
|
|
|
|
|
|
" remain=", (defined $self->{content_length_remain} ? $self->{content_length_remain} : "(undef)"), "\n") |
539
|
|
|
|
|
|
|
if Perlbal::DEBUG >= 3; |
540
|
|
|
|
|
|
|
|
541
|
130
|
|
|
|
|
522
|
$svc->run_hook('prepend_body', $self, $client); |
542
|
|
|
|
|
|
|
|
543
|
130
|
100
|
66
|
|
|
3504
|
if (defined $self->{content_length} && ! $self->{content_length_remain}) { |
544
|
2
|
|
|
|
|
5
|
print " done. detaching.\n" if Perlbal::DEBUG >= 3; |
545
|
|
|
|
|
|
|
# order important: next_request detaches us from client, so |
546
|
|
|
|
|
|
|
# $client->close can't kill us |
547
|
2
|
|
|
|
|
13
|
$self->next_request; |
548
|
|
|
|
|
|
|
$client->write(sub { |
549
|
2
|
|
|
2
|
|
100
|
$client->backend_finished; |
550
|
2
|
|
|
|
|
20
|
}); |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
sub may_reproxy { |
555
|
19
|
|
|
19
|
0
|
65
|
my Perlbal::BackendHTTP $self = shift; |
556
|
19
|
|
|
|
|
45
|
my Perlbal::Service $svc = $self->{service}; |
557
|
19
|
50
|
|
|
|
55
|
return 0 unless $svc; |
558
|
19
|
|
|
|
|
95
|
return $svc->{enable_reproxy}; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
# Backend |
562
|
|
|
|
|
|
|
sub event_read { |
563
|
280
|
|
|
280
|
1
|
4361413
|
my Perlbal::BackendHTTP $self = shift; |
564
|
280
|
|
|
|
|
516
|
print "Backend $self is readable!\n" if Perlbal::DEBUG >= 2; |
565
|
|
|
|
|
|
|
|
566
|
280
|
100
|
|
|
|
1155
|
return $self->event_read_waiting_options if $self->{waiting_options}; |
567
|
|
|
|
|
|
|
|
568
|
279
|
|
|
|
|
1226
|
my Perlbal::ClientProxy $client = $self->{client}; |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# with persistent connections, sometimes we have a backend and |
571
|
|
|
|
|
|
|
# no client, and backend becomes readable, either to signal |
572
|
|
|
|
|
|
|
# to use the end of the stream, or because a bad request error, |
573
|
|
|
|
|
|
|
# which I can't totally understand. in any case, we have |
574
|
|
|
|
|
|
|
# no client so all we can do is close this backend. |
575
|
279
|
50
|
|
|
|
774
|
return $self->close('read_with_no_client') unless $client; |
576
|
|
|
|
|
|
|
|
577
|
279
|
100
|
|
|
|
1958
|
unless ($self->{res_headers}) { |
578
|
150
|
50
|
|
|
|
1308
|
return unless $self->read_response_headers; |
579
|
150
|
|
|
|
|
919
|
return $self->handle_response; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
# if our client's behind more than the max limit, stop buffering |
583
|
129
|
50
|
|
|
|
1205
|
if ($client->too_far_behind_backend) { |
584
|
0
|
|
|
|
|
0
|
$self->watch_read(0); |
585
|
0
|
|
|
|
|
0
|
$client->{backend_stalled} = 1; |
586
|
0
|
|
|
|
|
0
|
return; |
587
|
|
|
|
|
|
|
} |
588
|
|
|
|
|
|
|
|
589
|
129
|
|
|
|
|
1449
|
my $bref = $self->read(BACKEND_READ_SIZE); |
590
|
|
|
|
|
|
|
|
591
|
129
|
50
|
|
|
|
3707
|
if (defined $bref) { |
592
|
129
|
|
|
|
|
470
|
$client->write($bref); |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
# HTTP/1.0 keep-alive support to backend. we just count bytes |
595
|
|
|
|
|
|
|
# until we hit the end, then we know we can send another |
596
|
|
|
|
|
|
|
# request on this connection |
597
|
129
|
50
|
|
|
|
568
|
if ($self->{content_length}) { |
598
|
129
|
|
|
|
|
357
|
$self->{content_length_remain} -= length($$bref); |
599
|
129
|
100
|
|
|
|
606
|
if (! $self->{content_length_remain}) { |
600
|
|
|
|
|
|
|
# order important: next_request detaches us from client, so |
601
|
|
|
|
|
|
|
# $client->close can't kill us |
602
|
128
|
|
|
|
|
580
|
$self->next_request; |
603
|
128
|
|
|
128
|
|
920
|
$client->write(sub { $client->backend_finished; }); |
|
128
|
|
|
|
|
7518
|
|
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
} |
606
|
129
|
|
|
|
|
1303
|
return; |
607
|
|
|
|
|
|
|
} else { |
608
|
|
|
|
|
|
|
# backend closed |
609
|
0
|
|
|
|
|
0
|
print "Backend $self is done; closing...\n" if Perlbal::DEBUG >= 1; |
610
|
|
|
|
|
|
|
|
611
|
0
|
|
|
|
|
0
|
$client->backend(undef); # disconnect ourselves from it |
612
|
0
|
|
|
|
|
0
|
$self->{client} = undef; # .. and it from us |
613
|
0
|
|
|
|
|
0
|
$self->close('backend_disconnect'); # close ourselves |
614
|
|
|
|
|
|
|
|
615
|
0
|
|
|
0
|
|
0
|
$client->write(sub { $client->backend_finished; }); |
|
0
|
|
|
|
|
0
|
|
616
|
0
|
|
|
|
|
0
|
return; |
617
|
|
|
|
|
|
|
} |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
# if $initial is on, then don't increment use count |
621
|
|
|
|
|
|
|
sub next_request { |
622
|
150
|
|
|
150
|
0
|
406
|
my Perlbal::BackendHTTP $self = $_[0]; |
623
|
150
|
|
|
|
|
267
|
my $initial = $_[1]; |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
# don't allow this if we're closed |
626
|
150
|
50
|
|
|
|
599
|
return if $self->{closed}; |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
# set alive_time so reproxy can intelligently reuse this backend |
629
|
150
|
|
|
|
|
325
|
my $now = time(); |
630
|
150
|
|
|
|
|
692
|
$self->{alive_time} = $now; |
631
|
150
|
100
|
|
|
|
940
|
$NodeStats{$self->{ipport}}->{requests}++ unless $initial; |
632
|
150
|
|
|
|
|
428
|
$NodeStats{$self->{ipport}}->{lastresponse} = $now; |
633
|
|
|
|
|
|
|
|
634
|
150
|
|
|
|
|
336
|
my $hd = $self->{res_headers}; # response headers |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
# verify that we have keep-alive support. by passing $initial to res_keep_alive, |
637
|
|
|
|
|
|
|
# we signal that req_headers may be undef (if we just did an options request) |
638
|
150
|
100
|
|
|
|
1478
|
return $self->close('next_request_no_persist') |
639
|
|
|
|
|
|
|
unless $hd->res_keep_alive($self->{req_headers}, $initial); |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# and now see if we should closed based on the pool we're from |
642
|
135
|
50
|
66
|
|
|
1909
|
return $self->close('pool_requested_closure') |
643
|
|
|
|
|
|
|
if $self->{pool} && ! $self->{pool}->backend_should_live($self); |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
# we've been used |
646
|
135
|
100
|
|
|
|
448
|
$self->{use_count}++ unless $initial; |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
# service specific |
649
|
135
|
50
|
|
|
|
520
|
if (my Perlbal::Service $svc = $self->{service}) { |
650
|
|
|
|
|
|
|
# keep track of how many times we've been used, and don't |
651
|
|
|
|
|
|
|
# keep using this connection more times than the service |
652
|
|
|
|
|
|
|
# is configured for. |
653
|
135
|
50
|
33
|
|
|
695
|
if ($svc->{max_backend_uses} && ($self->{use_count} > $svc->{max_backend_uses})) { |
654
|
0
|
|
|
|
|
0
|
return $self->close('exceeded_max_uses'); |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
# if backend told us, keep track of when the backend |
659
|
|
|
|
|
|
|
# says it's going to boot us, so we don't use it within |
660
|
|
|
|
|
|
|
# a few seconds of that time |
661
|
135
|
100
|
100
|
|
|
480
|
if (($hd->header("Keep-Alive") || '') =~ /\btimeout=(\d+)/i) { |
662
|
15
|
|
|
|
|
69
|
$self->{disconnect_at} = $now + $1; |
663
|
|
|
|
|
|
|
} else { |
664
|
120
|
|
|
|
|
294
|
$self->{disconnect_at} = undef; |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
|
667
|
135
|
|
|
|
|
277
|
$self->{client} = undef; |
668
|
|
|
|
|
|
|
|
669
|
135
|
|
|
|
|
790
|
$self->state("bored"); |
670
|
135
|
|
|
|
|
842
|
$self->watch_write(0); |
671
|
|
|
|
|
|
|
|
672
|
135
|
|
|
|
|
2324
|
$self->{req_headers} = undef; |
673
|
135
|
|
|
|
|
823
|
$self->{res_headers} = undef; |
674
|
135
|
|
|
|
|
319
|
$self->{headers_string} = ""; |
675
|
135
|
|
|
|
|
347
|
$self->{req_headers} = undef; |
676
|
|
|
|
|
|
|
|
677
|
135
|
|
|
|
|
289
|
$self->{read_size} = 0; |
678
|
135
|
|
|
|
|
361
|
$self->{content_length_remain} = undef; |
679
|
135
|
|
|
|
|
337
|
$self->{content_length} = undef; |
680
|
135
|
|
|
|
|
247
|
$self->{buffered_upload_mode} = 0; |
681
|
|
|
|
|
|
|
|
682
|
135
|
|
|
|
|
1471
|
$self->{reportto}->register_boredom($self); |
683
|
135
|
|
|
|
|
466
|
return; |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
# Backend: bad connection to backend |
687
|
|
|
|
|
|
|
sub event_err { |
688
|
1
|
|
|
1
|
1
|
4
|
my Perlbal::BackendHTTP $self = shift; |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
# FIXME: we get this after backend is done reading and we disconnect, |
691
|
|
|
|
|
|
|
# hence the misc checks below for $self->{client}. |
692
|
|
|
|
|
|
|
|
693
|
1
|
|
|
|
|
2
|
print "BACKEND event_err\n" if |
694
|
|
|
|
|
|
|
Perlbal::DEBUG >= 2; |
695
|
|
|
|
|
|
|
|
696
|
1
|
50
|
|
|
|
5
|
if ($self->{client}) { |
697
|
|
|
|
|
|
|
# request already sent to backend, then an error occurred. |
698
|
|
|
|
|
|
|
# we don't want to duplicate POST requests, so for now |
699
|
|
|
|
|
|
|
# just fail |
700
|
|
|
|
|
|
|
# TODO: if just a GET request, retry? |
701
|
0
|
|
|
|
|
0
|
$self->{client}->close('backend_error'); |
702
|
0
|
|
|
|
|
0
|
$self->close('error'); |
703
|
0
|
|
|
|
|
0
|
return; |
704
|
|
|
|
|
|
|
} |
705
|
|
|
|
|
|
|
|
706
|
1
|
50
|
33
|
|
|
6
|
if ($self->{state} eq "connecting" || |
707
|
|
|
|
|
|
|
$self->{state} eq "verifying_backend") { |
708
|
|
|
|
|
|
|
# then tell the service manager that this connection |
709
|
|
|
|
|
|
|
# failed, so it can spawn a new one and note the dead host |
710
|
1
|
|
|
|
|
7
|
$self->{reportto}->note_bad_backend_connect($self, 1); |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
# close ourselves first |
714
|
1
|
|
|
|
|
4
|
$self->close("error"); |
715
|
|
|
|
|
|
|
} |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
# Backend |
718
|
|
|
|
|
|
|
sub event_hup { |
719
|
0
|
|
|
0
|
1
|
|
my Perlbal::BackendHTTP $self = shift; |
720
|
0
|
|
|
|
|
|
print "HANGUP for $self\n" if Perlbal::DEBUG; |
721
|
0
|
|
|
|
|
|
$self->close("after_hup"); |
722
|
|
|
|
|
|
|
} |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
sub as_string { |
725
|
0
|
|
|
0
|
1
|
|
my Perlbal::BackendHTTP $self = shift; |
726
|
|
|
|
|
|
|
|
727
|
0
|
|
|
|
|
|
my $ret = $self->SUPER::as_string; |
728
|
0
|
0
|
|
|
|
|
my $name = $self->{sock} ? getsockname($self->{sock}) : undef; |
729
|
0
|
0
|
|
|
|
|
my $lport = $name ? (Socket::sockaddr_in($name))[0] : undef; |
730
|
0
|
0
|
|
|
|
|
$ret .= ": localport=$lport" if $lport; |
731
|
0
|
0
|
|
|
|
|
if (my Perlbal::ClientProxy $cp = $self->{client}) { |
732
|
0
|
|
|
|
|
|
$ret .= "; client=$cp->{fd}"; |
733
|
|
|
|
|
|
|
} |
734
|
0
|
|
|
|
|
|
$ret .= "; uses=$self->{use_count}; $self->{state}"; |
735
|
0
|
0
|
0
|
|
|
|
if (defined $self->{service} && $self->{service}->{verify_backend}) { |
736
|
0
|
|
|
|
|
|
$ret .= "; has_attention="; |
737
|
0
|
0
|
|
|
|
|
$ret .= $self->{has_attention} ? 'yes' : 'no'; |
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
|
740
|
0
|
|
|
|
|
|
return $ret; |
741
|
|
|
|
|
|
|
} |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
sub die_gracefully { |
744
|
|
|
|
|
|
|
# see if we need to die |
745
|
0
|
|
|
0
|
0
|
|
my Perlbal::BackendHTTP $self = shift; |
746
|
0
|
0
|
|
|
|
|
$self->close('graceful_death') if $self->state eq 'bored'; |
747
|
|
|
|
|
|
|
} |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
sub DESTROY { |
750
|
0
|
|
|
0
|
|
|
Perlbal::objdtor($_[0]); |
751
|
0
|
|
|
|
|
|
$_[0]->SUPER::DESTROY; |
752
|
|
|
|
|
|
|
} |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
1; |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
# Local Variables: |
757
|
|
|
|
|
|
|
# mode: perl |
758
|
|
|
|
|
|
|
# c-basic-indent: 4 |
759
|
|
|
|
|
|
|
# indent-tabs-mode: nil |
760
|
|
|
|
|
|
|
# End: |