AMF-Connection

 view release on metacpan or  search on metacpan

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

	    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);

	my $response_stream = new AMF::Connection::InputStream( $http_response->decoded_content, $class->{'input_amf_options'});
	my $response = new AMF::Connection::Message;
	$response->deserialize( $response_stream );

	#print STDERR Dumper( $response )."\n";

	# process AMF response headers
	$class->_process_response_headers( $response );

	my @all = @{ $response->getBodies() };

	# 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 {
	my ($class,$source,$operation,$headers,$body,$destination) = @_;

	return bless( {
		'clientId' => _generateID(),
                'destination' => $destination,
                'messageId' => _generateID(),
                'timestamp' => time() . '00',
                'timeToLive' => 0,
                'headers' => ($headers) ? $headers : {},
                'body' => $body,
                'correlationId' => undef,
                'operation' => $operation,
		'source' => $source # for backwards compatibility - google for it!
                 }, 'flex.messaging.messages.RemotingMessage' );
        };

sub _generateID {
        my $uniqueid;

        if($HASUUID) {
                eval {
                        my $ug = new Data::UUID;
                        $uniqueid = $ug->to_string( $ug->create() );
                        };
        } elsif ($HASMD5) {
                eval {
                        $uniqueid = substr(Digest::MD5::md5_hex(Digest::MD5::md5_hex(time(). {}. rand(). $$)), 0, 32);
                        };
        } else {
                $uniqueid  ="";
                my $length=16;

                my $j;
                for(my $i=0 ; $i< $length ;) {
                        $j = chr(int(rand(127)));

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

=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);

and make sure any date passed to an AMF::Connection as parameter is encoded with new_date().

=head2 OPEN ISSUES AND SHORTCOMINGS

There is still an issue when arbitrary Date structures are returned from an AMF server to an AMF::Connection (E.g. as part of values of structured AMF Objects). In this case, the AMF::Connection does not try to reparse the Perl object structure retur...

All this means that an AMF::Connection client application can not rely on those Date returned by the server as being Perl timestamps (seconds since the epoch) and will need explicitly call perl_date() or divide the timestamp by 1000 *explicitly*.

=head2 USING OLD Storable::AMF VERSIONS

In older versions (pre 0.79) of Storable::AMF there was no support for the AMF0/AFM3 Date Type, and everything was being passed as string to the server (E.g. "2010-09-22T02:34:00"). Or as double (number) if the date was in timestamp seconds since the...

=head1 MAPPING OBJECTS

By combining the power of Perl bless {}, $package_name syntax and the Storable::AMF freeze()/thaw() interface, it is possible to pass arbitrary structured AMF Objects the corresponding AMF server can interpret. This is possible due a simple Perl obje...

This means that an AMF::Connection application can wrap all ActionScript / Flex AMF Objects around Perl Objects and get them sent; and returned into a Perl object BLOB using the power of Storable::AMF freeze()/thaw().A

For example to send a SearchQueryFx AMF Object to the server an AMF::Connection advanced search call(), the following code could be used:

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

# ... prepare parameters...

my $searchAMFObject = bless( {
                   'searchId' => $searchId,
                   'startHit' => int($startHit),
                   'searchString' => $searchString,
                   'hitsPerPage' => ($hitsPerPage) ? int($hitsPerPage) : 20,
                   'sortId' => $sortId,
       }, 'com.mycompany.application.flex.data.SearchQueryFx');

my $response = $client->call( "MySearchSevice.searchAdvanced", [ $searchAMFObject ] );

#....

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.



( run in 1.507 second using v1.01-cache-2.11-cpan-f56aa216473 )