The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

XMLRPC::Fast - fast XML-RPC encoder/decoder

SYNOPSIS

    use XMLRPC::Fast;

    my $xml = encode_xmlrpc_request("auth.login" => {
        username => "cjohnson", password => "tier3"
    });

    my $rpc = decode_xmlrpc($xml);

DESCRIPTION

XMLRPC::Fast, as its name suggests, tries to be a fast XML-RPC encoder & decoder. Contrary to most other XML-RPC modules on the CPAN, it doesn't offer a RPC-oriented framework, and instead behaves more like a serialization module with a purely functional interface. The other main difference is that, contrary to other XML-RPC modules, which all use regexps to detect scalar types, XMLRPC::Fast uses Perl's internal flags. See "MAPPING" for more details. This choice was made because there are many kinds of data which can defeat the regexps, and a string can be confused with an integer. This module should DWIM most of the time, but it might not correspond to your own use cases.

RATIONALE

This module was born because in my current $work, we heavily use XML-RPC messages over a pure TCP socket, not over HTTP like most modules assume. As such, the RPC framework provided by the other modules is of no use, and we simply use their serialization methods (which are not always well documented). The module we use the most (because yes, we use more than one; don't ask) is XMLRPC::Lite, and basically only in one of these ways:

  • encoding a XML-RPC message:

        my $xml = XMLRPC::Serializer->envelope($type, @message);
  • decoding a XML-RPC message:

        my $rpc = XMLRPC::Deserializer->deserialize($xml)->root

XMLRPC::Fast API was therefore made to follow these use cases, all the while being faster.

MAPPING

This section describes how XMLRPC::Fast maps types between Perl and XML-RPC. It tries to do the right thing, but probably fails in some corner cases.

XML-RPC to Perl

array

A XML-RPC array becomes a Perl array reference.

base64

A XML-RPC base64 is decoded with MIME::Base64 and provided as a Perl string value.

boolean

A XML-RPC boolean becomes a Perl integer value (IV). Note that the value is coerced to become an integer, which can lead to surprises if the value was incorrectly typed.

date/time

A XML-RPC dateTime.iso8601 becomes a Perl string value

double

A XML-RPC double becomes a Perl float value (NV). Note that the value is coerced to become a float, which can lead to surprises if the value was incorrectly typed.

integer

A XML-RPC integer becomes a Perl integer value (IV). Note that the value is coerced to become an integer, which can lead to surprises if the value was incorrectly typed.

nil

A XML-RPC nil becomes the undefined value (undef).

string

A XML-RPC string becomes a Perl string value (PV). The string is not decoded, and is therefore provided as octets.

struct

A XML-RPC struct becomes a Perl array reference.

Perl to XML-RPC

scalar

There is unfortunately no way in Perl to know the type of a scalar value as we humans expect it. Perl has its own set of internal types, not exposed at language level, and some can overlap with others. The following heuristic(*) is applied, in this order:

  • if the scalar is undef, it is converted to a XML-RPC nil;

  • if the scalar has the SVf_NOK flag (NV, PVNV), it is assumed to be a float value, and converted to a XML-RPC double;

  • if the scalar has the SVf_IOK flag (IV, PVIV), it is assumed to be an integer, and converted to a XML-RPC int;

  • otherwise, the scalar is assumed to be a string (PV); if it a string of Perl characters, it is first encoded to UTF-8 (this may change in the future if it appears to create more problems than it tries to solve); if control characters are detected, the value is encoded to Base64 and sent as a XML-RPC base64; otherwise, XML specific characters (&, <, >) are protected and the value is sent as a XML-RPC string.

(*) To quote Mark Jason Dominus, "this is a fancy way of saying that it doesn't work," yet my guess (and experience) is that this one is less buggy at guessing types than regexps. Obviously, your mileage may vary.

array reference

Array references are converted to XML-RPC array structures.

hash reference

Hash references are converted to XML-RPC struct structures.

object

DateTime and DateTime::Tiny objects are mapped to dateTime.iso8601 values, and formatted accordingly. Other types of objects are ignored.

EXPORTS

XMLRPC::Fast by default exports all its public functions: decode_xmlrpc, encode_xmlrpc, encode_xmlrpc_request, encode_xmlrpc_response, encode_xmlrpc_fault.

FUNCTIONS

decode_xmlrpc

Parse a XML-RPC message and return a structure representing the message.

Argument: XML octets

Return: structure

Examples:

    # parsing a request message
    my $xml = <<'XML';
    <?xml version="1.0" encoding="UTF-8"?>
    <methodCall>
      <methodName>fluttergency.set_level</methodName>
      <params>
        <param>
          <value>
            <struct>
              <member><name>level</name><value><int>3</int></value></member>
            </struct>
          </value>
        </param>
      </params>
    </methodCall>
    XML

    my $rpc = decode_xmlrpc($xml);

    # $rpc = {
    #     type => "request",
    #     methodName => "fluttergency.set_level",
    #     params => [{ level => 3 }],
    # }


    # parsing a response message
    my $xml = <<'XML';
    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
      <params>
        <param>
          <value>
            <struct>
              <member>
                <name>angel.alert</name>
                <value> <string>missing Fluttershy</string> </value>
              </member>
            </struct>
          </value>
        </param>
      </params>
    </methodResponse>
    XML

    my $rpc = decode_xmlrpc($xml);

    # $rpc = {
    #     type  => "response",
    #     params => [{ "angel.alert" => "missing Fluttershy" }],
    # }


    # parsing a fault message
    my $xml = <<'XML'
    <?xml version="1.0" encoding="UTF-8"?>
    <methodResponse>
      <fault>
        <value>
          <struct>
            <member>
              <name>faultCode</name>
              <value> <int>20</int> </value>
            </member>
            <member>
              <name>faultString</name>
              <value> <string>needs to be 20% cooler</string> </value>
            </member>
          </struct>
        </value>
      </fault>
    </methodResponse>
    XML

    my $rpc = decode_xmlrpc($xml);

    # $rpc = {
    #     type  => "fault",
    #     fault => {
    #         faultCode => 20,  faultString => "it needs to be 20% cooler"
    #     },
    # }

encode_xmlrpc

Create a XML-RPC method message and return the corresponding XML document. Type is "method" for a request message, "response" for a normal response message, "fault" for a fault response message. Method name is only used for request messages.

Arguments: type of message, method name, parameters

Return: XML octets

Examples:

    # create a request message
    my $xml = encode_xmlrpc(request =>
        "fluttergency.set_level", { level => 3 });

    # create a normal response message
    my $xml = encode_xmlrpc(response => "",
        {"angel.alert" => "missing Fluttershy"});

    # create a fault response message
    my $xml = encode_xmlrpc(fault => 20, "it needs to be 20% cooler");

encode_xmlrpc_request

Create a XML-RPC method request message and return the corresponding XML document. Calls encode_xmlrpc() with the type "method" and the rest of the arguments.

Arguments: method name, parameters

Return: XML octets

Example:

    my $xml = encode_xmlrpc_request("fluttergency.set_level", { level => 3 });

    # <?xml version="1.0" encoding="UTF-8"?>
    # <methodCall>
    #   <methodName>fluttergency.set_level</methodName>
    #   <params>
    #     <param>
    #       <value>
    #         <struct>
    #           <member>
    #             <name>level</name>
    #             <value> <int>3</int> </value>
    #           </member>
    #         </struct>
    #       </value>
    #     </param>
    #   </params>
    # </methodCall>

encode_xmlrpc_response

Create a XML-RPC method response message and return the corresponding XML document. Calls encode_xmlrpc() with the type "response" and the rest of the arguments.

Arguments: parameters

Return: XML octets

Example:

    my $xml = encode_xmlrpc_response({"angel.alert" => "missing Fluttershy"});

    # <?xml version="1.0" encoding="UTF-8"?>
    # <methodResponse>
    #   <params>
    #     <param>
    #       <value>
    #         <struct>
    #           <member>
    #             <name>angel.alert</name>
    #             <value> <string>missing Fluttershy</string> </value>
    #           </member>
    #         </struct>
    #       </value>
    #     </param>
    #   </params>
    # </methodResponse>

encode_xmlrpc_fault

Create a XML-RPC method fault message and return the corresponding XML document. Calls encode_xmlrpc() with the type "response" and the appropriate structure filled with the given arguments.

Arguments: fault code, fault string

Return: XML octets

Example:

    my $xml = encode_xmlrpc_fault(20, "it needs to be 20% cooler");

    # <?xml version="1.0" encoding="UTF-8"?>
    # <methodResponse>
    #   <fault>
    #     <value>
    #       <struct>
    #         <member>
    #           <name>faultCode</name>
    #           <value> <int>20</int> </value>
    #         </member>
    #         <member>
    #           <name>faultString</name>
    #           <value> <string>needs to be 20% cooler</string> </value>
    #         </member>
    #       </struct>
    #     </value>
    #   </fault>
    # </methodResponse>

SIMILAR MODULES

This section describes the author's impressions about the other XML-RPC modules available on the CPAN. You can find scripts to runs bench tests, with both Benchmark and Dumbbench, in the tools/ directory of the distribution.

  • Frontier::RPC2 -- As I understand it, the grandparent of all XML-RPC modules on the CPAN, made by the people who proposed the XML-RPC spec in the first place, back in 1998. Very old (last release in 2002 or 2004). Documented. Very fast; actually the fastest XML-RPC module on the CPAN until XMLRPC::Fast, and still is for decoding.

    Encoding is very fast, but relies on regexps to detect scalar types.

        my $xml = Frontier::RPC2->new->encode_call(@message);

    Decoding is very fast, based on XML::Parser, but returns a structure with objects, making it less practical than a pure Perl structure.

        my $rpc = Frontier::RPC2->new->decode($xml);
  • RPC::XML -- Developped since a long time (2001-today). Very well documented.

    Encoding is pretty fast, but relies on regexps to detect scalar types.

        my $xml = RPC::XML::request->new(@message)->as_string;

    Decoding is pretty fast, using either XML::Parser or XML::LibXML.

        my $rpc = RPC::XML::ParserFactory->new->parse($xml);
  • XML::Compile::RPC -- Recent (2009-2013). Heavily object oriented, complex to use. Strangely documented. Completely RPC/HTTP oriented, client-side only, can't be used for generic encoding/decoding.

    Encoding is slow, and a bit tedious given the complex structure you need to give it in order to specify everything.

  • XML::RPC -- Old (2008), basic documentation. Does not handle the base64 type.

    Encoding is slow and relies on regexps to detect scalar types

        my $xml = XML::RPC->new("")->create_call_xml(@message);

    Decoding uses XML::TreePP, and is therefore very slow.

        my $client = XML::RPC->new("");
        my ($method, @params) = $client->unparse_call($client->{tpp}->parse($xml));
  • XMLRPC::Lite -- Barely documented, based on SOAP::Lite, therefore very object oriented and more than a bit heavy. On the positive side, this allows you to override how the values are guessed.

    Encoding is slow and relies on regexps to detect scalar types.

        my $xml = XMLRPC::Serializer->envelope(method => @message);

    Decoding is quite slow.

        my $rpc = XMLRPC::Deserializer->deserialize($xml)->root;

If XMLRPC::Fast doesn't fit your needs, RPC::XML is probably your best bet.

CREDITS

The XML-RPC standard is Copyright 1998-2004 UserLand Software, Inc. See http://www.xmlrpc.com/ for more information about the XML-RPC specification.

LICENSE

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

AUTHOR

Sébastien Aperghis-Tramoni <saper@cpan.org>