2009-07-06(月) [長年日記]
■ Amazon API認証のPROXYを書いたよ(2)
amazon-auth-proxyの仕様
(2009-07-07:URL形式の仕様追加。レスポンスについて仕様追加。)
(2009-07-23:レスポンスとして302リダイレクトを強く推奨。)
(2009-07-29:Styleパラメタ対応について追記。)
先月公開したamazon-auth-proxyだが、実は満足に動いてなかった(笑)。というか、単純なPROXYになればいいと考えていたけど、ちょっと複雑な事情があったので、仕様をもう少し確定する必要があった。コードは引き続きGitHubから:
とりあえずこれで、手元のtDiaryは動いてる。
仕様的には、これくらい決めておけばいいかなー、と:
- 提供するURLは「http://(任意)/(locale)/」とする。(locale)はca、de、fr、jp、uk、usから任意のものを選択できるが、それぞれに対応するAmazon APIを呼び出すこと
- RESTタイプのAPIに対応し、認証抜きの呼び出しを認証付きに変更してする
- パラメタ内にあるAWSAccessKeyIdもしくはSubscriptionIdは、自身のアクセスキーに変更*1
- Timestampはパラメタにあっても無視し、現在時刻で付け直す
- AssociateTagが指定されていない場合は自身のものを付加してよい*2
- Styleパラメタが指定されている場合は、XSLT用のエンドポイント(xml-(locale).amznxslt.com/onca/xml)を使う
- レスポンスは以下のいずれかを選択可能
- 【強く推奨】認証signature付きのURLを302でリダイレクト(受け取ったクライアントはそのURLを使ってAmazon APIをcall)
- AmazonのAPIをcallして、その結果を丸々返す
こうやって仕様を決めておかないと、従来のアプリのエントリポイントだけをPROXYのものに置き換えれば余計な変更なしで動くという目標が達成できない。ということで、異なる実装をする人はこのへんに準拠して欲しい。今のところこの方向の実装になりそうなのはこのふたつ?:
- poppen版(on Google App Engine)
- furyu-tei版(on Google App Engine)
逆に、danさんの実装はシンプルでいいかも知れないけど既存のAPI利用者が嬉しくない。
amazon-auth-proxyクラスタに関するアイデア
ところで、open proxyにする場合に、毎秒1回制限とか、不正利用による利用停止の問題が指摘されているが、これは複数のPROXYを束ねて順番に使いまわすことでリスクを低減できると考えている。最初は提案されたDNSラウンドロビンで良いと考えていたけど、参加制約がありすぎるので没。
あとはreverse proxyを用意するか(背後で複数のproxyに振り分け)、302リダイレクトを使うかどっちかかなー。一長一短があるけど後者が楽か。もっとも、Rubyでは$SAFE=1のときにopen-uriが302リダイレクトを処理してくれないという(バグ|仕様)があるので前者じゃないと困るというのがある(笑)。
あと、reverse proxyにするとアクセス元を絞れるので、ダイレクトに不正利用されるのを防げるという利点もあるな。というわけで、誰かreverse proxyを提供してくれないかのぅ。
Web APIに対する私見
あと最後に、これだけは書いておきたい。
デジモノに埋もれる日々のProduct Advertising APIの秘密鍵とクライアント型ソフトに関する公式見解という記事にこんな記述があったけど:
これは本来あるべき姿に戻ったというか、javascriptなどから直接APIを 呼んでいたケースについてはこのように自前のサーバ(CGI等)をクッションとして クライアント直コール型からサーバ型に変換されるべきという意味で 非常にいい仕組みだと思います。
冗談じゃない。こんなクソみたいな仕掛けを考えなきゃいけないなんて、間違ってるとしか言いようがないだろ。
「Amazon APIが無尽蔵に使える共有DB」ではない、という点にはおおいに同意するが、それはあらゆるリソースについて言えることだ。今回の新しい制限によって、APIを嫌がってWebページのスクレイピングに戻る開発者だって出てくるだろう*3。そんな実装のせいでHTMLへのアクセスが1日数百万回になったら、AmazonはAPIと同じようにWebへのアクセスも認証制に移行するか? しないよね。
Web APIは、サーバだろうがクライアントだろうが、HTTPというプロトコルさえ喋れれば誰でもcallできることに意味があるのだ。WebブラウザさえあればどんなWebページも表示できるように、HTTPさえ喋れればどんなAPIにもアクセスできるべきだ*4。真にユビキタスな社会がすぐそこまで来ているこの時代に、Amazonの今回の仕様変更は考えうる最悪のやり方だよ。Amazon超カッコワルイ。
| RESTタイプのAPIに対応し、認証抜きの呼び出しを認証付きに変更、結果はそのまま返す
http://d.hatena.ne.jp/furyu-tei/20090706/1246898166
でも書いたんですけれど、proxyが直接Amazonに取りに行かず、クライアントに302でSignature付URIを返してあげてもいいかな、と思ったり。
これならAmazonにアクセスするのはクライアントになるし、proxyサーバ側の負荷も減るかと。
あと、仕様としてはLocale指定方法も決めておいた方がいいかな、と。
私的には、poppenさん実装のhttp://~/(Locale)/がよさそうに思います。
この辺りが決まっていないと、reverse proxyも書きにくいですしね。
なるほど、302を返すのはアリですね。どちらもでOKなので、仕様に含めましょうか。
Localeについては、たとえば「usは負荷が高いので対応したくない」というPROXY運営者がいてもおかしくないので、Locale単位に個別対応するしかないと思うんですが、いかがでしょう。
| Locale単位に個別対応するしかないと思うんですが、いかがでしょう。
あ、はい、その意味では個別対応でよいと思うのですが、reverse proxyに登録することを考えた場合、ひとつのproxyサーバなのにLocale毎にURLを指定するのは面倒だなぁ、と思ったもので。
・ベースURLにアクセスしたらデフォルトLocale使用(必須)。
・ベースURL/(Locale)/にアクセスすることでLocale指定可能(オプション)。
という仕様になっていれば、reverse proxyへの登録の際、proxy単位では
1. proxyのベースURL(必須)
2. デフォルトLocale(必須)
3. 対応Locale(オプション・ca,de,fr,jp,uk,usから複数選択可)
を指定するだけで済むなぁ、と思ったものですから。
この流れはきっと、風柳さんがreverse proxyを提供してくれる感じなので(笑)、基本的に同意します。
ただ、呼び出すまでどのLocaleが使われるのかわからないデフォルト指定には(reverse proxy的には)意味がないんじゃないでしょうか。単独のproxy提供者には意味があるかも知れないけど。
作ってみたので(笑)
http://d.hatena.ne.jp/furyu-tei/20090709/1247142040
よろしければ↓からプロキシ登録お願いします。
http://honnomemo.appspot.com/rpaproxy/
今更ですが、お願いが。
(reverse proxyからコールされたときには)Amazon APIを直接呼ばずに302を返すように出来ます?
reverse proxyの負荷の関係で、そうじゃないと早々に破綻しそうなので…。
反応できなくてすみません、できれば今日中に対応したいと思います(ちょっとRuby側の制約があるのでゴニョゴニョなんですが)。
Rubyの知識はない(昨日初めて短いコードを書いた超初心者)のであれですが、$SAFE=1だとopen-uriがSecurityErrorを返すんでしたっけ?
net/httpを使って、Net::HTTPRedirectionのときはlocationの値を.untaintする、とかいう処理が必要にあるのかな?
すみません、面倒なことを言って……。
そんな感じです。untaintはいらないかも知れないけど。いずれにしてもtDiary側の話なので、proxyには関係ありません。
つか、初めてコードを書いた人がそこまで読めるのか……。スゲー。