AMF-Connection

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

10
11
12
13
14
15
16
17
18
19
20
21
0.21
        - Fixed check on Storable version for parse_option()
 
0.30    Wed May 11 18:07:11 BST 2011
        - Added callBatch () to make AMF batch RPC possible
 
0.31    Fri May 4 17:33:56 2012 +0100
        - Support different source and destination in AMF remoting package (patch by Aleksey Komarov)
 
0.32    Fri May  4 18:59:18 BST 2012
        - Fixed bug to correctly decode the HTTP response using decoded_content() when the AMF
          gateway supports HTTP Accept-Encoding request headers (bug report by Markus Ortner)

META.json  view on Meta::CPAN

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
   },
   "name" : "AMF-Connection",
   "no_index" : {
      "directory" : [
         "t",
         "inc"
      ]
   },
   "prereqs" : {
      "build" : {
         "requires" : {
            "ExtUtils::MakeMaker" : 0
         }
      },
      "configure" : {
         "requires" : {
            "ExtUtils::MakeMaker" : 0
         }
      },
      "runtime" : {
         "requires" : {
            "LWP::UserAgent" : "5.829",
            "Storable::AMF" : "0.71"
         }
      }
   },
   "release_status" : "stable",
   "version" : "0.32"
}

META.yml  view on Meta::CPAN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---
abstract: 'A simple library to write AMF clients.'
author:
  - 'Alberto Attilio Reggiori, <areggiori@cpan.org>'
build_requires:
  ExtUtils::MakeMaker: 0
configure_requires:
  ExtUtils::MakeMaker: 0
dynamic_config: 1
generated_by: 'ExtUtils::MakeMaker version 6.59, CPAN::Meta::Converter version 2.112150'
license: unknown
meta-spec:
  version: 1.4
name: AMF-Connection
no_index:
  directory:
    - t
    - inc
requires:
  LWP::UserAgent: 5.829
  Storable::AMF: 0.71
version: 0.32

README  view on Meta::CPAN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
AMF-Connection
==============
 
A simple module to write AMF [1] clients to invoke remote services as used by 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 back and forth in AMF binary format (AMFChannel). Other access patterns
such as pub/sub and channels transport are out of scope of this inital release.
 
 
 
INSTALLATION
 
To install this module type the following:
 
   perl Makefile.PL
   make
   make test
   make install
 
DEPENDENCIES
 
This module requires these other modules and libraries:
 
  Storable::AMF ( for serialisation/deserialisation of AMF )
  LWP::UserAgent ( for RPC services over HTTP/S POST )
 
COPYRIGHT AND LICENCE
 
Put the correct copyright and licence information here.
 
Copyright (C) 2010 by Alberto Attilio Reggiori

TODO  view on Meta::CPAN

1
2
3
4
5
- tests
- more documentation
- more testing
- look into results/class mapping

examples/amfclient.pl  view on Meta::CPAN

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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);
my $json_data = $json->encode( $response->getData );
 
if ( $response->is_success ) {
        print $json_data;
} else {
        die "Can not send remote request for $service.$method method with params on $endpoint using AMF".$client->getEncoding()." encoding:\n".$json_data."\n";
        };

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

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
                                                         'videoRefId' => undef,
                                                         'videoId' => $videoId, # param 2 - videoId
                                                         'featuredLineupFetchInfo' => {
                                                                                        'fetchLevelEnum' => '4',
                                                                                        'contentType' => 'VideoLineup',
                                                                                        'childLimit' => '100'
                                                                                      }
                                                       }
                                                     ];
 
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);
my $json_data = $json->encode( $response->getData );
 
if ( $response->is_success ) {
        print $json_data;
} else {
        die "Can not send remote request for $service.$method method with params on $endpoint using AMF".$client->getEncoding()." encoding:\n".$json_data."\n";
        };

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

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
sub new {
        my ($proto, $endpoint) = @_;
        my $class = ref($proto) || $proto;
 
        my $self = {
                'endpoint' => $endpoint,
                'headers' => [],
                'http_headers' => {},
                'http_cookie_jar' => new HTTP::Cookies(),
                'response_counter' => 0,
                'encoding' => 0, # default is AMF0 encoding
                'ua'    => new LWP::UserAgent(),
                'append_to_endpoint' => ''
                };
 
        $self->{'ua'}->cookie_jar( $self->{'http_cookie_jar'} );
 
        return bless($self, $class);
        };

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

223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
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);

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

256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
        # 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) );
                        };

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

377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
  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";
        };
 
  my @response = $client->callBatch ( { "operation" => $service.$method", "arguments" => \@params }, ... );
 
=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);
 
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)

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

506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
=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)

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

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
                # TODO - make sure Storable::AMF does not store string "true" as boolean - or make sure value is right typed
                $stream->writeAMFData( $class->getEncoding(), $header->getValue() );
                };
 
        $stream->writeInt(scalar(@{$class->{'bodies'}}));
        foreach my $body (@{$class->{'bodies'}}) {
                my $target = $body->getTarget();
                $stream->writeInt(length($target));
                $stream->writeBuffer($target);
 
                my $response = $body->getResponse();
                $stream->writeInt(length($response));
                $stream->writeBuffer($response);
 
                $stream->writeLong(-1);
                $stream->writeAMFData( $class->getEncoding(), $body->getData() );
                };
 
        };
 
sub deserialize {
        my ($class, $stream) = @_;

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

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
        return $class->{'encoding'};
        };
 
 
1;
__END__
 
=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
 
Storable::AMF0, Storable::AMF3, AMF::Connection::MessageHeader, AMF::Connection::MessageBody
 
=head1 AUTHOR
 
Alberto Attilio Reggiori, <areggiori at cpan dot org>
 
=head1 COPYRIGHT AND LICENSE

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 
use strict;
use Carp;
 
sub new {
        my $proto = shift;
        my $class = ref($proto) || $proto;
        my ($target,$response,$data) = @_;
         
        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) = @_;

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

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
        my ($class) = @_;
 
        return ($class->{'target'} =~ m|onDebug|) ? 1 : 0 ;
        };
 
1;
__END__
 
=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
 
AMF::Connection::Message
 
=head1 AUTHOR
 
Alberto Attilio Reggiori, <areggiori at cpan dot org>
 
=head1 COPYRIGHT AND LICENSE

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

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
        $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 );
 
  # ...

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

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  } else {
        # 2...
        };
 
  # ..
  my $header2 = new AMF::Connection::MessageHeader($name,$value,0);
 
 
=head1 DESCRIPTION
 
The AMF::Connection::MessageHeader class encapsulates a request or response protocol packet/message header.
 
=head1 SEE ALSO
 
AMF::Connection::Message
 
=head1 AUTHOR
 
Alberto Attilio Reggiori, <areggiori at cpan dot org>
 
=head1 COPYRIGHT AND LICENSE



( run in 1.115 second using v1.01-cache-2.11-cpan-3cd7ad12f66 )