Perlbal

 view release on metacpan or  search on metacpan

lib/Perlbal/Service.pm  view on Meta::CPAN

######################################################################
# Service class
######################################################################
#
# Copyright 2004, Danga Interactive, Inc.
# Copyright 2005-2007, Six Apart, Ltd.
#

package Perlbal::Service;
use strict;
use warnings;
no  warnings qw(deprecated);

use Perlbal::BackendHTTP;
use Perlbal::Cache;
use Perlbal::Util;
use fields (
            'name',            # scalar: name of this service
            'role',            # scalar: role type 'web_server', 'reverse_proxy', etc...
            'enabled',         # scalar: bool, whether we're enabled or not (enabled = listening)

            'pool',            # Perlbal::Pool that we're using to allocate nodes if we're in proxy mode
            'listener',        # Perlbal::TCPListener object, when enabled
            'reproxy_cache',             # Perlbal::Cache object, when enabled

            # end-user tunables
            'listen',             # scalar IP:port of where we're listening for new connections
            'docroot',            # document root for webserver role
            'dirindexing',        # bool: directory indexing?  (for webserver role)  not async.
            'index_files',        # arrayref of filenames to try for index files
            'enable_concatenate_get',   # bool:  if user can request concatenated files
            'enable_put', # bool: whether PUT is supported
            'enable_md5', # bool: whether Content-MD5 is supported on PUT
            'max_put_size', # int: max size in bytes of a put file
            'max_chunked_request_size',  # int: max size in bytes of a chunked request (to be written to disk first)
            'min_put_directory', # int: number of directories required to exist at beginning of URIs in put
            'enable_delete', # bool: whether DELETE is supported
            'high_priority_cookie',          # cookie name to check if client can 'cut in line' and get backends faster
            'high_priority_cookie_contents', # aforementioned cookie value must contain this substring
            'backend_persist_cache',   # scalar: max number of persistent backends to hold onto while no clients
            'persist_client',  # bool: persistent connections for clients
            'persist_backend', # bool: persistent connections for backends
            'verify_backend',  # bool: get attention of backend before giving it clients (using OPTIONS)
            'verify_backend_path', # path to check with the OPTIONS request (default *)
            'max_backend_uses',  # max requests to send per kept-alive backend (default 0 = unlimited)
            'connect_ahead',           # scalar: number of spare backends to connect to in advance all the time
            'buffer_size', # int: specifies how much data a ClientProxy object should buffer from a backend
            'buffer_size_reproxy_url', # int: same as above but for backends that are reproxying for us
            'queue_relief_size', # int; number of outstanding standard priority
                                 # connections to activate pressure relief at
            'queue_relief_chance', # int:0-100; % chance to take a standard priority
                                   # request when we're in pressure relief mode
            'trusted_upstream_proxies', # Net::Netmask object containing netmasks for trusted upstreams
            'always_trusted', # bool; if true, always trust upstreams
            'blind_proxy', # bool: if true, do not modify X-Forwarded-For, X-Host, or X-Forwarded-Host headers
            'enable_reproxy', # bool; if true, advertise that server will reproxy files and/or URLs
            'reproxy_cache_maxsize', # int; maximum number of reproxy results to be cached. (0 is disabled and default)
            'client_sndbuf_size',    # int: bytes for SO_SNDBUF
            'server_process' ,       # scalar: path to server process (executable)
            'persist_client_idle_timeout',  # int: keep-alive timeout in seconds for clients (default is 30)
            'idle_timeout',                 # int: idle timeout outside of keep-alive time (default is 30)

            # Internal state:
            'waiting_clients',         # arrayref of clients waiting for backendhttp conns
            'waiting_clients_highpri', # arrayref of high-priority clients waiting for backendhttp conns
            'waiting_clients_lowpri',  # arrayref of low-priority clients waiting for backendhttp conns
            'waiting_client_count',    # number of clients waiting for backends
            'waiting_client_map'  ,    # map of clientproxy fd -> 1 (if they're waiting for a conn)
            'pending_connects',        # hashref of "ip:port" -> $time (only one pending connect to backend at a time)
            'pending_connect_count',   # number of outstanding backend connects
            'bored_backends',          # arrayref of backends we've already connected to, but haven't got clients
            'hooks',    # hashref: hookname => [ [ plugin, ref ], [ plugin, ref ], ... ]
            'plugins',  # hashref: name => 1
            'plugin_order', # arrayref: name, name, name...
            'plugin_setters', # hashref: { plugin_name => { key_name => coderef } }
            'extra_config', # hashref: extra config options; name => values
            'spawn_lock', # bool: if true, we're currently in spawn_backends
            'extra_headers', # { insert => [ [ header, value ], ... ], remove => [ header, header, ... ],
                             #   set => [ [ header, value ], ... ] }; used in header management interface
            'generation', # int; generation count so we can slough off backends from old pools
            'backend_no_spawn', # { "ip:port" => 1 }; if on, spawn_backends will ignore this ip:port combo
            'buffer_backend_connect', # 0 for of, else, number of bytes to buffer before we ask for a backend
            'selector',    # CODE ref, or undef, for role 'selector' services
            'default_service', # Perlbal::Service; name of a service a selector should default to
            'buffer_uploads', # bool; enable/disable the buffered uploads to disk system
            'buffer_uploads_path', # string; path to store buffered upload files
            'buffer_upload_threshold_time', # int; buffer uploads estimated to take longer than this
            'buffer_upload_threshold_size', # int; buffer uploads greater than this size (in bytes)
            'buffer_upload_threshold_rate', # int; buffer uploads uploading at less than this rate (in bytes/sec)

            'upload_status_listeners',  # string: comma separated list of ip:port of UDP upload status receivers
            'upload_status_listeners_sockaddr',  # arrayref of sockaddrs (packed ip/port)

            'enable_ssl',         # bool: whether this service speaks SSL to the client
            'ssl_key_file',       # file:  path to key pem file
            'ssl_cert_file',      # file:  path to key pem file
            'ssl_cipher_list',    # OpenSSL cipher list string
            'ssl_ca_path',        # directory:  path to certificates
            'ssl_verify_mode',    # int:  verification mode, see IO::Socket::SSL documentation 

            'enable_error_retries',  # bool: whether we should retry requests after errors
            'error_retry_schedule',  # string of comma-separated seconds (full or partial) to delay between retries
            'latency',               # int: milliseconds of latency to add to request
            'server_tokens',         # bool: whether to provide a "Server" header

            # stats:
            '_stat_requests',       # total requests to this service
            '_stat_cache_hits',     # total requests to this service that were served via the reproxy-url cache
            );

