Net-Curl

 view release on metacpan or  search on metacpan

lib/Net/Curl/examples.pod  view on Meta::CPAN

     $self->SUPER::add_handle( $easy );
 }

 # perform until some handle finishes, does all the magic needed
 # to make it efficient (check as soon as there is some data)
 # without overusing the cpu.
 sub get_one($)
 {
     my $self = shift;

     if ( my @result = $self->info_read() ) {
         $self->remove_handle( $result[ 1 ] );
         return @result;
     }

     while ( $$self ) {
         my $t = $self->timeout;
         if ( $t != 0 ) {
             $t = 10000 if $t < 0;
             my ( $r, $w, $e ) = $self->fdset;

             select $r, $w, $e, $t / 1000;
         }

         my $ret = $self->perform();
         if ( $$self != $ret ) {
             $$self = $ret;
             if ( my @result = $self->info_read() ) {
                 $self->remove_handle( $result[ 1 ] );
                 return @result;
             }
         }
     };

     return ();
 }

 1;

=head2 TEST APPLICATION

Sample application using this module looks like this:

 #!perl
 use strict;
 use warnings;
 use Multi::Simple;
 use Net::Curl::Share qw(:constants);


 sub easy
 {
     my $uri = shift;
     my $share = shift;

     require Net::Curl::Easy;

     my $easy = Net::Curl::Easy->new( { uri => $uri, body => '' } );
     $easy->setopt( Net::Curl::Easy::CURLOPT_VERBOSE(), 1 );
     $easy->setopt( Net::Curl::Easy::CURLOPT_URL(), $uri );
     $easy->setopt( Net::Curl::Easy::CURLOPT_WRITEHEADER(),
         \$easy->{headers} );
     $easy->setopt( Net::Curl::Easy::CURLOPT_FILE(),
         \$easy->{body} );
     $easy->setopt( Net::Curl::Easy::CURLOPT_SHARE(), $share );

     # This wasn't needed prior to curl 7.67, which changed the interface
     # so that an easy that uses a cookie-share now requires an explicit
     # cookie-engine enable to use cookies. Previously the easy's use of
     # a cookie-share implicitly enabled the easy's cookie engine.
     $easy->setopt( Net::Curl::Easy::CURLOPT_COOKIEFILE(), q<> );

     return $easy;
 }

 my $multi = Multi::Simple->new();

 my @uri = (
     "http://www.google.com/search?q=perl",
     "http://www.google.com/search?q=curl",
     "http://www.google.com/search?q=perl+curl",
 );

 {
     # share cookies between all handles
     my $share = Net::Curl::Share->new();
     $share->setopt( CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE );
     $multi->add_handle( easy( shift ( @uri ), $share ) );
 }

 my $ret = 0;
 while ( my ( $msg, $easy, $result ) = $multi->get_one() ) {
     print "\nFinished downloading $easy->{uri}: $result:\n";
     printf "Body is %d bytes long\n", length $easy->{body};
     print "=" x 80 . "\n";

     $ret = 1 if $result;

     $multi->add_handle( easy( shift ( @uri ), $easy->share ) )
         if @uri;
 }

 exit $ret;

=cut
=head1 Multi::Event

=head4 I<Extracted from C<examples/03-multi-event.pl>>

This module shows how to use Net::Curl::Multi interface with an event
library, AnyEvent in this case.

=head2 Motivation

This is the most efficient method for using Net::Curl::Multi interface,
but it requires a really good understanding of it. This code tries to show
the quirks found when using event-based programming.

=head2 MODULE CODE

 package Multi::Event;

lib/Net/Curl/examples.pod  view on Meta::CPAN

     # Calling socket_action with default arguments will trigger
     # socket callback and register IO events.
     #
     # It _must_ be called _after_ add_handle(); AE will take care
     # of that.
     #
     # We are delaying the call because in some cases socket_action
     # may finish inmediatelly (i.e. there was some error or we used
     # persistent connections and server returned data right away)
     # and it could confuse our application -- it would appear to
     # have finished before it started.
     AE::timer 0, 0, sub {
         $multi->socket_action();
     };

     $multi->SUPER::add_handle( $easy );
 }

 # perform and call any callbacks that have finished
 sub socket_action
 {
     my $multi = shift;

     my $active = $multi->SUPER::socket_action( @_ );
     return if $multi->{active} == $active;

     $multi->{active} = $active;

     while ( my ( $msg, $easy, $result ) = $multi->info_read() ) {
         if ( $msg == Net::Curl::Multi::CURLMSG_DONE ) {
             $multi->remove_handle( $easy );
             $easy->finish( $result );
         } else {
             die "I don't know what to do with message $msg.\n";
         }
     }
 }

 1;

