| 53 | | use fields ( |
| 54 | | 'name', |
| 55 | | 'role', # scalar: role type 'web_server', 'reverse_proxy', etc... |
| 56 | | 'enabled', # scalar: bool, whether we're enabled or not (enabled = listening) |
| 57 | | |
| 58 | | 'pool', # Moobal::Pool that we're using to allocate nodes if we're in proxy mode |
| 59 | | 'listener', # Moobal::TCPListener object, when enabled |
| 60 | | 'reproxy_cache', # Moobal::Cache object, when enabled |
| 61 | | |
| 62 | | # end-user tunables |
| 63 | | 'listen', # scalar IP:port of where we're listening for new connections |
| 64 | | 'docroot', # document root for webserver role |
| 65 | | 'dirindexing', # bool: direcotry indexing? (for webserver role) not async. |
| 66 | | 'index_files', # arrayref of filenames to try for index files |
| 67 | | 'enable_concatenate_get', # bool: if user can request concatenated files |
| 68 | | 'enable_put', # bool: whether PUT is supported |
| 69 | | 'max_put_size', # int: max size in bytes of a put file |
| 70 | | 'max_chunked_request_size', # int: max size in bytes of a chunked request (to be written to disk first) |
| 71 | | 'min_put_directory', # int: number of directories required to exist at beginning of URIs in put |
| 72 | | 'enable_delete', # bool: whether DELETE is supported |
| 73 | | 'high_priority_cookie', # cookie name to check if client can 'cut in line' and get backends faster |
| 74 | | 'high_priority_cookie_contents', # aforementioned cookie value must contain this substring |
| 75 | | 'backend_persist_cache', # scalar: max number of persistent backends to hold onto while no clients |
| 76 | | 'persist_client', # bool: persistent connections for clients |
| 77 | | 'persist_backend', # bool: persistent connections for backends |
| 78 | | 'verify_backend', # bool: get attention of backend before giving it clients (using OPTIONS) |
| 79 | | 'verify_backend_path', # path to check with the OPTIONS request (default *) |
| 80 | | 'max_backend_uses', # max requests to send per kept-alive backend (default 0 = unlimited) |
| 81 | | 'connect_ahead', # scalar: number of spare backends to connect to in advance all the time |
| 82 | | 'buffer_size', # int: specifies how much data a ClientProxy object should buffer from a backend |
| 83 | | 'buffer_size_reproxy_url', # int: same as above but for backends that are reproxying for us |
| 84 | | 'queue_relief_size', # int; number of outstanding standard priority |
| 85 | | # connections to activate pressure relief at |
| 86 | | 'queue_relief_chance', # int:0-100; % chance to take a standard priority |
| 87 | | # request when we're in pressure relief mode |
| 88 | | 'trusted_upstream_proxies', # Net::Netmask object containing netmasks for trusted upstreams |
| 89 | | 'always_trusted', # bool; if true, always trust upstreams |
| 90 | | 'blind_proxy', # bool: if true, do not modify X-Forwarded-For, X-Host, or X-Forwarded-Host headers |
| 91 | | 'enable_reproxy', # bool; if true, advertise that server will reproxy files and/or URLs |
| 92 | | 'reproxy_cache_maxsize', # int; maximum number of reproxy results to be cached. (0 is disabled and default) |
| 93 | | 'client_sndbuf_size', # int: bytes for SO_SNDBUF |
| 94 | | 'server_process' , # scalar: path to server process (executable) |
| 95 | | 'persist_client_timeout', # int: keep-alive timeout in seconds for clients (default is 30) |
| 96 | | |
| 97 | | # Internal state: |
| 98 | | 'waiting_clients', # arrayref of clients waiting for backendhttp conns |
| 99 | | 'waiting_clients_highpri', # arrayref of high-priority clients waiting for backendhttp conns |
| 100 | | 'waiting_clients_lowpri', # arrayref of low-priority clients waiting for backendhttp conns |
| 101 | | 'waiting_client_count', # number of clients waiting for backendds |
| 102 | | 'waiting_client_map' , # map of clientproxy fd -> 1 (if they're waiting for a conn) |
| 103 | | 'pending_connects', # hashref of "ip:port" -> $time (only one pending connect to backend at a time) |
| 104 | | 'pending_connect_count', # number of outstanding backend connects |
| 105 | | 'bored_backends', # arrayref of backends we've already connected to, but haven't got clients |
| 106 | | 'hooks', # hashref: hookname => [ [ plugin, ref ], [ plugin, ref ], ... ] |
| 107 | | 'plugins', # hashref: name => 1 |
| 108 | | 'plugin_order', # arrayref: name, name, name... |
| 109 | | 'plugin_setters', # hashref: { plugin_name => { key_name => coderef } } |
| 110 | | 'extra_config', # hashref: extra config options; name => values |
| 111 | | 'spawn_lock', # bool: if true, we're currently in spawn_backends |
| 112 | | 'extra_headers', # { insert => [ [ header, value ], ... ], remove => [ header, header, ... ], |
| 113 | | # set => [ [ header, value ], ... ] }; used in header management interface |
| 114 | | 'generation', # int; generation count so we can slough off backends from old pools |
| 115 | | 'backend_no_spawn', # { "ip:port" => 1 }; if on, spawn_backends will ignore this ip:port combo |
| 116 | | 'buffer_backend_connect', # 0 for of, else, number of bytes to buffer before we ask for a backend |
| 117 | | 'selector', # CODE ref, or undef, for role 'selector' services |
| 118 | | 'default_service', # Moobal::Service; name of a service a selector should default to |
| 119 | | 'buffer_uploads', # bool; enable/disable the buffered uploads to disk system |
| 120 | | 'buffer_uploads_path', # string; path to store buffered upload files |
| 121 | | 'buffer_upload_threshold_time', # int; buffer uploads estimated to take longer than this |
| 122 | | 'buffer_upload_threshold_size', # int; buffer uploads greater than this size (in bytes) |
| 123 | | 'buffer_upload_threshold_rate', # int; buffer uploads uploading at less than this rate (in bytes/sec) |
| 124 | | |
| 125 | | 'upload_status_listeners', # string: comma separated list of ip:port of UDP upload status receivers |
| 126 | | 'upload_status_listeners_sockaddr', # arrayref of sockaddrs (packed ip/port) |
| 127 | | |
| 128 | | 'enable_ssl', # bool: whether this service speaks SSL to the client |
| 129 | | 'ssl_key_file', # file: path to key pem file |
| 130 | | 'ssl_cert_file', # file: path to key pem file |
| 131 | | 'ssl_cipher_list', # OpenSSL cipher list string |
| 132 | | |
| 133 | | 'enable_error_retries', # bool: whether we should retry requests after errors |
| 134 | | 'error_retry_schedule', # string of comma-separated seconds (full or partial) to delay between retries |
| 135 | | 'latency', # int: milliseconds of latency to add to request |
| 136 | | |
| 137 | | # stats: |
| 138 | | '_stat_requests', # total requests to this service |
| 139 | | '_stat_cache_hits', # total requests to this service that were served via the reproxy-url cache |
| 140 | | ); |
| 141 | | |
| 142 | | # hash; 'role' => coderef to instantiate a client for this role |
| | 124 | =head1 NAME |
| | 125 | |
| | 126 | Moobal::Service - A Service |
| | 127 | |
| | 128 | =head1 SYNIPSIS |
| | 129 | |
| | 130 | Moobal::Service->new( |
| | 131 | role => 'webserver', |
| | 132 | config => { ... }, # arbitrary, service specific options |
| | 133 | ); |
| | 134 | |
| | 135 | =cut |
| | 136 | |
| | 137 | |
| 145 | | our $tunables = { |
| 146 | | |
| 147 | | 'role' => { |
| 148 | | des => "What type of service. One of 'reverse_proxy' for a service that load balances to a pool of backend webserver nodes, 'web_server' for a typical webserver', 'management' for a Moobal management interface (speaks both command-line or HTTP, auto-detected), or 'selector', for a virtual service that maps onto other services.", |
| 149 | | required => 1, |
| 150 | | |
| 151 | | check_type => sub { |
| 152 | | my ($self, $val, $errref) = @_; |
| 153 | | return 0 unless $val; |
| 154 | | return 1 if $val =~ /^(?:reverse_proxy|web_server|management|selector|upload_tracker)$/; |
| 155 | | return 1 if $PluginRoles{$val}; |
| 156 | | $$errref = "Role not valid for service $self->{name}"; |
| 157 | | return 0; |
| 158 | | }, |
| 159 | | check_role => '*', |
| 160 | | setter => sub { |
| 161 | | my ($self, $val, $set, $mc) = @_; |
| 162 | | my $rv = $set->(); |
| 163 | | $self->init; # now that service role is set |
| 164 | | return $rv; |
| 165 | | }, |
| 166 | | }, |
| 167 | | |
| 168 | | 'listen' => { |
| 169 | | check_role => "*", |
| 170 | | des => "The ip:port to listen on. For a service to work, you must either make it listen, or make another selector service map to a non-listening service.", |
| 171 | | check_type => ["regexp", qr/^\d+\.\d+\.\d+\.\d+:\d+$/, "Expecting IP:port of form a.b.c.d:port."], |
| 172 | | setter => sub { |
| 173 | | my ($self, $val, $set, $mc) = @_; |
| 174 | | |
| 175 | | # close/reopen listening socket |
| 176 | | if ($val ne ($self->{listen} || "") && $self->{enabled}) { |
| 177 | | $self->disable(undef, "force"); |
| 178 | | $self->{listen} = $val; |
| 179 | | $self->enable(undef); |
| 180 | | } |
| 181 | | |
| 182 | | return $set->(); |
| 183 | | }, |
| 184 | | }, |
| 185 | | |
| 186 | | 'backend_persist_cache' => { |
| 187 | | des => "The number of backend connections to keep alive on reserve while there are no clients.", |
| 188 | | check_type => "int", |
| 189 | | default => 2, |
| 190 | | check_role => "reverse_proxy", |
| 191 | | }, |
| 192 | | |
| 193 | | 'persist_client' => { |
| 194 | | des => "Whether to enable HTTP keep-alives to the end user.", |
| 195 | | default => 0, |
| 196 | | check_type => "bool", |
| 197 | | check_role => "*", |
| 198 | | }, |
| 199 | | |
| 200 | | 'persist_backend' => { |
| 201 | | des => "Whether to enable HTTP keep-alives to the backend webnodes. (Off by default, but highly recommended if Moobal will be the only client to your backends. If not, beware that Moobal will hog the connections, starving other clients.)", |
| 202 | | default => 0, |
| 203 | | check_type => "bool", |
| 204 | | check_role => "reverse_proxy", |
| 205 | | }, |
| 206 | | |
| 207 | | 'verify_backend' => { |
| 208 | | des => "Whether Moobal should send a quick OPTIONS request to the backends before sending an actual client request to them. If your backend is Apache or some other process-based webserver, this is HIGHLY recommended. All too often a loaded backend box will reply to new TCP connections, but it's the kernel's TCP stack Moobal is talking to, not an actual Apache process yet. Using this option reduces end-user latency a ton on loaded sites.", |
| 209 | | default => 0, |
| 210 | | check_type => "bool", |
| 211 | | check_role => "reverse_proxy", |
| 212 | | }, |
| 213 | | |
| 214 | | 'verify_backend_path' => { |
| 215 | | des => "What path the OPTIONS request sent by verify_backend should use. Default is '*'.", |
| 216 | | default => '*', |
| 217 | | check_role => "reverse_proxy", |
| 218 | | }, |
| 219 | | |
| 220 | | 'max_backend_uses' => { |
| 221 | | check_role => "reverse_proxy", |
| 222 | | des => "The max number of requests to be made on a single persistent backend connection before releasing the connection. The default value of 0 means no limit, and the connection will only be discarded once the backend asks it to be, or when Moobal is sufficiently idle.", |
| 223 | | default => 0, |
| 224 | | }, |
| 225 | | |
| 226 | | 'max_put_size' => { |
| 227 | | default => 0, # no limit |
| 228 | | des => "The maximum content-length that will be accepted for a PUT request, if enable_put is on. Default value of 0 means no limit.", |
| 229 | | check_type => "size", |
| 230 | | check_role => "web_server", |
| 231 | | }, |
| 232 | | |
| 233 | | 'max_chunked_request_size' => { |
| 234 | | default => 209715200, # 200 MB. (0: no limit) |
| 235 | | des => "The maximum size that will be accepted for a chunked request. Default is 200MB (which is written to disk, buffered uploads must be on). A value of 0 means no limit.", |
| 236 | | check_type => "size", |
| 237 | | check_role => "*", |
| 238 | | }, |
| 239 | | |
| 240 | | 'buffer_size' => { |
| 241 | | des => "How much we'll ahead of a client we'll get while copying from a backend to a client. If a client gets behind this much, we stop reading from the backend for a bit.", |
| 242 | | default => "256k", |
| 243 | | check_type => "size", |
| 244 | | check_role => "reverse_proxy", |
| 245 | | }, |
| 246 | | |
| 247 | | 'buffer_size_reproxy_url' => { |
| 248 | | des => "How much we'll get ahead of a client we'll get while copying from a reproxied URL to a client. If a client gets behind this much, we stop reading from the reproxied URL for a bit. The default is lower than the regular buffer_size (50k instead of 256k) because it's assumed that you're only reproxying to large files on event-based webservers, which are less sensitive to many open connections, whereas the 256k buffer size is good for keeping heavy process-based free of slow clients.", |
| 249 | | default => "50k", |
| 250 | | check_type => "size", |
| 251 | | check_role => "reverse_proxy", |
| 252 | | }, |
| 253 | | |
| 254 | | 'queue_relief_size' => { |
| 255 | | default => 0, |
| 256 | | check_type => "int", |
| 257 | | check_role => "reverse_proxy", |
| 258 | | }, |
| 259 | | |
| 260 | | 'queue_relief_chance' => { |
| 261 | | default => 0, |
| 262 | | check_type => sub { |
| 263 | | my ($self, $val, $errref) = @_; |
| 264 | | return 1 if $val =~ /^\d+$/ && $val >= 0 && $val <= 100; |
| 265 | | $$errref = "Expecting integer value between 0 and 100, inclusive."; |
| 266 | | return 0; |
| 267 | | }, |
| 268 | | check_role => "reverse_proxy", |
| 269 | | }, |
| 270 | | |
| 271 | | 'buffer_backend_connect' => { |
| 272 | | des => "How much content-body (POST/PUT/etc) data we read from a client before we start sending it to a backend web node. If 'buffer_uploads' is enabled, this value is used to determine how many bytes are read before Moobal makes a determination on whether or not to spool the upload to disk.", |
| 273 | | default => '100k', |
| 274 | | check_type => "size", |
| 275 | | check_role => "reverse_proxy", |
| 276 | | }, |
| 277 | | |
| 278 | | 'docroot' => { |
| 279 | | des => "Directory root for web server.", |
| 280 | | |
| 281 | | check_role => "web_server", |
| 282 | | val_modify => sub { my $valref = shift; $$valref =~ s!/$!!; }, |
| 283 | | check_type => sub { |
| 284 | | my ($self, $val, $errref) = @_; |
| 285 | | #FIXME: require absolute paths? |
| 286 | | return 1 if $val && -d $val; |
| 287 | | $$errref = "Directory not found for service $self->{name}"; |
| 288 | | return 0; |
| 289 | | }, |
| 290 | | }, |
| 291 | | |
| 292 | | 'enable_put' => { |
| 293 | | des => "Enable HTTP PUT requests.", |
| 294 | | default => 0, |
| 295 | | check_role => "web_server", |
| 296 | | check_type => "bool", |
| 297 | | }, |
| 298 | | |
| 299 | | 'enable_delete' => { |
| 300 | | des => "Enable HTTP DELETE requests.", |
| 301 | | default => 0, |
| 302 | | check_role => "web_server", |
| 303 | | check_type => "bool", |
| 304 | | }, |
| 305 | | |
| 306 | | 'enable_reproxy' => { |
| 307 | | des => "Enable 'reproxying' (end-user-transparent internal redirects) to either local files or other URLs. When enabled, the backend servers in the pool that this service is configured for will have access to tell this Moobal instance to serve any local readable file, or connect to any other URL that this Moobal can connect to. Only enable this if you trust the backend web nodes.", |
| 308 | | default => 0, |
| 309 | | check_role => "reverse_proxy", |
| 310 | | check_type => "bool", |
| 311 | | }, |
| 312 | | |
| 313 | | 'reproxy_cache_maxsize' => { |
| 314 | | des => "Set the maximum number of cached reproxy results (X-REPROXY-CACHE-FOR) that may be kept in the service cache. These cached requests take up about 1.25KB of ram each (on Linux x86), but will vary with usage. Moobal still starts with 0 in the cache and will grow over time. Be careful when adjusting this and watch your ram usage like a hawk.", |
| 315 | | default => 0, |
| 316 | | check_role => "reverse_proxy", |
| 317 | | check_type => "int", |
| 318 | | setter => sub { |
| 319 | | my ($self, $val, $set, $mc) = @_; |
| 320 | | if ($val) { |
| 321 | | $self->{reproxy_cache} ||= Moobal::Cache->new(maxsize => 1); |
| 322 | | $self->{reproxy_cache}->set_maxsize($val); |
| 323 | | } else { |
| 324 | | $self->{reproxy_cache} = undef; |
| 325 | | } |
| 326 | | return $mc->ok; |
| 327 | | }, |
| 328 | | }, |
| 329 | | |
| 330 | | 'upload_status_listeners' => { |
| 331 | | des => "Comma separated list of hosts in form 'a.b.c.d:port' which will receive UDP upload status packets no faster than once a second per HTTP request (PUT/POST) from clients that have requested an upload status bar, which they request by appending the URL get argument ?client_up_session=[xxxxxx] where xxxxx is 5-50 'word' characters (a-z, A-Z, 0-9, underscore).", |
| 332 | | default => "", |
| 333 | | check_role => "reverse_proxy", |
| 334 | | check_type => sub { |
| 335 | | my ($self, $val, $errref) = @_; |
| 336 | | my @packed; |
| 337 | | foreach my $ipa (grep { $_ } split(/\s*,\s*/, $val)) { |
| 338 | | unless ($ipa =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/) { |
| 339 | | $$errref = "Invalid UDP endpoint: \"$ipa\". Must be of form a.b.c.d:port"; |
| 340 | | return 0; |
| 341 | | } |
| 342 | | push @packed, scalar Socket::sockaddr_in($2, Socket::inet_aton($1)); |
| 343 | | } |
| 344 | | $self->{upload_status_listeners_sockaddr} = \@packed; |
| 345 | | return 1; |
| 346 | | }, |
| 347 | | }, |
| 348 | | |
| 349 | | 'min_put_directory' => { |
| 350 | | des => "If PUT requests are enabled, require this many levels of directories to already exist. If not, fail.", |
| 351 | | default => 0, # no limit |
| 352 | | check_role => "web_server", |
| 353 | | check_type => "int", |
| 354 | | }, |
| 355 | | |
| 356 | | 'dirindexing' => { |
| 357 | | des => "Show directory indexes when an HTTP request is for a directory. Warning: this is not an async operation, so will slow down Moobal on heavily loaded sites.", |
| 358 | | default => 0, |
| 359 | | check_role => "web_server", |
| 360 | | check_type => "bool", |
| 361 | | }, |
| 362 | | |
| 363 | | 'enable_concatenate_get' => { |
| 364 | | des => "Enable Moobal's multiple-files-in-one-request mode, where a client have use a comma-separated list of files to return, always in text/plain. Useful for webapps which have dozens/hundreds of tiny css/js files, and don't trust browsers/etc to do pipelining. Decreases overall roundtrip latency a bunch, but requires app to be modified to support it. See t/17-concat.t test for details.", |
| 365 | | default => 0, |
| 366 | | check_role => "web_server", |
| 367 | | check_type => "bool", |
| 368 | | }, |
| 369 | | |
| 370 | | 'connect_ahead' => { |
| 371 | | des => "How many extra backend connections we keep alive in addition to the current ones, in anticipation of new client connections.", |
| 372 | | default => 0, |
| 373 | | check_type => "int", |
| 374 | | check_role => "reverse_proxy", |
| 375 | | setter => sub { |
| 376 | | my ($self, $val, $set, $mc) = @_; |
| 377 | | my $rv = $set->(); |
| 378 | | $self->spawn_backends if $self->{enabled}; |
| 379 | | return $rv; |
| 380 | | }, |
| 381 | | }, |
| 382 | | |
| 383 | | 'always_trusted' => { |
| 384 | | des => "Whether to trust all incoming requests' X-Forwarded-For and related headers. Set to true only if you know that all incoming requests from your own proxy servers that clean/set those headers.", |
| 385 | | default => 0, |
| 386 | | check_type => "bool", |
| 387 | | check_role => "*", |
| 388 | | }, |
| 389 | | |
| 390 | | 'blind_proxy' => { |
| 391 | | des => "Flag to disable any modification of X-Forwarded-For, X-Host, and X-Forwarded-Host headers.", |
| 392 | | default => 0, |
| 393 | | check_type => "bool", |
| 394 | | check_role => "reverse_proxy", |
| 395 | | }, |
| 396 | | |
| 397 | | 'high_priority_cookie' => { |
| 398 | | des => "The cookie name to inspect to determine if the client goes onto the high-priority queue.", |
| 399 | | check_role => "reverse_proxy", |
| 400 | | }, |
| 401 | | |
| 402 | | 'high_priority_cookie_contents' => { |
| 403 | | des => "A string that the high_priority_cookie must contain to go onto the high-priority queue.", |
| 404 | | check_role => "reverse_proxy", |
| 405 | | }, |
| 406 | | |
| 407 | | 'trusted_upstream_proxies' => { |
| 408 | | des => "A Net::Netmask filter (e.g. 10.0.0.0/24, see Net::Netmask) that determines whether upstream clients are trusted or not, where trusted means their X-Forwarded-For/etc headers are not munged.", |
| 409 | | check_role => "*", |
| 410 | | check_type => sub { |
| 411 | | my ($self, $val, $errref) = @_; |
| 412 | | unless (my $loaded = eval { require Net::Netmask; 1; }) { |
| 413 | | $$errref = "Net::Netmask not installed"; |
| 414 | | return 0; |
| 415 | | } |
| 416 | | |
| 417 | | return 1 if $self->{trusted_upstream_proxies} = Net::Netmask->new2($val); |
| 418 | | $$errref = "Error defining trusted upstream proxies: " . Net::Netmask::errstr(); |
| 419 | | return 0; |
| 420 | | }, |
| 421 | | setter => sub { |
| 422 | | my ($self, $val, $set, $mc) = @_; |
| 423 | | # Do nothing here, we don't want the default setter because we've |
| 424 | | # already set the value in the type_check step. |
| 425 | | return $mc->ok; |
| 426 | | }, |
| 427 | | }, |
| 428 | | |
| 429 | | 'index_files' => { |
| 430 | | check_role => "web_server", |
| 431 | | default => "index.html", |
| 432 | | des => "Comma-seperated list of filenames to load when a user visits a directory URL, listed in order of preference.", |
| 433 | | setter => sub { |
| 434 | | my ($self, $val, $set, $mc) = @_; |
| 435 | | $self->{index_files} = [ split(/[\s,]+/, $val) ]; |
| 436 | | return $mc->ok; |
| 437 | | }, |
| 438 | | }, |
| 439 | | |
| 440 | | 'default_service' => { |
| 441 | | des => "Name of previously-created service to default requests that aren't matched by a selector plugin to.", |
| 442 | | check_role => "selector", |
| 443 | | check_type => sub { |
| 444 | | my ($self, $val, $errref) = @_; |
| 445 | | |
| 446 | | my $svc = Moobal->service($val); |
| 447 | | unless ($svc) { |
| 448 | | $$errref = "Service '$svc' not found"; |
| 449 | | return 0; |
| 450 | | } |
| 451 | | |
| 452 | | $self->{default_service} = $svc; |
| 453 | | return 1; |
| 454 | | }, |
| 455 | | setter => sub { |
| 456 | | # override default so we don't set it to the text |
| 457 | | return $_[3]->ok; |
| 458 | | }, |
| 459 | | }, |
| 460 | | |
| 461 | | 'pool' => { |
| 462 | | des => "Name of previously-created pool object containing the backend nodes that this reverse proxy sends requests to.", |
| 463 | | check_role => "reverse_proxy", |
| 464 | | check_type => sub { |
| 465 | | my ($self, $val, $errref) = @_; |
| 466 | | my $pl = Moobal->pool($val); |
| 467 | | unless ($pl) { |
| 468 | | $$errref = "Pool '$val' not found"; |
| 469 | | return 0; |
| 470 | | } |
| 471 | | $self->{pool}->decrement_use_count if $self->{pool}; |
| 472 | | $self->{pool} = $pl; |
| 473 | | $self->{pool}->increment_use_count; |
| 474 | | $self->{generation}++; |
| 475 | | return 1; |
| 476 | | }, |
| 477 | | setter => sub { |
| 478 | | my ($self, $val, $set, $mc) = @_; |
| 479 | | # override the default, which is to set "pool" to the |
| 480 | | # stringified name of the pool, but we already set it in |
| 481 | | # the type-checking phase. instead, we do nothing here. |
| 482 | | return $mc->ok; |
| 483 | | }, |
| 484 | | }, |
| 485 | | |
| 486 | | 'server_process' => { |
| 487 | | des => "Executable which will be the HTTP server on stdin/stdout. (ALPHA, EXPERIMENTAL!)", |
| 488 | | check_role => "reverse_proxy", |
| 489 | | check_type => sub { |
| 490 | | my ($self, $val, $errref) = @_; |
| 491 | | #FIXME: require absolute paths? |
| 492 | | return 1 if $val && -x $val; |
| 493 | | $$errref = "Server process ($val) not executable."; |
| 494 | | return 0; |
| 495 | | }, |
| 496 | | }, |
| 497 | | |
| 498 | | 'persist_client_timeout' => { |
| 499 | | des => "Timeout in seconds for HTTP keep-alives to the end user (default is 30)", |
| 500 | | check_type => "int", |
| 501 | | default => 30, |
| 502 | | check_role => "*", |
| 503 | | }, |
| 504 | | |
| 505 | | 'buffer_uploads_path' => { |
| 506 | | des => "Directory root for storing files used to buffer uploads.", |
| 507 | | |
| 508 | | check_role => "reverse_proxy", |
| 509 | | val_modify => sub { my $valref = shift; $$valref =~ s!/$!!; }, |
| 510 | | check_type => sub { |
| 511 | | my ($self, $val, $errref) = @_; |
| 512 | | #FIXME: require absolute paths? |
| 513 | | return 1 if $val && -d $val; |
| 514 | | $$errref = "Directory ($val) not found for service $self->{name} (buffer_uploads_path)"; |
| 515 | | return 0; |
| 516 | | }, |
| 517 | | }, |
| 518 | | |
| 519 | | 'buffer_uploads' => { |
| 520 | | des => "Used to enable or disable the buffer uploads to disk system. If enabled, 'buffer_backend_connect' bytes worth of the upload will be stored in memory. At that point, the buffer upload thresholds will be checked to see if we should just send this upload to the backend, or if we should spool it to disk.", |
| 521 | | default => 0, |
| 522 | | check_role => "reverse_proxy", |
| 523 | | check_type => "bool", |
| 524 | | }, |
| 525 | | |
| 526 | | 'buffer_upload_threshold_time' => { |
| 527 | | des => "If an upload is estimated to take more than this number of seconds, it will be buffered to disk. Set to 0 to not check estimated time.", |
| 528 | | default => 5, |
| 529 | | check_role => "reverse_proxy", |
| 530 | | check_type => "int", |
| 531 | | }, |
| 532 | | |
| 533 | | 'buffer_upload_threshold_size' => { |
| 534 | | des => "If an upload is larger than this size in bytes, it will be buffered to disk. Set to 0 to not check size.", |
| 535 | | default => '250k', |
| 536 | | check_role => "reverse_proxy", |
| 537 | | check_type => "size", |
| 538 | | }, |
| 539 | | |
| 540 | | 'buffer_upload_threshold_rate' => { |
| 541 | | des => "If an upload is coming in at a rate less than this value in bytes per second, it will be buffered to disk. Set to 0 to not check rate.", |
| 542 | | default => 0, |
| 543 | | check_role => "reverse_proxy", |
| 544 | | check_type => "int", |
| 545 | | }, |
| 546 | | |
| 547 | | 'latency' => { |
| 548 | | des => "Forced latency (in milliseconds) to add to request.", |
| 549 | | default => 0, |
| 550 | | check_role => "selector", |
| 551 | | check_type => "int", |
| 552 | | }, |
| 553 | | |
| 554 | | 'enable_ssl' => { |
| 555 | | des => "Enable SSL to the client.", |
| 556 | | default => 0, |
| 557 | | check_type => "bool", |
| 558 | | check_role => "*", |
| 559 | | }, |
| 560 | | |
| 561 | | 'ssl_key_file' => { |
| 562 | | des => "Path to private key PEM file for SSL.", |
| 563 | | default => "certs/server-key.pem", |
| 564 | | check_type => "file_or_none", |
| 565 | | check_role => "*", |
| 566 | | }, |
| 567 | | |
| 568 | | 'ssl_cert_file' => { |
| 569 | | des => "Path to certificate PEM file for SSL.", |
| 570 | | default => "certs/server-cert.pem", |
| 571 | | check_type => "file_or_none", |
| 572 | | check_role => "*", |
| 573 | | }, |
| 574 | | |
| 575 | | 'ssl_cipher_list' => { |
| 576 | | des => "OpenSSL-style cipher list.", |
| 577 | | default => "ALL:!LOW:!EXP", |
| 578 | | check_role => "*", |
| 579 | | }, |
| 580 | | |
| 581 | | 'enable_error_retries' => { |
| 582 | | des => 'Whether Moobal should transparently retry requests to backends if a backend returns a 500 server error.', |
| 583 | | default => 0, |
| 584 | | check_type => "bool", |
| 585 | | check_role => "reverse_proxy", |
| 586 | | }, |
| 587 | | |
| 588 | | 'error_retry_schedule' => { |
| 589 | | des => 'String of comma-separated seconds (full or partial) to delay between retries. For example "0,2" would mean do at most two retries, the first zero seconds after the first failure, and the second 2 seconds after the 2nd failure. You probably don\'t need to modify the default value', |
| 590 | | default => '0,.25,.50,1,1,1,1,1', |
| 591 | | check_role => "reverse_proxy", |
| 592 | | }, |
| 593 | | |
| 594 | | 'client_sndbuf_size' => { |
| 595 | | des => "How large to set the client's socket SNDBUF.", |
| 596 | | default => 0, |
| 597 | | check_type => "size", |
| 598 | | check_role => '*', |
| 599 | | }, |
| 600 | | |
| 601 | | |
| 602 | | }; |