# hash; 'role' => coderef to instantiate a client for this role
our %PluginRoles;

# used by set_defaults
our $defaults = {};

lib/Perlbal/Service.pm  view on Meta::CPAN

                $self->{reproxy_cache} = undef;
            }
            return $mc->ok;
        },
    },

    'upload_status_listeners' => {
        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 ap...
        default => "",
        check_role => "reverse_proxy",
        check_type => sub {
            my ($self, $val, $errref) = @_;
            my @packed;
            foreach my $ipa (grep { $_ } split(/\s*,\s*/, $val)) {
                unless ($ipa =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/) {
                    $$errref = "Invalid UDP endpoint: \"$ipa\".  Must be of form a.b.c.d:port";
                    return 0;
                }
                push @packed, scalar Socket::sockaddr_in($2, Socket::inet_aton($1));
            }
            $self->{upload_status_listeners_sockaddr} = \@packed;
            return 1;
        },
    },

    'min_put_directory' => {
        des => "If PUT requests are enabled, require this many levels of directories to already exist.  If not, fail.",
        default => 0,   # no limit
        check_role => "web_server",
        check_type => "int",
    },

    'dirindexing' => {
        des => "Show directory indexes when an HTTP request is for a directory.  Warning:  this is not an async operation, so will slow down Perlbal on heavily loaded sites.",
        default => 0,
        check_role => "web_server",
        check_type => "bool",
    },

    'enable_concatenate_get' => {
        des => "Enable Perlbal'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 web apps which have dozens/hundreds of tiny css/js files, and don't trust browse...
        default => 0,
        check_role => "web_server",
        check_type => "bool",
    },

    'connect_ahead' => {
        des => "How many extra backend connections we keep alive in addition to the current ones, in anticipation of new client connections.",
        default => 0,
        check_type => "int",
        check_role => "reverse_proxy",
        setter => sub {
            my ($self, $val, $set, $mc) = @_;
            my $rv = $set->();
            $self->spawn_backends if $self->{enabled};
            return $rv;
        },
    },

    'always_trusted' => {
        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.",
        default => 0,
        check_type => "bool",
        check_role => "*",
    },

    'blind_proxy' => {
        des => "Flag to disable any modification of X-Forwarded-For, X-Host, and X-Forwarded-Host headers.",
        default => 0,
        check_type => "bool",
        check_role => "reverse_proxy",
    },

    'high_priority_cookie' => {
        des => "The cookie name to inspect to determine if the client goes onto the high-priority queue.",
        check_role => "reverse_proxy",
    },

    'high_priority_cookie_contents' => {
        des => "A string that the high_priority_cookie must contain to go onto the high-priority queue.",
        check_role => "reverse_proxy",
    },

    'trusted_upstream_proxies' => {
        des => "A comma separated list of Net::Netmask filters (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.",
        check_role => "*",
        check_type => sub {
            my ($self, $val, $errref) = @_;
            unless (my $loaded = eval { require Net::Netmask; 1; }) {
                $$errref = "Net::Netmask not installed";
                return 0;
            }

            my @val = split /\s*,\s*/, $val;
            my @trusted_upstreams = ();

            for my $ip (@val) {
                my $net = Net::Netmask->new2($ip);
                unless ($net) {
                    $$errref = "Error defining trusted upstream proxies: " . Net::Netmask::errstr();
                    return 0;
                }
                push @trusted_upstreams, $net;
            }

            unless (@trusted_upstreams) {
                $$errref = "Error defining trusted upstream proxies: None found";
                return 0;
            }
            $self->{trusted_upstream_proxies} = \@trusted_upstreams;
        },
        setter => sub {
            my ($self, $val, $set, $mc) = @_;
	    # Do nothing here, we don't want the default setter because we've
	    # already set the value in the type_check step.
            return $mc->ok;
	},
    },

    'index_files' => {
        check_role => "web_server",
        default => "index.html",
        des => "Comma-separated list of filenames to load when a user visits a directory URL, listed in order of preference.",
        setter => sub {
            my ($self, $val, $set, $mc) = @_;
            $self->{index_files} = [ split(/[\s,]+/, $val) ];
            return $mc->ok;
        },
        dumper => sub {
            my ($self, $val) = @_;
            return unless defined $val;
            return join(', ', @$val);
        },
    },

    'default_service' => {
        des => "Name of previously-created service to default requests that aren't matched by a selector plugin to.",
        check_role => "selector",
        check_type => sub {
            my ($self, $val, $errref) = @_;

            my $svc = Perlbal->service($val);
            unless ($svc) {
                $$errref = "Service '$svc' not found";
                return 0;



( run in 1.000 second using v1.01-cache-2.11-cpan-39bf76dae61 )