AMF-Connection

 view release on metacpan or  search on metacpan

examples/amfclient.pl  view on Meta::CPAN

          'flex.messaging.io.ArrayCollection'
        );
  }

my $endpoint = 'http://swxformat.org/php/amf.php';
my $service = 'Twitter';
my $method = 'search';

my $client = new AMF::Connection( $endpoint );

$client->setEncoding(3);
#$client->setHTTPProxy('http://127.0.0.1:8888');
#$client->addHeader( 'serviceBrowser', 'true' );
$client->setHTTPCookieJar( HTTP::Cookies->new(file => "/tmp/lwpcookies.txt", autosave => 1, ignore_discard => 1 ) );

my $params = [  "italy" ];
my ($response) = $client->call( $service.'.'.$method, $params );

my $json = JSON->new;
$json->ascii(1);
$json->utf8(1);
$json->pretty(1);
$json->allow_blessed(1);
$json->convert_blessed(1);

examples/get-brightcove-videos-metadata.pl  view on Meta::CPAN

  }

my $endpoint = 'http://c.brightcove.com/services/amfgateway';
my $service = 'com.brightcove.templating.TemplatingFacade';
my $method = 'getContentForTemplateInstance';

my $client = new AMF::Connection( $endpoint );

# $client->config( ... ); # LWP::UserAgent extra params (proxy, auth etc... )

#$client->setEncoding(3);
#$client->setHTTPProxy('http://127.0.0.1:8888');
#$client->setHTTPCookieJar( HTTP::Cookies->new(file => "/tmp/lwpcookies.txt", autosave => 1, ignore_discard => 1 ) );

# eg taken from http://link.brightcove.com/services/player/bcpid34762914001?bctid=672454611001
# works only with AMF0 encoding - at least it seems so - because using openamf ?
#
my $player_id = '34762914001';
my $videoId = '672454611001';

my $params = [
                                                       $player_id, # param 1 - playerId
                                                       {

lib/AMF/Connection.pm  view on Meta::CPAN

	$self->{'ua'}->cookie_jar( $self->{'http_cookie_jar'} );

        return bless($self, $class);
	};

# plus add paramters, referer, user agent, authentication/credentials ( see also SecureAMFChannel stuff ), 
# plus timezone on retunred dates to pass to de-serializer - see AMF3 spec saying "it is suggested that time zone be queried independnetly as needed" - unelss local DateTime default to right locale!

# we pass the string, and let Storable::AMF to parse the options into a scalar - see Input/OutputStream and Storable::AMF0 documentation

sub setInputAMFOptions {
	my ($class, $options) = @_;

	$class->{'input_amf_options'} = $options;
	};

sub setOutputAMFOptions {
	my ($class, $options) = @_;

	$class->{'output_amf_options'} = $options;
	};

# useful when input and output options are the same
sub setAMFOptions {
	my ($class, $options) = @_;

	$class->setInputAMFOptions ($options);
	$class->setOutputAMFOptions ($options);
	};

sub getInputAMFOptions {
	my ($class) = @_;

	return $class->{'input_amf_options'};
	};

sub getOutputAMFOptions {
	my ($class) = @_;

	return $class->{'output_amf_options'};
	};

sub setEndpoint {
	my ($class, $endpoint) = @_;

	$class->{'endpoint'} = $endpoint;
	};

sub getEndpoint {
	my ($class) = @_;

	return $class->{'endpoint'};
	};

sub setHTTPProxy {
	my ($class, $proxy) = @_;

	if(	($proxy =~ m!^socks://(.*?):(\d+)!) &&
		(!$HAS_LWP_PROTOCOL_SOCKS) ) {
		croak "LWP::Protocol::socks is required for SOCKS support";
		};

	$class->{'http_proxy'} = $proxy;

	$class->{'ua'}->proxy( [qw(http https)] => $class->{'http_proxy'} );
	};

sub getHTTPProxy {
	my ($class) = @_;

	return $class->{'http_proxy'};
	};

sub setEncoding {
	my ($class, $encoding) = @_;

	croak "Unsupported AMF encoding $encoding"
		unless( $encoding==0 or $encoding==3 );

	$class->{'encoding'} = $encoding;
	};

sub getEncoding {
	my ($class) = @_;

lib/AMF/Connection.pm  view on Meta::CPAN


	push @{ $class->{'headers'} }, $header;
	};

sub addHTTPHeader {
	my ($class, $name, $value) = @_;

	$class->{'http_headers'}->{ $name } = $value ;
	};

sub setUserAgent {
	my ($class, $ua) = @_;

	croak "Not a valid User-Agent $ua"
		unless( ref($ua) and $ua->isa("LWP::UserAgent") and $ua->can("post") );

	# the passed UA might have a different agent and cookie jar settings
	$class->{'ua'} = $ua;

	# make sure we set the proxy if was already set
	# NOTE - we do not re-check SOCKS support due we assume the setHTTPProxy() was called earlier
	$class->{'ua'}->proxy( [qw(http https)] => $class->{'http_proxy'} )
		if( exists $class->{'http_proxy'} and defined $class->{'http_proxy'} );

	# copy/pass over cookies too
	$class->{'ua'}->cookie_jar( $class->{'http_cookie_jar'} );
	};

sub setHTTPCookieJar {
	my ($class, $cookie_jar) = @_;

	croak "Not a valid cookies jar $cookie_jar"
		unless( ref($cookie_jar) and $cookie_jar->isa("HTTP::Cookies") );

	# TODO - copy/pass over the current cookies (in-memory by default) if any set
	$class->{'http_cookie_jar'}->scan( sub { $cookie_jar->set_cookie( @_ ); } );

	$class->{'http_cookie_jar'} = $cookie_jar;

	# tell user agent to use new cookie jar
        $class->{'ua'}->cookie_jar( $class->{'http_cookie_jar'} );
	};

sub getHTTPCookieJar {
        my ($class) = @_;
		

lib/AMF/Connection.pm  view on Meta::CPAN

					"arguments" => $arguments,
					"destination" => $destination });

	return (wantarray) ? @call : $call[0];
	};

