The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package ExternalCatty;
use strict;
use warnings;
use Catalyst;
use Catalyst::ScriptRunner;
use IO::Socket::INET;

__PACKAGE__->config( name => 'ExternalCatty' );
__PACKAGE__->setup;

sub MAX_PORT_TRIES() { 5 }

# The Cat HTTP server background option is useless here :-(
# Thus we have to provide our own background method.
sub background {
    my $self  = shift;
    my $port  = shift;
    $port = $self->assert_or_find_available_port($port);
    my $child = fork;
    die "Can't fork Cat HTTP server: $!" unless defined $child;
    return($child, $port) if $child;

    if ( $^O !~ /MSWin32/ ) {
        require POSIX;
        POSIX::setsid() or die "Can't start a new session: $!";
    }
    local @ARGV = ('-p', $port);
    Catalyst::ScriptRunner->run(__PACKAGE__, 'Server');
}

sub assert_or_find_available_port {
    my($self, $port) = @_;
    for my $i (1..MAX_PORT_TRIES) {
        IO::Socket::INET->new(
            LocalAddr => 'localhost',
            LocalPort => $port,
            Proto     => 'tcp'
        ) and return $port;
        $port += int(rand 100) + 1;
    }
    die q{Can't find an open port to run external server on after }
        . MAX_PORT_TRIES . q{tries};
}

1;