• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:LWPのPOSTでバイナリが壊れる)

LWPのPOSTでバイナリが壊れる

このQ&Aのポイント
  • LWPを使って、multipart/form-dataでバイナリデータを含むリクエストをSSL経由でPOSTすると、そのバイナリデータの部分が壊れてしまいます。
  • バイナリデータのPOSTでエンコードに失敗する可能性がある
  • LWPに依存しすぎており、他のHTTPでPOSTする方法を探しています

質問者が選んだベストアンサー

  • ベストアンサー
  • taknak08
  • ベストアンサー率50% (8/16)
回答No.4

何度もすみません、そして誤ったことを書いてしまいました。 No.3で用いた「CGI」は、他テスト用のもので、tasekiさん作成のcheck.cgiではありませんでした。 (なぜ「Internal Server Error」を返したのかはまだ調べていませんが・・・) tasekiさん作成のcheck.cgiに向けて、HTTP/HTTPS および use utf-8あり/なし でPOSTしてみたところ、CGI、POSTスクリプトともすべてOKとなりました。 そして「HTTPS で use utf-8あり」の場合のみ、request_body.datのMD5値が異なりました。 (なおrequest_body.datのサイズはいずれのケースでも同じでした) 以上訂正いたします。

taseki
質問者

お礼

taknak08さん、本当にありがとうございます。 おかげ様で(根本的な解決ではありませんが)バグの所在など分かり疑問も晴れました。 Internal Server Errorそのものは問題ありません。壊れたデータをバイナリエディタなどで見れば解るのですが、boundaryも壊れるので、POSTデータの解析&復元自体が失敗します。その場合にInternal Server Errorを返すかどうかはそのスクリプト次第ですから、お使いになったテストスクリプトがそうだっただけで、私が書いたcheck.cgiは解析&復元をせずにそのままファイルに書き込むだけなので、つまり壊れていようが何だろうが内容には一切関知しないので常にOKを返します。 そんなわけで、結果として、やはりutf8フラグのついたデータをSSLエンコードするときに壊れた、という推測がビンゴ!だったようですね。 そしてさらに判ったことは、taknak08さんの環境でも発生したということは、すなわち少なくともCrypt::SSLeayは無関係だった可能性が高い、という点です。 おそらく、前にも書いたようにwgetなどは問題ないので、これはLWP周りだと思います。 ハンドラの受け渡しLWP::Protocol::httpsあたりでフラグを落としていないのが原因なのかな、と推測していますが、もうそのあたりはメンテナさんに任せるとして、暫定策としてPOSTする前に呼ぶ側でフラグを落とせば良さそうです。 この問題が起きる可能性の盲点として、多く使われているであろうWWW::MechanizeとかでHTML::Formにて日本語などのページを解析する際で、HTMLフォームをutf8つきで取得、そのままPOST、なんてやると、この問題が起きますね。 しかも、そんなのに遭遇した人は、原因にたどり着くまで長そうです…。 本当に助かりました。ありがとうございます。 一応もう少し情報を待ってみてから、このスレッドは閉じたいと思います。 (ちなみに、CPANにレポートしたのは…以下略)

その他の回答 (3)

  • taknak08
  • ベストアンサー率50% (8/16)
回答No.3

こんにちは。 新しいスクリプトでPOSTしてみたところ、おもしろい結果になりました。 「use utf8;」ありでHTTPSリクエストをすると、  $http_res->is_success or die $http_res->message; の行で  Internal Server Error at ./xxx.pl line xx. になります。test.gifもHTTPSサイトへPOSTされていないようです。 その他の場合はすべて大丈夫でした。つまり「use utf8;」をコメントアウトして実行すると、HTTPSでもtest.gifが正常にPOSTできます。 またHTTPの場合は「use utf8;」があっても無くても問題なくPOSTできました。 「use utf8;」は鬼門ですね、本当に・・・Perlファンとしては、UTF-8処理ごとき(?!)で変にコケられてしまうのは悲しいかぎりです。。。 解答が分かってしまったところでググってみれば、やはりお仲間がいるようですね。 (「Perl LWP::UserAgent HTTPS utf8 POST」でググってみました) http://markmail.org/message/varrwabxflwsh4vo#query:Perl%20LWP%3A%3AUserAgent%20HTTPS%20utf8%20POST+page:1+mid:tzotme5w5h5tz5yy+state:results それでは、世のPerlファンのためにバグ報告を!っと思ったら、早速CPANにレポートされているご様子。すばらしい! 私も勉強になりました。ありがとうございました。

  • taknak08
  • ベストアンサー率50% (8/16)
回答No.2

