--- AnyEvent/lib/AnyEvent/Handle.pm 2009/06/30 22:42:33 1.131 +++ AnyEvent/lib/AnyEvent/Handle.pm 2009/07/06 01:03:09 1.141 @@ -16,7 +16,7 @@ =cut -our $VERSION = 4.45; +our $VERSION = 4.452; =head1 SYNOPSIS @@ -97,7 +97,7 @@ If an EOF condition has been detected but no C callback has been set, then a fatal error will be raised with C<$!> set to <0>. -=item on_error => $cb->($handle, $fatal) +=item on_error => $cb->($handle, $fatal, $message) This is the error callback, which is called when, well, some error occured, such as not being able to resolve the hostname, failure to @@ -109,13 +109,19 @@ errors are an EOF condition with active (but unsatisifable) read watchers (C) or I/O errors. +AnyEvent::Handle tries to find an appropriate error code for you to check +against, but in some cases (TLS errors), this does not work well. It is +recommended to always output the C<$message> argument in human-readable +error messages (it's usually the same as C<"$!">). + Non-fatal errors can be retried by simply returning, but it is recommended to simply ignore this parameter and instead abondon the handle object when this callback is invoked. Examples of non-fatal errors are timeouts C) or badly-formatted data (C). On callback entrance, the value of C<$!> contains the operating system -error (or C, C, C or C). +error code (or C, C, C, C or +C). While not mandatory, it is I recommended to set this callback, as you will not be notified of errors otherwise. The default simply calls @@ -129,7 +135,7 @@ read buffer). To access (and remove data from) the read buffer, use the C<< ->rbuf >> -method or access the C<$handle->{rbuf}> member directly. Note that you +method or access the C<< $handle->{rbuf} >> member directly. Note that you must not enlarge or modify the read buffer, you can only remove data at the beginning from it. @@ -239,11 +245,13 @@ yet. This data will be lost. Calling the C method in time might help. -=item common_name => $string +=item peername => $string + +A string used to identify the remote site - usually the DNS hostname +(I IDN!) used to create the connection, rarely the IP address. -The common name used by some verification methods (most notably SSL/TLS) -associated with this connection. Usually this is the remote hostname used -to connect, but can be almost anything. +Apart from being useful in error messages, this string is also used in TLS +peername verification (see C in L). =item tls => "accept" | "connect" | Net::SSLeay::SSL object @@ -251,6 +259,9 @@ AnyEvent will start a TLS handshake as soon as the conenction has been established and will transparently encrypt/decrypt data afterwards. +All TLS protocol errors will be signalled as C, with an +appropriate error message. + TLS mode requires Net::SSLeay to be installed (it will be loaded automatically when you try to create a TLS handle): this module doesn't have a dependency on that module, so if your module requires it, you have @@ -329,24 +340,25 @@ sub _shutdown { my ($self) = @_; - delete @$self{qw(_tw _rw _ww fh rbuf wbuf on_read _queue)}; + delete @$self{qw(_tw _rw _ww fh wbuf on_read _queue)}; $self->{_eof} = 1; # tell starttls et. al to stop trying &_freetls; } sub _error { - my ($self, $errno, $fatal) = @_; + my ($self, $errno, $fatal, $message) = @_; $self->_shutdown if $fatal; $! = $errno; + $message ||= "$!"; if ($self->{on_error}) { - $self->{on_error}($self, $fatal); + $self->{on_error}($self, $fatal, $message); } elsif ($self->{fh}) { - Carp::croak "AnyEvent::Handle uncaught error: $!"; + Carp::croak "AnyEvent::Handle uncaught error: $message"; } } @@ -669,6 +681,27 @@ =back +=item $handle->push_shutdown + +Sometimes you know you want to close the socket after writing your data +before it was actually written. One way to do that is to replace your +C handler by a callback that shuts down the socket. This method +is a shorthand for just that, and replaces the C callback with: + + sub { shutdown $_[0]{fh}, 1 } # for push_shutdown + +This simply shuts down the write side and signals an EOF condition to the +the peer. + +You can rely on the normal read queue and C handling +afterwards. This is the cleanest way to close a connection. + +=cut + +sub push_shutdown { + $_[0]->{on_drain} = sub { shutdown $_[0]{fh}, 1 }; +} + =item AnyEvent::Handle::register_write_type type => $coderef->($handle, @args) This function (not method) lets you add your own types to C. @@ -825,7 +858,7 @@ if ($self->{on_eof}) { $self->{on_eof}($self) } else { - $self->_error (0, 1); + $self->_error (0, 1, "Unexpected end-of-file"); } } @@ -1185,13 +1218,13 @@ register_read_type json => sub { my ($self, $cb) = @_; - require JSON; + my $json = $self->{json} ||= + eval { require JSON::XS; JSON::XS->new->utf8 } + || do { require JSON; JSON->new->utf8 }; my $data; my $rbuf = \$self->{rbuf}; - my $json = $self->{json} ||= JSON->new->utf8; - sub { my $ref = eval { $json->incr_parse ($self->{rbuf}) }; @@ -1344,7 +1377,29 @@ } } +our $ERROR_SYSCALL; +our $ERROR_WANT_READ; +our $ERROR_ZERO_RETURN; + +sub _tls_error { + my ($self, $err) = @_; + + return $self->_error ($!, 1) + if $err == Net::SSLeay::ERROR_SYSCALL (); + + my $err =Net::SSLeay::ERR_error_string (Net::SSLeay::ERR_get_error ()); + + # reduce error string to look less scary + $err =~ s/^error:[0-9a-fA-F]{8}:[^:]+:([^:]+):/\L$1: /; + + $self->_error (&Errno::EPROTO, 1, $err); +} + # poll the write BIO and send the data if applicable +# also decode read data if possible +# this is basiclaly our TLS state machine +# more efficient implementations are possible with openssl, +# but not with the buggy and incomplete Net::SSLeay. sub _dotls { my ($self) = @_; @@ -1354,6 +1409,12 @@ while (($tmp = Net::SSLeay::write ($self->{tls}, $self->{_tls_wbuf})) > 0) { substr $self->{_tls_wbuf}, 0, $tmp, ""; } + + $tmp = Net::SSLeay::get_error ($self->{tls}, $tmp); + return $self->_tls_error ($tmp) + if $tmp != $ERROR_WANT_READ + && ($tmp != $ERROR_SYSCALL || $!) + && $tmp != $ERROR_ZERO_RETURN; } while (defined ($tmp = Net::SSLeay::read ($self->{tls}))) { @@ -1370,16 +1431,10 @@ } $tmp = Net::SSLeay::get_error ($self->{tls}, -1); - - if ($tmp != Net::SSLeay::ERROR_WANT_READ ()) { - if ($tmp == Net::SSLeay::ERROR_SYSCALL ()) { - return $self->_error ($!, 1); - } elsif ($tmp == Net::SSLeay::ERROR_SSL ()) { - return $self->_error (&Errno::EIO, 1); - } - - # all other errors are fine for our purposes - } + return $self->_tls_error ($tmp) + if $tmp != $ERROR_WANT_READ + && ($tmp != $ERROR_SYSCALL || $!) + && $tmp != $ERROR_ZERO_RETURN; while (length ($tmp = Net::SSLeay::BIO_read ($self->{_wbio}))) { $self->{wbuf} .= $tmp; @@ -1411,6 +1466,8 @@ =cut +our %TLS_CACHE; #TODO not yet documented, should we? + sub starttls { my ($self, $ssl, $ctx) = @_; @@ -1419,17 +1476,27 @@ Carp::croak "it is an error to call starttls more than once on an AnyEvent::Handle object" if $self->{tls}; + $ERROR_SYSCALL = Net::SSLeay::ERROR_SYSCALL (); + $ERROR_WANT_READ = Net::SSLeay::ERROR_WANT_READ (); + $ERROR_ZERO_RETURN = Net::SSLeay::ERROR_ZERO_RETURN (); + $ctx ||= $self->{tls_ctx}; if ("HASH" eq ref $ctx) { require AnyEvent::TLS; local $Carp::CarpLevel = 1; # skip ourselves when creating a new context - $ctx = new AnyEvent::TLS %$ctx; + + if ($ctx->{cache}) { + my $key = $ctx+0; + $ctx = $TLS_CACHE{$key} ||= new AnyEvent::TLS %$ctx; + } else { + $ctx = new AnyEvent::TLS %$ctx; + } } $self->{tls_ctx} = $ctx || TLS_CTX (); - $self->{tls} = $ssl = $self->{tls_ctx}->_get_session ($ssl, $self); + $self->{tls} = $ssl = $self->{tls_ctx}->_get_session ($ssl, $self, $self->{peername}); # basically, this is deep magic (because SSL_read should have the same issues) # but the openssl maintainers basically said: "trust us, it just works". @@ -1521,8 +1588,8 @@ =item $handle->destroy Shuts down the handle object as much as possible - this call ensures that -no further callbacks will be invoked and resources will be freed as much -as possible. You must not call any methods on the object afterwards. +no further callbacks will be invoked and as many resources as possible +will be freed. You must not call any methods on the object afterwards. Normally, you can just "forget" any references to an AnyEvent::Handle object and it will simply shut down. This works in fatal error and EOF