jeanluca has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks
I'm trying to send XML to an XMLRCP server and for some reason it doesn't work

Here is the code (copied from the web) for the XMLRPC server:
#!/usr/bin/perl # sum() server use strict; use warnings; use Frontier::Daemon; my $d = Frontier::Daemon->new( LocalPort => 8999, methods => { sum => \&sum, }, LocalAddr => 'localhost' ) or die "Cannot start HTTP server $!\n" ; sub sum { my ($arg1, $arg2) = @_; print "summing $arg1 en $arg2\n"; return $arg1 + $arg2; }
Client
use strict; use Socket; # initialize host and port my $host = 'localhost'; my $port = 8999; my $proto = getprotobyname('tcp'); # get the port address my $iaddr = inet_aton($host); my $paddr = sockaddr_in($port, $iaddr); # create the socket, connect to the port socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; connect(SOCKET, $paddr) or die "connect: $!"; # create data my $data = <<"START" ; <?xml version="1.0"?> <methodCall> <methodName>sum</methodName> <params> <param> <value><double>1.12</double></value> </param> <param> <value><double>4.12</double></value> </param> </params> </methodCall> START my $header = <<"START" ; POST /quotes.cgi HTTP/1.0 Host: localhost Content-Type: text/xml START $header .= "Content-length: " . length($data) ; my $content = $header. "\n" . $data ; print $content ; print SOCKET "$content\n" ; my $line; while ($line = <SOCKET> ) { print $line; } close SOCKET or die "close: $!";
I get the impression the client can connect, and probably sends the xml, but the server doesn't respond

Here is code from an other client I've tested:
use LWP::UserAgent; my $ua = new LWP::UserAgent; # create data my $data = <<"START" ; <?xml version="1.0"?> <methodCall> <methodName>sum</methodName> <params> <param> <value><double>1.12</double></value> </param> <param> <value><double>4.12</double></value> </param> </params> </methodCall> START my $header = <<"START" ; POST /quotes.cgi HTTP/1.0 Host: localhost Content-Type: text/xml START $header .= "Content-length: " . length($data) ; my $content = $header. "\n" . $data ; print $content ; my $response = $ua->post('http://localhost:8999', $content) ; print $response->content;
Now I get a response
<title>403 Forbidden</title> <h1>403 Forbidden</h1>
Only when I used Frontier::Client the client worked!!
Any suggestions why these two attemps failed ?

Thnx
LuCa

UPDATE: thnx for the replies. With the input below I managed to get the second client app to work too :)
#!/usr/bin/perl -w use strict; use LWP::UserAgent; my $ua = new LWP::UserAgent; use LWP::Debug '+'; # create data my $content = <<"START" ; <?xml version="1.0"?> <methodCall> <methodName>sum</methodName> <params> <param> <value><double>1.12</double></value> </param> <param> <value><double>4.12</double></value> </param> </params> </methodCall> START print $content ; my $req = HTTP::Request->new (POST => 'http://10.0.0.2:8999/RPC2'); $req->header('Content-Type' => 'text/xml'); $req->content($content); my $response = $ua->request($req); print $response->content;

Replies are listed 'Best First'.
Re: XMLRPC - doing it the hard way
by jethro (Monsignor) on Dec 15, 2008 at 14:29 UTC

    Frontier::Client has a debug option where it prints out the xml it wants to send. Did you try to enable debug and compare it to what you are sending?

    You might also use a packet sniffer like tcpdump to compare the information sent with the Frontier::Client and your client programs.

    UPDATE: By the way, you don't have an empty line between header and body (your /n is just the missing newline of the content-length line). I'm not sure if that is only necessary with emails, just thought I'd mention it without any shred of knowledge either way

Re: XMLRPC - doing it the hard way
by ig (Vicar) on Dec 15, 2008 at 16:11 UTC

    Update: changed the client code and results to use the correct port (reversing a change I made while testing).

    In your first client, you need to flush your output, otherwise nothing is sent to the server, and, as jethro said, you need two linefeeds between the headers and body of your request. You can try the following:

    #!/usr/bin/perl use strict; use Socket; use IO::Handle; # initialize host and port my $host = 'localhost'; my $port = 8999; # my $port = 80; my $proto = getprotobyname('tcp'); # get the port address my $iaddr = inet_aton($host); my $paddr = sockaddr_in($port, $iaddr); # create the socket, connect to the port socket(SOCKET, PF_INET, SOCK_STREAM, $proto) or die "socket: $!"; connect(SOCKET, $paddr) or die "connect: $!"; SOCKET->autoflush(1); # create data my $data = <<"START" ; <?xml version="1.0"?> <methodCall> <methodName>sum</methodName> <params> <param> <value><double>1.12</double></value> </param> <param> <value><double>4.12</double></value> </param> </params> </methodCall> START my $header = <<"START" ; POST /quotes.cgi HTTP/1.0 Host: localhost Content-Type: text/xml START $header .= "Content-length: " . length($data) ; my $content = $header. "\n\n" . $data ; print $content ; print SOCKET "$content\n" or die "print SOCKET: $!"; print "waiting for response\n"; my $line; while ($line = <SOCKET> ) { print $line; } close SOCKET or die "close: $!";

    On my system, this produces:

    POST /quotes.cgi HTTP/1.0 Host: localhost Content-Type: text/xml Content-length: 209 <?xml version="1.0"?> <methodCall> <methodName>sum</methodName> <params> <param> <value><double>1.12</double></value> </param> <param> <value><double>4.12</double></value> </param> </params> </methodCall> waiting for response HTTP/1.1 403 Forbidden Date: Mon, 15 Dec 2008 16:09:28 GMT Server: libwww-perl-daemon/5.818 Content-Type: text/html Content-Length: 53 <title>403 Forbidden</title> <h1>403 Forbidden</h1>

    I am not familiar with the Frontier XML RPC2 protocol, but looking at the server it appears your request must use the path /RPC2. From Frontier/Daemon.pm:

    while ($conn = $self->accept) { my $rq = $conn->get_request; if ($rq) { if ($rq->method eq 'POST' && $rq->url->path eq '/RPC2') { ${*$self}{'response'}->content(${*$self}{'decode'}->se +rve($rq->content, ${*$self}{'methods'})); $conn->send_response(${*$self}{'response'}); } else { print STDERR "got an invalid request\n"; $conn->send_error(RC_FORBIDDEN); } } $conn->close; $conn = undef; # close connection }

    Update: Change the path in your request to /RPC2 and it appears to work:

Re: XMLRPC - doing it the hard way
by Anonymous Monk on Dec 15, 2008 at 16:14 UTC