sub callBatch {
	my ($class, @batch) = @_;

	my $request = new AMF::Connection::Message;
	$request->setEncoding( $class->{'encoding'} );

	# add AMF any request headers
	map { $request->addHeader( $_ ); } @{ $class->{'headers'} };

	# TODO - prepare HTTP/S request headers based on AMF headers received/set if any - and credentials

	foreach my $call (@batch)
          {
	    next
              unless (defined $call && ref ($call) =~ m/HASH/
		      && defined $call->{'operation'} && defined $call->{'arguments'});

	    my $operation = $call->{'operation'};
	    my $arguments = $call->{'arguments'};

	    my $body = new AMF::Connection::MessageBody;
	    $class->{'response_counter'}++;
	    $body->setResponse( "/".$class->{'response_counter'} );

	    if( $class->{'encoding'} == 3 ) { # AMF3
		$body->setTarget( 'null' );

		my (@operation) = split('\.',$operation);
		my $method = pop @operation;
		my $service = join('.',@operation);
		my $destination = (defined $call->{'destination'}) ? $call->{'destination'} : $service;

		my $remoting_message = $class->_brew_flex_remoting_message( $service, $method, {}, $arguments, $destination);

		$body->setData( [ $remoting_message ] ); # it seems we need array ref here - to be checked
	    } else {
		$body->setTarget( $operation );
		$body->setData( $arguments );
		};

	    $request->addBody( $body );
          }

	my $request_stream = new AMF::Connection::OutputStream($class->{'output_amf_options'});

	# serialize request
	$request->serialize($request_stream);

	#use Data::Dumper;
	#print STDERR Dumper( $request );

	# set any extra HTTP header
	map { $class->{'ua'}->default_header( $_ => $class->{'http_headers'}->{$_} ); } keys %{ $class->{'http_headers'} };

	my $http_response = $class->{'ua'}->post(
		$class->{'endpoint'}.$class->{'append_to_endpoint'}, # TODO - check if append to URL this really work for HTTP POST
		Content_Type => "application/x-amf",
		Content => $request_stream->getStreamData()
		);

	croak "HTTP POST error: ".$http_response->status_line."\n"
		unless($http_response->is_success);

lib/AMF/Connection.pm  view on Meta::CPAN


	# we make sure the main response is always returned first
	return (wantarray) ? @all : $all[0];
	};

# TODO
#
# sub command { } - to send "flex.messaging.messages.CommandMessage" instead
#

sub setCredentials {
	my ($class, $username, $password) = @_;

	$class->addHeader( 'Credentials', { 'userid' => $username,'password' => $password }, 0 );
	};


