unit class IO::Socket::SSL does IO::Socket; use OpenSSL; use OpenSSL::Err; sub v4-split($uri) { $uri.split(':', 2); } sub v6-split($uri) { my ($host, $port) = ($uri ~~ /^'[' (.+) ']' \: (\d+)$/)[0, 1]; $host ?? ($host, $port) !! $uri; } has Str $.host; has Int $.port = 443; has Str $.localhost; has Int $.localport; has Str $.certfile; has Bool $.listening; has Str $.input-line-separator is rw = "\n"; has Int $.ins = 0; has $.client-socket; has $.listen-socket; has $.accepted-socket; has $!socket; has OpenSSL $.ssl; method new(*%args is copy) { fail "Nothing given for new socket to connect or bind to" unless %args || %args || %args || %args || %args; if %args { my ($host, $port) = %args && %args == PIO::PF_INET6() ?? v6-split(%args) !! v4-split(%args); if $port { %args //= $port; %args = $host; } } if %args { my ($peer, $port) = %args && %args == PIO::PF_INET6() ?? v6-split(%args) !! v4-split(%args); if $port { %args //= $port; %args = $peer; } } %args.=Bool if %args.EXISTS-KEY('listen'); self.bless(|%args)!initialize; } method !initialize { if $!client-socket || ($!host && $!port) { # client stuff $!socket = $!client-socket || IO::Socket::INET.new(:host($!host), :port($!port)); # handle errors $!ssl = OpenSSL.new(:client); $!ssl.set-socket($!socket); $!ssl.set-connect-state; my $ret = $!ssl.connect; if $ret < 0 { my $e = OpenSSL::Err::ERR_get_error(); repeat { say "err code: $e"; say OpenSSL::Err::ERR_error_string($e, Str); $e = OpenSSL::Err::ERR_get_error(); } while $e != 0 && $e != 4294967296; } } elsif $!accepted-socket { $!socket = $!accepted-socket; $!ssl = OpenSSL.new(); $!ssl.set-socket($!socket); $!ssl.set-accept-state; $!ssl.use-certificate-file($!certfile); $!ssl.use-privatekey-file($!certfile); $!ssl.check-private-key; my $ret = $!ssl.accept; if $ret < 0 { my $e = OpenSSL::Err::ERR_get_error(); repeat { say "err code: $e"; say OpenSSL::Err::ERR_error_string($e); $e = OpenSSL::Err::ERR_get_error(); } while $e != 0 && $e != 4294967296; } } elsif $!listen-socket || $!listening { $!socket = $!listen-socket || IO::Socket::INET.new(:localhost($!localhost), :localport($!localport), :listen); } self; } method recv(Int $n = 1048576, Bool :$bin = False) { $!ssl.read($n, :$bin); } method read(Int $n) { my $res = buf8.new; my $buf; repeat { $buf = self.recv($n - $res.elems, :bin); $res ~= $buf; } while $res.elems < $n && $buf.elems; $res; } method send(Str $s) { $!ssl.write($s); } method print(Str $s) { $!ssl.write($s); } method put(Str $s) { $!ssl.write($s ~ $.nl-out); } method write(Blob $b) { $!ssl.write($b); } method get() { my $buf = buf8.new; my $nl-bytes = $.input-line-separator.encode.bytes; loop { my $more = self.recv(1, :bin); if !$more { return Str unless $buf.bytes; return $buf.decode; } $buf ~= $more; next unless $buf.bytes >= $nl-bytes; if $buf.subbuf($buf.bytes - $nl-bytes, $nl-bytes).decode('latin-1') eq $.input-line-separator { return $buf.subbuf(0, $buf.bytes - $nl-bytes).decode; } } } method accept { my $newsock = $!socket.accept; self.bless(:accepted-socket($newsock))!initialize; } method close { $!ssl.close; $!socket.close; } method connect(Str() $host, Int() $port) { self.new(:$host, :$port) } method listen(Str() $localhost, Int() $localport) { self.new(:$localhost, :$localport, :listen) } =begin pod =head1 NAME IO::Socket::SSL - interface for SSL connection =head1 SYNOPSIS use IO::Socket::SSL; my $ssl = IO::Socket::SSL.new(:host, :port(443)); if $ssl.print("GET / HTTP/1.1\r\n\r\n") { say $ssl.recv; } =head1 DESCRIPTION This module provides an interface for SSL connections. It uses C to setting up the connection so far (hope it will change soon). =head1 METHODS =head2 method new method new(*%params) returns IO::Socket::SSL Gets params like: =item encoding : connection's encoding =item input-line-separator : specifies how lines of input are separated for client state: =item host : host to connect =item port : port to connect for server state: =item localhost : host to use for the server =item localport : port for the server =item listen : create a server and listen for a new incoming connection =item certfile : path to a file with certificates =head2 method recv method recv(IO::Socket::SSL:, Int $n = 1048576, Bool :$bin = False) Reads $n bytes from the other side (server/client). Bool :$bin if we want it to return Buf instead of Str. =head2 method print method print(IO::Socket::SSL:, Str $s) =head2 method send DEPRECATED. Use `.print` instead method send(IO::Socket::SSL:, Str $s) Sends $s to the other side (server/client). =head2 method accept method accept(IO::Socket::SSL:) Waits for a new incoming connection and accepts it. =head2 close method close(IO::Socket::SSL:) Closes the connection. =head1 SEE ALSO L =head1 EXAMPLE To download sourcecode of e.g. github.com: use IO::Socket::SSL; my $ssl = IO::Socket::SSL.new(:host, :port(443)); my $content = Buf.new; $ssl.print("GET /\r\n\r\n"); while my $read = $ssl.recv { $content ~= $read; } say $content; =end pod