=head2 TEST Easy package

Multi::Event requires Easy object to provide finish() method.

 package Easy::Event;
 use strict;
 use warnings;
 use Net::Curl::Easy qw(/^CURLOPT_/);
 use base qw(Net::Curl::Easy);

 sub new
 {
     my $class = shift;
     my $uri = shift;
     my $cb = shift;

     my $easy = $class->SUPER::new(
         { uri => $uri, body => '', cb => $cb }
     );
     $easy->setopt( CURLOPT_URL, $uri );
     $easy->setopt( CURLOPT_WRITEHEADER, \$easy->{headers} );
     $easy->setopt( CURLOPT_FILE, \$easy->{body} );

     return $easy;
 }

 sub finish
 {
     my ( $easy, $result ) = @_;

     printf "\nFinished downloading %s: %s: %d bytes\n",
         $easy->{uri}, $result, length $easy->{body};

     $easy->{cb}->( $easy->{body} );
 }

 1;

=head2 TEST APPLICATION

 #!perl
 use strict;
 use warnings;
 use Easy::Event;
 use Multi::Event;
 use AnyEvent;

 my $multi = Multi::Event->new();
 my $cv = AE::cv;


 my @uris = (
     "http://www.google.com/search?q=perl",
     "http://www.google.com/search?q=curl",
     "http://www.google.com/search?q=perl+curl",
 );


 my $i = scalar @uris;
 sub done
 {
     my $body = shift;

     # process...

     unless ( --$i ) {
         $cv->send;
     }
 }

 my $timer;
 $timer = AE::timer 0, 0.1, sub {
     my $uri = shift @uris;
     $multi->add_handle( Easy::Event->new( $uri, \&done ) );

     unless ( @uris ) {
         undef $timer;
     }
 };

 $cv->recv;

lib/Net/Curl/examples.pod  view on Meta::CPAN


     # create a shared share object
     my $self :shared = $class->SUPER::new( \%base );

     # share both cookies and dns
     $self->setopt( CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE );
     $self->setopt( CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS );

     # Net::Curl::Share locks each datum automatically, this will
     # prevent memory corruption.
     #
     # we use semaphore to lock share completely
     $self->{sem} = Thread::Semaphore->new();

     return $self;
 }

 # this locks way too much, but works as expected
 sub lock
 {
     my $share = shift;
     $share->{sem}->down();
     $share->{blocker} = threads->tid();
 }

 sub unlock
 {
     my $share = shift;
     unless ( exists $share->{blocker} ) {
         warn "Tried to unlock share that wasn't locked\n";
         return;
     }
     unless ( $share->{blocker} == threads->tid() ) {
         warn "Tried to unlock share from another thread\n";
         return;
     }
     delete $share->{blocker};
     $share->{sem}->up();
 }

 1;

=head2 TEST Easy package

This Easy::Threads object will block whole share object for duration of dns
name resolution and until headers are completely received.

 package Easy::Threads;
 use strict;
 use warnings;
 use Net::Curl::Easy qw(/^CURLOPT_.*/);
 use base qw(Net::Curl::Easy);

 sub new
 {
     my $class = shift;
     my $share = shift;

     my $easy = $class->SUPER::new( { body => '', head => '' } );
     $easy->setopt( CURLOPT_VERBOSE, 1 );
     $easy->setopt( CURLOPT_WRITEHEADER, \$easy->{head} );
     $easy->setopt( CURLOPT_FILE, \$easy->{body} );
     $easy->setopt( CURLOPT_HEADERFUNCTION, \&cb_header );
     $easy->setopt( CURLOPT_SHARE, $share );

     return $easy;
 }

 sub cb_header {
     my ( $easy, $data, $uservar ) = @_;

     if ( $data eq "\r\n" ) {
         # we have all the headers now, allow other threads to run
         $easy->share->unlock()
             unless $easy->{unlocked};

         $easy->{unlocked} = 1;
     }

     $$uservar .= $data;

     return length $data;
 }

 sub get
 {
     my $easy = shift;
     my $uri = shift;

     $easy->setopt( CURLOPT_URL, $uri );
     $easy->{uri} = $uri;
     $easy->{body} = '';
     $easy->{head} = '';
     delete $easy->{unlocked};

     # lock share
     $easy->share->lock();

     # ok, now we can request
     eval {
         $easy->perform();
     };

     # There may have been some problem, make sure we unlock the share.
     # This should issue a warning, check $easy->{unlocked} to see
     # whether we really need to unlock.
     $easy->share->unlock();

     # return something
     return $easy->{body};
 }

 1;

