AnyEvent Internals (samples of async programming) Mons Anderson <mons@cpan.org>
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' ; };
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
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 ; } } }
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} \n &quot; ; warn   &quot;Body: &quot; . length ( $body ). &quot; bytes \n &quot; ; $cv -> send ; }, ; $cv -> recv ; $ perl http_request.pl 200: OK Body: 10875 bytes
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; ; # ... }
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  = () }; }
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 ; };
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 \015\012 &quot; . join (   &quot;&quot; ,  map  {  defined  $hdr { $_ } ?   &quot;\u$_: &quot; . delete ( $hdr { $_ }). &quot; \015\012 &quot; :  delete  $hdr { $_ }||() }  keys  %hdr   ). &quot; \015\012 &quot; . delete ( $arg { body }) ); $state { handle }-> push_read   ( line  =>  qr { \015 ? \012 },  sub  { # ... }); },  sub  {   $arg { timeout } }; # ... }
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 }
AnyEvent::Internals AE::HTTP sub  http_request ($$ @ ) { # ... $state { handle }-> push_read   ( line  =>  qr { \015 ? \012 }, sub   { $_ [ 1 ] =~   /^HTTP\/ ([ 0 - 9 \ .]+)  \s + ([ 0 - 9 ]{ 3 }) ( ? :  \s + ([^ \015\012 ]*) ) ? / 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 {( ? <![^ \012 ]) \015 ? \012 }, sub   { # ... } ); } ); # ... }
AnyEvent::Internals AE::HTTP sub  http_request ($$ @ ) { # ... $_[0] -> unshift_read   ( line  =>  qr {( ? <![^ \012 ]) \015 ? \012 }, sub   { for   ( &quot;$_[1]&quot; ) {  y /\015/ / d ; $hdr { lc  $1 } .=   &quot;,$2&quot;   while /  \ G   ([^: \000 - \037 ]*): [ \011\040 ]* ((  ? : [^ \012 ]+ |   \012 [ \011\040 ] )*)   \012   / 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; ; } # ... }); # ... }
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 ]); } }; # ... }
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   { # ...   } # ... }
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  {}); } # ... }
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 \015\012 &quot; . join (   &quot;&quot; ,  map  {  defined  $hdr { $_ } ?   &quot;\u$_: &quot; . delete ( $hdr { $_ }). &quot; \015\012 &quot; :  delete  $hdr { $_ }||() }  keys  %hdr   ). &quot; \015\012 &quot; . delete ( $arg { body }) ); $state { handle }-> push_read   ( line  =>  qr { \015 ? \012 },   sub   { $_ [ 1 ] =~  /^HTTP\/ ([ 0 - 9 \ .]+)  \s + ([ 0 - 9 ]{ 3 }) ( ? :  \s + ([^ \015\012 ]*) ) ? / 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 {( ? <![^ \012 ]) \015 ? \012 },   sub   { for   ( &quot;$_[1]&quot; ) {  y/\015/ / d ; $hdr { lc  $1 } .=   &quot;,$2&quot;   while /  \ G   ([^: \000 - \037 ]*): [ \011\040 ]* ((  ? : [^ \012 ]+ |   \012 [ \011\040 ] )*)   \012   / 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   = () } }
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   { # ... } }, );
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 ; };
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 ; }
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 ; } } ; }
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+) \040 (\S+) \040 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 );   } }); }; }); }
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 {( ? <![^ \012 ]) \015 ? \012 } 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 ]); # ... } ); }
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 }; } }
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 ; }
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; ); } }
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 \015\012 &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; ; } # ...
AnyEvent::Internals AE::HTTPServer # ... $hdr ->{ 'content-length' } =  length  $content if not   ( defined  $hdr ->{ 'content-length' }) and not  ref  $content and   $code   !~   /^(?:1\d\d|[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 }}); } }
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 ;
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; \015\012 $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 }, ); }
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 ; }
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 ; } ); # ...
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 } ); } }
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 ); } }
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 ; }
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 (); }
AnyEvent::Internals Resources Documentation AE
AnyEvent
AnyEvent::Intro Authors