sub _process_response_headers {
	my ($class,$message) = @_;

	foreach my $header (@{ $message->getHeaders()}) {
		if($header->getName eq 'ReplaceGatewayUrl') { # another way used by server to keep cookies-less sessions
			$class->setEndpoint( $header->getValue )
				unless( ref($header->getValue) );
		} elsif($header->getName eq 'AppendToGatewayUrl') { # generally used for cokies-less sessions E.g. ';jsessionid=99226346ED3FF5296D08146B02ECCA28'
			$class->{'append_to_endpoint'} = $header->getValue
				unless( ref($header->getValue) );
			};
		};
	};

# just an hack to avoid rewrite class mapping local-to-remote and viceversa and make Storable::AMF happy
sub _brew_flex_remoting_message {

lib/AMF/Connection.pm  view on Meta::CPAN


  use AMF::Connection;

  my $endpoint = 'http://myserver.com/flex/amf/'; #AMF server/gateway

  my $service = 'myService';
  my $method = 'myMethod';

  my $client = new AMF::Connection( $endpoint );

  $client->setEncoding(3); # use AMF3 default AMF0

  $client->setHTTPCookieJar( HTTP::Cookies->new(file => "/tmp/mycookies.txt", autosave => 1, ignore_discard => 1 ) );

  my @params = ( 'param1', { 'param2' => 'value2' } );
  my $response = $client->call( "$service.$method", \@params );

  if ( $response->is_success ) {
        my $result_object = $response->getData();
	# ...
  } else {
        die "Can not send remote request for $service.$method method on $endpoint\n";
        };

lib/AMF/Connection.pm  view on Meta::CPAN

=head1 DESCRIPTION

I was looking for a simple Perl module to automate data extraction from an existing Flash+Flex/AMS application, and I could not find a decent client implementation. So, this module was born based on available online documentation.

This module has been inspired to SabreAMF PHP implementation of AMF client libraries.

AMF::Connection is meant to provide a simple AMF library to write client applications for invocation of remote services as used by most flex/AIR RIAs. 

The module includes basic support for synchronous HTTP/S based RPC request-response access, where the client sends a request to the server to be processed and the server returns a response to the client containing the processing outcome. Data is sent...

AMF0 and AMF3 support is provided using the Storable::AMF module. While HTTP/S requestes to the AMF endpoint are carried out using the LWP::UserAgent module. The requests are sent using the HTTP POST method as AMF0 encoded data by default. AMF3 encod...

If encoding is set to AMF3 the Flex Messaging framework is used on returned responses content (I.e. objects casted to "flex.messaging.messages.AcknowledgeMessage" and "flex.messaging.messages.ErrorMessage" are returned).

Simple batch requests and responses is provided also.

See the sample usage synopsis above to start using the module.

=head1 DATE TYPE SUPPORT

The latest 0.79 version of Storable::AMF added basic date support with the new_date() and perl_date() utilitiy functions. This is just great. Internally an AMF Date Type represents a timestamp in milliseconds since the epoch in UTC ("neutral") timezo...

 use Storable::AMF qw(new_date perl_date);

lib/AMF/Connection.pm  view on Meta::CPAN

For other Java to ActionScript type mappings possibilities see http://livedocs.adobe.com/blazeds/1/javadoc/flex/messaging/io/amf/ActionMessageOutput.html#writeObject(java.lang.Object)

For PHP gateways at the moment there is not a known/documented way to map client to server objects.

Future versions of AMF::Connection may add a proper configurable factory for application specific ActionScript/Flex object mappings.

=head1 METHODS

=head2 new ($endpoint)

Create new AMF::Connection object. An endpoint can be specified as the only parameter. Or set in a second moment with the setEndpoint() method.

=head2 call ($operation, $arguments)

Call the remote service method with given parameters/arguments on the set endpoint and return an AMF::Connection::MessageBody response. Or an array of responses if requsted (wantarray call scope). The $arguments is generally an array reference, but t...

=head2 callBatch (@batch)

Call the remote service once in batch. Each element of @batch must be an hash like { "operation" => $operation, "arguments" => $arguments }, where $operation and $arguments are as specified in C<call>. The commands are called and responses returned i...

=head2 setEndpoint ($endpoint)

Set the AMF service endpoint.

=head2 getEndpoint ()

Return the AMF service endpoint.

=head2 setEncoding ($encoding)

Set the AMF encoding to use.

=head2 getEncoding ()

Return the AMF encoding in use.

=head2 setHTTPProxy ($proxy)

Set the HTTP/S proxy to use. If LWP::Protocol is installed SOCKS proxies are supported.

=head2 getHTTPProxy ()

Return the HTTP/S procy in use if any.

=head2 addHeader ($header[, $value, $required])

Add an AMF AMF::Connection::MessageHeader to the requests. If $header is a string the header value $value and $required flag can be specified.

=head2 addHTTPHeader ($name, $value)

Add an HTTP header to sub-sequent HTTP requests.

=head2 setUserAgent ($ua)

Allow to specify an alternative LWP::UserAgent. The $ua must support the post() method, proxy() and cookie_jar() if necessary.

=head2 setHTTPCookieJar ($cookie_jar)

Allow to specify an alternative HTTP::Cookies jar. By default AMF::Connection keeps cookies into main-memory and the cookie jar is reset when a new connection is created. When a new cookies jar is set, any existing AMF::Connection cookie is copied ov...

=head2 getHTTPCookieJar ()

Return the current HTTP::Cookies jar in use.

=head2 setCredentials ($username,$password)

Minimal support for AMF authentication. Password seems to be wanted in clear.

=head2 setInputAMFOptions ($options)

Set input stream parsing options. See Storable::AMF0 for available options.

=head2 setOutputAMFOptions ($options)

Set output stream serialization options. See Storable::AMF0 for available options.

=head2 setAMFOptions ($options)

Set input and output options the same. See Storable::AMF0 for available options.

=head2 getInputAMFOptions ()

Get input stream parsing options.

=head2 getOutputAMFOptions ()

Get output stream serialization options.

lib/AMF/Connection/InputStream.pm  view on Meta::CPAN

	
	my $self = {
		'stream' => $stream,
		'cursor' => 0
		};

	if (defined $storable_amf_options)
	  {
	    if ($Storable::AMF::VERSION < 0.84)
	      {
	        croak "Storable::AMF 0.84 or newer needed to set stream options\n";
	      }
	    $self->{'options'} = Storable::AMF::parse_option ($storable_amf_options);
	  }

	return bless($self, $class);
	};

