ストレージの一部をさくらオブジェクトストレージに切り替えてからだいぶ経ちました。
AWS SDK for PHPを使ってAPI利用していたのですが、v2系のままでないといけないところとか、一部機能しか使わないのにライブラリが大きくてメモリ的にちょっと、と思っていたので、自前で出来ないかと試行錯誤したらかなりハマってしまったので、ポイントを書いておこうと思います。
【公式リファレンス】
ObjectStorage_API_20150205.pdf
基本はこちらのリファレンスを見ながら、curlでリクエストを作って投げるような実験をしました。
ポイントとしては、必要なヘッダをちゃんと指定することと、Authorizationのシグネチャをきちんと作って添えることですね。
今回ハマったところは以下の2点でした。
その1 Dataの日付フォーマット
タイムゾーン指定はUTC、というところまでは良かったんですが、
日付フォーマットを下記のようにしていたら、InternalErrorで通りませんでした。
$t =new DateTime('now', new DateTimeZone('UTC')); $date = $t->format('Ymd\THis\Z');
よく見るとこれはAWSのv4でのフォーマットのようでして、オブジェクトストレージはv2シグネチャでないとなりません。
RFC2822とかRFC1123あたりに変える必要がありました。
$date = gmdate ( \DateTime::RFC2822 );
GMTで取ってますけど別にこれでも大丈夫だったみたいです。
この値をリクエストヘッダのDateとシグネチャ用の文字列両方に使えばよいです。
エラーメッセージは
We encountered an internal error. Please try again.
なんて書いてあるので、何度か時間あけてリトライしても一向に改善せずだいぶ困りました。
その2 リクエスト型とシグネチャ発行時のパス
リクエストの方式にバーチャルホスト型とパス型があるようですが、
これはどちらでもいいと書かれています。
●バーチャルホスト型
PUT /ObjectName HTTP/1.1
Host: My_Bucket_Name.b.sakurastorage.jp
●パス型
PUT /My_Bucket_Name/ObjectName HTTP/1.1
Host: b.sakurastorage.jp
バーチャルホスト型でリクエストする場合、アクセスパスにはバケット名は含まれない、
となるのかなと思っていました。
実際curlで叩く先のURLはその通りなのですが、シグネチャ生成する時のアクセスパスは、バーチャルホスト型でもパス型のパスを入れないとシグネチャ違反になるみたいです。
StringToSign = HTTP-VERB + "\n" + Content-MD5 + "\n" + Content-Type + "\n" + Date + "\n" + CanonicalizedResource;
上記がシグネチャ生成時の署名文字列なのですが、
CanonicalizedResourceはマニュアル上は「アクセスパス 及び パラメータ文字列を指定します」と書かれています。
バーチャルホスト型で進めていたところ、どうあってもシグネチャ違反でエラーになるのでどうしたものかと思っていたのですが、パス型に切り替えたら通りました。
色々探ったところ、署名のCanonicalizedResourceはリクエストURLがバーチャルホスト型でも、パス型のアクセスパスを指定しないといけないみたいです。
調べたらここに書いてありますね…。
REST リクエストの署名と認証 – Amazon Simple Storage Service
ちなみに、シグネチャ生成ロジックのところでは
Signature = URL-Encode( Base64( HMAC-SHA1( SecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) ) );
なんてありますが、別にurlencodeはしなくてもいいですし、PHPファイル自体がUTF-8なら、わざわざ署名文字列をエンコードしなくても大丈夫でした。
//$secret:オブジェクトストレージのシークレットアクセスキー //$StringToSign:署名文字列 $signature = base64_encode( hash_hmac('sha1', $StringToSign, $secret, true ) ); //実際のヘッダの指定はこんな感じ $key:アクセスキー $auth = 'Authorization: AWS '.$key.':'.$signature;
curlで色々やる部分については割愛します。
とりあえず、なんとかGET、PUT、DELETEあたりを動作させることができました。
コメント