2008-09-25

はてなダイアリー AtomPub レビュー: その1 実装編

はてなブックマーク   livedoor clip

はてなさんがダイアリーAtomPubインターフェースをリリースしていました。

私は AtomPub が大好きなので、少しだけ試してみました。簡単にレビューを書こうと思ってエントリを起したのですが、意外と長くなりそうなので3部構成でお送りします。まずは実装編です。

認証

とりあえず普通のGETリクエストをサービス文書に送ってみます。http://d.hatena.ne.jp/{hatena-id}/atom がサービス文書の URI です。

GET http://d.hatena.ne.jp/yohei/atom HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 401 Unauthorized
Date: Fri, 19 Sep 2008 07:00:10 GMT
Server: Apache/2.2.3 (CentOS)
WWW-Authenticate: WSSE profile="UsernameToken"
Content-Length: 16
Content-Type: text/plain;charset=utf-8
Set-Cookie: b=xxx; path=/; expires=Thu, 14-Sep-28 05:05:06 GMT; domain=.hatena.ne.jp
Vary: Accept-Encoding

401 Unauthorized

401 が返ってきました。WWW-Authenticate ヘッダによれば、WSSE 認証が必要なようです。

レスポンスヘッダで一つ気になるのが、なぜか Set-Cookie ヘッダがあることです。Cookie 認証のなごりでしょうか…。セキュリティの観点からもこのヘッダはなくした方がよさそうです。ちなみに expires の年が2桁で 28 なのもちょっと困りますね。UIの方は普通の Cookie を返すので、何かのバグなんでしょうか。以下の例でも全て Set-Cookie ヘッダが返ってくるのですが、省略しています。

サービス文書

ということで、X-WSSE ヘッダを加えてサービス文書を GET してみます。以下、全てのリクエストで認証が必要ですが、長いので、次以降の例ではこのヘッダは省略します。

