ziguzagu.org

SOAP::Lite のバージョンアップで文字化けた

perl のバージョンあげたのにともなって XMLRPC サーバーの実装につかっていた SOAP::Lite を、0.68 から最新(0.715)にあげたら、レスポンスに日本語含むやつが文字化けしつつ途中できれたXMLが返ってきてしまうという問題がおきた。

アプリが古めかしくて、アプリ内で扱う文字列が utf8 flagged な文字列ではなく binary な文字列で統一されていて、XMLRPC のレスポンス作るとこでも、

SOAP::Data->type(string => $user->name);  ## $user->name は binary string を返す

みたいなことしてた。で、SOAP::Lite 0.711(より正確には develop なバージョンである 0.71.01 )から SOAP::Transport::HTTP#send_receive で以下のようなことをやるようになった。

if ($] < 5.008) {
    $envelope = pack( 'C0A*', $envelope );
}
else {
    require Encode;
    $envelope = Encode::encode($encoding, $envelope);
}

とにかく Encode::encode してしまう。全部の SOAP::Data#type で string つくってるとこで Encode::decode する以外に回避方法がなさげだったので、SOAP::Data#type をコピってきて上書きしてしまうクソハックで対応した。

{
    no warnings 'redefine';
    *SOAP::Data::type = sub {
        my $self = UNIVERSAL::isa($_[0] => 'SOAP::Data') ? shift->new() : SOAP::Data->new();
        if (@_) {
            $self->{_type} = shift;
            if ($self->{_type} eq 'string') {
                my $val = shift;
                $self->value(Encode::decode_utf8($val)) unless Encode::is_utf8($val);
            }
            else {
                $self->value(@_);
            }
            return $self;
        }
        if (!defined $self->{_type} && (my @types = grep {/^\{$SOAP::Constants::NS_XSI_ALL}type$/o} keys %{$self->{_attr}})) {
            $self->{_type} = (SOAP::Utils::splitlongname(delete $self->{_attr}->{shift(@types)}))[1];
        }
        return $self->{_type};
    }
}

早く内部文字列を utf8 flagged なものに統一したいけど、あちこちにちりばめられた、

Encode::is_utf8($str) && Encode::_utf8_off($str);

的なコードを改修する量が….。