Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Problem with SOAP::Lite module

by oldwarrior32 (Sexton)
on Jun 07, 2012 at 03:43 UTC ( #974867=perlquestion: print w/ replies, xml ) Need Help??
oldwarrior32 has asked for the wisdom of the Perl Monks concerning the following question:

Hi. I would like to ask for your wisdom. I hope if you can guide me. If not, it's OK :)(is an oscure topic presented by a noob and with not low level details).

I need to connect to some machines using SOAP protocol, the idea is to connect to one machine, get the data, connect to the next machine, get the data and so on. I am using a foreach loop when in each loop i change the IP address.

I can connect to one machine at once and get the data, but using the loop in the second iteration I got an error.

use warnings; use SOAP::Lite + trace => 'debug'; use Data::Dumper; use MIME::Base64; my $cucmip = "10.10.100.1"; my $axl_port = "8443"; my $user = "ccm"; my $password = "password"; my $axltoolkit = "AXLAPI.wsdl"; my $cm; my $res; $cm = SOAP::Lite ->encodingStyle('') ->uri($axltoolkit) ->proxy("https://$cucmip:$axl_port/axl/", timeout => 5); $cm->transport->http_request->header ( 'Authorization' => 'Basic ' . encode_base64("$user:$password", '')); #axl request my $test = new SOAP::Data(); $res = $cm->listPhoneByName($test->name("searchString" => "%")); unless ($res->fault) { print $res->valueof('//listPhoneByNameResponse/return/phone'); } else { print join ', ', "FAULTCODE: " . $res->faultcode, "FAULTSTRING: " . $res->faultstring; }

That code works just fine. But when I add a loop to iterate a pool of IP addresses i got the error.

use warnings; use SOAP::Lite + trace => 'debug'; use Data::Dumper; use MIME::Base64; my $cucmip = "10.10.100.1"; my $axl_port = "8443"; my $user = "ccm"; my $password = "password"; my $axltoolkit = "AXLAPI.wsdl"; my $cm; @ips = qw(10.10.100.1 10.10.100.2 10.10.100.3); my $res; foreach(@ips){ $cm = SOAP::Lite ->encodingStyle('') ->uri($axltoolkit) ->proxy("https://$_:$axl_port/axl/", timeout => 5); $cm->transport->http_request->header ( 'Authorization' => 'Basic ' . encode_base64("$user:$password", '')); #axl request my $test = new SOAP::Data(); $res = $cm->listPhoneByName($test->name("searchString" => "%")); unless ($res->fault) { print $res->valueof('//listPhoneByNameResponse/return/phone'); } else { print join ', ', "FAULTCODE: " . $res->faultcode, "FAULTSTRING: " . $res->faultstring; } }

The error is this:

SOAP::Transport::HTTP::Client::send_receive: 500 Status read failed: invalid argument

Status read failed: Invalid argument at C:/strawberry /perl/site/lib/Net/HTTP/Methods.pm line 269. 500 Status read failed: Invalid argument. at cm.pl lin e 41

Line 41 is:

$res = $cm->listPhoneByName($test->name("searchString" => "%"));

I think is some low level thing, since if I run the same program, once a time, but with a different IP address I got the data from the 3 machines.

In other words, the program is this: start a TCP session using SOAP destined to IP address 10.10.100.1. Get the requested data. Close the session(this works fine). Start a new TCP session using SOAP to IP address 10.10.100.2. Get the data(here the program crashes).

As a last resort I copied the first code two times in the same file. Changed variable names in the copied code so all objects are new. In this way I got the same program two times in the same file simulating a loop with two iterations, but with different variable names. The error appears again.

The only solution I got is to run the program once a time with a different IP address each time. In this way I got the data required but involves some extra hand work :(.

By the way, I tried the sleep command to delay the execution of each interation by 5 or 10 seconds(thinking about a buffer thing) with no success.

I made a packet capture too. Unfortunately the traffic is encripted so I can't find a clue about problem reading the payload of the http traffic. But my computer resets the session, not the remote machine.

A last note. The Soap module has a debug option. This prints the message sended to the remote machine. In each iteration is exactly the same, except for the IP address.

Any suggestion is welcome!

Comment on Problem with SOAP::Lite module
Select or Download Code
Re: Problem with SOAP::Lite module
by Anonymous Monk on Jun 07, 2012 at 06:53 UTC

      Thanks for the info about the AXL module. I am gonna give it a try.

      It was a sort of steep learning curve, but I am now partial to the XML::Compile* libraries. Especially since I was interacting with a .NET SOAP service that had WSDL files available.

      Update:

      I've also been figuring out the best way to throw exceptions from this library. XML::Compile internally catches and logs errors, but wasn't throwing errors. The framework I've settled on is:

      sub wsdl { my $self = shift; return $self->{WSDL} if $self->{WSDL}; $self->{WSDL} = XML::Compile::WSDL11->new($wsdl_file); return $self->{WSDL}; } sub cc { my ( $self, $op, $full_response ) = @_; return $self->{$op} if $self->{$op}; my $f = $self->wsdl()->compileClient( operation => $op, port => $port, @dbug, ); $self->{$op} = sub { my $r = eval { $f->( @_ ) }; unless ($r) { my $err = $@; confess "Error calling SOAP method $op: $err"; } my $fault = $r->{Fault}; confess "$fault->{faultcode}: $fault->{faultstring}" if $fault +; return $full_response ? $r : $r->{parameters}; }; }
      Then to call a SOAP method, it's:
      $self->cc($method_name)->(%args);

      Advantages? One place (per service) to maintain all SOAP calls. One place to insert certain auth parameters or put certain behavior. E.g., in one service I need to insert wsa and wsse headers. In another, I can not make two simultaneous SOAP calls (with the same account), so I wrap the call with lock file code, and in the same service I get a certain sort of common error so I wrap the whole SOAP call with some retry logic.

      And I need the $full_response parameter in one service because I have to use data in the header of the response from the first call (basically a 'Login' sort of call) in all subsequent calls. I don't need the header after that so I just return the 'parameters' part of the response.

      Update: I've replaced:

      my $r = eval { $f->( @_ ) }; unless ($r) { my $err = $@; confess "Error calling SOAP method $op: $err"; }
      with:
      my $r = eval { my $tmp = $f->( @_ ); $eval_err = $@; $tmp; }; my $err = $@; close $fh; unless ($r) { no warnings 'uninitialized'; # Retry these errors if ( $eval_err =~ /answer is not xml/ ) { if ( $retry-- > 0 ) { Print("[$$] Recieved text/html response. Will retr +y $op request."); sleep 2; next; } } confess "Error calling SOAP method $op: [$err][$eval_e +rr]"; }
      There are non-fatal errors that cause undef to be returned from the SOAP call, and this catches them. There are evals inside XML::Compile, so catching $@ at that level catches the error message, and I can retry based on that error message.

        It was a sort of steep learning curve, but I am now partial to the XML::Compile* libraries ...

        Do you have tips to share? What helped you get a handle on it?

        It look like chinese for me. At least I just did the thing manually IP by IP. Anyway I'll keep investigating the issue.

        Thanks four your help.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://974867]
Approved by stevieb
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (14)
As of 2014-07-11 14:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    When choosing user names for websites, I prefer to use:








    Results (227 votes), past polls