File Coverage

blib/lib/AMF/Perl.pm
Criterion Covered Total %
statement 33 157 21.0
branch 0 36 0.0
condition 0 3 0.0
subroutine 11 28 39.2
pod 3 13 23.0
total 47 237 19.8


line stmt bran cond sub pod time code
1             package AMF::Perl;
2              
3 1     1   7745 use 5.00000;
  1         3  
  1         39  
4 1     1   6 use strict;
  1         2  
  1         52  
5              
6             require Exporter;
7 1     1   6 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         5  
  1         158  
8             @ISA = qw(Exporter);
9              
10             # Items to export into callers namespace by default. Note: do not export
11             # names by default without a very good reason. Use EXPORT_OK instead.
12             # Do not simply export all your public functions/methods/constants.
13              
14             # This allows declaration use AMF::Perl ':all';
15             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
16             # will save memory.
17             %EXPORT_TAGS = ( 'all' => [ qw(
18            
19             ) ] );
20              
21             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
22              
23             @EXPORT = qw(amf_throw);
24              
25             $VERSION = '0.15';
26              
27              
28             =head1 NAME
29              
30             AMF::Perl - Flash Remoting in Perl
31             Translated from PHP Remoting v. 0.5b from the -PHP project.
32              
33             Main gateway class. This is always the file you call from flash remoting-enabled server scripts.
34              
35             =head1 SYNOPSIS
36              
37             This code should be present in your AMF::Perl gateway script, the one called by the Flash client.
38            
39             To enable the client to call method bar() under service Foo,
40             make sure MyCLass has a method called bar() and register an instance of your class.
41              
42             my $object = new MyClass();
43             my $gateway = AMF::Perl->new;
44             $gateway->registerService("Foo",$object);
45             $gateway->service();
46              
47             Or, if you have many services to register, create a package corresponding to each service
48             and put them into a separate directory. Then register this directory name.
49              
50             In the example below directory "services" may contain Foo.pm, Bar.pm etc.
51             Therefore, services Foo and Bar are available. However, these packages must have a function
52             called methodTable returning the names and descriptions of all possible methods to invoke.
53             See the documentation and examples for details.
54              
55             my $gateway = AMF::Perl->new;
56             $gateway->setBaseClassPath('./services');
57             $gateway->service();
58              
59              
60              
61             =head1 ABSTRACT
62              
63             Macromedia Flash Remoting server-side support.
64              
65             =head1 DESCRIPTION
66              
67             This file accepts the data and deserializes it using the InputStream and Deserializer classes.
68             Then the gateway builds the executive class which then loads the targeted class file
69             and executes the targeted method via flash remoting.
70             After the target uri executes the the gateway determines the data type of the data
71             and serializes and returns the data back to the client.
72              
73              
74             =head2 EXPORT
75              
76             None by default.
77              
78             =head1 SEE ALSO
79              
80             There is a mailing list for AMF::Perl. You can subscribe here:
81             http://lists.sourceforge.net/lists/listinfo/flaph-general
82              
83             The web page for the package is at:
84             http://www.simonf.com/flap
85              
86             =head1 AUTHOR
87              
88             Vsevolod (Simon) Ilyushchenko, simonf@simonf.com
89              
90             =head1 COPYRIGHT AND LICENSE
91              
92             Copyright (c) 2003 by Vsevolod (Simon) Ilyushchenko. All rights reserved.
93              
94             This library is free software; you can redistribute it and/or modify it
95             under the same terms as Perl itself.
96             The code is based on the -PHP project (http://amfphp.sourceforge.net/)
97              
98             ORIGINAL PHP Remoting CONTRIBUTORS
99             Musicman - original design
100             Justin - gateway architecture, class structure, datatype io additions
101             John Cowen - datatype io additions, class structure
102             Klaasjan Tukker - modifications, check routines, and register-framework
103              
104             ==head1 CHANGES
105              
106             =head2 Sun Jul 11 18:45:40 EDT 2004
107              
108             =item Chaned eval{} and amf_throw() to enable die() to work as well (instead of amf_throw()).
109              
110             =head2 Sun Jun 20 13:32:31 EDT 2004
111              
112             =over 4
113              
114             =item Made printing output a separate function, requested by Scott Penrose.
115              
116             =item Wrote exportable amf_throw() for exception handling.
117              
118             =back
119              
120             =head2 Thu Apr 29 17:20:07 EDT 2004
121              
122             =over 4
123              
124             =item Changed "use Apache2" to "require Apache2" to avoid breaking on non-modperl systems.
125              
126             =back
127              
128             =head2 Sat Apr 24 20:41:10 EDT 2004
129              
130             =over 4
131              
132             =item Another patch from Kostas Chatzikokolakis fixing MP2 issues.
133              
134             =back
135              
136             =head2 Sat Mar 13 16:25:00 EST 2004
137              
138             =over 4
139              
140             =item Patch from Kostas Chatzikokolakis handling encoding.
141              
142             =item Changed non-mod_perl behavior for reading POST data from using <> to using read()
143             to work around a bug in IIS
144              
145             =item Joined code for mod_perl 1 and 2. Separated the output code for the mod_perl and non-mod_perl
146             cases.
147              
148             =back
149              
150             =head2 Sat Aug 2 14:01:15 EDT 2003
151              
152             =over 4
153              
154             =item Changed new() to be invokable on objects, not just strings.
155              
156             =back
157              
158             =head2 Sun Jul 20 19:27:44 EDT 2003
159              
160             =over 4
161              
162             =item Added "binmode STDIN" before reading input to prevent treating 0x1a as EOF on Windows.
163              
164             =back
165              
166             =head2 Wed Apr 23 19:22:56 EDT 2003
167              
168             =over 4
169              
170             =item Added "binmode STDOUT" before printing headers to prevent conversion of 0a to 0d0a on Windows.
171              
172             =item Added modperl 1 support and (so far commented out) hypothetical modperl 2 support.
173              
174             =back
175              
176             =head2 Sun Mar 23 13:27:00 EST 2003
177              
178             =over 4
179              
180             =item Synching with AMF-PHP:
181              
182             Added functions debugDir() and log() (debug() in PHP), added reading headers to service().
183             Added fromFile() to enable parsing traffic dumps.
184              
185             =back
186            
187             =cut
188              
189 1     1   1435 use Devel::StackTrace;
  1         6539  
  1         30  
190 1     1   823 use Exception::Class ('AMFException');
  1         5304  
  1         5  
191              
192             # load the required system packagees
193 1     1   991 use AMF::Perl::IO::InputStream;
  1         3  
  1         29  
194 1     1   624 use AMF::Perl::IO::Deserializer;
  1         4  
  1         37  
195 1     1   1007 use AMF::Perl::App::Executive;
  1         2  
  1         34  
196 1     1   583 use AMF::Perl::IO::Serializer;
  1         3  
  1         42  
197 1     1   961 use AMF::Perl::IO::OutputStream;
  1         2  
  1         31  
198 1     1   527 use AMF::Perl::Util::Object;
  1         2  
  1         1565  
199              
200             # constructor
201             sub new
202             {
203 0     0 1   my ($proto) = @_;
204 0   0       my $class = ref($proto) || $proto;
205 0           my $self = {};
206 0           bless $self, $class;
207 0           $self->{exec} = new AMF::Perl::App::Executive();
208 0           $self->{"response"} = "/onResult";
209 0           $self->{debug}=0;
210 0           return $self;
211             }
212              
213             sub debug
214             {
215 0     0 0   my $self = shift;
216 0 0         if (@_) {$self->{debug} = shift;}
  0            
217 0           return $self->{debug};
218             }
219              
220             sub service
221             {
222 0     0 0   my ($self)=@_;
223              
224 0           my $inputStream;
225 0           my $content = "";
226            
227             #Otherwise Apache on Windows treats 0x1a as EOF.
228 0           binmode STDIN;
229              
230 0 0         if($ENV{MOD_PERL})
231             {
232 0           require mod_perl;
233 0           my $MP2 = ($mod_perl::VERSION >= 1.99);
234 0 0         if ($MP2)
235             {
236 0           require Apache2;
237 0           require Apache::RequestUtil; # needed for Apache->request
238             }
239 0           my $r = Apache->request();
240 0           $r->read($content, $r->headers_in->{'Content-Length'});
241             }
242             else
243             {
244             #$content = do { local $/, <> }; #This does not work under IIS
245 0           read(STDIN, $content, $ENV{'CONTENT_LENGTH'});
246             #read the whole STDIN into one variable
247             }
248              
249 0           $self->_service($content);
250              
251             }
252              
253             sub fromFile
254             {
255 0     0 0   my ($self, $file) = @_;
256              
257 0 0         $file = $self->debugDir."input.amf" unless $file;
258              
259             # temporary load the contents from a file
260 0           my $content = $self->_loadRawDataFromFile($file);
261              
262             # build the input stream object from the file contents
263 0           my $inputStream = new AMF::Perl::IO::InputStream($content);
264            
265             # build the deserializer and pass it a reference to the inputstream
266 0           my $deserializer = new AMF::Perl::IO::Deserializer($inputStream, $self->{encoding});
267            
268             # get the returned Object
269 0           my $amfin = $deserializer->getObject();
270              
271 0           return $amfin;
272             }
273              
274             sub _service
275             {
276 0     0     my ($self, $content) = @_;
277            
278 0 0         if($self->debug)
279             {
280             # WATCH OUT, THIS IS NOT THREAD SAFE, DON'T USE IN CONCURRENT ENVIRONMENT
281             # temporary load the contents from a file
282 0           $content = $self->_loadRawDataFromFile($self->debugDir."/input.amf");
283            
284             # save the raw amf data to a file
285             #$self->_saveRawDataToFile ($self->debugDir."/input.amf", $content);
286             }
287            
288             # build the input stream object from the file contents
289 0           my $inputStream = new AMF::Perl::IO::InputStream($content);
290            
291             # build the deserializer and pass it a reference to the inputstream
292 0           my $deserializer = new AMF::Perl::IO::Deserializer($inputStream, $self->{encoding});
293            
294             # get the returned Object
295 0           my $amfin = $deserializer->getObject();
296            
297             # we can add much functionality with the headers here, like turn on server debugging, etc.
298 0           my $headercount = $amfin->numHeader();
299            
300 0           for (my $i=0; $i<$headercount; $i++)
301             {
302 0           my $header = $amfin->getHeaderAt($i);
303 0 0         if ($header->{'key'} eq "DescribeService")
304             {
305 0           $self->{exec}->setHeaderFilter("DescribeService");
306             }
307             # other headers like net debug config
308             # and Credentials
309             }
310              
311            
312             # get the number of body elements
313 0           my $bodycount = $amfin->numBody();
314            
315             # create Object for storing the output
316 0           my $amfout = new AMF::Perl::Util::Object();
317            
318             # loop over all of the body elements
319 0           for (my $i=0; $i<$bodycount; $i++)
320             {
321 0           my $body = $amfin->getBodyAt($i);
322             # set the packagePath of the executive to be our method's uri
323             #Simon - unused for now
324 0           $self->{exec}->setTarget( $body->{"target"} );
325             #/Simon
326             # execute the method and pass it the arguments
327            
328 0           my ($results, $returnType);
329              
330             # try
331             eval
332 0           {
333 0           $results = $self->{exec}->doMethodCall( $body->{"value"} );
334             # get the return type
335 0           $returnType = $self->{exec}->getReturnType();
336             };
337              
338            
339 0 0         if ( $@ )
340             {
341 0 0         $results = UNIVERSAL::isa( $@, 'AMFException' ) ? $@->error : constructException($@);
342 0           $self->{"response"} = "/onStatus";
343 0           $returnType = "AMFObject";
344             }
345              
346             # save the result in our amfout object
347 0           $amfout->addBody($body->{"response"}.$self->{"response"}, "null", $results, $returnType);
348             }
349            
350             # create a new output stream
351 0           my $outstream = new AMF::Perl::IO::OutputStream ();
352              
353             # create a new serializer
354 0           my $serializer = new AMF::Perl::IO::Serializer ($outstream, $self->{encoding});
355            
356             # serialize the data
357 0           $serializer->serialize($amfout);
358              
359 0           if(0)
360             {
361             # save the raw data to a file for debugging
362             $self->_saveRawDataToFile ($self->debugDir."/results.amf", $outstream->flush());
363             }
364              
365             # send the correct header
366 0           my $response = $outstream->flush();
367              
368             #Necessary on Windows to prevent conversion of 0a to 0d0a.
369 0           binmode STDOUT;
370              
371 0           $self->output($response);
372              
373 0           return $self;
374             }
375              
376             sub output
377             {
378 0     0 1   my ($self, $response) = @_;
379              
380 0           my $resLength = length $response;
381              
382 0 0         if($ENV{MOD_PERL})
383             {
384 0           my $MP2 = ($mod_perl::VERSION >= 1.99);
385 0           my $r = Apache->request();
386             #$r->header_out("Content-Length", $resLength);
387             #$r->send_http_header("application/x-amf");
388 0           $r->content_type("application/x-amf");
389 0           $r->headers_out->{'Content-Length'} = $resLength;
390 0 0         $r->send_http_header unless $MP2;
391 0           $r->print($response);
392              
393             }
394             else
395             {
396 0           print <
397             Content-Type: application/x-amf
398             Content-Length: $resLength
399              
400             $response
401             EOF
402             }
403             }
404              
405             sub debugDir
406             {
407 0     0 0   my ($self, $dir) = @_;
408 0 0         $self->{debugDir} = $dir if $dir;
409 0           return $self->{debugDir};
410             }
411              
412              
413             sub setBaseClassPath
414             {
415 0     0 0   my ($self, $path) = @_;
416 0 0         if (-d $path)
417             {
418 0           $self->{exec}->setBaseClassPath($path);
419             }
420             else
421             {
422 0           print STDERR "Directory $path does not exist and could not be registered.\n";
423 0           die;
424             }
425             }
426              
427             sub registerService
428             {
429 0     0 0   my ($self, $package, $servicepackage) = @_;
430 0           $self->{exec}->registerService($package, $servicepackage);
431             }
432              
433              
434             sub constructException
435             {
436 0     0 0   my ($description) = @_;
437 0           my $stack = Devel::StackTrace->new();
438              
439 0           my %result;
440 0 0         $description = "An error occurred" unless $description;
441 0           $result{"description"} = $description;
442 0           $result{"exceptionStack"} = $stack->as_string;
443 0           my @frames = $stack->frames;
444 0           $result{"details"} = $frames[1]->filename();
445 0           $result{"line"} = $frames[1]->line();
446 0           $result{"level"} = "Error";
447 0           $result{"code"} = "1";
448 0           return \%result;
449             }
450              
451              
452             sub amf_throw
453             {
454 0     0 1   my ($description) = @_;
455              
456 0           AMFException->throw( error => constructException($description) );
457             }
458              
459              
460             sub setSafeExecution
461             {
462 0     0 0   my ($self, $safe) = @_;
463 0           print STDERR "There is no need to call setSafeExecution anymore!\n";
464             }
465              
466             sub encoding
467             {
468 0     0 0   my $self = shift;
469 0 0         $self->{encoding} = shift if @_;
470 0           return $self->{encoding};
471             }
472              
473             # usefulldebugging method
474             # You can save the raw data sent from the
475             # flash client by calling
476             # $self->_saveRawDataToFile("file.amf", $contents);
477              
478             sub _saveRawDataToFile
479             {
480 0     0     my ($self, $filepath, $data)=@_;
481             # open the file for writing
482 0 0         if (!open(HANDLE, "> $filepath"))
483             {
484 0           die "Could not open file $filepath: $!\n";
485             }
486             # write to the file
487 0 0         if (!print HANDLE $data)
488             {
489 0           die "Could not print to file $filepath: $!\n";
490             }
491             # close the file resource
492 0           close HANDLE;
493             }
494              
495             sub _appendRawDataToFile
496             {
497 0     0     my ($self, $filepath, $data) = @_;
498             # open the file for writing
499 0 0         if (!open (HANDLE, ">>$filepath"))
500             {
501 0           die "Could not open file $filepath: $!\n";
502             }
503             # write to the file
504 0 0         if (!print HANDLE $data)
505             {
506 0           die "Could not print to file $filepath: $!\n";
507             }
508             # close the file resource
509 0           close HANDLE;
510             }
511              
512              
513             # get contents of a file into a string
514             sub _loadRawDataFromFile
515             {
516 0     0     my ($self, $filepath)=@_;
517             # open a handle to the file
518 0           open (HANDLE, $filepath);
519             # read the entire file contents
520 0           my @contents = ;
521             # close the file handle
522 0           close HANDLE;
523             # return the contents
524 0           return join "", @contents;
525             }
526              
527             sub log
528             {
529 0     0 0   my ($self, $content) = @_;
530 0           $self->_appendRawDataToFile ($self->debugDir."processing.txt",$content."\n");
531             }
532              
533             1;
534             __END__