=head2 TEST APPLICATION

Sample application using this module looks like this:

 #!perl
 use threads;
 use threads::shared;

lib/Net/Curl/examples.pod  view on Meta::CPAN

     }, '' );

     $multi->{active} = -1;
     $multi->SUPER::add_handle( $easy );
 }

 # perform and call any callbacks that have finished
 sub socket_action
 {
     my $multi = shift;

     my $active = $multi->SUPER::socket_action( @_ );
     return if $multi->{active} == $active;

     $multi->{active} = $active;

     while ( my ( $msg, $easy, $result ) = $multi->info_read() ) {
         if ( $msg == Net::Curl::Multi::CURLMSG_DONE ) {
             $multi->remove_handle( $easy );
             $easy->finish( $result );
         } else {
             die "I don't know what to do with message $msg.\n";
         }
     }
 }


 # we use just one global multi object
 my $multi;

 # put the add() function in some package we know
 sub Net::Curl::Multi::add($)
 {
     unless ( $multi ) {
         $multi = __PACKAGE__->new();
     }
     $multi->add_handle( shift );
 }


 package Irssi::Curl::Easy;
 use strict;
 use warnings;
 use Net::Curl;
 use Net::Curl::Easy qw(/^CURLOPT_/);
 use base qw(Net::Curl::Easy);

 my $has_zlib = ( Net::Curl::version_info()->{features}
     & Net::Curl::CURL_VERSION_LIBZ ) != 0;

 sub new
 {
     my $class = shift;
     my $uri = shift;
     my $cb = shift;

     my $easy = $class->SUPER::new(
         { body => '', headers => '' }
     );
     # some sane defaults
     $easy->setopt( CURLOPT_WRITEHEADER, \$easy->{headers} );
     $easy->setopt( CURLOPT_FILE, \$easy->{body} );
     $easy->setopt( CURLOPT_TIMEOUT, 300 );
     $easy->setopt( CURLOPT_CONNECTTIMEOUT, 60 );
     $easy->setopt( CURLOPT_MAXREDIRS, 20 );
     $easy->setopt( CURLOPT_FOLLOWLOCATION, 1 );
     $easy->setopt( CURLOPT_ENCODING, 'gzip,deflate' ) if $has_zlib;
     $easy->setopt( CURLOPT_SSL_VERIFYPEER, 0 );
     $easy->setopt( CURLOPT_COOKIEFILE, '' );
     $easy->setopt( CURLOPT_USERAGENT, 'Irssi + Net::Curl' );

     return $easy;
 }

 sub finish
 {
     my ( $easy, $result ) = @_;
     $easy->{referer} = $easy->getinfo(
         Net::Curl::Easy::CURLINFO_EFFECTIVE_URL
     );

     my $cb = $easy->{cb};
     $cb->( $easy, $result );
 }

 sub _common_add
 {
     my ( $easy, $uri, $cb ) = @_;
     if ( $easy->{referer} ) {
         $easy->setopt( CURLOPT_REFERER, $easy->{referer} );
     }
     $easy->setopt( CURLOPT_URL, $uri );
     $easy->{uri} = $uri;
     $easy->{cb} = $cb;
     $easy->{body} = '';
     $easy->{headers} = '';
     Net::Curl::Multi::add( $easy );
 }

 # get some uri
 sub get
 {
     my ( $easy, $uri, $cb ) = @_;
     $easy->setopt( CURLOPT_HTTPGET, 1 );
     $easy->_common_add( $uri, $cb );
 }

 # request head on some uri
 sub head
 {
     my ( $easy, $uri, $cb ) = @_;
     $easy->setopt( CURLOPT_NOBODY, 1 );
     $easy->_common_add( $uri, $cb );
 }

 # post data to some uri
 sub post
 {
     my ( $easy, $uri, $cb, $post ) = @_;
     $easy->setopt( CURLOPT_POST, 1 );
     $easy->setopt( CURLOPT_POSTFIELDS, $post );



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