=begin pod :kind("Type") :subkind("class") :category("domain-specific")

=TITLE class IO::Socket::Async

=SUBTITLE Asynchronous socket in TCP or UDP

    class IO::Socket::Async {}

C<IO::Socket::Async> provides asynchronous sockets, for both the
server and the client side.

Here is a simple example of a simple "hello world" HTTP server that
listens on port 3333:

=begin code :solo
react {
    whenever IO::Socket::Async.listen('0.0.0.0', 3333) -> $conn {
        whenever $conn.Supply.lines -> $line {
            $conn.print: qq:heredoc/END/;
                HTTP/1.1 200 OK
                Content-Type: text/html; charset=UTF-8
                Content-Encoding: UTF-8

                <html>
                <body>
                    <h1>Hello World!</h1>
                    <p>{ $line }</p>
                </body>
                </html>
                END
            $conn.close;
        }
    }
    CATCH {
        default {
            say .^name, ': ', .Str;
            say "handled in $?LINE";
        }
    }
}
=end code

And a client that connects to it, and prints out what the server answers:

=begin code
await IO::Socket::Async.connect('127.0.0.1', 3333).then( -> $promise {
    given $promise.result {
        .print("Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n");
        react {
            whenever .Supply() -> $v {
                $v.print;
                done;
            }
        }
        .close;
    }
});
=end code

L<IO::Socket::Async|/type/CompUnit> can also send and receive
L<UDP|https://en.wikipedia.org/wiki/User_Datagram_Protocol> messages
An example server that outputs all the data it receives would be:

=begin code
my $socket = IO::Socket::Async.bind-udp('localhost', 3333);

react {
    whenever $socket.Supply -> $v {
        if $v.chars > 0 {
            say $v;
        }
    }
}
=end code

And an associated client might be:

=begin code
my $socket = IO::Socket::Async.udp();
await $socket.print-to('localhost', 3333, "Hello, Raku!");
=end code

The L<C<CATCH> phaser|/language/phasers#CATCH> can be included to deal
specifically with problems that might occur in this kind of sockets, such as a
port being already taken:

=begin code
react {
    whenever IO::Socket::Async.listen('0.0.0.0', 3000) -> $conn {
        whenever $conn.Supply.lines -> $line {
            $conn.print: qq:heredoc/END/;
                HTTP/1.1 200 OK
                Content-Type: text/html; charset=UTF-8
                Content-Encoding: UTF-8

                <html>
                <body>
                    <h1>Hello World!</h1>
                    <p>{ $line }</p>
                </body>
                </html>
                END
            $conn.close;
        }
        QUIT {
            default {
                say .^name, '→ ', .Str;
                say "handled in line $?LINE";
            }
        }
    }

}
# Will print this, if address 3000 is already in use:
# X::AdHoc→ address already in use
# handled in 23
=end code

Main difference with using other phasers such as C<CATCH> is that this kind of
exception will be caught within the C<whenever> block and will put exiting the
program, or not, under your control.

=head1 Methods

The L<IO::Socket::Async|/type/IO::Socket::Async> cannot be constructed directly, either
C<connect> or C<listen> (for TCP connections) or C<udp> or C<bind-udp>
(for UDP data) should be used to create a client or a server
respectively.

=head2 method connect

    method connect(Str $host, Int $port --> Promise)

Attempts to connect to the TCP server specified by C<$host> and
C<$port>, returning a L<Promise|/type/Promise> that will either be kept with a
connected L<IO::Socket::Async|/type/IO::Socket::Async> or broken if the connection cannot be
made.

=head2 method listen

    method listen(Str $host, Int $port --> Supply)

Creates a listening socket on the specified C<$host> and C<$port>,
returning a L<Supply|/type/Supply> to which the accepted client
L<IO::Socket::Async|/type/IO::Socket::Async>s will be emitted. This
L<Supply|/type/Supply> should be tapped start listening for client
connections.  You can set C<$port> to C<0> if you want the operating
system to find one for you.

The L<IO::Socket::Async::ListenSocket|/type/IO::Socket::Async::ListenSocket>
returned by calling the L<tap|/type/Supply#method_tap> method on the supply
returned represents the underlying listening TCP socket, which can be closed
using its L<close|/type/Tap#method_close> method. If C<$port> was set to C<0>,
you can get the port the socket ended up with using its
L<socket-port|/type/IO::Socket::Async::ListenSocket#method_socket-port> method.

=head2 method udp

    method udp(IO::Socket::Async:U: :$broadcast --> IO::Socket::Async)

