=encoding utf-8

=head1 NAME

IO::Framed - Convenience wrapper for frame-based I/O

=head1 SYNOPSIS

Reading:

    #See below about seed bytes.
    my $iof = IO::Framed->new( $fh, 'seed bytes' );

    #This returns undef if the $in_fh doesn���t have at least
    #the given length (5 in this case) of bytes to read.
    $frame = $iof->read(5);

Writing, unqueued (i.e., for blocking writes):

    #The second parameter (if given) is executed immediately after the final
    #byte of the payload is written. For blocking I/O this happens
    #before the following method returns.
    $iof->write('hoohoo', sub { print 'sent!' } );

Writing, queued (for non-blocking writes):

    $iof->enable_write_queue();

    #This just adds to a memory queue:
    $iof->write('hoohoo', sub { print 'sent!' } );

    #This will be 1, since we have 1 message/frame queued to send.
    $iof->get_write_queue_count();

    #Returns 1 if it empties out the queue; 0 otherwise.
    #Partial frame writes are accommodated; the callback given as 2nd
    #argument to write() only fires when the queue item is sent completely.
    my $empty = $iof->flush_write_queue();

You can also use C<IO::Framed::Read> and C<IO::Framed::Write>, which
contain just the read and write features. (C<IO::Framed> is actually a
subclass of them both.)

=head1 DESCRIPTION

While writing L<Net::WAMP> I noticed that I was reimplementing some of the
same patterns I���d used in L<Net::WebSocket> to parse frames from a stream:

=over

=item * Only read() entire frames, with a read queue for any partials.

=item * Continuance when a partial frame is delivered.

=item * Write queue with callbacks for non-blocking I/O

=item * Signal resilience: resume read/write after Perl receives a trapped
signal rather than throwing/giving EINTR. (cf. L<IO::SigGuard>)

=back

These are now made available in this distribution.

=head1 ABOUT READS

The premise here is that you expect a given number of bytes at a given time
and that a partial read should be continued once it is sensible to do so.

As a result, C<read()> will throw an exception if the number of bytes given
for a continuance is not the same number as were originally requested.

Example:

    #This reads only 2 bytes, so read() will return undef.
    $iof->read(10);

    #��� wait for readiness if non-blocking ���

    #XXX This die()s because we���re in the middle of trying to read
    #10 bytes, not 4.
    $iof->read(4);

    #If this completes the read (i.e., takes in 8 bytes), then it���ll
    #return the full 10 bytes; otherwise, it���ll return undef again.
    $iof->read(10);

EINTR prompts a redo of the read operation. EAGAIN and EWOULDBLOCK (the same
error generally, but not always) prompt an undef return.
Any other failures prompt an instance of L<IO::Framed::X::ReadError> to be
thrown.

=head2 EMPTY READS

This class���s C<read()> method will, by default, throw an instance of
L<IO::Framed::X::EmptyRead> on an empty read. This is normal and logical
behavior in contexts (like L<Net::WebSocket>) where the data stream itself
indicates when no more data will come across. In such cases an empty read
is genuinely an error condition: it either means you���re reading past when
you should, or the other side prematurely went away.

In some other cases, though, that empty read is the normal and expected way
to know that a filehandle/socket has no more data to read.

If you prefer, then, you can call the C<allow_empty_read()> method to switch
to a different behavior, e.g.:

    $framed->allow_empty_read();

    my $frame = $framed->read(10);

    if (length $frame) {
        #yay, we got a frame!
    }
    elsif (defined $frame) {
        #no more data will come in, so let���s close up shop
    }
    else {
        #undef means we just haven���t gotten as much data as we want yet;
        #in this case, that means fewer than 10 bytes are available.
    }

Instead of throwing the aforementioned exception, C<read()> now returns
empty-string on an empty read. That means that you now have to distinguish
between multiple ���falsey��� states: undef for when the requested number
of bytes hasn���t yet arrived, and empty string for when no more bytes
will ever arrive. But it is also true now that the only exceptions thrown
are bona fide B<errors>, which will suit some applications better than the
default behavior.

NB: If you want to be super-light, you can bring in IO::Framed::Read instead
of the full IO::Framed. (IO::Framed is already pretty lightweight, though.)

=head1 ABOUT WRITES

Writes for blocking I/O are straightforward: the system will always send
the entire buffer. The OS���s C<write()> won���t return until everything
meant to be written is written. Life is pleasant; life is simple. :)

Non-blocking I/O is trickier. Not only can the OS���s C<write()> write
a subset of the data it���s given, but we also can���t know that the output
filehandle is ready right when we want it. This means that we have to queue up
our writes
then write them once we know (e.g., through C<select()>) that the filehandle
is ready. Each C<write()> call, then, enqueues one new buffer to write.

Since it���s often useful to know when a payload has been sent,
C<write()> accepts an optional callback that will be executed immediately
after the last byte of the payload is written to the output filehandle.

Empty out the write queue by calling C<flush_write_queue()> and looking for
a truthy response. (A falsey response means there is still data left in the
queue.) C<get_write_queue_count()> gives you the number of queue items left
to write. (A partially-written item is treated the same as a fully-unwritten
one.)

Note that, while it���s acceptable to activate and deactive the write queue,
the write queue must be empty in order to deactivate it. (You���ll get a
nasty, untyped exception otherwise!)

C<write()> returns undef on EAGAIN and EWOULDBLOCK. It retries on EINTR,
so you should never actually see this error from this module.
Other errors prompt a thrown exception.

NB: C<enable_write_queue()> and C<disable_write_queue()> return the object,
so you can instantiate thus:

    my $nb_writer = IO::Framed::Write->new($fh)->enable_write_queue();

NB: If you want to be super-light, you can bring in IO::Framed::Write instead
of the full IO::Framed. (IO::Framed is already pretty lightweight, though.)

=head1 ERROR RESPONSES

An empty read or any I/O error besides the ones mentioned previously
are indicated via an instance of one of the following exceptions.

All exceptions subclass L<X::Tiny::Base>.

=over

=item L<IO::Framed::X::ReadError>

=item L<IO::Framed::X::WriteError>

These both have an C<OS_ERROR> property (cf. L<X::Tiny::Base>���s accessor
method).

=item L<IO::Framed::X::EmptyRead>

No properties. If this is thrown, your peer has probably closed the connection.
Unless you have called C<allow_empty_read()> to set an alternate behavior,
you might want to trap this exception if you call C<read()>.

=back

B<NOTE:> This distribution doesn���t write to C<$!>. EAGAIN and EWOULDBLOCK on
C<flush_write_queue()> are ignored; all other errors are converted
to thrown exceptions.

=head1 LEGACY CLASSES

This distribution also includes the following B<DEPRECATED> legacy classes:

=over

=item * IO::Framed::Write::Blocking

=item * IO::Framed::Write::NonBlocking

=item * IO::Framed::ReadWrite

=item * IO::Framed::ReadWrite::Blocking

=item * IO::Framed::ReadWrite::NonBlocking

=back

I���ll keep these in for the time being but eventually B<WILL> remove them.
Please adjust any calling code that you might have.

=head1 REPOSITORY

L<https://github.com/FGasper/p5-IO-Framed>

=head1 AUTHOR

Felipe Gasper (FELIPE)

=head1 COPYRIGHT

Copyright 2017 by L<Gasper Software Consulting, LLC|http://gaspersoftware.com>

=head1 LICENSE

This distribution is released under the same license as Perl.