Changeset 11728

Show
Ignore:
Timestamp:
05/17/08 13:51:04 (5 years ago)
Author:
daisuke
Message:

work under progress...

Location:
lang/perl/Moobal/trunk/lib
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • lang/perl/Moobal/trunk/lib/Moobal.pm

    r11724 r11728  
    3232); 
    3333 
     34has 'config_file' => ( 
     35    is => 'rw', 
     36    isa => 'Str', # coerce from/to Path::Class? 
     37); 
     38 
    3439has 'hooks' => ( 
    3540    is => 'rw', 
     
    5257); 
    5358 
     59has 'services' => ( 
     60    is => 'rw', 
     61    isa => 'HashRef', 
     62    required => 1, 
     63    default => sub { +{} }, 
     64); 
     65 
     66has 'pools' => ( 
     67    is => 'rw', 
     68    isa => 'HashRef', 
     69    required => 1, 
     70    default => sub { +{} }, 
     71); 
     72 
    5473after 'load_plugin' => sub { 
    5574    my ($self, $plugin) = @_; 
     
    6281    my $self = $next->(@args); 
    6382    $self->load_plugin('Core'); 
     83 
     84    $self->loader->parse_file( $self, file => $self->config_file ); 
    6485    return $self; 
    6586}; 
     
    131152} 
    132153 
    133  
     154sub apply_config 
     155{ 
     156    my ($self, $ctxt) = @_; 
     157 
     158    # XXX Refine later... for now we just suck all the services and 
     159    # pools from $ctxt 
     160 
     161    # XXX I think we need to "merge" the old pool/service with the 
     162    # new ones, instead of overwriting them 
     163 
     164    my $pools    = $ctxt->pools; 
     165    my $services = $ctxt->services; 
     166 
     167    $self->pools( $pools );  
     168    $self->services( $services ); 
     169 
     170    foreach my $service (values %$services) { 
     171        $service->start(); 
     172    } 
     173} 
    134174 
    135175 
  • lang/perl/Moobal/trunk/lib/Moobal/CLI/Command/Start.pm

    r11724 r11728  
    1717    my ($self, $opt, $args) = @_; 
    1818 
    19     my $moobal = Moobal->new(config => $self->config); 
     19    my $moobal = Moobal->new(config_file => $self->config); 
    2020    $moobal->run; 
    2121} 
  • lang/perl/Moobal/trunk/lib/Moobal/ConfigLoader.pm

    r11724 r11728  
    4747    while (my $line = <$fh>) { 
    4848        $line =~ s/\$(\w+)/$ENV{$1}/g; 
    49 warn "Doing $line"; 
    5049        return 0 unless  
    5150            $self->run_manage_command($c, $line); 
    5251    } 
     52 
     53    # Now at this point we should be done parsing the file, and 
     54    # should have populated $self->context accordingly.  
     55 
     56    # We only ask $c (the app) to apply the configuration at this 
     57    # point, when we know that the config file didn't have any errors. 
     58 
     59    $c->apply_config( $self->context ); 
     60 
    5361    return 1; 
    5462} 
  • lang/perl/Moobal/trunk/lib/Moobal/Service.pm

    r11724 r11728  
    2727); 
    2828 
     29has 'config' => ( 
     30    is => 'rw', 
     31    isa => 'HashRef', 
     32    default => sub { +{} } 
     33); 
     34 
    2935has 'enabled' => ( 
    3036    is => 'rw', 
     
    3440); 
    3541 
     42has 'running' => ( 
     43    is => 'rw', 
     44    isa => 'Bool', 
     45    required => 1, 
     46    default => 0, 
     47); 
     48 
    3649has 'pool' => ( 
    3750    is => 'rw', 
     
    4760sub disable { shift->enabled(0) } 
    4861 
     62sub start { 
     63    my $self = shift; 
     64 
     65    if ($self->running) { 
     66        $mc && $mc->err("service @{[$self->name]} is already running"); 
     67        return 0; 
     68    } 
     69 
     70    my $listener; 
     71 
     72    # create UDP upload tracker listener 
     73    if ($self->role eq "upload_tracker") { 
     74        # TODO Later 
     75        $listener = Moobal::UploadListener->new($self->{listen}, $self); 
     76    } 
     77 
     78    # create TCP listening socket 
     79    if (! $listener && $self->{listen}) { 
     80        my %sslopts; 
     81        if ($self->{enable_ssl}) { 
     82            # TODO: Later; 
     83            $sselopts = ( 
     84                SSL_key_file    => $self->{ssl_key_file}, 
     85                SSL_cert_file   => $self->{ssl_cert_file}, 
     86                SSL_cipher_list => $self->{ssl_cipher_list}, 
     87            ); 
     88            return $mc->err("IO::Socket:SSL (0.97+) not available.  Can't do SSL.") unless eval "use IO::Socket::SSL 0.97 (); 1;"; 
     89            return $mc->err("SSL key file ($self->{ssl_key_file}) doesn't exist")   unless -f $self->{ssl_key_file}; 
     90            return $mc->err("SSL cert file ($self->{ssl_cert_file}) doesn't exist") unless -f $self->{ssl_cert_file}; 
     91        } 
     92 
     93        my $tl = Moobal::Listener::TCP->new( 
     94            service  => $self, 
     95            hostport => $self->{listen}, # いらねーじゃんw 
     96            sslopts  => \%sslopts, 
     97        ); 
     98 
     99        unless ($tl) { 
     100            $mc && $mc->err("Can't start service '$self->{name}' on $self->{listen}: $Moobal::last_error"); 
     101            return 0; 
     102        } 
     103 
     104        $listener = $tl; 
     105    } 
     106 
     107    if (! $listener ) { 
     108        # if we failed to start it, mark it as disabled 
     109        $self->listener( undef ); 
     110        $self->enabled( 0 ); 
     111        $self->running( 0 ); 
     112    } else { 
     113        $self->listener( $listener ); 
     114        $self->runnig( 1 ); 
     115    } 
     116 
     117    return 1; 
     118} 
     119 
    491201; 
    50121 
    51122__END__ 
    52123 
    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 
     126Moobal::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 
    143138our %PluginRoles; 
    144139 
    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 }; 
    603140sub autodoc_get_tunables { return $tunables; } 
    604141 
     
    1448985} 
    1449986 
    1450 # Service 
    1451 sub enable { 
    1452     my Moobal::Service $self; 
    1453     my $mc; 
    1454  
    1455     ($self, $mc) = @_; 
    1456  
    1457     if ($self->{enabled}) { 
    1458         $mc && $mc->err("service $self->{name} is already enabled"); 
    1459         return 0; 
    1460     } 
    1461  
    1462     my $listener; 
    1463  
    1464     # create UDP upload tracker listener 
    1465     if ($self->{role} eq "upload_tracker") { 
    1466         $listener = Moobal::UploadListener->new($self->{listen}, $self); 
    1467     } 
    1468  
    1469     # create TCP listening socket 
    1470     if (! $listener && $self->{listen}) { 
    1471         my $opts = {}; 
    1472         if ($self->{enable_ssl}) { 
    1473             $opts->{ssl} = { 
    1474                 SSL_key_file    => $self->{ssl_key_file}, 
    1475                 SSL_cert_file   => $self->{ssl_cert_file}, 
    1476                 SSL_cipher_list => $self->{ssl_cipher_list}, 
    1477             }; 
    1478             return $mc->err("IO::Socket:SSL (0.97+) not available.  Can't do SSL.") unless eval "use IO::Socket::SSL 0.97 (); 1;"; 
    1479             return $mc->err("SSL key file ($self->{ssl_key_file}) doesn't exist")   unless -f $self->{ssl_key_file}; 
    1480             return $mc->err("SSL cert file ($self->{ssl_cert_file}) doesn't exist") unless -f $self->{ssl_cert_file}; 
    1481         } 
    1482  
    1483         my $tl = Moobal::TCPListener->new($self->{listen}, $self, $opts); 
    1484         unless ($tl) { 
    1485             $mc && $mc->err("Can't start service '$self->{name}' on $self->{listen}: $Moobal::last_error"); 
    1486             return 0; 
    1487         } 
    1488         $listener = $tl; 
    1489     } 
    1490  
    1491     $self->{listener} = $listener; 
    1492     $self->{enabled}  = 1; 
    1493     return $mc ? $mc->ok : 1; 
    1494 } 
    1495987 
    1496988# Service