IO-Handle-Record

 view release on metacpan or  search on metacpan

lib/IO/Handle/Record.pm  view on Meta::CPAN

    my $L=($I->record_opts && $I->record_opts->{local_encoding}) ? 'L' : 'N';
    if( $I->can('expect_fds') ) {
      ($I->expected, $I->expect_fds)=unpack $L.'2', $I->read_buffer;
    } else {
      ($I->expected)=unpack $L.'2', $I->read_buffer;
    }
    $I->read_buffer='';
  }

  my $wanted=$I->expected;
  my $buflen=length($I->read_buffer);
  while( $buflen<$wanted ) {
    my $len=$reader->( $I, $I->read_buffer, $wanted-$buflen, $buflen );

    if( defined $len and $len>0 ) {
      $buflen+=$len;
    } elsif( defined $len ) {	# EOF
      $len=length $I->read_buffer;
      undef $I->read_buffer;
      croak "IO::Handle::Record: premature end of file";
    } elsif( $!==EAGAIN ) {
      return;
    } elsif( $!==EINTR ) {
      next;
    } else {
      undef $I->read_buffer;
      croak "IO::Handle::Record: sysread";
    }
  }

  if( $I->can('expect_fds') and
      $I->expect_fds>0 and defined $I->_received_fds ) {
    $I->received_fds=[splice @{$I->_received_fds}, 0, $I->expect_fds];
  }
  my $rc=eval {
    local $Storable::Eval;
    $I->record_opts and $Storable::Eval=$I->record_opts->{receive_CODE};
    Storable::thaw( $I->read_buffer );
  };
  if( $@ ) {
    my $e=$@;
    $e=~s/ at .*//s;
    croak $e;
  }

  undef $I->expected;
  undef $I->read_buffer;

  return @{$rc};
}

sub write_record {
  my $I=shift;

  my $writer=(issock($I)
	      ? sub { sendmsg( $_[0], $_[1], $_[2], (@_>3?$_[3]:0) ); }
	      : sub { syswrite $_[0], $_[1], $_[2], (@_>3?$_[3]:()); });

  my $can_fds_to_send=$I->can('fds_to_send');
  if( @_ ) {
    croak "IO::Handle::Record: busy"
      if( defined $I->write_buffer );
    my $L=($I->record_opts && $I->record_opts->{local_encoding}) ? 'L' : 'N';
    my $msg=eval {
      local $Storable::Deparse;
      local $Storable::forgive_me;
      $I->record_opts and do {
	$Storable::forgive_me=$I->record_opts->{forgive_me};
	$Storable::Deparse=$I->record_opts->{send_CODE};
      };
      local $SIG{__WARN__}=sub {};
      $L eq 'L'
	? Storable::freeze \@_
	: Storable::nfreeze \@_;
    };
    if( $@ ) {
      my $e=$@;
      $e=~s/ at .*//s;
      croak $e;
    }

    if( $can_fds_to_send ) {
      $I->write_buffer=pack( $L.'2', length($msg),
			     (defined $I->fds_to_send
			      ? 0+@{$I->fds_to_send}
			      : 0) ).$msg;
    } else {
      $I->write_buffer=pack( $L.'2', length($msg), 0 ).$msg;
    }
    $I->written=0;
  }

  my $written;

  # if there are file descriptors to send send them first along with the length
  # header only. (work around a bug in the suse 11.1 kernel)
  if( $I->written==0 and
      $can_fds_to_send and
      defined $I->fds_to_send and
      @{$I->fds_to_send} ) {
    while(!defined ($written=$writer->($I, $I->write_buffer, HEADERLENGTH))) {
      if( $!==EINTR ) {
	next;
      } elsif( $!==EAGAIN ) {
	return;
      } else {
	croak "IO::Handle::Record: syswrite";
      }
    }
    $I->written+=$written;
  }

  while( $I->written<length($I->write_buffer) and
	 (defined ($written=$writer->($I, $I->write_buffer,
				      length($I->write_buffer)-$I->written,
				      $I->written)) or
	  $!==EINTR) ) {
    if( defined $written ) {
      $I->written+=$written;
    }
  }

lib/IO/Handle/Record.pm  view on Meta::CPAN


=item B<@data=$handle-E<gt>read_record>

reads one record of perl data structures.

On success it returns the record as list. An empty list is returned if
C<$handle> is in non blocking mode and not enough data has been read.
Check $!==EAGAIN to catch this condition. When the handle becomes ready
just repeat the operation to read the next data chunk. If a complete record
has arrived it is returned.

On EOF an empty list is returned. To distinguish this from the non blocking
empty list return check C<$handle-E<gt>end_of_input>.

EINTR is handled internally.

Example:

 ($array, $sub, $hash)=$handle->read_record;

=item B<$handle-E<gt>end_of_input>

When an end of file condition is read this is set to true.

=item B<($pid, $uid, $gid)=$handle-E<gt>peercred>

B<ONLY FOR UNIX DOMAIN SOCKETS ON LINUX>

Return the PID, eUID and eGID of the peer at the time of the connect.

=item B<$handle-E<gt>read_buffer>

=item B<$handle-E<gt>expected>

=item B<$handle-E<gt>expect_fds>

=item B<$handle-E<gt>_received_fds>

=item B<$handle-E<gt>write_buffer>

=item B<$handle-E<gt>written>

these methods are used internally to provide a read and write buffer for
non blocking operations.

=back

=head2 Exceptions

=over 4

=item * C<IO::Handle::Record: sysread>

thrown in C<read_record>. Check C<$!> for more information.

=item * C<IO::Handle::Record: premature end of file>

thrown in C<read_record> on end of file if according to the internal
protocol more input is expected.

=item * C<IO::Handle::Record: busy>

thrown in C<write_record> if a non-blocking write is not yet finished. There
may be only one write operation at a time. If that hits you organise a queue.

=item * C<IO::Handle::Record: syswrite>

thrown in C<write_record> on an error of the underlying transport method.
Check C<$!> for more information.

=item * Other exceptions

thrown in C<read_record> and C<write_record> if something cannot be encoded
or decoded by the C<Storable> module. If that hits you the C<Storable> module
at one side is probably too old.

=back

=head2 EXPORT

None.

=head1 Data Transfer Format

The Perl data is serialized using Storable::freeze or Storable::nfreeze.
Storable::freeze is used if the C<local_encoding> option is set,
Storable::nfreeze otherwise.

The length in bytes of this data chunk and the number of file handles
that are passed along with the data are then each C<pack()>ed as a 4 byte
binary value using the C<L> or C<N> template. C<L> is used of C<local_encoding>
is in effect.

If there are file descriptors to be passed they are sent by a separate
sendmsg call along with 2 length fields only.

Both fields is the prepended to the data chunk:

 +-----------------+------------------------+
 | data length (N) | number of file handles |
 | 4 bytes         | 4 bytes                |
 +-----------------+------------------------+
 |                                          |
 |                                          |
 |                                          |
 |                                          |
 |                   data                   |
 |                                          |
 |                 N bytes                  |
 |                                          |
 |                                          |
 |                                          |
 |                                          |
 |                                          |
 +------------------------------------------+

B<WARNING:> The transfer format has changed in version 0.07 (never made it
to CPAN) and again in version 0.08.

=head1 TODO



( run in 2.248 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )