The original IPv6 patch for HTTP::Server::Simple proposed by Mats [0]
produces the following warnings:

Subroutine HTTP::Server::Simple::pack_sockaddr_in6 redefined at 
/usr/share/perl/5.14/Exporter.pm line 67.
 at /usr/share/perl5/HTTP/Server/Simple.pm line 7
Subroutine HTTP::Server::Simple::unpack_sockaddr_in6 redefined at 
/usr/share/perl/5.14/Exporter.pm line 67.
 at /usr/share/perl5/HTTP/Server/Simple.pm line 7
Subroutine HTTP::Server::Simple::sockaddr_in6 redefined at 
/usr/share/perl/5.14/Exporter.pm line 67.
 at /usr/share/perl5/HTTP/Server/Simple.pm line 7

The attached patch avoids those warnings while still enabling IPv6
support.

I can confirm that this patch provides a baseline of IPv6 support for
HTTP::Server::Simple.  Please adopt it, or provide an alternate IPv6
implementation for this package.

Thanks for maintaining HTTP::Server::Simple!

       --dkg

[0] http://bugs.debian.org/596176#10

Description: Upgrade the module to accept IPv6.
 The contructor and the listener methods are extended to allow
 a domain parameter.  A new method, family(), mediates in deciding
 between AF_INET and AF_INET6.
 .
 The request processing method detects the correct domain for an
 incoming socket.
Author: Mats Erik Andersson <deb...@gisladisker.se>
Forwarded: no
Last-Update: 2010-10-28
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport-cgi?bug=596176

--- libhttp-server-simple-perl-0.43.debian/lib/HTTP/Server/Simple.pm
+++ libhttp-server-simple-perl-0.43/lib/HTTP/Server/Simple.pm
@@ -4,6 +4,7 @@
 package HTTP::Server::Simple;
 use FileHandle;
 use Socket;
+use Socket6 qw(in6addr_any);
 use Carp;
 use IO::Select;
 
@@ -125,15 +126,17 @@
 
 =head1 METHODS
 
-=head2 HTTP::Server::Simple->new($port)
+=head2 HTTP::Server::Simple->new($port, $family)
 
 API call to start a new server.  Does not actually start listening