Ae internals

  • 1.
    AnyEvent Internals (samplesof async programming) Mons Anderson <mons@cpan.org>
  • 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.
    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.
    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.
    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} \n &quot; ; warn &quot;Body: &quot; . length ( $body ). &quot; bytes \n &quot; ; $cv -> send ; }, ; $cv -> recv ; $ perl http_request.pl 200: OK Body: 10875 bytes
  • 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.
    AnyEvent::Internals AE::HTTP useAnyEvent :: 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.
    AnyEvent::Internals AE::HTTP useAnyEvent :: Socket ; tcp_connect $host , $port , sub { # Connect sub my ( $fh , $addr , $port ) = @_ ; # ... }, sub { # Prepare sub my ( $socket ) = @_ ; return $timeout ; };
  • 9.
    AnyEvent::Internals AE::HTTP useAnyEvent :: 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 \015\012 &quot; . join ( &quot;&quot; , map { defined $hdr { $_ } ? &quot;\u$_: &quot; . delete ( $hdr { $_ }). &quot; \015\012 &quot; : delete $hdr { $_ }||() } keys %hdr ). &quot; \015\012 &quot; . delete ( $arg { body }) ); $state { handle }-> push_read ( line => qr { \015 ? \012 }, sub { # ... }); }, sub { $arg { timeout } }; # ... }
  • 10.
    AnyEvent::Internals AE::HTTP useAnyEvent :: 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.
    AnyEvent::Internals AE::HTTP sub http_request ($$ @ ) { # ... $state { handle }-> push_read ( line => qr { \015 ? \012 }, sub { $_ [ 1 ] =~ /^HTTP\/ ([ 0 - 9 \ .]+) \s + ([ 0 - 9 ]{ 3 }) ( ? : \s + ([^ \015\012 ]*) ) ? / 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 {( ? <![^ \012 ]) \015 ? \012 }, sub { # ... } ); } ); # ... }
  • 12.
    AnyEvent::Internals AE::HTTP sub http_request ($$ @ ) { # ... $_[0] -> unshift_read ( line => qr {( ? <![^ \012 ]) \015 ? \012 }, sub { for ( &quot;$_[1]&quot; ) { y /\015/ / d ; $hdr { lc $1 } .= &quot;,$2&quot; while / \ G ([^: \000 - \037 ]*): [ \011\040 ]* (( ? : [^ \012 ]+ | \012 [ \011\040 ] )*) \012 / 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.
    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.
    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.
    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.
    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 \015\012 &quot; . join ( &quot;&quot; , map { defined $hdr { $_ } ? &quot;\u$_: &quot; . delete ( $hdr { $_ }). &quot; \015\012 &quot; : delete $hdr { $_ }||() } keys %hdr ). &quot; \015\012 &quot; . delete ( $arg { body }) ); $state { handle }-> push_read ( line => qr { \015 ? \012 }, sub { $_ [ 1 ] =~ /^HTTP\/ ([ 0 - 9 \ .]+) \s + ([ 0 - 9 ]{ 3 }) ( ? : \s + ([^ \015\012 ]*) ) ? / 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 {( ? <![^ \012 ]) \015 ? \012 }, sub { for ( &quot;$_[1]&quot; ) { y/\015/ / d ; $hdr { lc $1 } .= &quot;,$2&quot; while / \ G ([^: \000 - \037 ]*): [ \011\040 ]* (( ? : [^ \012 ]+ | \012 [ \011\040 ] )*) \012 / 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.
    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.
    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.
    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.
    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.
    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+) \040 (\S+) \040 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.
    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 {( ? <![^ \012 ]) \015 ? \012 } 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.
    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.
    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.
    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.
    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 \015\012 &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.
    AnyEvent::Internals AE::HTTPServer #... $hdr ->{ 'content-length' } = length $content if not ( defined $hdr ->{ 'content-length' }) and not ref $content and $code !~ /^(?:1\d\d|[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.
    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.
    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; \015\012 $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.
    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.
    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.
    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.
    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.
    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.
    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.
  • 37.
  • 38.