GET http://d.hatena.ne.jp/yohei/atom HTTP/1.1
Accept: */*
X-Wsse: UsernameToken Username="yohei", PasswordDigest="foo", Nonce="bar", Created="2008-09-19T22:53:55+09:00"
Host: d.hatena.ne.jp
HTTP/1.0 200 OK
Date: Fri, 19 Sep 2008 05:05:06 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 676
Content-Type: application/atomsvc+xml;charset=utf-8
Vary: Accept-Encoding

<?xml version="1.0" encoding="utf-8"?>
<service xmlns="http://www.w3.org/2007/app">
  <workspace>
    <atom:title xmlns:atom="http://www.w3.org/2005/Atom">Hatena::Diary - yohei</atom:title>
    <collection href="http://d.hatena.ne.jp/yohei/atom/draft">
      <atom:title xmlns:atom="http://www.w3.org/2005/Atom">yoheiさんの下書き</atom:title>
      <accept>application/atom+xml;type=entry</accept>
    </collection>
    <collection href="http://d.hatena.ne.jp/yohei/atom/blog">
      <atom:title xmlns:atom="http://www.w3.org/2005/Atom">yoheiさんの日記</atom:title>
      <accept>application/atom+xml;type=entry</accept>
    </collection>
  </workspace>
</service>

サービス文書が取れました。はてなダイアリーAtomPub では二つのコレクションリソースが用意されているのがわかります。draftコレクション(http://d.hatena.ne.jp/{hatena-id}/atom/draft)とblogコレクション(http://d.hatena.ne.jp/{hatena-id}/atom/blog)です。両方とも、Atom エントリ文書が POST できることがわかります。

ちなみに HEAD してみると…

HEAD http://d.hatena.ne.jp/yohei/atom HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 200 OK
Date: Fri, 19 Sep 2008 05:07:26 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 676
Content-Type: application/atomsvc+xml;charset=utf-8
Vary: Accept-Encoding

nil

HEAD リクエストへのレスポンスなのに、ボディに “nil” とあるのが残念です。

コレクションリソース

次にコレクションリソースです。まずは GET してみました。

GET http://d.hatena.ne.jp/yohei/atom/blog HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 200 OK
Date: Fri, 19 Sep 2008 05:34:08 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 20722
Content-Type: application/atom+xml;charset=type=feed
Vary: Accept-Encoding

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <updated>2006-11-24T00:00:00+09:00</updated>
  <id>tag:d.hatena.ne.jp,2006:diary-yohei</id>
  <title>傭兵日記</title>
  <author>
    <name>yohei</name>
  </author>
  <link rel="self" href="http://d.hatena.ne.jp/yohei/atom/blog"/>
  <link rel="next" href="http://d.hatena.ne.jp/yohei/atom/blog?page=2"/>
  <entry>
  ...

プレーンな Atom フィードが返ってきました。rel=”next” なリンクもありますね。これは便利です。

ということで、2ページ目を GET してみました。

GET http://d.hatena.ne.jp/yohei/atom/blog?page=2 HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 200 OK
Date: Fri, 19 Sep 2008 07:06:51 GMT
Server: Apache/2.2.3 (CentOS)
Content-Length: 24964
Content-Type: application/atom+xml;charset=type=feed
Vary: Accept-Encoding
X-Cache: MISS from unknown

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <updated>2006-11-24T00:00:00+09:00</updated>
  <id>tag:d.hatena.ne.jp,2006:diary-yohei</id>
  <title>傭兵日記</title>
  <author>
    <name>yohei</name>
  </author>
  <link rel="self" href="http://d.hatena.ne.jp/yohei/atom/blog?page=2"/>
  <link rel="next" href="http://d.hatena.ne.jp/yohei/atom/blog?page=3"/>
  <entry>
...

2ページ目のリソースからは3ページ目にリンクしてますね。ここで惜しいのは、rel=”prev” なリンクがないことです。リンクが片道通行になっちゃってます。

もうひとつ気になったのはフィードの id です。tag スキームを使っているのですが、これに含まれる年がフィードに含まれる最新エントリの年になるようなのです。MT 3.x で同様の問題がありましたが、idは一定の値でないとまずいので、たとえば2008をハードコーディングした方がよいのではないかなと思います。

それから、Content-Type が

application/atom+xml;charset=type=feed

なんですが、これは正しくは

application/atom+xml;charset=utf-8;type=feed

ですね。

ちなみに、コレクションでも HEAD リクエストを送ってみました。

HEAD http://d.hatena.ne.jp/yohei/atom/draft HTTP/1.1
Accept: */*
Host: d.hatena.ne.jp
HTTP/1.0 405 Method Not Allowed
Date: Fri, 19 Sep 2008 05:16:12 GMT
Server: Apache/2.2.3 (CentOS)
Allow: GET, POST
Content-Length: 22
Content-Type: application/atom+xml;charset=type=entry
Vary: Accept-Encoding
X-Cache: MISS from unknown

nil

おおー、405 が返ってきました。しかもちゃんと Allow ヘッダもあります。ただ、Content-Type のパラメータが charset=type=entry なのはおかしいですね。entry ではないですし、charset=utf-8;type=feed であるべきです。

エントリのPOST

ではエントリを POST してみましょう。まずは draft コレクションに POST してみました。

