line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Perlbal::Plugin::SessionAffinity; |
2
|
|
|
|
|
|
|
$Perlbal::Plugin::SessionAffinity::VERSION = '0.110'; |
3
|
1
|
|
|
1
|
|
65697
|
use strict; |
|
1
|
|
|
|
|
13
|
|
|
1
|
|
|
|
|
30
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
25
|
|
5
|
|
|
|
|
|
|
# ABSTRACT: Sane session affinity (sticky sessions) for Perlbal |
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
589
|
use Perlbal; |
|
1
|
|
|
|
|
217028
|
|
|
1
|
|
|
|
|
41
|
|
8
|
1
|
|
|
1
|
|
9
|
use Hash::Util; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
11
|
|
9
|
1
|
|
|
1
|
|
550
|
use CGI::Cookie; |
|
1
|
|
|
|
|
7203
|
|
|
1
|
|
|
|
|
38
|
|
10
|
1
|
|
|
1
|
|
546
|
use MIME::Base64; |
|
1
|
|
|
|
|
704
|
|
|
1
|
|
|
|
|
70
|
|
11
|
1
|
|
|
1
|
|
8
|
use Digest::MD5 'md5'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
50
|
|
12
|
1
|
|
|
1
|
|
509
|
use Digest::SHA 'sha1_hex'; |
|
1
|
|
|
|
|
2601
|
|
|
1
|
|
|
|
|
2496
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
my $default_cookie_hdr = 'X-SERVERID'; |
15
|
|
|
|
|
|
|
my $cookie_hdr_sub = sub { encode_base64( md5( $_[0] ) ) }; |
16
|
|
|
|
|
|
|
my $salt = join q{}, map { $_ = rand 999; s/\.//; $_ } 1 .. 10; |
17
|
|
|
|
|
|
|
my $use_salt = 0; |
18
|
|
|
|
|
|
|
my $use_domain = 0; |
19
|
|
|
|
|
|
|
my $use_dynamic_cookie = 0; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub get_domain_from_req { |
22
|
0
|
|
|
0
|
0
|
0
|
my $req = shift; |
23
|
|
|
|
|
|
|
my $domain = ref $req eq 'Perlbal::XS::HTTPHeaders' ? |
24
|
|
|
|
|
|
|
$req->getHeader('host') : # XS version |
25
|
0
|
0
|
|
|
|
0
|
$req->{'headers'}{'host'}; # PP version |
26
|
|
|
|
|
|
|
|
27
|
0
|
|
|
|
|
0
|
return $domain; |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# get the ip and port of the requested backend from the cookie |
31
|
|
|
|
|
|
|
sub get_ip_port { |
32
|
0
|
|
|
0
|
1
|
0
|
my ( $svc, $req ) = @_; |
33
|
|
|
|
|
|
|
|
34
|
0
|
|
|
|
|
0
|
my $domain = get_domain_from_req($req); |
35
|
0
|
0
|
|
|
|
0
|
my $cookie_hdr = $use_dynamic_cookie ? |
36
|
|
|
|
|
|
|
$cookie_hdr_sub->($domain) : |
37
|
|
|
|
|
|
|
$default_cookie_hdr; |
38
|
|
|
|
|
|
|
|
39
|
0
|
|
|
|
|
0
|
my $cookie = $req->header('Cookie'); |
40
|
0
|
|
|
|
|
0
|
my %cookies = (); |
41
|
|
|
|
|
|
|
|
42
|
0
|
0
|
|
|
|
0
|
if ( defined $cookie ) { |
43
|
0
|
|
|
|
|
0
|
%cookies = CGI::Cookie->parse($cookie); |
44
|
|
|
|
|
|
|
|
45
|
0
|
0
|
|
|
|
0
|
if ( defined $cookies{$cookie_hdr} ) { |
46
|
0
|
|
0
|
|
|
0
|
my $id = $cookies{$cookie_hdr}->value || ''; |
47
|
0
|
|
|
|
|
0
|
my $backend = find_backend_by_id( $svc, $id ); |
48
|
|
|
|
|
|
|
|
49
|
0
|
0
|
|
|
|
0
|
ref $backend and return join ':', @{$backend}; |
|
0
|
|
|
|
|
0
|
|
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
0
|
|
|
|
|
0
|
return; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# create a domain ID |
57
|
|
|
|
|
|
|
sub create_domain_id { |
58
|
0
|
|
0
|
0
|
1
|
0
|
my $domain = shift || ''; |
59
|
0
|
|
|
|
|
0
|
my @nodes = @_; |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# the ID is determined by the specific server |
62
|
|
|
|
|
|
|
# that has the matching index for the domain |
63
|
0
|
|
|
|
|
0
|
my $index = domain_index( $domain, scalar @nodes ); |
64
|
0
|
|
|
|
|
0
|
my $node = join ':', @{ $nodes[$index] }; |
|
0
|
|
|
|
|
0
|
|
65
|
0
|
0
|
|
|
|
0
|
return sha1_hex( $use_salt ? $salt . $node : $node ); |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# create an id from ip and optional port |
69
|
|
|
|
|
|
|
sub create_id { |
70
|
0
|
|
|
0
|
1
|
0
|
my $ip = shift; |
71
|
0
|
|
0
|
|
|
0
|
my $port = shift || ''; |
72
|
0
|
0
|
|
|
|
0
|
my $str = $use_salt ? $salt . "$ip:$port" : "$ip:$port"; |
73
|
0
|
|
|
|
|
0
|
return sha1_hex($str); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# a nifty little trick: |
77
|
|
|
|
|
|
|
# we create a numeric value of the domain name |
78
|
|
|
|
|
|
|
# then we use that as a seed for the random function |
79
|
|
|
|
|
|
|
# then create a random number which is predictable |
80
|
|
|
|
|
|
|
# that is the index of the domain |
81
|
|
|
|
|
|
|
sub domain_index { |
82
|
200
|
|
|
200
|
1
|
229419
|
my $domain = shift; |
83
|
200
|
|
|
|
|
393
|
my $max = shift; |
84
|
200
|
|
|
|
|
342
|
my $seed = 0; |
85
|
|
|
|
|
|
|
|
86
|
200
|
|
|
|
|
703
|
foreach my $char ( split //, $domain ) { |
87
|
1500
|
|
|
|
|
2321
|
$seed += ord $char; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
200
|
|
|
|
|
602
|
return ( $seed % $max); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# using an sha1 checksum id, find the matching backend |
94
|
|
|
|
|
|
|
sub find_backend_by_id { |
95
|
0
|
|
|
0
|
1
|
|
my ( $svc, $id ) = @_; |
96
|
|
|
|
|
|
|
|
97
|
0
|
|
|
|
|
|
foreach my $backend ( @{ $svc->{'pool'}{'nodes'} } ) { |
|
0
|
|
|
|
|
|
|
98
|
0
|
|
|
|
|
|
my $backendid = create_id( @{$backend} ); |
|
0
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
|
100
|
0
|
0
|
|
|
|
|
if ( $backendid eq $id ) { |
101
|
0
|
|
|
|
|
|
return $backend; |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
0
|
|
|
|
|
|
return; |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# TODO: refactor this function |
109
|
|
|
|
|
|
|
sub find_backend_by_domain_id { |
110
|
0
|
|
|
0
|
1
|
|
my ( $svc, $id ) = @_; |
111
|
|
|
|
|
|
|
|
112
|
0
|
|
|
|
|
|
foreach my $backend ( @{ $svc->{'pool'}{'nodes'} } ) { |
|
0
|
|
|
|
|
|
|
113
|
0
|
|
|
|
|
|
my $backendid = create_id( @{$backend} ); |
|
0
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
|
115
|
0
|
0
|
|
|
|
|
if ( $backendid eq $id ) { |
116
|
0
|
|
|
|
|
|
return $backend; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
0
|
|
|
|
|
|
return; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub load { |
124
|
|
|
|
|
|
|
# the name of header in the cookie that stores the backend ID |
125
|
|
|
|
|
|
|
Perlbal::register_global_hook( |
126
|
|
|
|
|
|
|
'manage_command.affinity_cookie_header', sub { |
127
|
0
|
|
|
0
|
|
|
my $mc = shift->parse(qr/^\s*affinity_cookie_header\s+=\s+(.+)\s*$/, |
128
|
|
|
|
|
|
|
"usage: AFFINITY_COOKIE_HEADER = "); |
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
|
|
|
($default_cookie_hdr) = $mc->args; |
131
|
|
|
|
|
|
|
|
132
|
0
|
|
|
|
|
|
return $mc->ok; |
133
|
|
|
|
|
|
|
}, |
134
|
0
|
|
|
0
|
0
|
|
); |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Perlbal::register_global_hook( |
137
|
|
|
|
|
|
|
'manage_command.affinity_salt', sub { |
138
|
0
|
|
|
0
|
|
|
my $mc = shift->parse(qr/^\s*affinity_salt\s+=\s+(.+)\s*$/, |
139
|
|
|
|
|
|
|
"usage: AFFINITY_SALT = "); |
140
|
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
|
($salt) = $mc->args; |
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
|
return $mc->ok; |
144
|
|
|
|
|
|
|
}, |
145
|
0
|
|
|
|
|
|
); |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
Perlbal::register_global_hook( |
148
|
|
|
|
|
|
|
'manage_command.affinity_use_salt', sub { |
149
|
0
|
|
|
0
|
|
|
my $mc = shift->parse(qr/^\s*affinity_use_salt\s+=\s+(.+)\s*$/, |
150
|
|
|
|
|
|
|
"usage: AFFINITY_USE_SALT = "); |
151
|
|
|
|
|
|
|
|
152
|
0
|
|
|
|
|
|
my ($res) = $mc->args; |
153
|
0
|
0
|
0
|
|
|
|
if ( $res eq 'yes' || $res == 1 ) { |
|
|
0
|
0
|
|
|
|
|
154
|
0
|
|
|
|
|
|
$use_salt = 1; |
155
|
|
|
|
|
|
|
} elsif ( $res eq 'no' || $res == 0 ) { |
156
|
0
|
|
|
|
|
|
$use_salt = 0; |
157
|
|
|
|
|
|
|
} else { |
158
|
0
|
|
|
|
|
|
die qq"affinity_use_salt must be boolean (yes/no/1/0)"; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
0
|
|
|
|
|
|
return $mc->ok; |
162
|
|
|
|
|
|
|
}, |
163
|
0
|
|
|
|
|
|
); |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Perlbal::register_global_hook( |
166
|
|
|
|
|
|
|
'manage_command.affinity_use_domain', sub { |
167
|
0
|
|
|
0
|
|
|
my $mc = shift->parse(qr/^\s*affinity_use_domain\s+=\s+(.+)\s*$/, |
168
|
|
|
|
|
|
|
"usage: AFFINITY_USE_DOMAIN = "); |
169
|
|
|
|
|
|
|
|
170
|
0
|
|
|
|
|
|
my ($res) = $mc->args; |
171
|
0
|
0
|
0
|
|
|
|
if ( $res eq 'yes' || $res == 1 ) { |
|
|
0
|
0
|
|
|
|
|
172
|
0
|
|
|
|
|
|
$use_domain = 1; |
173
|
|
|
|
|
|
|
} elsif ( $res eq 'no' || $res == 0 ) { |
174
|
0
|
|
|
|
|
|
$use_domain = 0; |
175
|
|
|
|
|
|
|
} else { |
176
|
0
|
|
|
|
|
|
die qq"affinity_use_domain must be boolean (yes/no/1/0)"; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
|
return $mc->ok; |
180
|
|
|
|
|
|
|
}, |
181
|
0
|
|
|
|
|
|
); |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
Perlbal::register_global_hook( |
184
|
|
|
|
|
|
|
'manage_command.affinity_use_dynamic_cookie', sub { |
185
|
0
|
|
|
0
|
|
|
my $mc = shift->parse(qr/^\s*affinity_use_dynamic_cookie\s+=\s+(.+)\s*$/, |
186
|
|
|
|
|
|
|
"usage: AFFINITY_USE_DYNAMIC_COOKIE = "); |
187
|
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
my ($res) = $mc->args; |
189
|
0
|
0
|
0
|
|
|
|
if ( $res eq 'yes' || $res == 1 ) { |
|
|
0
|
0
|
|
|
|
|
190
|
0
|
|
|
|
|
|
$use_dynamic_cookie = 1; |
191
|
|
|
|
|
|
|
} elsif ( $res eq 'no' || $res == 0 ) { |
192
|
0
|
|
|
|
|
|
$use_dynamic_cookie = 0; |
193
|
|
|
|
|
|
|
} else { |
194
|
0
|
|
|
|
|
|
die qq"affinity_use_dynamic_cookie must be boolean (yes/no/1/0)"; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
0
|
|
|
|
|
|
return $mc->ok; |
198
|
|
|
|
|
|
|
}, |
199
|
0
|
|
|
|
|
|
); |
200
|
|
|
|
|
|
|
|
201
|
0
|
|
|
|
|
|
return 1; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub register { |
205
|
0
|
|
|
0
|
1
|
|
my ( $class, $gsvc ) = @_; |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
my $check_cookie = sub { |
208
|
0
|
|
|
0
|
|
|
my $client = shift; |
209
|
0
|
0
|
|
|
|
|
my $req = $client->{'req_headers'} or return 0; |
210
|
0
|
|
|
|
|
|
my $svc = $client->{'service'}; |
211
|
0
|
|
|
|
|
|
my $pool = $svc->{'pool'}; |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# make sure all nodes in this service have their own pool |
214
|
0
|
|
|
|
|
|
foreach my $node ( @{ $pool->{'nodes'} } ) { |
|
0
|
|
|
|
|
|
|
215
|
0
|
|
|
|
|
|
my ( $ip, $port ) = @{$node}; |
|
0
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# pool |
218
|
0
|
|
|
|
|
|
my $poolid = create_id( $ip, $port ); |
219
|
0
|
0
|
|
|
|
|
exists $Perlbal::pool{$poolid} and next; |
220
|
|
|
|
|
|
|
|
221
|
0
|
|
|
|
|
|
my $nodepool = Perlbal::Pool->new($poolid); |
222
|
0
|
|
|
|
|
|
$nodepool->add( $ip, $port ); |
223
|
0
|
|
|
|
|
|
$Perlbal::pool{$poolid} = $nodepool; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# service |
226
|
0
|
|
|
|
|
|
my $serviceid = "${poolid}_service"; |
227
|
0
|
0
|
|
|
|
|
exists $Perlbal::service{$serviceid} and next; |
228
|
|
|
|
|
|
|
|
229
|
0
|
|
|
|
|
|
my $nodeservice = Perlbal->create_service($serviceid); |
230
|
0
|
|
|
|
|
|
my $svc_role = $svc->{'role'}; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# role sets up constraints for the rest |
233
|
|
|
|
|
|
|
# so it goes first |
234
|
0
|
|
|
|
|
|
$nodeservice->set( role => $svc_role ); |
235
|
|
|
|
|
|
|
|
236
|
0
|
|
|
|
|
|
foreach my $tunable_name ( keys %{$Perlbal::Service::tunables} ) { |
|
0
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# skip role because we had already set it |
238
|
0
|
0
|
|
|
|
|
$tunable_name eq 'role' and next; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# persist_client_timeout is DEPRECATED |
241
|
|
|
|
|
|
|
# but not marked anywhere as deprecated. :( |
242
|
|
|
|
|
|
|
# (well, nowhere we can actually predictably inspect) |
243
|
0
|
0
|
|
|
|
|
$tunable_name eq 'persist_client_timeout' and next; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# we skip the pool because we're gonna set it to a specific one |
246
|
0
|
0
|
|
|
|
|
$tunable_name eq 'pool' and next; |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# make sure svc has value for this tunable |
249
|
|
|
|
|
|
|
# we use 'exists' first because if it's an unknown key |
250
|
|
|
|
|
|
|
# in a lock hash, it will crash with 'disallowed key' access |
251
|
0
|
0
|
0
|
|
|
|
exists $svc->{$tunable_name} && defined $svc->{$tunable_name} |
252
|
|
|
|
|
|
|
or next; |
253
|
|
|
|
|
|
|
|
254
|
0
|
|
|
|
|
|
my $tunable = $Perlbal::Service::tunables->{$tunable_name}; |
255
|
0
|
|
|
|
|
|
my $role = $tunable->{'check_role'}; |
256
|
|
|
|
|
|
|
|
257
|
0
|
0
|
0
|
|
|
|
if ( $role eq '*' || $role eq $svc_role ) { |
258
|
0
|
|
|
|
|
|
my $value = ref $svc->{$tunable_name}; |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# It might be an arrayref |
261
|
|
|
|
|
|
|
$value eq 'ARRAY' |
262
|
0
|
0
|
|
|
|
|
and $value = join '', @{$value}; |
|
0
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
|
264
|
0
|
|
|
|
|
|
$nodeservice->set( $tunable_name, $value ); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# restricted hashes are stupid |
269
|
|
|
|
|
|
|
# so we have to use the API to add them |
270
|
0
|
|
|
|
|
|
foreach my $hook_name ( keys %{ $svc->{'hooks'} } ) { |
|
0
|
|
|
|
|
|
|
271
|
0
|
|
|
|
|
|
foreach my $set ( @{ $svc->{'hooks'}{$hook_name} } ) { |
|
0
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
|
my ( $plugin, $sub ) = @{$set}; |
|
0
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
|
$nodeservice->register_hook( $plugin, $hook_name, $sub ); |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# add all the extra config and extra headers |
278
|
0
|
|
|
|
|
|
$nodeservice->{'extra_config'} = $svc->{'extra_config'}; |
279
|
0
|
|
|
|
|
|
$nodeservice->{'extra_headers'} = $svc->{'extra_headers'}; |
280
|
|
|
|
|
|
|
|
281
|
0
|
|
|
|
|
|
$nodeservice->set( pool => $poolid ); |
282
|
|
|
|
|
|
|
|
283
|
0
|
|
|
|
|
|
$Perlbal::service{$serviceid} = $nodeservice; |
284
|
|
|
|
|
|
|
} |
285
|
|
|
|
|
|
|
|
286
|
0
|
|
|
|
|
|
my $ip_port = get_ip_port( $svc, $req ); |
287
|
|
|
|
|
|
|
|
288
|
0
|
0
|
|
|
|
|
if ( ! $ip_port ) { |
289
|
0
|
0
|
|
|
|
|
$use_domain or return 0; |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# we're going to override whatever Perlbal found |
292
|
|
|
|
|
|
|
# because we only care about the domain |
293
|
0
|
|
|
|
|
|
my $domain = get_domain_from_req($req); |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
my @ordered_nodes = sort { |
296
|
0
|
|
|
|
|
|
( join ':', @{$a} ) cmp ( join ':', @{$b} ) |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
297
|
0
|
|
|
|
|
|
} @{ $svc->{'pool'}{'nodes'} }; |
|
0
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
|
299
|
0
|
|
|
|
|
|
my $id = create_domain_id( $domain, @ordered_nodes ); |
300
|
0
|
|
|
|
|
|
my $backend = find_backend_by_domain_id( $svc, $id ); |
301
|
0
|
|
|
|
|
|
$ip_port = join ':', @{$backend}; |
|
0
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
0
|
|
|
|
|
|
my ( $ip, $port ) = split /:/, $ip_port; |
305
|
0
|
|
|
|
|
|
my $req_pool_id = create_id( $ip, $port ); |
306
|
0
|
|
|
|
|
|
my $req_svc = $Perlbal::service{"${req_pool_id}_service"}; |
307
|
0
|
|
|
|
|
|
$client->{'service'} = $req_svc; |
308
|
|
|
|
|
|
|
|
309
|
0
|
|
|
|
|
|
return 0; |
310
|
0
|
|
|
|
|
|
}; |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
my $set_cookie = sub { |
313
|
0
|
|
|
0
|
|
|
my $backend = shift; # Perlbal::BackendHTTP |
314
|
|
|
|
|
|
|
|
315
|
0
|
0
|
|
|
|
|
defined $backend or return 0; |
316
|
|
|
|
|
|
|
|
317
|
0
|
|
|
|
|
|
my $res = $backend->{'res_headers'}; |
318
|
0
|
|
|
|
|
|
my $req = $backend->{'req_headers'}; |
319
|
0
|
|
|
|
|
|
my $svc = $backend->{'service'}; |
320
|
0
|
|
|
|
|
|
my $backend_id = create_id( split /:/, $backend->{'ipport'} ); |
321
|
0
|
|
|
|
|
|
my $domain = get_domain_from_req($req); |
322
|
0
|
0
|
|
|
|
|
my $cookie_hdr = $use_dynamic_cookie ? |
323
|
|
|
|
|
|
|
$cookie_hdr_sub->($domain) : |
324
|
|
|
|
|
|
|
$default_cookie_hdr; |
325
|
|
|
|
|
|
|
|
326
|
0
|
|
|
|
|
|
my %cookies = (); |
327
|
0
|
0
|
|
|
|
|
if ( my $cookie = $req->header('Cookie') ) { |
328
|
0
|
|
|
|
|
|
%cookies = CGI::Cookie->parse($cookie); |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
0
|
0
|
0
|
|
|
|
if ( ! defined $cookies{$cookie_hdr} || |
332
|
|
|
|
|
|
|
$cookies{$cookie_hdr}->value ne $backend_id ) { |
333
|
|
|
|
|
|
|
|
334
|
0
|
|
|
|
|
|
my $backend_cookie = CGI::Cookie->new( |
335
|
|
|
|
|
|
|
-name => $cookie_hdr, |
336
|
|
|
|
|
|
|
-value => $backend_id, |
337
|
|
|
|
|
|
|
); |
338
|
|
|
|
|
|
|
|
339
|
0
|
0
|
|
|
|
|
if ( defined $res->header('set-cookie') ) { |
340
|
0
|
|
|
|
|
|
my $value = $res->header('set-cookie') . |
341
|
|
|
|
|
|
|
"\r\nSet-Cookie: " . |
342
|
|
|
|
|
|
|
$backend_cookie->as_string; |
343
|
|
|
|
|
|
|
|
344
|
0
|
|
|
|
|
|
$res->header( 'Set-Cookie' => $value ); |
345
|
|
|
|
|
|
|
} else { |
346
|
0
|
|
|
|
|
|
$res->header( 'Set-Cookie' => $backend_cookie->as_string ); |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
0
|
|
|
|
|
|
return 0; |
351
|
0
|
|
|
|
|
|
}; |
352
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
|
$gsvc->register_hook( |
354
|
|
|
|
|
|
|
'SessionAffinity', 'start_proxy_request', $check_cookie, |
355
|
|
|
|
|
|
|
); |
356
|
|
|
|
|
|
|
|
357
|
0
|
|
|
|
|
|
$gsvc->register_hook( |
358
|
|
|
|
|
|
|
'SessionAffinity', 'backend_response_received', $set_cookie, |
359
|
|
|
|
|
|
|
); |
360
|
|
|
|
|
|
|
|
361
|
0
|
|
|
|
|
|
return 1; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
sub unregister { |
365
|
0
|
|
|
0
|
1
|
|
my ( $class, $svc ) = @_; |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
# TODO: are we using setters? |
368
|
0
|
|
|
|
|
|
$svc->unregister_hooks('SessionAffinity'); |
369
|
0
|
|
|
|
|
|
$svc->unregister_setters('SessionAffinity'); |
370
|
|
|
|
|
|
|
|
371
|
0
|
|
|
|
|
|
return 1; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
1; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
__END__ |