最初の者です。 以下、POSTスクリプトを動かした環境をお伝えします。 $ rpm -qa | grep -i libwww perl-libwww-perl-5.805-1.1.1 $ perl -MCrypt::SSLeay -le 'print $MCrypt::SSLeay::VERSION' Can't locate Crypt/SSLeay.pm in @INC ... $ perl -MNet::SSLeay -le 'print $Net::SSLeay::VERSION' 1.30 $ perl -MIO::Socket::SSL -le 'print $IO::Socket::SSL::VERSION' 1.01 tasekiさんの現象ですが、私にも原因はよく分かりません。うーん、何なんでしょうねぇ。。。 ちなみに私の環境では、上記のとおりCrypt::SSLeayというパッケージは用いられていないようです。このあたり何かヒントがあるかもしれません。 ではがんばってください。ご検討をお祈りしています。。。

taseki
質問者

補足

何度もありがとうございます。 確かlibwwwの過去バージョンはCrypt::SSLeayが見つからない場合は代替モジュールを探す仕様だったので、おっしゃるとおりtaknak08さんの環境ではCrypt::SSLeayが用いられていないようですね。 それも含めていろいろ検証を続け、おかげ様で、だいぶ判ってきました。 まずは、関係ないかもしれませんが、Crypt::SSLeayのドキュメントを見ると、「これを使えばSSLでGET, HEAD, POST ができます。POSTについて詳しくはLWPを見てください」と、どういうわけかわざわざPOSTだけ特別扱い(?)しているのは、何か懸念事項があるから、という気もするような、しないような…。 そして、条件が絞り込まれていくうち、UTF-8が浮かび上がってきました。 バイナリデータの中身によって問題が起きたり起きなかったりするのが、どうしても不思議だったんですが、ついに見つけ出しました…、ほぼ確実に問題が起きる条件を。 taknak08さんには何度も申し訳ないのですが、本当にもしお時間があったらで良いので、以下を試していただけないでしょうか。 POSTスクリプトの改訂版です。 --------------------------------------- #!/usr/local/bin/perl use strict; use utf8; use LWP::UserAgent; use HTTP::Request::Common 'POST'; my $ua = LWP::UserAgent->new(); my $http_res = $ua->request(POST('​https://host/check.cgi',​ Content_Type => 'form-data', Content => [ test_data => 'ABCDEFG', bin_data => ['./test.gif'], ], Head1 => 'A', )); $http_res->is_success or die $http_res->message; print "OK\n"; exit; --------------------------------------- 2行追加しただけです。 もし変化が無いようでしたら、test.gifを別のファイルにしてみていただければと思います。 もしこれで問題が起きる、つまりやはりUTF-8が絡んでいたのなら、少し解るところもあります。 LWP::UserAgentのソースを読んでみたんですが、requestメソッドの中で、受け取ったRequestオブジェクトに対して追加のヘッダがあれば、内部で処理して追加しているんですね。 それが上記では「Head1」ですが、ここにutf8フラグが立っていると、SSLの処理で問題が起きる、と言うことかもしれません。 そして、「SSLの処理」というのは、上記スクリプトでtaknak08さんの環境では問題が起きないなら、Crypt::SSLeayだけの問題かもしれません。 といっても、そもそもASCIIしか使っていないのでUTF-8なんて関係ない気もするんですけどね…。 上記の場合ですが、とりあえずの解決策も判りました。 判りましたというか…、単にutf8フラグを落とせば良いだけで、試してみたら正常にPOSTできました。 しかし推測が当たっていて、かつ上記のような条件でPOSTする場合、とケースが限られますが。

  • taknak08
  • ベストアンサー率50% (8/16)
回答No.1

tasekiさんが作成された、チェック用CGIおよびPOSTスクリプトを 手元のhttpおよびhttpsのサーバで動かしてみたのですが、 生成されたファイル(request_body.dat)には差異はありませんでした。 POSTスクリプトを動かした環境は以下のとおりです。  ・CentOS 5.2 x86_64  ・$ rpm -qa | grep -i ssl | sort | uniq の結果:    openssl-0.9.8b-10.el5    openssl-devel-0.9.8b-10.el5    perl-IO-Socket-SSL-1.01-1.fc6    perl-Net-SSLeay-1.30-4.fc6 なおチェック用CGIはレンタルサーバで動かしたため、 パッケージなどの詳しい情報はわかりませんでした。 またチェック用CGIを上記サーバへ置き、HTTPでPOSTしてみましたが、 結果は同じでした。(=レンタルサーバへのHTTPSと差異はありませんでした) 回答にはなっていませんが、ご参考まで・・・。

taseki
質問者

お礼

有用な情報ありがとうございます。 おそらくPOST側の問題なのでしょうね。 ひとつ判ったのが、極端に小さいデータ、たとえば数ピクセルの画像などの場合は問題ないことと、私のほうでもレンタルサーバーやレンタルブログへの画像投稿など試してみましたが、相手サーバー=POST先に関係なく問題が起きる、あるいはデータによってはPOST先に関係なく起きないので、やはりPOSTする側の問題だと思います。 POSTする側を複数で試していますが、wgetなどは正常なので、やはりOpenSSLなどではなくLWPまわりの何かだと推測しています。 もしよろしければ、libwww-perl、Crypt::SSLeayなどのバージョンもお教えいただけますでしょうか。 また、他に何か情報ありましたら、引き続きお待ちいたします。

関連するQ&A