my class IO::Socket::INET does IO::Socket { my module PIO { constant MIN_PORT = 0; constant MAX_PORT = 65_535; # RFC 793: TCP/UDP port limit } has ProtocolFamily:D $.family = PF_UNSPEC; has SocketType:D $.type = SOCK_STREAM; has ProtocolType:D $.proto = PROTO_TCP; has Str $.host; has Int $.port; has Str $.localhost; has Int $.localport; has Int $.backlog; has Bool $.listening; # XXX: this could be a bit smarter about how it deals with unspecified # families... my sub split-host-port(:$host is copy, :$port is copy, :$family) { if ($host) { my ($split-host, $split-port) = $family == PF_INET6 ?? v6-split($host) !! v4-split($host); if $split-port { $host = $split-host.Str; $port //= $split-port.Int } } fail "Invalid port $port.gist(). Must be {PIO::MIN_PORT}..{PIO::MAX_PORT}" unless $port.defined and PIO::MIN_PORT <= $port <= PIO::MAX_PORT; return ($host, $port); } my sub v4-split($uri) { return $uri.split(':', 2); } my sub v6-split($uri) { my ($host, $port) = ($uri ~~ /^'[' (.+) ']' \: (\d+)$/)[0,1]; return $host ?? ($host, $port) !! $uri; } # Create new socket that listens on $localhost:$localport multi method new( Bool :$listen! where .so, Str :$localhost is copy, Int :$localport is copy, ProtocolFamily:D(Int:D) :$family = PF_UNSPEC, *%rest, --> IO::Socket::INET:D) { ($localhost, $localport) = ( split-host-port :host($localhost), :port($localport), :$family orelse fail $_) unless $family == PF_UNIX; self.bless( :$localhost, :$localport, :$family, :listening($listen), |%rest, )!initialize(nqp::socket(10)) } # Open new connection to socket on $host:$port multi method new( Str:D :$host! is copy, Int :$port is copy, ProtocolFamily:D(Int:D) :$family = PF_UNSPEC, *%rest, --> IO::Socket::INET:D) { ($host, $port) = split-host-port( :$host, :$port, :$family, ) unless $family == PF_UNIX; self.bless( :$host, :$port, :$family, |%rest, )!initialize(nqp::socket(0)) } # Fail if no valid parameters are passed multi method new() { fail "Nothing given for new socket to connect or bind to. " ~ "Invalid arguments to .new?"; } method !initialize(Mu $PIO is raw) { CATCH { nqp::closefh($PIO) } # Quoting perl's SIO::INET: # If Listen is defined then a listen socket is created, else if the socket type, # which is derived from the protocol, is SOCK_STREAM then connect() is called. if $!listening || $!localhost || $!localport { nqp::bindsock($PIO, nqp::unbox_s($!localhost || "0.0.0.0"), nqp::unbox_i($!localport || 0), nqp::decont_i($!family), nqp::unbox_i($!backlog || 128)); } if $!listening { #?if !js $!localport = nqp::getport($PIO) unless $!localport || ($!family == PF_UNIX); #?endif } elsif $!type == SOCK_STREAM { nqp::connect($PIO, nqp::unbox_s($!host), nqp::unbox_i($!port), nqp::decont_i($!family)); } nqp::bindattr(self, $?CLASS, '$!PIO', $PIO); self; } method connect(IO::Socket::INET:U: Str() $host, Int() $port, ProtocolFamily:D :$family = PF_UNSPEC) { self.new(:$host, :$port, :family($family.value)) } method listen(IO::Socket::INET:U: Str() $localhost, Int() $localport, ProtocolFamily:D :$family = PF_UNSPEC) { self.new(:$localhost, :$localport, :family($family.value), :listen) } method accept() { # A solution as proposed by moritz my $new_sock := $?CLASS.bless(:$!family, :$!proto, :$!type, :$!nl-in); nqp::bindattr($new_sock, $?CLASS, '$!PIO', nqp::accept(nqp::getattr(self, $?CLASS, '$!PIO')) ); return $new_sock; } } # vim: expandtab shiftwidth=4