読者です 読者をやめる 読者になる 読者になる

anti scroll

ブラウザと小説の新しい関係を模索する

OCaml から OAuth

プログラミング

OCamlでOAuthを使う方法を探していたら既にooauthというライブラリがあって助かった、と思ったのですが結構はまったのでメモしておきます。

まずCryptokit。これをooauthが使うのですが、ooauthがfindlib経由でcryptokitを参照するのに、本体のcryptokitはデフォルトのままだと標準ライブラリのディレクトリにインストールされてしまうので、METAファイルをこんな感じで用意して、site-lib以下にインストールします。

name="cryptokit"
version="1.3"
description="lib for crypt"
requires="unix num"
archive(byte)="cryptokit.cma"
archive(native)="cryptokit.cmxa"

次に、ooauthが使うクライアントにocurlを使うのですが、現在のocurlのモジュール名がこのライブラリが出た当初と変わっていることもあって、ooauthモジュール内のoauth_ocurl_http_client.mlをいじらないといけませんでした。

まずは、ocurlを使う際に参照するモジュール名が全て「Ocurl」となっているので、これらを全て「Curl」に変えます。

Ocurl.init()Curl.init()
<以下同様>

次に、oauth_ocurl_http_client.ml の32行

Ocurl.set_headerfunction oc (Buffer.add_string h);
Ocurl.set_writefunction oc (Buffer.add_string b);

となっている箇所ですが、現行のocurlのset_headerfunction ではインタフェースが

t -> (string -> unit) -> unit

から、

t -> (string -> int) -> unit;

と変わっているので、set_headerfunctionやset_writefunctionに与えるクロージャーを、戻り値に書き込んだバイト数を返す関数に置き換えます。

つまりこんな感じで関数を定義して

let add_string buff str =
  let prev = Buffer.length buff in
  let () = Buffer.add_string buff str in
  let after = Buffer.length buff in
    after - prev

以下の箇所を

Ocurl.set_headerfunction oc (Buffer.add_string h);
Ocurl.set_writefunction oc (Buffer.add_string b);

こう書き換える(Ocurlとなっている箇所も、Curlと書き換える)。

Curl.set_headerfunction oc (add_string h);
Curl.set_writefunction oc (add_string b);

しかしこのままでもビルドは通らず……これはOcamlbuildがOcurlのパッケージ名を「ocurl」で取りに行っているからなので、ソースディレクトリの「_tags」にある次の箇所

<oauth_ocurl_http_client.ml> : pkg_ocurl

<oauth_ocurl_http_client.ml> : pkg_curl

と修正します。

これでようやくインストール完了となり、晴れてoauthの超簡単クライアントがOauth_clientファンクターにOauth_ocurl_http_clientモジュールを喰わせて

module OC = Oauth_client.Make(Oauth_ocurl_http_client)

のように取得できるように。

2010/05/01追記

しかしこのままでも、例えばPOSTするとき中身のデータにOAuthが固有の仕様で予約している文字列が入っている時に、サーバー側とのsignatureが一致しませんでした。

oauth_common.mlの中身を覗くと、

let rfc3986_encode s = Netencoding.Url.encode s

となっていますが、これをこう書き換えます。

let reserved_chars = Netstring_pcre.regexp "[^a-zA-Z0-9\\-\\.\\_\\~]"

let rfc3986_encode str =
  Netstring_pcre.global_substitute reserved_chars (fun ret s ->
    let ms = Netstring_pcre.matched_string ret str in
      if ms = "!" then
	"%21" (* Netencoding.Url.encode がなぜか上手く働いてくれない *)
      else
	Netencoding.Url.encode ms ~plus:false
  ) str

Netencoding.Url.encode "!"はなぜか"%21"ではなくて、"!"を返してしまうのでハマリました。