| 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: |