POST http://d.hatena.ne.jp/yohei/atom/draft HTTP/1.1
Accept: */*
Content-Type: application/atom+xml;type=entry;charset=utf-8
Content-Length: 131
Host: d.hatena.ne.jp

<entry xmlns="http://www.w3.org/2005/Atom">
<title>test</title>
<content type="text">- test
-てすと</content>
</entry>
HTTP/1.0 201 Created
Date: Fri, 19 Sep 2008 07:45:24 GMT
Server: Apache/2.2.3 (CentOS)
Location: http://d.hatena.ne.jp/yohei/atom/draft/1221810325
Content-Length: 549
Content-Type: application/atom+xml;charset=type=entry
Vary: Accept-Encoding

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
  <id>tag:d.hatena.ne.jp,2008:diary-draft-yohei-1221810325</id>
  <link rel="edit" href="http://d.hatena.ne.jp/yohei/atom/draft/1221810325"/>
  <author>
    <name>yohei</name>
  </author>
  <title>test</title>
  <updated>2008-09-19T16:45:25+09:00</updated>
  <published>2008-09-19T16:45:25+09:00</published>
  <app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-19T16:45:25+09:00</app:edited>
  <content type="text/plain">- test
-てすと</content>
</entry>

無事エントリが作成されます。ちなみに、content 要素の内容は draft コレクションでははてな記法の文字列が text/plain 形式で入り、blog コレクションではhtmlに変換済みの文字列が text/html で入ります。そして、blog コレクションでのはてな記法の文字列は hatena:syntax 要素に入ってくるそうです。これは最初わからず、ちょっとはまりました…

ただ、ちょっと気になるのは content 要素の type 属性の値です。RFC 4287 では text/html/xhtml or MIME タイプを取ることになっており、ここでは text/plain ではなく text が、そして text/html ではなく html が望ましいですね。

その他の操作

とりあえず仕様書どおりに実装すれば PUT/DELETE はできました。blog コレクションでも PUT するときは content 要素にはてな記法を入れるというのにははまりましたが…。これについては設計編で詳しく触れたいと思います。

それから、ちょっとした不正 URI のテスト(全てGET)をしてみましたが、以下のような結果でした。

  • http://d.hatena.ne.jp/{hatena-id}/ato -> 302
  • http://d.hatena.ne.jp/{hatena-id}/atom/blo -> 404
  • http://d.hatena.ne.jp/{hatena-id}/atom/blog/20 -> 404
  • http://d.hatena.ne.jp/{hatena-id}/atom/blog/{yyymmdd}/foo -> 403

これらは全て 404 が望ましいと思います。

また、全体的に Content-Type が正しく設定されていない場面がみうけられます。

たとえば DELETE のレスポンスが空なのに text/html;charset=utf-8 だったり、400/403 が返るときにプレーンテキストなのにapplication/atom+xml;type=entry が返ってきたりなどです。

この辺はテストケースの不足だと思いますが、AtomPub には Tim Bray が作った Ape や Joe Gregorio の Atom Publishing Protocol Test Suite など、テストケースが用意されているので、これらを使ってテストするといいと思います。

2008-09-05

proxy 認証の通し方まとめ

はてなブックマーク   livedoor clip

こんにちは、日野原です。

Google のブラウザ、「Google Chrome」が発表されてダウンロードできるようになりましたが、社内でインストールしようとしてみたところプロキシ認証に対応していないらしく、インストールに失敗してしまいました。プロキシ自体には対応しているようなので、ローカルプロキシを立てて認証を任せてあげればインストールできるようです。

と言うわけで今日はプロキシ認証についてです。

自宅で趣味のプログラミングをしているときなどは良いのですが、会社で何かをしようとするとこれのせいではまることがとっても多いので、同じような悩みを持っている人達のために情報をまとめておこうと思います。

ただし、基本的にLinux(Fedora もしくは CentOS 系)での情報です。また、以下の例では、プロキシの情報は下表のようなものとします。

ホスト proxy.example.com
ポート 8080
ユーザ foo
パスワード bar

基本編

まず基本として、環境変数 http_proxy にプロキシの URL を指定します。
bash や zsh を使っている場合は .bashrc、.zshrc に以下の行を書いておきます。

export http_proxy=http://foo:bar@proxy.example.com:8080

これで wget、curl や Plagger は OK です。例外設定は NO_PROXY ですね。ただ、wget は NO_PROXY には対応していないようです。

yum 編

/etc/yum.conf 内で proxy を設定します。(次の一行を追加します)

proxy=http://foo:bar@proxy.example.com:8080/

最後の / は、付けないと動かなかったことがあるので付けておきました。現象の再現ができていないため、詳細はわかっていないのですが。

subversion 編

ホームディレクトリ下の ~/.subversion/servers に指定します。
初期状態でこのファイルの最後の [global] セクションにコメントアウトされた状態で設定が書かれているので、それを書き換えます。

http-proxy-exceptions = *.example.com
http-proxy-host = proxy.example.com
http-proxy-port = 8080
http-proxy-username = foo
http-proxy-password = bar

subversion の場合は社内にアクセスすることも多いと思うので、例外設定(http-proxy-exceptions)もしておかないと後悔するでしょう。

rubygems 編

rubygems は環境変数を見てくれるので list 等のコマンドは基本編と同じでいけます。
しかし install など root 権限が必要になるコマンドでは su 後に export して環境変数を設定する必要があります。また、sudo では環境変数を引き継げないので、-p オプションでプロキシの設定を渡します。

sudo gem install rails -p http://foo:bar@proxy.example.com:8080

ただ、1.0より前のバージョンの rubygems ではバグがあって -p オプションが効かないので、rubygems の config_file.rb(私の環境では /usr/lib/ruby/site_ruby/1.8/rubygems/config_file.rb)内にある class Gem::ConfigFile に以下のメソッドを追加する必要があります。

  def []=(key, value)
    @hash[key] = value
  end

ruby/open-uri 編

Rails の script/plugin など、ruby の open-uri を使用しているプログラムはそのままでは環境変数にプロキシを設定しても認証には対応してくれません。(1.8.6.114までではだめでした。)
open-uri.rb(私の環境では /usr/lib/ruby/1.8/open-uri.rb)に以下のパッチを当てる必要があります。

216c216
<         klass = Net::HTTP::Proxy(proxy.host, proxy.port)
---
>         klass = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password)

これで、環境変数 http_proxy の設定でいけるようになります。

apache ant 編

最近 ant を使って java のアプリをインストールしたんですが、ivy で必要なライブラリを自動でダウンロードしてくれるところに感動しました。
でもその感動の前にはプロキシ認証の壁が立ちはだかったのです。
と言うわけで環境変数 ANT_OPTS にプロキシの設定を渡しましょう。

export ANT_OPTS="-Dhttp.proxyHost=proxy.example.com \
                 -Dhttp.proxyPort=8080 \
                 -Dhttp.proxyUser=foo -Dhttp.proxyPassword=bar"

Amazon Web Services(EC2) 編

ant が JVM に渡すパラメータを独自の環境変数で管理しているということは、同じ java のアプリである EC2 の管理ツールも同じようになっているわけです。
EC2_JVM_ARGS に指定してください。(EC2の場合は https も使うので、https の設定も必要です。)

export EC2_JVM_ARGS="-Dhttp.proxyHost=proxy.example.com \
                     -Dhttps.proxyHost=proxy.example.com \
                     -Dhttp.proxyPort=8080  -Dhttps.proxyPort=8080 \
                     -Dhttp.proxyUser=foo -Dhttp.proxyPass=bar"

Amazon Web Service を試してみたので次は Google App Engine… と行きたいところですが、Python はわからないのでそれはまた次の機会にでも。

【番外?】Google Chrome(Windows版)インストール編【本題?】

最後に、冒頭で触れた Google Chrome のインストールをしましょう。
今回はローカルプロキシに Proxomitron日本語の情報)を使用しました。

読者層を考えて、ダウンロードからインストールの手順は省略します。

起動したら、まずは右側の下の方にある「Proxy」をクリックして、HTTP Proxies とある欄のコンボボックスにプロキシのサーバ名とポートを「proxy.example.com:8080」というように入力します。

次にパスワードを入力するため、今入力したコンボボックスを右クリックして開くメニューで「Advanced Proxy settings」を選択します。
普通こんな操作、気付きませんよね。隣に座っている同僚が教えてくれました。

ダイアログが開いたら一番下の「Send username and password to proxy」にチェックを入れて、ユーザ名とパスワードを入れます。これでプロキシサーバの設定は終了です。

次にこのサーバを使うようにするため、一番はじめのウインドウで左下にある「Bypass」をクリックし、その上にある「Use Remote Proxy」にチェックを入れます。

これでローカルプロキシの設定は終わりです。

後は Google Chrome のインストーラがこのプロキシを使うように、IE と Firefox のプロキシの設定を localhost:8080 にして、ChromeSetup.exe を起動するだけです。
(IEだけだとインストールが途中で止まってしまう場合もあるようです。)

以上で番外編も終了です。

今回はこんなところで打ち止めですが、またなにかあったら情報を載せていきます。