-until you call C<-E<gt>run()>.  If omitted, C<$port> defaults to 8080.
+until you call C<-E<gt>run()>.  If omitted, C<$port> defaults to 8080,
+and C<$family> defaults to L<Socket::AF_INET>.
+The alternative domain is L<Socket::AF_INET6>.
 
 =cut
 
 sub new {
-    my ( $proto, $port ) = @_;
+    my ( $proto, $port, $family ) = @_;
     my $class = ref($proto) || $proto;
 
     if ( $class eq __PACKAGE__ ) {
@@ -144,6 +147,7 @@ sub new {
     my $self = {};
     bless( $self, $class );
     $self->port( $port || '8080' );
+    $self->family( $family || AF_INET );
 
     return $self;
 }
@@ -152,7 +156,7 @@
 =head2 lookup_localhost
 
 Looks up the local host's IP address, and returns it.  For most hosts,
-this is C<127.0.0.1>.
+this is C<127.0.0.1>, or possibly C<::1>.
 
 =cut
 
@@ -160,9 +164,14 @@ sub lookup_localhost {
     my $self = shift;
 
     my $local_sockaddr = getsockname( $self->stdio_handle );
-    my ( undef, $localiaddr ) = sockaddr_in($local_sockaddr);
-    $self->host( gethostbyaddr( $localiaddr, AF_INET ) || "localhost");
-    $self->{'local_addr'} = inet_ntoa($localiaddr) || "127.0.0.1";
+    my $local_family = sockaddr_family($local_sockaddr);
+    my ( undef, $localiaddr ) =
+        ($local_family == AF_INET6) ? sockaddr_in6($local_sockaddr)
+                                    : sockaddr_in($local_sockaddr);
+
+    $self->host( gethostbyaddr( $localiaddr, $local_family ) || "localhost");
+    $self->{'local_addr'} = Socket::inet_ntop($local_family, $localiaddr)
+                            || (($local_family == AF_INET6) ? "::1" : "127.0.0.1");
 }
 
 
@@ -181,6 +190,31 @@
 
 }
 
+=head2 family [NUMBER]
+
+Takes an optional address family for this server to use.  Valid values
+are Socket::AF_INET and Socket::AF_INET6.  All other values are silently
+changed into Socket::AF_INET for backwards compatibility with previous
+versions of the module.
+
+Returns the address family of the present listening socket.  (Defaults to
+Socket::AF_INET.)
+
+=cut
+
+sub family {
+    my $self = shift;
+    if (@_) {
+        if ($_[0] == AF_INET || $_[0] == AF_INET6) {
+            $self->{'family'} = shift;
+        } else {
+            $self->{'family'} = AF_INET;
+        }
+    }
+    return ( $self->{'family'} );
+
+}
+
 =head2 host [address]
 
 Takes an optional host address for this server to bind to.
@@ -384,8 +418,15 @@ sub _process_request {
         # ( http://dev.catalyst.perl.org/changeset/5195, 5221 )
         
         my $remote_sockaddr = getpeername( $self->stdio_handle );
-        my ( $iport, $iaddr ) = $remote_sockaddr ? sockaddr_in($remote_sockaddr) : (undef,undef);
-        my $peeraddr = $iaddr ? ( inet_ntoa($iaddr) || "127.0.0.1" ) : '127.0.0.1';
+        my $family = sockaddr_family($remote_sockaddr);
+
+        my ( $iport, $iaddr ) = $remote_sockaddr 
+                                ? ( ($family == AF_INET6) ? sockaddr_in6($remote_sockaddr)
+                                                          : sockaddr_in($remote_sockaddr) )
+                                : (undef,undef);
+
+        my $loopback = ($family == AF_INET6) ? "::1" : "127.0.0.1";
+        my $peeraddr = $iaddr ? ( Socket::inet_ntop($family, $iaddr) || $loopback ) : $loopback;
         
         my ( $method, $request_uri, $proto ) = $self->parse_request;
         
@@ -685,18 +726,32 @@ sub setup_listener {
     my $self = shift;
 
     my $tcp = getprotobyname('tcp');
-    socket( HTTPDaemon, PF_INET, SOCK_STREAM, $tcp ) or croak "socket: $!";
+    my $sockaddr;
+    socket( HTTPDaemon, $self->{'family'}, SOCK_STREAM, $tcp )
+        or croak "socket: $!";
     setsockopt( HTTPDaemon, SOL_SOCKET, SO_REUSEADDR, pack( "l", 1 ) )
         or warn "setsockopt: $!";
-    bind( HTTPDaemon,
-        sockaddr_in(
-            $self->port(),
-            (   $self->host
-                ? inet_aton( $self->host )
-                : INADDR_ANY
-            )
-        )
-        )
+
+    if ($self->host) { # Explicit listening address
+        my @res = getaddrinfo($self->host, $self->port, $self->{'family'}, SOCK_STREAM);
+        while (scalar(@res) >= 5) {
+            my ($af, undef, undef, $tmp, undef) = splice(@res, 0, 5);
+            # Be certain on the address family.
+            # TODO Accept AF_UNSPEC, reject SITE-LOCAL
+            next unless ($self->{'family'} == $af);
+
+            # Use the first plausible address.
+            $sockaddr = $tmp;
+            last;
+        }
+    }
+    else { # Use the wildcard address
+        $sockaddr = ($self->{'family'} == AF_INET6)
+                        ? sockaddr_in6($self->port(), in6addr_any)
+                        : sockaddr_in($self->port(), INADDR_ANY);
+    }
+
+    bind( HTTPDaemon, $sockaddr)
         or croak "bind to @{[$self->host||'*']}:@{[$self->port]}: $!";
     listen( HTTPDaemon, SOMAXCONN ) or croak "listen: $!";
 }

Attachment: pgp3yW1OhXBfa.pgp
Description: PGP signature

Reply via email to