Starting Apache on first request

I haven’t figured this one out yet. I’m going to describe the things I’ve tried so far.

So first, the story. A long time ago you could run apache through inetd. This is kind of a silly thing to do because apache takes a few seconds to start up, so they dropped the feature.

I don’t want to start apache through inetd, because that would be too slow. But I’d be OK with waiting a few seconds on the first request, if subsequent requests went straight to apache. So I’d like a program that would:

  1. Listen on port 80.
  2. When it gets a request, start apache.
  3. Forward the traffic to apache.
  4. Exit.

Now you might ask, why not just start apache at boot? After all, it’s not using hardly any CPU time when no requests are being serviced. The answer is that it’s not just apache. I have a plethora of fairly bloated services that I like to have available for when I need them, and together they drag out the boot process considerably. RAM isn’t an issue at this point, but they do collectively consume a decent amount of RAM.

So here’s the attempt I made, in (gasp) Perl. I don’t have much experience in socket programming, and it’s just a test so errors are mostly unchecked.

#!/usr/bin/perl

use IO::Socket;
use Getopt::Long;

my $port     = $ARGV[1];
my $service  = $ARGV[0];

print "port    =  $port\n";
print "service =  $service\n";

my $socket = new IO::Socket::INET(
        LocalPort => $port,
        Proto     => 'tcp',
        Listen    => 1,
        ReuseAddr => 1,
        ResuePort => 1
);

$socket or die("cannot create listener socket: $!");

my $connect = $socket->accept();
$socket->shutdown(2);
close($socket);

print STDERR "starting service...\n";
`$service`;

# Should automatically call connect()
my $redirect = new IO::Socket::INET(
        PeerHost => 'localhost',
        PeerPort => $port,
        Proto     => 'tcp'
);

$redirect or die("Failed to establish client connection: $!");

my $pipe_child = fork();

if (! $pipe_child) {
        copy($redirect, $connect, 'server -> client');

        exit(0);
}
else {
        print STDERR "forked $pipe_child to do server -> client\n";

        copy($connect, $redirect, 'server <- client');

        # Reap all children
        waitpid(-1, WNOHANG);
}

sub copy {
        my $a = shift;
        my $b = shift;
        my $name = shift;

        while (<$a>) {
                print $b $_;
        }

        print "done getting lines ($name)\n";

        $b->shutdown(2) or die($!);
        close($b) or die($!);
}

sub owner {
        my $foo = `fuser -n tcp 8000 2>&1`;
        return $foo;
}

Then to use it, run

sudo ./listener '/etc/init.d/apache2 start' 80

Open a web-browser and go to http://localhost/. It works!

But there’s a serious problem. Apache will think the request is coming from localhost. It needs to be tricked somehow, but I do not know how to trick it.

Any ideas?

Advertisements
This entry was posted in networking. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s