君の瞳はまるでルビー - Ruby 関連まとめサイト

xmlrpc モジュールについて - Exception parsing 問題について

最終更新: 2015-03-31 (火) 21:53:22 (2274d)

xmlrpc モジュールについて

目的

XMLRPC::Client クラスを使ってサーバにアクセスした際に発生する Exception parsing の問題について説明します。

XMLRPC::Client でアクセスした際に以下のような例外が発生する場合があります。

#<NoMethodError: undefined method `[]' for nil:NilClass>
{Ruby フォルダー}/1.9.1/rexml/parsers/baseparser.rb:335:in `pull_event'
{Ruby フォルダー}/1.9.1/rexml/parsers/baseparser.rb:183:in `pull'
{Ruby フォルダー}/1.9.1/rexml/parsers/streamparser.rb:16:in `parse'
{Ruby フォルダー}/1.9.1/rexml/document.rb:205:in `parse_stream'
{Ruby フォルダー}/1.9.1/xmlrpc/parser.rb:716:in `parse'
{Ruby フォルダー}/1.9.1/xmlrpc/parser.rb:459:in `parseMethodResponse'
{Ruby フォルダー}/1.9.1/xmlrpc/client.rb:421:in `call2'
{Ruby フォルダー}ruby/1.9.1/xmlrpc/client.rb:410:in `call'
...
Exception parsing
Line: 0
Position: 1
Last 80 unconsumed characters:
</methodRespons

本ページは、以上の例外が発生するメカニズムおよびその解決方法についての情報を提供します。

直接的原因

表面上出ている例外は NoMethodError ですが、実際に発生しているのは通信障害です。

この問題の原因は、サーバ側の環境にあり、クライアント側での回避はできません

XML-RPC による通信は、サーバ/クライアント間で HTTP 通信による XML の文書交換が基本になります。

  1. クライアントから処理の要求の XML 文章を送り。
  2. サーバが受け取った要求を処理し、その結果を応答 の XML 文章として送り返す。

HTTP には「Content-Length という通信内容のバイト数を表す情報を表現する項目」があります。

Content-Length の値が実際の通信内容のバイト数と食い違ったときにこの問題が発生します。

事例として挙げた例外メッセージを例に問題を説明します。以下の部分に着目してください。

Exception parsing
Line: 0
Position: 1
Last 80 unconsumed characters:
</methodRespons

最後の一行に「</methodRespons」とあります。

本来は「</methodResponse>」という情報が来るところを2バイト分の「e>」が不足したために通信が成立しなかったのです。

これはサーバの応答にあった Content-Length の値が2バイト足りなかったことを意味します。

この事例では2バイトの不足ですが、状況によってはもっと足りない場合も起こりえます。

根本的原因

サーバ環境に起因する問題であるため、根本的原因はまちまちです。しかし、以下のようないくつかのパターンがあります。

いずれも実際に発生を確認した事象です。その他の原因と思われるものを知っている方がいたらコメントをくれると嬉しいです。

原因を確認するには、実際にクライアントがサーバから受け取った応答 XML 文章を見るのが一番です。

例外メッセージにある以下の行に着目してください。

{Ruby フォルダー}/1.9.1/xmlrpc/parser.rb:459:in `parseMethodResponse'

この行にデバッグ用のコードを一時的に書き足して通信内容を標準出力に出力してみましょう。以下、該当箇所の修正例です。

   class AbstractStreamParser
     def parseMethodResponse(str)
       puts str  # ここに応答 XML 文章を出力する記述を追加
       parser = @parser_class.new
       parser.parse(str)

根本的原因1

サーバからの応答 XML 文章の先頭に BOM がついており、その分のバイト数が Content-Length に加味されていないことが問題になる場合があります。

応答 XML 文章の先頭に BOM が確認できれば、本原因に該当する可能性があります。

■■■<?xml version="1.0" encoding="UTF-8"?>

通常は <?xml から開始するはずの出力の前に読めない何かが出力されます。これが BOM です。

根本的原因2

サーバが Content-Length の計算に応答 XML 文章の「バイト数」を使うところに「文字数」を使ってしまい、マルチバイト文字(≒日本語)分のずれが生じて問題になる場合があります。

この場合、応答 XML 文章にマルチバイト文字(≒日本語)が含まれていなければ正常動作し、マルチバイト文字を含む通信を行ったときだけ例外が発生するという特徴があります。

マルチバイト文字を含む応答 XML 文章を受け取った際に、マルチバイト文字と末尾の不足分の応答 XML 文字数を照らし合わせることで、本原因に該当するか確認できます。

本問題はマルチバイト文字を扱う言語圏特有の問題であるため、ASCII 文字コードだけを使う言語圏では問題にならないことから、 海外産で日本語対応されていないソフトウェアに多いのが特徴です。

解決方法

サーバ環境を調査し、改善するしかありません。

根本的原因1の解決方法

以下のような対応方法が考えられます。

  • BOM を排除する。
  • BOM 分の値を Content-Length に追加する。

Content-Length の値を計算している箇所を探し出して修正します。

ソースコード全体を「Content-Length」をキーワードに全検索するのが一番の早道です*1

根本的原因2の解決方法

まず最初に Content-Length の計算をどのように行っているのか調査が必要です。

各言語処理系には文字列に対して「バイト数」と「文字数」それぞれの計算機能が提供されているのが普通です。そのどちらが使われているのかを調べる必要があります。

Content-Length の値を計算している箇所を探し出して修正します。

ソースコード全体を「Content-Length」をキーワードに全検索するのが一番の早道です*2

通常の処理系には文字列の「バイト数」と「文字数」それぞれを計算するための機能が用意されているので「バイト数」で Content-Length の値を計算するよう修正します。

その際、注意すべき点として動作環境の関連ソフトウェアのバージョンです。

マルチバイト文字列の扱いは、歴史的に徐々に発展していっているものであり、単純ではありません。特定のバージョンでの処理系の動きと、異なるバージョンでの処理系の動きが変わる場合があります。

その加味した対応が必要になります。

マルチバイト文字列にしっかりと対応したソフトウェアであれば、バージョンや機能の有無を判断し、処理を振り分けている場合があります。

そこに問題が潜んでいたり、古い環境に対応していない場合があり得ます。

コメント

本ページの内容に関して何かコメントがある方は、以下に記入してください。

コメントはありません。 コメント/xmlrpc/client/exception_parsing_problem

お名前: