Search code examples
multithreadingperlsoapmod-perlsoaplite

SOAP::Lite - clients using version 1.1 and 1.2 threaded in mod_perl


I have several SOAP::Lite clients running under mod_perl in Apache hhtpd. Some of them use 1.1 soap-servers and some of them use 1.2 servers. So I have code like:

# In client 1:
my $soap1 = SOAP::Lite->soapversion("1.1");
$result1 = $soap1->method1();

# In client 2:
my $soap2 = SOAP::Lite->soapversion("1.2");
$result2 = $soap2->method2();

This works in stand-alone clients, but when I run the code under mod_perl, I seem to get stung by that the soapversion method has side-effects:

# From SOAP::Lite.pm 
sub soapversion {
    my $self = shift;
    my $version = shift or return $SOAP::Constants::SOAP_VERSION;

    ($version) = grep {
        $SOAP::Constants::SOAP_VERSIONS{$_}->{NS_ENV} eq $version
        } keys %SOAP::Constants::SOAP_VERSIONS
            unless exists $SOAP::Constants::SOAP_VERSIONS{$version};

    die qq!$SOAP::Constants::WRONG_VERSION Supported versions:\n@{[
        join "\n", map {"  $_ ($SOAP::Constants::SOAP_VERSIONS{$_}->{NS_ENV})"} keys %SOAP::Constants::SOAP_VERSIONS
        ]}\n!
        unless defined($version) && defined(my $def = $SOAP::Constants::SOAP_VERSIONS{$version});

    foreach (keys %$def) {
        eval "\$SOAP::Constants::$_ = '$SOAP::Constants::SOAP_VERSIONS{$version}->{$_}'";
    }

    $SOAP::Constants::SOAP_VERSION = $version;

    return $self;
}

This is what I believe happens:

Basically, the soapversion call rededefines essential constants in $SOAP::Constants. And since this is mod_perl, the $SOAP::Constants are global and shared between every server-thread (I believe. Please correct me if I'm wrong). This leads to a race-condition: Most of the times, the codelines gets executed more-or-less in the sequence seen above. But once in at while (actually about 2% of the calls) the execution sequence is:

Thread1: my $soap1 = SOAP::Lite->soapversion("1.1");
Thread2: my $soap2 = SOAP::Lite->soapversion("1.2");
Thread1: $result1 = $soap1->method1();
Thread2: $result2 = $soap2->method2();

And so, the $soap1->method1() gets called with $SOAP::Constants set as befitting version 1.2 - causing several namespace to be wrong, notably:

xmlns:soapenc="http://www.w3.org/2003/05/soap-encoding" 

Which is wrong for 1.1 - who prefers:

xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"                                                 

If I could somehow make $SOAP::Constants localized to a serverthread or something like that, I guess things would be fine. But any solution will be appreciated.


Solution

  • Run Apache with the prefork model instead of the threading model (mpm_prefork_module instead of mpm_event_module or mpm_worker_module), so that each Apache child will have its own Perl interpreter, hence its own set of constants.

    Otherwise have a look on the modperl documentation regarding the PerlOptions directive, specifically the clone and/or parent value. But stop using threads seem simpler to me, threads and Perl were never friends.