おぎろぐはてブロ

なんだかんだエンジニアになって10年以上

Services_Amazon_SQSが、マルチバイトを含むリクエストだと動作しない理由

Services_Amazon_SQSで、マルチバイト文字を含めたデータを送ると、

The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

という、要はこちらが計算して送っているHMACの値と、Amazon側で計算した値が違うのでエラーということで失敗してしまう。
マルチバイトはそもそも送れない?のかと、Amazon側のGetting Started Guideで使っているPHPスクリプトでも試してみると、マルチバイトでも問題なく動いた。

成功するかどうかの違いは、Content-Typeのcharset

いろいろ差分を見ていてわかったのは、リクエスト時のHTTPヘッダのContent-Typeに、application/x-www-form-urlencoded; charset=utf-8と、charset指定が無いと失敗するということ。

  • PEARの方は、Content-Type: application/x-www-form-urlencoded
  • Amazonのサンプルの方は、Content-Type: application/x-www-form-urlencoded; charset=utf-8

Services_Amazon_SQSを直せるか

答えは否。HTTPリクエストを投げる、HTTP_Reqeust2も修正が必要となり、修正箇所も影響が読めない微妙な感じ。

Services/Amazon/SQS.phpで、Content-Typeを明示的に設定する。

--- SQS.php	2010-01-16 17:39:19.000000000 +0900
+++ SQS.php.new	2010-01-16 17:41:45.000000000 +0900
@@ -287,6 +287,7 @@
             $request->setUrl($url);
             $request->setMethod(HTTP_Request2::METHOD_POST);
             $request->setHeader('User-Agent', $this->_getUserAgent());
+            $request->setHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
             $request->addPostParameter($params);
 
             $count = 0;

HTTP/Request2.php で、getBody() の Content-Typeでの条件分岐を、完全一致でなく、前方一致に書き換える。

--- Request2.php.orig	2010-01-15 16:45:18.000000000 +0900
+++ Request2.php	2010-01-16 17:36:03.000000000 +0900
@@ -554,7 +554,7 @@
         if (self::METHOD_POST == $this->method &&
             (!empty($this->postParams) || !empty($this->uploads))
         ) {
-            if ('application/x-www-form-urlencoded' == $this->headers['content-type']) {
+            if (0 === strpos($this->headers['content-type'], 'application/x-www-form-urlencoded')) {
                 $body = http_build_query($this->postParams, '', '&');
                 if (!$this->getConfig('use_brackets')) {
                     $body = preg_replace('/%5B\d+%5D=/', '=', $body);

現象は確認しているらしいけど、放置中

ここのスレッドで、UTF-8文字列が使えるってマニュアルにあるけど、使えなくね?とやりとりがあって、

I can confirm that specifying a utf-8 charset doesn't currently work with Amazon SQS. I've passed this thread to the Amazon SQS developers as a feature request.

と、動かないことを確認して開発者にfeature requestとして渡すとあるけど、今のところ放置の模様。

メッセージ本文は、base64エンコードしておいたほうがよさげですね。