sub readBuffer {
	my ($class, $length) = @_;

lib/AMF/Connection/InputStream.pm  view on Meta::CPAN

sub readLong {
	my ($class) = @_;

	my $block = $class->readBuffer(4);
	my @long = unpack("N",$block);

        return $long[0];
	};

# deparse out the next avail AMF entity
# TODO - make sure ref counts are reset/preserved between calls in the scope of the same InputStream - study Storable::AMF API
sub readAMFData {
	my ($class) = @_;

	my $type = $class->readByte();

	# Storable::AMF will take care of deparsing the right AMF format
	$class->{'cursor'}--;

	local $@ = undef;

lib/AMF/Connection/Message.pm  view on Meta::CPAN

sub deserialize {
	my ($class, $stream) = @_;

	$class->{'headers'} = [];
	$class->{'bodies'} = [];

        $stream->readByte();

        my $sent_encoding = $stream->readByte();
	# need to make AMF1 returned encoding the same as AMF0 - see more about the bug at http://balazs.sebesteny.com/footprints-in-blazeds/
        $class->setEncoding( ( $sent_encoding!=0 and $sent_encoding!=3 ) ? 0 : $sent_encoding );

        my $totalHeaders = $stream->readInt();
	for(my $i=0;$i<$totalHeaders;$i++) {
		my $header = new AMF::Connection::MessageHeader();

		my $strLen = $stream->readInt();
		$header->setName( $stream->readBuffer($strLen) );

		$header->setRequired( $stream->readByte() );

                $stream->readLong();
		$header->setValue( $stream->readAMFData() ); # we deparse the next read value out

                $class->addHeader( $header );
		};

	my $totalBodies = $stream->readInt();
	for(my $i=0;$i<$totalBodies;$i++) {
		my $body = new AMF::Connection::MessageBody();

		my $strLen = $stream->readInt();
		$body->setTarget( $stream->readBuffer($strLen) );

		$strLen = $stream->readInt();
		$body->setResponse( $stream->readBuffer($strLen) );

		# TODO - make sure we deal properly with avm+ object marker stuff here - and have message containing multiple encodings
                $stream->readLong();
		$body->setData( $stream->readAMFData() ); # we deparse the next read value out

                $class->addBody( $body );
		};
	}; 

sub addBody {
	my ($class, $body) = @_;

	croak "Body $body is not a valid message body"
		unless(ref($body) and $body->isa("AMF::Connection::MessageBody"));

lib/AMF/Connection/Message.pm  view on Meta::CPAN


	return $class->{'headers'};
	};

sub getBodies {
	my ($class) = @_;

	return $class->{'bodies'};
	};

sub setEncoding {
        my ($class, $encoding) = @_;

	croak "Unsupported AMF encoding $encoding"
		unless( $encoding==0 or $encoding==3 );

        $class->{'encoding'} = $encoding;
        };

sub getEncoding {
        my ($class) = @_;

lib/AMF/Connection/Message.pm  view on Meta::CPAN


=head1 NAME

AMF::Connection::Message - Encapsulates a request or response protocol packet/message

=head1 SYNOPSIS

  # ...

  my $request = new AMF::Connection::Message;
  $request->setBody( $body );

  # ..


=head1 DESCRIPTION

The AMF::Connection::Message class encapsulates a request or response protocol packet/message.

=head1 SEE ALSO

lib/AMF/Connection/MessageBody.pm  view on Meta::CPAN

	
	my $self = {
		'target' => $target,
		'response' => $response,
		'data' => $data # we might want to have some kind of mapper between remote objects and local / user registered ones
		};

	return bless($self, $class);
	};

sub setTarget {
	my ($class, $target) = @_;

	$class->{'target'} = $target;
	};

sub getTarget {
	my ($class) = @_;

	return $class->{'target'};
	};

sub setResponse {
	my ($class, $response) = @_;

	$class->{'response'} = $response;
	};

sub getResponse {
	my ($class) = @_;

	return $class->{'response'};
	};

sub setData {
	my ($class, $data) = @_;

	$class->{'data'} = $data;
	};

sub getData {
	my ($class) = @_;

	return $class->{'data'};
	};

lib/AMF/Connection/MessageBody.pm  view on Meta::CPAN

=head1 NAME

AMF::Connection::MessageBody - Encapsulates a request or response protocol packet/message body.

=head1 SYNOPSIS

  # ...

  my $request = new AMF::Connection::Message;
  my $body = new AMF::Connection::MessageBody;
  $body->setTarget('myService.myOperation);
  $body->setResponse('/1');
  $body->setData( { 'param1' => 'value1', 'param2' => 'value2' } );
  $request->setBody( $body );

  # ..


=head1 DESCRIPTION

The AMF::Connection::MessageBody class encapsulates a request or response protocol packet/message body.

=head1 SEE ALSO

lib/AMF/Connection/MessageHeader.pm  view on Meta::CPAN


	return $class->{'name'};
	};

sub getValue {
	my ($class) = @_;

	return $class->{'value'};
	};

sub setName {
	my ($class, $name) = @_;

	$class->{'name'} = $name;
	};

sub setValue {
	my ($class, $value) = @_;

	$class->{'value'} = $value;
	};

sub setRequired {
	my ($class, $required) = @_;

	$class->{'required'} = ($required) ? 1 : 0 ;
	};


1;
__END__

=head1 NAME

AMF::Connection::MessageHeader - Encapsulates a request or response protocol packet/message header.

=head1 SYNOPSIS

  # ...
  my $header = new AMF::Connection::MessageHeader;
  $header->setName( 'Foo' );
  $header->setValue( 'Bar' );
  $header->setRequired( 1 );

  # ...
  if( $header->isRequired ) {
	# 1...
  } else {
	# 2...
	};

  # ..
  my $header2 = new AMF::Connection::MessageHeader($name,$value,0);

lib/AMF/Connection/OutputStream.pm  view on Meta::CPAN

 	my ($storable_amf_options) = @_;
	
	my $self = {
		'stream' => ''
		};

	if (defined $storable_amf_options)
          {
            if ($Storable::AMF::VERSION < 0.84)
              {
                croak "Storable::AMF 0.84 or newer needed to set stream options\n";
              }
            $self->{'options'} = Storable::AMF::parse_option ($storable_amf_options);
          }

	return bless($self, $class);
	};

sub writeBuffer {
	my ($class, $str) = @_;



( run in 1.419 second using v1.01-cache-2.11-cpan-49f99fa48dc )