ControlFreak

 view release on metacpan or  search on metacpan

lib/ControlFreak/Socket.pm  view on Meta::CPAN


Each socket object has a unique name inside B<ControlFreak> controller,
services interested in a socket just reference it using this name.
The controller pipes the socket to children's stdin after forking,
and before executing the service.

=head1 METHODS

=head2 new(%param)

Creates a socket objects. Params are:

=over 4

=item * ctrl

The controller to attach the socket to. If not specified, the
socket object won't be created, C<new()> will just return undef.

=item * name

The name of the socket, MUST be unique within C<ctrl>.

=item * host

eg. '127.0.0.0', '0.0.0.0', 'unix/', '[::1]'.

=item * service

eg. '80', '/tmp/cfk.sock'.

=back

If a socket with that name already exists, it will return undef
and log the error.

=cut

sub new {
    my $class = shift;
    my %param = @_;

    delete $param{fh};
    my $ctrl = $param{ctrl};
    unless ($ctrl) {
        warn "Socket creation attempt without ctrl";
        return;
    }

    unless ($param{name}) {
        $ctrl->log->error("Socket creation attempt without a name");
        return;
    }

    my $socket = $class->SUPER::new(%param);
    $socket->{ctrl} = $ctrl;
    unless ($ctrl->add_socket($socket)) {
        $ctrl->log->error("A socket by that name already exists");
        return;
    }
    Scalar::Util::weaken($socket->{ctrl});
    return $socket;
}

=head2 bind

Creates, binds the socket and puts it in listen mode, then returns
immediately.
Once bound, $socket->fh will return the filehandle.

=cut

sub bind {
    my $socket = shift;

    my $ctrl = $socket->{ctrl};
    my $name = $socket->name;
    if ($socket->{fh}) {
        $ctrl->log->error("'$name' socket is already bound");
        return;
    }

    my ($fh, $host, $service) = $socket->_bind;
    unless ($fh) {
        $ctrl->log->error("cannot bind '$name': $!");
        return;
    }
    $ctrl->log->info("'$name' socket is now bound: $fh");
    ## reset with real values
    $socket->{service} = $service;
    $socket->{host}    = $host;
    $socket->{fh}      = $fh;
    return;
}

sub _bind {
    my $socket = shift;

    my $host = $socket->host;
    my $service = $socket->service;

    ## part reaped from AnyEvent::Socket

    my $ipn = AnyEvent::Socket::parse_address($host)
        or Carp::croak "AnyEvent::Socket::tcp_server: "
                     . "cannot parse '$host' as host address";

    my $af = AnyEvent::Socket::address_family($ipn);

    my $fh;

    # win32 perl is too stupid to get this right :/
    Carp::croak "tcp_server/socket: address family not supported"
        if AnyEvent::WIN32 && $af == AF_UNIX;

    socket $fh, $af, SOCK_STREAM, 0
        or Carp::croak "tcp_server/socket: $!";

   if ($af == AF_INET || $af == AF_INET6) {
       setsockopt $fh, SOL_SOCKET, SO_REUSEADDR, 1
           or Carp::croak "tcp_server/so_reuseaddr: $!"



( run in 2.331 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )