powerman / perl-IO-Stream

Perl module: IO::Stream - ease non-blocking I/O streams based on EV
https://metacpan.org/release/IO-Stream
Other
0 stars 1 forks source link

Build Status Coverage Status

NAME

IO::Stream - ease non-blocking I/O streams based on EV

VERSION

This document describes IO::Stream version v2.0.3

SYNOPSIS

use EV;
use IO::Stream;

IO::Stream->new({
    host        => 'google.com',
    port        => 80,
    cb          => \&client,
    wait_for    => SENT|EOF,
    in_buf_limit=> 102400,
    out_buf     => "GET / HTTP/1.0\nHost: google.com\n\n",
});

$EV::DIED = sub { warn $@; EV::unloop };
EV::loop;

sub client {
    my ($io, $e, $err) = @_;
    if ($err) {
        $io->close();
        die $err;
    }
    if ($e & SENT) {
        print "request sent, waiting for reply...\n";
    }
    if ($e & EOF) {
        print "server reply:\n", $io->{in_buf};
        $io->close();
        EV::unloop;         # ALL DONE
    }
}

DESCRIPTION

Non-blocking event-based low-level I/O is hard to get right. Code usually error-prone and complex... and it very similar in all applications. Things become much worse when you need to alter I/O stream in some way - use proxies, encryption, SSL, etc.

This module designed to give user ability to work with I/O streams on higher level, using input/output buffers (just scalars) and high-level events like CONNECTED, SENT or EOF. As same time it doesn't hide low-level things, and user still able to work on low-level without any limitations.

PLUGINS

Architecture of this module make it ease to write plugins, which will alter I/O stream in any way - route it through proxies, encrypt, log, etc.

Here are few available plugins, you may find more on CPAN: IO::Stream::Crypt::RC4, IO::Stream::Proxy::HTTPS, IO::Stream::MatrixSSL::Client, IO::Stream::MatrixSSL::Server.

If you interested in writing own plugin, check source for "skeleton" plugins: IO::Stream::Noop and IO::Stream::NoopAlias.

EXPORTS

This modules doesn't export any functions/methods/variables, but it exports a lot of constants. There two groups of constants: events and errors (which can be imported using tags ':Event' and ':Error'). By default all constants are exported.

Events:

RESOLVED CONNECTED IN OUT EOF SENT

Errors:

EINBUFLIMIT
ETORESOLVE ETOCONNECT ETOWRITE
EDNS EDNSNXDOMAIN EDNSNODATA
EREQINBUFLIMIT EREQINEOF

Errors are similar to $! - they're dualvars, having both textual and numeric values.

NOTE: Since v2.0.0 ETORESOLVE, EDNSNXDOMAIN and EDNSNODATA are not used anymore (EDNS is used instead), but they're still exported for compatibility.

OVERVIEW

You can create IO::Stream object using any "stream" fh (file, TTY, UNIX socket, TCP socket, pipe, FIFO). Or, if you need TCP socket, you can create IO::Stream object using host+port instead of fh (in this case IO::Stream will do non-blocking host resolving, create TCP socket and do non-blocking connect).

After you created IO::Stream object, it will handle read/write on this fh, and deliver only high-level events you asked for into your callback, where you will be able to operate with in/out buffers instead of doing sysread()/syswrite() manually.

There no limitations on what you can do with fh after you've created IO::Stream object - you can even do sysread()/syswrite() (but there no reasons for you to do this anymore).

IMPORTANT! When you want to close this fh, you MUST use $io->close() method for closing fh instead of doing close($fh). This is because IO::Stream doesn't require from you to keep object returned by new(), and without call to $io->close() IO::Stream object will continue to exists and may receive/generate some events, which is not what you expect after closing fh. Also, if you keep object returned by IO::Stream->new() somewhere in your variables, you should either undef all such variables after you called $io->close(), or you should use Scalar::Util::weaken() on these variables after storing IO::Stream object. (The same is applicable for all plugin objects too.)

EVENTS

TIMEOUTS

IO::Stream has 30-second timeouts for connect and write, to timeout DNS resolve it use default AnyEvent::DNS timeout. If you need to timeout other operations, you have to create own timers using EV::timer().

Current version doesn't allow you to change these timeouts.

SERVER

If you need to run TCP/UNIX-server socket, then you should handle that socket manually. But you can create IO::Stream object for accept()'ed socket:

my ($host, $port) = ('0.0.0.0', 1234);
socket  my $srv_sock, AF_INET, SOCK_STREAM, 0;
setsockopt $srv_sock, SOL_SOCKET, SO_REUSEADDR, 1;
bind       $srv_sock, sockaddr_in($port, inet_aton($host));
listen     $srv_sock, SOMAXCONN;
fcntl      $srv_sock, F_SETFL, O_NONBLOCK;
$srv_w = EV::io($srv_sock, EV::READ, sub {
    if (accept my $sock, $srv_sock) {
        IO::Stream->new({
            fh          => $sock,
            cb          => \&server,
            wait_for    => IN,
        });
    }
    elsif ($! != EAGAIN) {
        die "accept: $!";
    }
});

INTERFACE

IO::Stream provide only three public methods: new(), write() and close(). new() will create new object, close() will destroy it and write() must be called when you want to modify (or just modified) output buffer.

All other operations are done using IO::Stream object fields - for simplicity and performance reasons. Moreover, you can keep your own data in it. There convention on field names, to avoid conflicts:

When some event arise which you're waited for, your callback will be called with 3 parameters: IO::Stream object, event mask, and error (if any):

sub callback {
    my ($io, $e, $err) = @_;
}

METHODS

new

IO::Stream->new( \%opt );

Create and return IO::Stream object. You may not keep returned object - you will get it in your callback (in first parameter) when some interesting for your event happens, and will exists until to call method close(). See OVERVIEW for more details.

Fields of %opt become fields of created IO::Stream object. There only few fields required, but you can set any other fields too, and can also set your custom fields (with names starting from upper-case letter).

Only required fields in %opt are {cb} and either {fh} or {host}+{port}. The {wait_for} field also highly recommended to set when creating object.

If {out_buf} will be set, then new() will automatically call write() after creating object.

IO::Stream->new({
    fh          => \*STDIN,
    cb          => \&console,
    wait_for    => IN,
});

write

$io->write();
$io->write($data);

Method write() MUST be called after any modifications of {out_buf} field, to ensure data in {out_buf} will be written to {fh} as soon as it will be possible.

If {fh} available for writing when calling write(), then it will write (may be partially) {out_buf} and may immediately call your callback function delivering OUT|SENT events there. So, if you call write() from that callback (as it usually happens), keep in mind it may be called again while executing write(), and object state may significantly change (it even may be close()'d) after it return from write() into your callback.

The write($data) is just a shortcut for:

$io->{out_buf} .= $data;
$io->write();

close

$io->close()

Method close() will close {fh} and destroy IO::Stream object. See OVERVIEW for more details.

PUBLIC FIELDS

If field marked *RO* that mean field is read-only and shouldn't be changed.

Some field have default values (shown after equal sign).

Some field modified on events.

DIAGNOSTICS

Exceptions may be thrown only in new(). All other errors will be delivered to user's callback in last parameter.

SEE ALSO

AnyEvent::Handle

SUPPORT

Bugs / Feature Requests

Please report any bugs or feature requests through the issue tracker at https://github.com/powerman/perl-IO-Stream/issues. You will be notified automatically of any progress on your issue.

Source Code

This is open source software. The code repository is available for public review and contribution under the terms of the license. Feel free to fork the repository and submit pull requests.

https://github.com/powerman/perl-IO-Stream

git clone https://github.com/powerman/perl-IO-Stream.git

Resources

AUTHOR

Alex Efros powerman@cpan.org

COPYRIGHT AND LICENSE

This software is Copyright (c) 2008- by Alex Efros powerman@cpan.org.

This is free software, licensed under:

The MIT (X11) License