Returns an initialized C<IO::Socket::Async> client object that is
configured to send UDP messages using C<print-to> or C<write-to>.  The
C<:broadcast> adverb will set the C<SO_BROADCAST> option which will
allow the socket to send packets to a broadcast address.

=head2 method bind-udp

    method bind-udp(IO::Socket::Async:U: Str() $host, Int() $port, :$broadcast --> IO::Socket::Async)

This returns an initialized C<IO::Socket::Async> server object that is
configured to receive UDP messages sent to the specified C<$host> and
C<$port> and is equivalent to C<listen> for a TCP socket.  The
C<:broadcast> adverb can be specified to allow the receipt of messages
sent to the broadcast address.

=head2 method print

    method print(IO::Socket::Async:D: Str $str --> Promise)

Attempt to send C<$str> on the L<IO::Socket::Async|/type/IO::Socket::Async> that will have been
obtained indirectly via C<connect> or C<listen>, returning a L<Promise|/type/Promise>
that will be kept with the number of bytes sent or broken if there was
an error sending.

=head2 method print-to

    method print-to(IO::Socket::Async:D: Str() $host, Int() $port, Str() $str --> Promise)

This is the equivalent of C<print> for UDP sockets that have been
created with the C<udp> method, it will try send a UDP message of
C<$str> to the specified C<$host> and C<$port> returning a
L<Promise|/type/Promise> that will be kept when the data is successfully
sent or broken if it was unable to send the data. In order to send to a
broadcast address the C<:broadcast> flag must have been specified when
the socket was created.

=head2 method write

    method write(IO::Socket::Async:D: Blob $b --> Promise)

This method will attempt to send the bytes in C<$b> on the
L<IO::Socket::Async|/type/IO::Socket::Async> that will have been obtained indirectly via
C<connect> or C<listen>, returning a L<Promise|/type/Promise> that will be kept with
the number of bytes sent or broken if there was an error sending.

=head2 method write-to

    method write-to(IO::Socket::Async:D: Str() $host, Int() $port, Blob $b --> Promise)

This is the equivalent of C<write> for UDP sockets that have been
created with the C<udp> method. It will try send a UDP message comprised
of the bytes in the L<Blob|/type/Blob>  C<$b> to the specified C<$host>
and C<$port> returning a L<Promise|/type/Promise> that will be kept when
the data is successfully sent or broken if it was unable to send the
data. In order to send to a broadcast address the C<:broadcast> flag
must have been specified when the socket was created.

=head2 method Supply

    method Supply(:$bin, :$buf = buf8.new --> Supply)

Returns a L<Supply|/type/Supply> which can be tapped to obtain the data read from
the connected L<IO::Socket::Async|/type/IO::Socket::Async> as it arrives.  By default the data
will be emitted as characters, but if the C<:bin> adverb is provided a
L<Buf|/type/Buf> of bytes will be emitted instead, optionally in this
case you can provide your own C<Buf> with the C<:buf> named parameter.

A UDP socket in character mode will treat each packet as a complete
message and decode it. In the event of a decoding error, the C<Supply>
will C<quit>.

On the other hand, a TCP socket treats the incoming packets as part of a
stream, and feeds the incoming bytes into a streaming decoder. It then
emits whatever characters the decoder considers ready. Since strings
work at grapheme level in Raku, this means that only known complete
graphemes will be emitted. For example, if the UTF-8 encoding were
being used and the last byte in the packet decoded to C<a>, this would
not be emitted since the next packet may include a combining character
that should form a single grapheme together with the C<a>. Control
characters (such as C<\n>) always serve as grapheme boundaries, so any
text-based protocols that use newlines or null bytes as terminators
will not need special consideration. A TCP socket will also C<quit>
upon a decoding error.

=head2 method close

    method close(IO::Socket::Async:D: )

Close the connected client L<IO::Socket::Async|/type/IO::Socket::Async> which will have been
obtained from the C<listen> L<Supply|/type/Supply> or the C<connect>
L<Promise|/type/Promise>.

In order to close the underlying listening socket created by C<listen> you can
C<close> the
L<IO::Socket::Async::ListenSocket|/type/IO::Socket::Async::ListenSocket>. See
L<listen|#method_listen> for examples.

=head2 method socket-host

    method socket-host(--> Str)

Returns the IP address of the local end of this socket.

=head2 method peer-host

    method peer-host(--> Str)

Returns the IP address of the remote end of this socket.

=head2 method socket-port

    method socket-port(--> Int)

Returns the port of the local end of this socket.

=head2 method peer-port

    method peer-port(--> Int)

Returns the port of the remote end of this socket.

=head2 method native-descriptor

    method native-descriptor(--> Int)

Returns the file descriptor of this socket.

=end pod
