Successfully reported this slideshow.

Ae internals

1,271 views

Published on

(c) Mons Anderson

Published in: Technology, Business
  • Be the first to comment

Ae internals

  1. 1. AnyEvent Internals (samples of async programming) Mons Anderson <mons@cpan.org>
  2. 2. AnyEvent::Internals AE::* use AE 5 ; my $w = AE::io $fh , 0 , sub { w arn '$fh is readable' ; }; my $w = AE::io $fh , 1 , sub { warn '$fh is writable' ; }; my $w = AE::timer 1 , 0 , sub { warn '1 second passed' ; }; my $w = AE::timer 0 , 1, sub { warn 'every 1 second' ; }; my $w = AE::signal TERM => sub { warn 'TERM received' ; }; my $w = AE::idle { warn 'Event loop is idle' ; };
  3. 3. AnyEvent::Internals AE::cv use AE 5 ; my $cv = AE:: cv ; any sub { async sub { calls sub { $cv -> send ; }; }; }; $cv -> recv ; # Run loop until get send
  4. 4. AnyEvent::Internals AE::cv use AE 5 ; my $cv = AE :: cv { warn &quot;All done&quot; }; for ( 1 . .10 ) { $cv -> begin ; async_call sub { may_be_nested sub { $cv -> end ; } } }
  5. 5. AnyEvent::Internals AE::HTTP use AnyEvent:: HTTP ; my $cv = AE:: cv ; http_request GET => 'http://www.google.ru' , sub { my ( $body , $hdr ) = @_ ; warn &quot;$hdr->{Status}: $hdr->{Reason} &quot; ; warn &quot;Body: &quot; . length ( $body ). &quot; bytes &quot; ; $cv -> send ; }, ; $cv -> recv ; $ perl http_request.pl 200: OK Body: 10875 bytes
  6. 6. AnyEvent::Internals AE::HTTP sub http_request ($$@) { my $cb = pop ; my ( $method , $url ) = (uc shift, shift); my ( %arg ) = ( timeout => 10 , recurse => 10 , @_ ); my %state ; my %hdr = (connection => 'close' ); my $err = sub { %state = (); $cb ->(undef, { Status => $_ [ 1 ], Reason => $_ [ 0 ] || 599 , URL => $_ [ 2 ] || $url } ); }; return $err ->( &quot;Too many redirections&quot; ) if $arg {recurse} < 0 ; while ( my ( $k , $v ) = each %{ $arg {headers}}) { $hdr {lc $k } = $v }; $hdr { &quot;user-agent&quot; } // = &quot;AnyEvent/$AnyEvent::VERSION&quot; ; $hdr { &quot;content-length&quot; } = length $arg {body} if length $arg {body} or $method ne &quot;GET&quot; ; # ... }
  7. 7. AnyEvent::Internals AE::HTTP use AnyEvent :: Socket ; sub http_request ($$@) { # ... my ( $rscheme , $uauthority , $rpath , $query , $fragment ) = $url =~ m{ (?:([^:/?#]+):)?(?://([^/?#]*))? ([^?#]*)(?:?([^#]*))?(?:#(.*))? }x; $rscheme = lc $rscheme ; my $rport = $rscheme eq &quot;http&quot; ? 80 : $rscheme eq &quot;https&quot; ? 443 : return $err ->( &quot;Only http/https are supported&quot; ); $uauthority =~ /^(?: .*@ )? ([^@:]+) (?: : (d+) )?$/ x or return $err ->( &quot;Unparsable URL&quot; ); my $rhost = $1 ; $rport = $2 if defined $2 ; $hdr {host} // = defined $2 ? &quot;$uhost:$2&quot; : &quot;$uhost&quot; ; $rhost =~ s/^[(.*)]$/$1/ ; $rpath .= &quot;?$query&quot; if length $query ; $rpath =~ s%^ /?%/ %; $state {connect_guard} = tcp_connect $rhost , $rport , sub { # ... }, sub { $arg {timeout} }; defined wantarray && AnyEvent::Util::guard { %state = () }; }
  8. 8. AnyEvent::Internals AE::HTTP use AnyEvent :: Socket ; tcp_connect $host , $port , sub { # Connect sub my ( $fh , $addr , $port ) = @_ ; # ... }, sub { # Prepare sub my ( $socket ) = @_ ; return $timeout ; };
  9. 9. AnyEvent::Internals AE::HTTP use AnyEvent :: Handle ; sub http_request ($$ @ ) { # ... $state { connect_guard } = tcp_connect $rhost , $rport , sub { $state { fh } = shift or return $err ->( &quot;$!&quot; ); return unless delete $state { connect_guard }; $state { handle } = new AnyEvent :: Handle fh => $state { fh }, timeout => $arg { timeout }, on_error => sub { $err ->( $_ [ 2 ]) }, on_eof => sub { $err ->( &quot;Unexpected end-of-file&quot; ) } ; $state { handle }-> starttls ( &quot;connect&quot; ) if $rscheme eq &quot;https&quot; ; $state { handle }-> push_write ( &quot;$method $rpath HTTP/1.0 1512 &quot; . join ( &quot;&quot; , map { defined $hdr { $_ } ? &quot;u$_: &quot; . delete ( $hdr { $_ }). &quot; 1512 &quot; : delete $hdr { $_ }||() } keys %hdr ). &quot; 1512 &quot; . delete ( $arg { body }) ); $state { handle }-> push_read ( line => qr { 15 ? 12 }, sub { # ... }); }, sub { $arg { timeout } }; # ... }
  10. 10. AnyEvent::Internals AE::HTTP use AnyEvent :: Handle ; $h -> push_write ( $data ); $h -> push_read ( chunk => $bytes , sub { my ( $h , $data ) = @_ ; # ... }); $h -> push_read ( line => qr /EOL/ , sub { ... } ); $h -> unshift_read ( chunk => $bytes , sub { ... } ); $h -> unshift_read ( line => qr /EOL/ , sub { ... } ); $h -> on_read ( sub { ... } ); $h ->{ rbuf }
  11. 11. AnyEvent::Internals AE::HTTP sub http_request ($$ @ ) { # ... $state { handle }-> push_read ( line => qr { 15 ? 12 }, sub { $_ [ 1 ] =~ /^HTTP/ ([ 0 - 9 .]+) s + ([ 0 - 9 ]{ 3 }) ( ? : s + ([^ 1512 ]*) ) ? / ix or return $err ->( &quot;Invalid server response ($_[1])&quot; ); my %hdr = ( Status => &quot;,$2&quot; , Reason => &quot;,$3&quot; , URL => &quot;,$url&quot; ); $_[0] -> unshift_read ( line => qr {( ? <![^ 12 ]) 15 ? 12 }, sub { # ... } ); } ); # ... }
  12. 12. AnyEvent::Internals AE::HTTP sub http_request ($$ @ ) { # ... $_[0] -> unshift_read ( line => qr {( ? <![^ 12 ]) 15 ? 12 }, sub { for ( &quot;$_[1]&quot; ) { y /15/ / d ; $hdr { lc $1 } .= &quot;,$2&quot; while / G ([^: 00 - 37 ]*): [ 1140 ]* (( ? : [^ 12 ]+ | 12 [ 1140 ] )*) 12 / gxc ; /G$/ or return $err ->( &quot;Garbled response headers&quot; ); } substr $_ , 0 , 1 , '' for values %hdr ; if ( $hdr { location } !~ /^(?: $ | [^:/ ? #]+ : )/x) { $hdr { location } =~ s/^./+/ /; my $url = &quot;$rscheme://$rhost:$rport&quot; ; unless ( $hdr { location } =~ s {^/}{}) { $url .= $rpath ; $url =~ s { /[^/ ]*$}{}; } $hdr { location } = &quot;$url/$hdr{location}&quot; ; } # ... }); # ... }
  13. 13. AnyEvent::Internals AE::HTTP sub http_request ($$ @ ) { # ... my $redirect ; if ( $arg { recurse }) { if ( ( $hdr { Status } =~ /^30[12]$/ and $method ne &quot;POST&quot; ) or ( $hdr { Status } == 307 and $method =~ /^(?:GET|HEAD)$/ ) ) { $redirect = 1 ; } elsif ( $hdr { Status } == 303 ) { $method = &quot;GET&quot; unless $method eq &quot;HEAD&quot; ; $redirect = 1 ; } } my $finish = sub { $state { handle }-> destroy if $state { handle }; %state = (); if ( $redirect && exists $hdr { location }) { http_request $method => $hdr { location }, %arg , recurse => $arg { recurse } - 1 , $cb ; } else { $cb ->( $_ [ 0 ], $_ [ 1 ]); } }; # ... }
  14. 14. AnyEvent::Internals AE::HTTP sub http_request ($$ @ ) { # ... my $len = $hdr { &quot;content-length&quot; }; if ( $hdr { Status } =~ /^(?:1..|[23]04)$/ or $method eq &quot;HEAD&quot; or ( defined $len && ! $len ) ) { # no body $finish ->( &quot;&quot; , %hdr ); } else { # ... } # ... }
  15. 15. AnyEvent::Internals AE::HTTP sub http_request ($$ @ ) { # ... $_ [ 0 ]-> on_eof ( undef ); if ( $len ) { # have content length $_ [ 0 ]-> on_error ( sub { $finish ->( undef , { Status => 599 , Reason => $_ [ 2 ], URL => $url }); }); $_ [ 0 ]-> on_read ( sub { $finish ->( substr ( delete $_ [ 0 ]{ rbuf }, 0 , $len , &quot;&quot; ), %hdr ) if $len <= length $_ [ 0 ]{ rbuf }; }); } else { # have no content-length, read until end $_ [ 0 ]-> on_error ( sub { $!{ EPIPE } || !$! ? $finish ->( delete $_ [ 0 ]{ rbuf }, %hdr ) : $finish ->( undef ,{ Status => 599 , Reason => $_ [ 2 ], URL => $url }); }); $_ [ 0 ]-> on_read ( sub {}); } # ... }
  16. 16. AnyEvent::Internals AE::HTTP use AnyEvent :: Socket ; use AnyEvent :: Handle ; sub http_request ($$ @ ) { my $cb = pop ; my ( $method , $url ) = ( uc shift , shift ); my ( %arg ) = ( timeout => 10 , recurse => 10 , @_ ); my %state ; my %hdr = ( connection => 'close' ); my $err = sub { %state = (); $cb ->( undef , Status => $_ [ 1 ], Reason => $_ [ 0 ]|| 599 , URL => $_ [ 2 ]|| $url ) }; return $err ->( &quot;Too many redirections&quot; ) if $arg { recurse } < 0 ; while ( my ( $k , $v ) = each %{ $arg { headers }}) { $hdr { lc $k } = $v }; my ( $rscheme , $uauthority , $rpath , $query , $fragment ) = $url =~ m |( ? :([^: /?#]+):)?(?://([^/? #]*))?([^?#]*)(?:?([^#]*))?(?:#(.*))?|; $rscheme = lc $rscheme ; my $rport = $rscheme eq &quot;http&quot; ? 80 : $rscheme eq &quot;https&quot; ? 443 : return $err ->( &quot;Only http and https URL schemes supported&quot; ); $uauthority =~ /^(?: .*@ )? ([^@:]+) (?: : (d+) )?$/ x or return $err ->( &quot;Unparsable URL&quot; ); my $rhost = $1 ; $rport = $2 if defined $2 ; $hdr { host } // = defined $2 ? &quot;$rhost:$2&quot; : &quot;$rhost&quot; ; $rhost =~ s/^[(.*)]$/$1/ ; $rpath .= &quot;?$query&quot; if length $query ; $rpath =~ s %^ /?%/ %; $hdr { &quot;user-agent&quot; } // = &quot;AnyEvent/$AnyEvent::VERSION&quot; ; $hdr { &quot;content-length&quot; } = length $arg { body } if length $arg { body } or $method ne &quot;GET&quot; ; $state { connect_guard } = tcp_connect $rhost , $rport , sub { $state { fh } = shift or return %state =(), $err ->( &quot;$!&quot; ); return unless delete $state { connect_guard }; $state { handle } = new AnyEvent :: Handle fh => $state { fh }, timeout => $arg { timeout }, on_error => sub { $err ->( $_ [ 2 ]) }, on_eof => sub { $err ->( &quot;Unexpected end-of-file&quot; ) }, ; $state { handle }-> starttls ( &quot;connect&quot; ) if $rscheme eq &quot;https&quot; ; $state { handle }-> push_write ( &quot;$method $rpath HTTP/1.0 1512 &quot; . join ( &quot;&quot; , map { defined $hdr { $_ } ? &quot;u$_: &quot; . delete ( $hdr { $_ }). &quot; 1512 &quot; : delete $hdr { $_ }||() } keys %hdr ). &quot; 1512 &quot; . delete ( $arg { body }) ); $state { handle }-> push_read ( line => qr { 15 ? 12 }, sub { $_ [ 1 ] =~ /^HTTP/ ([ 0 - 9 .]+) s + ([ 0 - 9 ]{ 3 }) ( ? : s + ([^ 1512 ]*) ) ? / ix or return $err ->( &quot;Invalid server response ($_[1])&quot; ); my %hdr = ( Status => &quot;,$2&quot; , Reason => &quot;,$3&quot; , URL => &quot;,$url&quot; ); $state { handle }-> unshift_read ( line => qr {( ? <![^ 12 ]) 15 ? 12 }, sub { for ( &quot;$_[1]&quot; ) { y/15/ / d ; $hdr { lc $1 } .= &quot;,$2&quot; while / G ([^: 00 - 37 ]*): [ 1140 ]* (( ? : [^ 12 ]+ | 12 [ 1140 ] )*) 12 / gxc ; /G$/ or return $err ->( &quot;Garbled response headers&quot; ); } substr $_ , 0 , 1 , '' for values %hdr ; if ( $hdr { location } !~ /^(?: $ | [^:/? #]+ : )/x) { $hdr { location } =~ s/^./+/ /; my $url = &quot;$rscheme://$rhost:$rport&quot; ; unless ( $hdr { location } =~ s {^/}{}) { $url .= $rpath ; $url =~ s { /[^/ ]*$}{}; } $hdr { location } = &quot;$url/$hdr{location}&quot; ; } my $redirect ; if ( $arg { recurse }) { if ( ( $hdr { Status } =~ /^30[12]$/ and $method ne &quot;POST&quot; ) or ( $hdr { Status } == 307 and $method =~ /^(?:GET|HEAD)$/ ) ) { $redirect = 1 ; } elsif ( $hdr { Status } == 303 ) { $method = &quot;GET&quot; unless $method eq &quot;HEAD&quot; ; $redirect = 1 ; } } my $finish = sub { $state { handle }-> destroy if $state { handle }; %state = (); if ( $redirect && exists $hdr { location }) { http_request $method => $hdr { location }, %arg , recurse => $arg { recurse } - 1 , $cb ; } else { $cb ->( $_ [ 0 ], $_ [ 1 ]); } }; my $len = $hdr { &quot;content-length&quot; }; if ( $hdr { Status } =~ /^(?:1..|[23]04)$/ or $method eq &quot;HEAD&quot; or ( defined $len && ! $len ) ) { # no body $finish ->( &quot;&quot; , %hdr ); } else { $_ [ 0 ]-> on_eof ( undef ); if ( $len ) { # have content length $_ [ 0 ]-> on_error ( sub { $finish ->( undef , { Status => 599 , Reason => $_ [ 2 ], URL => $url }) }); $_ [ 0 ]-> on_read ( sub { $finish ->( substr ( delete $_ [ 0 ]{ rbuf }, 0 , $len , &quot;&quot; ), %hdr ) if $len <= length $_ [ 0 ]{ rbuf }; }); } else { # have no content-length, read until end $_ [ 0 ]-> on_error ( sub { $!{ EPIPE } || !$! ? $finish ->( delete $_ [ 0 ]{ rbuf }, %hdr ) : $finish ->( undef ,{ Status => 599 , Reason => $_ [ 2 ], URL => $url }); }); $_ [ 0 ]-> on_read ( sub {}); } } }); }); }, sub { $arg { timeout } }; defined wantarray && AnyEvent :: Util :: guard { %state = () } }
  17. 17. AnyEvent::Internals AE::HTTPServer my $server = AnyEvent :: HTTP :: Server -> new ( port => 80 , request => sub { my $r = shift ; if ( $r -> wants_websocket ) { $r -> upgrade ( 'websocket' , sub { if ( my $ws = shift ) { $ws -> onmessage ( sub { $ws -> send ( &quot;@_&quot; ); }); $ws -> send ( &quot;Hello!&quot; ); } else { warn &quot;Upgrade failed: @_&quot; ; } }); } else { # ... } }, );
  18. 18. AnyEvent::Internals AE::HTTPServer tcp_server $self ->{ host }, $self ->{ port }, sub { my $fh = shift or return warn &quot;couldn't accept client: $!&quot; ; my ( $host , $port ) = @_ ; my $con = $self ->{ connection_class }-> new ( server => $self , fh => $fh , host => $host , port => $port , on_error => sub { my $con = shift ; warn &quot;@_&quot; ; delete $self ->{ con }{ $con ->{ id }}; }, ); $self ->{ con }{ $con ->{ id }} = $con ; }, sub { 1024 ; };
  19. 19. AnyEvent::Internals AE::HTTPServer sub Connection :: new { my $self = bless {}, shift ; weaken ( my $this = $self ); my %args = @_ ; my $srv = $args { server }; my $fh = $args { fh }; my $h = AnyEvent :: Handle :: Writer -> new ( fh => $args { fh }, on_eof => sub { $this or return ; $this -> error ( &quot;EOF&quot; ) }, on_error => sub { $this or return ; $this -> error ( &quot;$!&quot; ) }, ); weaken ( $self ->{ srv } = $args { server }); $self ->{ id } = int $self ; $self ->{ fh } = $args { fh }; $self ->{ h } = $h ; $self ->{ host } = $args { host }; $self ->{ port } = $args { port }; $self ->{ r } = []; # Request queue $self ->{ ka_timeout } = $srv ->{ keep_alive_timeout } || 30 ; if ( $srv ->{ keep_alive }) { $self ->{ touch } = AE :: now ; $self -> ka_timer ; } $self -> read_header (); return $self ; }
  20. 20. AnyEvent::Internals AE::HTTPServer sub Connection :: ka_timer { my $self = shift ; $self ->{ srv } or return $self -> destroy ; weaken ( my $this = $self ); $self ->{ ka } = AE :: timer $this ->{ ka_timeout } + 1 , 0 , sub { $this or return ; if ( AE :: now - $this ->{ touch } >= $this ->{ ka_timeout }) { $this -> close ; } else { $this -> ka_timer ; } } ; }
  21. 21. AnyEvent::Internals AE::HTTPServer sub Connection :: read_header { my $self = shift ; $self ->{ srv } or return $self -> destroy ; weaken ( my $con = $self ); $con ->{ h }-> push_read ( chunk => 3 => sub { $con or return ; shift ; my $pre = shift ; if ( $pre =~ m {^<}) { $con ->{ h }-> unshift_read ( regex => qr {.+ ? >} => sub { my $xml = $pre . shift (); # Hanlde XML here $con -> destroy (); }); } else { $con ->{ h }-> unshift_read ( line => sub { $con or return ; shift ; my $line = $pre . shift ; if ( $line =~ /(S+) 40 (S+) 40 HTTP/ ( d + . d +)/ xso ) { my ( $meth , $url , $hv ) = ( $1 , $2 , $3 ); $con ->{ method } = $meth ; $con ->{ uri } = $url ; $con ->{ version } = $hv ; $con -> read_headers (); } elsif ( $line eq '' ) { $con -> read_header (); } else { $con -> fatal_error ( 400 ); } }); }; }); }
  22. 22. AnyEvent::Internals AE::HTTPServer sub Connection :: read_headers { my ( $self ) = @_ ; $self ->{ srv } or return $self -> destroy ; weaken ( my $con = $self ); $self ->{ h }-> unshift_read ( line => qr {( ? <![^ 12 ]) 15 ? 12 } o , sub { $con or return ; my ( $h , $data ) = @_ ; my $hdr = HTTP :: Easy :: Headers -> decode ( $data ) // return $con -> fatal_error ( 400 ); my $r = $con ->{ srv }{ request_class }-> new ( method => delete $con ->{ method }, uri => delete $con ->{ uri }, host => $hdr ->{ Host }, headers => $hdr , ); push @ { $con ->{ r } }, $r ; weaken ( $r ->{ con } = $con ); weaken ( $con ->{ r }[- 1 ]); # ... } ); }
  23. 23. AnyEvent::Internals AE::HTTPServer sub { $con or return ; # ... $hdr ->{ connection } = lc $hdr ->{ connection }; if ( $hdr ->{ connection } eq 'close' or $con ->{ version } < 1.1 ) { delete $con ->{ ka }; $con ->{ type } = 'close' ; $con ->{ close } = 1 ; } elsif ( $hdr ->{ connection } =~ /keep-alive/ ) { $con ->{ close } = 0 ; $con ->{ type } = 'keep-alive' ; } elsif ( $hdr ->{ connection } eq 'upgrade' ) { delete $con ->{ ka }; $con ->{ type } = 'upgrade' ; $con ->{ close } = 0 ; } if ( defined $hdr ->{ 'content-length' }) { $con ->{ h }-> unshift_read ( chunk => $hdr ->{ 'content-length' }, sub { my ( $hdl , $data ) = @_ ; $con -> handle_request ( $r , $data ); $con -> read_header () if $con ->{ ka }; }); } else { $con -> handle_request ( $r ); $con -> read_header () if $con ->{ ka }; } }
  24. 24. AnyEvent::Internals AE::HTTPServer sub Request :: response { my $self = shift ; $self ->{ con } or return $self -> dispose ; $self ->{ con }-> response ( $self , @_ ); $self -> dispose ; } sub Request :: dispose { my $self = shift ; return % $self = (); } sub Request :: DESTROY { my $self = shift ; $self ->{ con } or return % $self = (); $self ->{ con }-> response ( $self , 404 , '' , msg => &quot;Request not handled&quot; ); } sub Request :: wants_websocket { my $self = shift ; return lc $self ->{ headers }{ connection } eq 'upgrade' && lc $self ->{ headers }{ upgrade } eq 'websocket' ? 1 : 0 ; }
  25. 25. AnyEvent::Internals AE::HTTPServer sub Request :: upgrade { my $self = shift ; my $cb = pop ; my $type = shift ; my $headers = shift || HTTP:: Easy:: Headers-> new ({}); if ( lc $type eq 'websocket' ) { $headers ->{ upgrade} = 'WebSocket' ; $headers ->{ connection} = 'Upgrade' ; $headers ->{ 'websocket-origin' } ||= $self ->{headers}{ origin}; $headers ->{ 'websocket-location' } ||= do { my $loc = URI-> new_abs ( $self ->{ uri}, &quot;http://$self->{headers}{host}&quot; ); $loc = &quot;$loc&quot; ; $loc =~ s{^ http}{ ws}; $loc ; }; $self ->{ con}-> response ( $self , 101 , '' , headers => $headers , msg => &quot;Web Socket Protocol Handshake&quot; ); my $ws = $self ->{con}{ srv}{ websocket_class}-> new ( con => $self ->{con}, ); $self -> dispose ; return $cb ->( $ws ); } else { return $cb ->(undef, &quot;Unsupported upgrade type: $type&quot; ); } }
  26. 26. AnyEvent::Internals AE::HTTPServer sub Connection :: response { my ( $con , $r , $code , $content , %args ) = @_ ; my $msg = $args { msg } || $HTTP :: Easy :: Status :: MSG { $code } ; my $hdr = $args { headers } || HTTP :: Easy :: Headers -> new ({}); # Resolve pipeline if ( @ { $con ->{ r }} and $con ->{ r }[ 0 ] == $r ) { shift @ { $con ->{ r } }; } else { $r ->{ ready } = [ $code , $msg , $hdr , $content ]; return ; } my $res = &quot;HTTP/$con->{version} $code $msg 1512 &quot; ; $hdr ->{ 'content-type' } ||= 'text/html' ; if ( ref $content eq 'HASH' ) { if ( $content ->{ sendfile }) { $content ->{ size } = $hdr ->{ 'content-length' } = - s $content ->{ sendfile }; } } $hdr ->{ connection } ||= $con ->{ type }; if ( $code >= 400 and ! length $content ) { $content = &quot;<h1>$code $msg</h1>&quot; ; } # ...
  27. 27. AnyEvent::Internals AE::HTTPServer # ... $hdr ->{ 'content-length' } = length $content if not ( defined $hdr ->{ 'content-length' }) and not ref $content and $code !~ /^(?:1dd|[23]04)$/ ; $res .= $hdr -> encode (); $con ->{ h }-> push_write ( $res ); if ( ref $content eq 'HASH' ) { if ( $content ->{ sendfile }) { $con ->{ h }-> push_sendfile ( $content ->{ sendfile }, $content ->{ size }); } else { die &quot;Bad response content&quot; ; } } else { $con ->{ h }-> push_write ( $content ) if length $content ; } if ( $con ->{ close }) { $con -> close (); } elsif ( @ { $con ->{ r }} and $con ->{ r }[ 0 ]{ ready }) { $con -> response ( $con ->{ r }[ 0 ], @ { $con ->{ r }[ 0 ]{ ready }}); } }
  28. 28. AnyEvent::Internals AE::Memcached my $cv = AE :: cv ; $cv -> begin ; $cv -> begin ( sub { $cv -> send }); my $memd = AnyEvent :: Memcached -> new ( servers => [ '127.0.0.1:11211' ], cv => $cv , namespace => &quot;test:&quot; , ); $memd -> set ( &quot;key1&quot; , &quot;val1&quot; , cb => sub { shift or return warn &quot;Set key1 failed: @_&quot; ; $memd -> get ( &quot;key1&quot; , cb => sub { my ( $v , $e ) = @_ ; $e and return warn &quot;Get failed: $e&quot; ; warn &quot;Got value for key1: $v&quot; ; }); }); $cv -> end ; $cv -> recv ;
  29. 29. AnyEvent::Internals AE::Memcached sub set { shift -> _set ( set => @_ ) } sub _set { my $self , $cmd , $key ) = splice @_ , 0 , 3 ; my $cas ; if ( $cmd eq 'cas' ) { $cas = shift ; } my $val = shift ; # Some preparations... $self -> _do ( $key , &quot;$cmd $self->{namespace}%s $flags $expire $len&quot; . ( defined $cas ? ' ' . $cas : '' ). &quot; 1512 $val&quot; , sub { local $_ = shift ; if ( $_ eq 'STORED' ) { return 1 } elsif ( $_ eq 'NOT_STORED' ) { return 0 } elsif ( $_ eq 'EXISTS' ) { return 0 } else { return undef , $_ } }, cb => $args { cb }, ); }
  30. 30. AnyEvent::Internals AE::Memcached sub _do { my ( $self , $key , $com , $wrk , %args ) = @_ ; my $servers = $self ->{ hash }-> servers ( $key ); my %res ; my %err ; my $res ; $_ and $_ -> begin for $self ->{ cv }, $args { cv }; my $cv = AE :: cv { if ( $res != - 1 ) { $args { cb }( $res ); } else { $args { cb }( undef , dumper ( %err ) ); } $_ and $_ -> end for $args { cv }, $self ->{ cv }; }; for my $srv ( keys % $servers ) { for my $real ( @ { $servers ->{ $srv } }) { $cv -> begin ; my $cmd = $com ; substr ( $cmd , index ( $cmd , '%s' ), 2 ) = $real ; $self ->{ peers }{ $srv }{ con }-> command ( '...' ); } } return ; }
  31. 31. AnyEvent::Internals AE::Memcached # ... $self ->{ peers }{ $srv }{ con }-> command ( $cmd , cb => sub { if ( defined ( local $_ = shift )) { my ( $ok , $fail ) = $wrk ->( $_ ); if ( defined $ok ) { $res { $real }{ $srv } = $ok ; $res = (! defined $res ) || $res == $ok ? $ok : - 1 ; } else { $err { $real }{ $srv } = $fail ; $res = - 1 ; } } else { $err { $real }{ $srv } = $_ ; $res = - 1 ; } $cv -> end ; } ); # ...
  32. 32. AnyEvent::Internals AE::Memcached sub Peer :: command { my $self = shift ; if ( $self ->{ connected }) { return $self ->{ con }-> command ( @_ ); } else { my ( $cmd , %args ) = @_ ; $self -> conntrack ( command => @_ , $args { cb } ); } }
  33. 33. AnyEvent::Internals AE::Memcached sub Peer :: conntrack { my $self = shift ; my ( $method , $args , $cb ) = @_ ; if ( $self ->{ connecting } and $self ->{ failed }) { $cb and $cb ->( undef , &quot;Not connected&quot; ); return ; } elsif (! $self ->{ connected }) { # connect } else { return $self ->{ con }-> $method ( @ $args ); } }
  34. 34. AnyEvent::Internals AE::Memcached sub Peer :: connect { my $self = shift ; $self ->{ connecting } and return ; $self -> reg_cb ( connected => sub { $self ->{ failed } = 0 ; } ); $self -> reg_cb ( connfail => sub { $self ->{ failed } = 1 ; } ); $self -> reg_cb ( disconnect => sub { shift ; shift ; % $self or return ; my $e = @_ ? &quot;@_&quot; : &quot;disconnected&quot; ; for ( keys %{ $self ->{ waitingcb }} ) { if ( $self ->{ waitingcb }{ $_ }) { $self ->{ waitingcb }{ $_ }( undef , $e ); } delete $self ->{ waitingcb }{ $_ }; } } ); $self -> next :: method ( @_ ); return ; }
  35. 35. AnyEvent::Internals AE::Memcached elsif (! $self ->{ connected }) { my @args = @ $args ; # copy to avoid rewriting my ( $c , $t ); weaken ( $self ); weaken ( $self ->{waitingcb}{int $cb } = $cb ) if $cb ; $c = $self -> reg_cb ( connected => sub { shift -> unreg_me ; undef $c ; undef $t ; $self or return ; delete $self ->{waitingcb}{int $cb } if $cb ; return $self ->{con}-> $method ( @args ); }); $t = AE:: timer $self ->{timeout}, 0, sub { undef $c ;undef $t ; $self or return ; if ( $cb ){ delete $self ->{waitingcb}{int $cb }; $cb ->(undef, &quot;Connect timeout&quot; ); } }, ); $self -> connect (); }
  36. 36. AnyEvent::Internals Resources Documentation <ul><li>AE
  37. 37. AnyEvent
  38. 38. AnyEvent::Intro Authors
  39. 39. Marc Lehmann (MLEHMANN)
  40. 40. Robin Redeker (ELMEX)
  41. 41. Mons Anderson (MONS) </li></ul>
  42. 42. AnyEvent::Internals JFDI Use Perl or die;
  43. 43. AnyEvent::Internals Author Mons Anderson ( aka Владимир Перепелица ) Rambler Internet Holding < [email_address] > < [email_address] > DevConf::Perl() © 2010 $cv -> send ;

×