タイトル/名前 | 更新者 | 更新日 |
---|---|---|
日記/2009/02/22/Subversionの認証で整理メモ | msakamoto-sf | 2009-02-22 12:25:25 |
技術/Apache/WebDAVメモ | msakamoto-sf | 2009-02-22 11:35:39 |
技術/Apache/mod_rewriteメモ(1):RewriteBaseの誤解 | msakamoto-sf | 2009-02-16 23:22:19 |
日記/2009/02/15/CentOS5.2でXfce4.4を使う | msakamoto-sf | 2009-02-15 15:29:18 |
日記/2009/02/14/JavaScript勉強始めるのが遅すぎたか。 | msakamoto-sf | 2009-02-14 14:29:58 |
JavaScript/prototypeメモ2 | msakamoto-sf | 2009-02-14 14:15:27 |
画像/JavaScript/js_prototype_memo2_05.png | msakamoto-sf | 2009-02-14 13:36:44 |
画像/JavaScript/js_prototype_memo2_04.png | msakamoto-sf | 2009-02-14 13:36:25 |
画像/JavaScript/js_prototype_memo2_03.png | msakamoto-sf | 2009-02-14 13:36:05 |
画像/JavaScript/js_prototype_memo2_01.png | msakamoto-sf | 2009-02-14 13:35:38 |
Tracをインストールしようとしていたのだけれど、Subversionの設定絡みで頭がこんがらがってきた。
特にSubversionの認証がよく分からない。svnserve.confに書いたり、httpd.confのAuthzSVNAccessFileに書いたりとよく分からなくなってしまった。
基本に戻ってSubversionのアーキテクチャを再確認してみる。
http://subversion.bluegate.org/doc/ch01s04.html
上のURLの末尾のファイル名は今後変わるかも。tigrisのSubversionページ(http://subversion.tigris.org/)のHelpで"Subversion Book"リンクをクリックすれば大元のページに行けます。
で、上記URL掲載の図を見るとSVNクライアントがリポジトリシステムにアクセスするルートが3種類あることが分かる。
ここで「認証」が必要になってくるのはどこかというと
まずsvnserver経由だけれど、ソースを見るとやっぱりというか何というか、svnserve/server.c の中でauthz(パスペース認証)の読み込みとチェックをしている箇所があった。ユーザ認証とパスベースの許可についてはsvnserver側で勝手に設定ファイルを読み込んでくれているみたい。
次にWebDAV経由。こちらは mod_authz_svn/mod_authz_svn.c の中で AuthzSVNAccessFile ディレクティブ値を取得し、何段か関数を経由してsvn_repos_authz_check_access() を呼んでいる。パラメータをざっと見ると、ユーザ名がrequest_rec構造体のuserメンバを使っている。これはApacheのユーザ認証をパスしたユーザ、つまりBasicなりDigest認証なりをかけてパスしたユーザ名が入る。
まとめるとこんな感じ?
svnserve経由 | WebDAV経由 | |
---|---|---|
インターフェイス | svnserve/{main,serve.c} | mod_dav_svn/mod_dav_svn.c |
ユーザ情報 | svnserver.conf経由 | ApacheによるBasic/Digestなど認証経由 |
(同)取得モジュール | svnserve/{main,serve.c} | (Apacheモジュール) |
パスベース認証情報 | svnserver.conf経由 | mod_authz_svnのAuthzSVNAccessFileディレクティブ経由 |
(同)取得モジュール | svnserve/{main,serve.c} | mod_authz_svn/mod_authz_svn.c |
ユーザ情報とかパスベース認証情報が、アーキテクチャ上の最終段、リポジトリシステムではなくて、クライアントとの接続インターフェイス部分で操作している為に WebDAV経由/svnserve経由でそれぞれ設定方法が分かれてしまったのが混乱の原因だったのかも。
WebDAV経由だとsvnserver.confが使われないのが最初分かりづらかったんだけど、まぁ確かにsvnserver.confはsvnserver用の設定ファイルなんだから、WebDAV経由は関係ないよな、と納得。
最後に当然の前提だったのだけれど、SubversionのコンパイルにApacheのソースが必要というのは、APRを使うのもあるけど、mod_dav_svnとかmod_authz_svnというApacheモジュールのソースがSubversionのソースツリーに最初から含まれているのもあるのかも。Win用のバイナリとかはApache2.0用/2.2用と二つに分かれてて、なんでかなぁと思ったのだけれどApacheライブラリやモジュールの仕様に引きずられるので、そういった事情なのだろう。
mod_dav はWebDAVプロトコルへのインターフェイス。これにより、HTTPが拡張されWebDAVプロトコルが使えるようになる。
実際にデータストアを操作するには別にモジュール("プロバイダ")が必要。Apacheにはファイルシステムプロバイダの mod_dav_fs モジュールが添付されている。
httpd.confで
DAV On
とすると、mod_dav_fs モジュールがプロバイダとして使われる。
この応用で、Subversionでは mod_dav_svn というモジュールをプロバイダとして提供しており、これをLoadしておき、
DAV svn
とすると、mod_dav_svn モジュールがプロバイダとして使われるようになる。
このように "DAV" ディレクティブはOn/Offの他にプロバイダ名を指定できる。プロバイダ名とモジュール名は違う。"On" は実際にはfilesystemというプロバイダのエイリアスだが、これはmod_dav_fsモジュールが提供している。
mod_dav (Apache2.0ドキュメント) : http://httpd.apache.org/docs/2.0/ja/mod/mod_dav.html
以下、やや古いがWebDAV関連の@ITの記事
mod_rewriteのRewriteBaseを完全に誤解していたのでメモ。
参考:
嵌った時の条件
→いくら頑張っても404になる。
書いたmod_rewrite設定はこんな感じでした。(RewriteRuleの正規表現は適当にごまかしてる偽物です。)
<DocumentRoot "/var/www/html"> RewriteEngine On RewriteBase /dir1 # これが間違い RewriteRule ^/([a-z]+)/([a-z]+) /cgi-bin/index.pl?p1=dir1&p2=$1&p3=$2 [L] </DocumentRoot>
RewriteBaseを「処理するURLのprefixを指定するもの」と思いこんで"/dir1"を指定していたのが間違いでした。
mod_rewriteのRewriteBaseの挙動をおさらいしてみますと、そもそもmod_rewriteって「rewriteした後のURLで再度Apacheに内部的にリクエストする」という特殊処理があり、この再リクエストの際のディレクトリ補正に使われるのがRewriteBaseです。
上記の設定例ですと、mod_rewriteの内部処理の イメージは こんな感じになるんでしょうか(自信ない・・・)
リクエスト : /dir1/foo/bar (1) /dir1/foo/bar -> /var/www/html/dir1/foo/bar (ApacheのDocumentRootによる変換) (2) /var/www/html/dir1/foo/bar -> "/var/www/html" + "/dir1/foo/bar" →mod_rewriteによるDocumentRoot位置での分割。 (3) "/dir1/foo/bar" -> (RewriteRule) -> "/cgi-bin/index.pl?p1=dir1&p2=dir1&p3=foo [L] →"/dir1"のせいでこの時点で既に意図した方向からズレてる。 (4) RewriteBase + (3) -> "/dir1/cgi-bin/index.pl?p1=dir1&p2=dir1&p3=foo" →内部的な再リクエストのためにRewriteBaseを使って再構築 (5) "/dir1/cgi-bin/index.pl?p1=dir1&p2=dir1&p3=foo" -> /var/www/html/dir1/cgi-bin/index.pl?p1=dir1&p2=dir1&p3=foo → 404 Not Found.
という訳で、いくら頑張っても404 Not Foundになってしまった次第です。
正しいRewriteBaseの設定は、この場合次の2パターンになります。
RewriteBase / # DocumentRootからの相対パス RewriteBase /var/www/html # ファイルシステム上の物理パス
これにより、(4)の時に実際の /cgi-bin/index.pl を指すDocumentRoot相対パス or 物理パスが取得され、(5)で正常にリクエストが渡ります。
RewriteRuleも "/dir1" をちゃんと入れてあげます。
RewriteRule ^/dir1/([a-z]+)/([a-z]+) /cgi-bin/index.pl?p1=dir1&p2=$1&p3=$2 [L]
Alias設定だったときはきちんと物理パスを指定した方が良い?らしい?です?(すみません、これまだ未実験です。)
というわけで、RewriteBaseは「mod_rewriteが書き換え後に再度内部的にリクエストする際のURLパスのprefix」と考えれば大きな勘違いは無くなると思います。
今回の様なパターンで、"dir1, dir2, ..."とかバリエーションが大きい場合は、素直にWebアプリのフロントディスパッチャとかで集中処理した方が、RewriteRuleが簡単に済むと思います。(少なくともsymfonyやCakePHPとかはそうした形の.htaccessになっていたような・・・)
一旦GNOMEかKDEでログインして、パッケージを入れないと駄目なのが少し癪だけど仕方ない。
$ sudo yum groupinstall XFCE-4.4
これやるのもなー。何だか使わないpluginとかもどかーっと入ってきそうで、やってない。
次にXfceでXを起動するまでがやや手間取った。
自分の場合、init 3でコンソールログインで起動するように設定している。(*1)
Xを起動したい場合はstartxコマンドを叩いている。
で、startxコマンドから叩くと相変わらずGnomeが立ち上がってしまう。
以降は完全にCentOS5.2の説明になってしまうが、下記のようにすればXfceをstartxで立ち上げられる。
1. /etc/X11/xinit/Xclients ファイルを $HOME にコピーし、.Xclients にリネームする。
$ cp /etc/X11/xinit/Xclients ~/.Xclients
2. 以下の行を .Xclients の上の方に追記する。
XFCE="$(which xfce4-session 2>/dev/null)" exec "$XFCE"
とりあえずこれで行けた。
何でこうすればxfceが動くのかについては、man startxしてください。
$HOME/.xinitrc -> /etc/X11/xinit/xinitrc -> $HOME/.Xclients -> /etc/X11/xinit/Xclients
の順で読み込んで行くので、$HOME/.Xclientsのところでxfceを立ち上げてそこでstopさせるわけです。/etc/X11/xinit/Xclientsまで進んでしまうと、Gnome/KDEが動いてしまうようです。
ここ暫くECMA262と戯れているのだけれど・・・。
ぶっちゃけ、下手な解説サイト読むよりはECMA262熟読した方が分かりやすい。特にJavaScriptの言語仕様に絡むところは。
なんというか、prototype周りとかスコープ絡みとか、ECMA262に記述されている「舞台裏」を把握しないと、「え?なんでAは予想通りに動いてくれるのにBの書き方は駄目なの?」というのが多すぎるような気がする。
今頃JavaScriptをまじめに勉強し始めた自分が、時期はずれというか、呆けているというか・・・。
Ajaxが流行りだし、Prototype.jsが出回り始めた頃には @IT とかMYCOMとかでJavaScriptのオブジェクト指向や裏側を解説した記事が多かったけど、やっぱり旬な内にしっかり・ちゃっかりやっておくべきだったかもしれない。
しかしjrunscriptというかRhinoは助かった。対話モードで実行できるので、ちょっとした実験コードの打ち込みとか実証がすごい楽だった。
※jdk1.6.0_01のjrunscriptを使ってます。
prototype周りで遊んでいたら、こんな疑問が出てきました。
# 1. プロトタイプオブジェクトをオブジェクトリテラルで用意 js> base = { prop1 : 'abc' } [object Object] # 2. コンストラクタ用の関数オブジェクトを用意して、1.のオブジェクトをprototypeにセット js> function C1() {} js> C1.prototype = base [object Object] # 3. C1をコンストラクタにしたオブジェクトを生成。 js> o1 = new C1() [object Object] js> o1.prop1 abc # 4. o1の"prop1"に"DEF"を設定 js> o1.prop1 = "DEF" DEF js> o1.prop1 DEF # 5. プロトタイプオブジェクトの中身は変わってない? js> base.prop1 abc
ということで、4. と 5. の間で o1 の prop1 プロパティに一体何が起こったのか?ということで、調べてみました。
先に結論に入ってしまいます。
オブジェクトのプロパティを単に評価するだけの時は "Prototype" チェーンを辿りますが、代入操作の場合は "Prototype" チェーンを辿らないのが原因です。
内部挙動を簡単に説明すると、4.のように値を取得するだけの時の挙動はこんな感じになっています。
一方 5. のように値を設定する時の挙動はこんな感じになっています。
図解で見てみれば分かりやすいと思います。
js> base = { prop1 : 'abc' } [object Object]
js> function C1() {} js> C1.prototype = base [object Object]
js> o1 = new C1() [object Object] js> o1.prop1 abc
この時点で "o1.prop1" を評価すると、o1にはprop1プロパティが無い為、prototypeチェーンを辿ったbaseオブジェクトのprop1が返されます。
js> o1.prop1 = "DEF" DEF js> o1.prop1 DEF
以上のような挙動の為、冒頭で実験したような動きになった訳です。
ところで、あるプロパティが本当にそのオブジェクトの持ち物なのか?prototypeチェーンの先のものなのか?を調べるには、Object.prototype.hasOwnPropertyが使えます。
js> o2 = new C1() [object Object] js> o2.hasOwnProperty("prop1") false js> base.hasOwnProperty("prop1") true
ECMA262の仕様書を読み慣れるに従い、うかつな解説が出来なくなって参りましたので、ここでは参考にしたセクションを引用するに留めます。
まず代入演算子の仕様です。
11.13.1 Simple Assignment ( = ) The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows: 1. Evaluate LeftHandSideExpression. 2. Evaluate AssignmentExpression. 3. Call GetValue(Result(2)). 4. Call PutValue(Result(1), Result(3)). 5. Return Result(3).
GetValue(), PutValue()という(これも解説の便宜上ラベリングされた機能です)メソッドを呼んでいます。この二つの仕様は次の通りです。
8.7.1 GetValue (V) 1. If Type(V) is not Reference, return V. 2. Call GetBase(V). 3. If Result(2) is null, throw a ReferenceError exception. 4. Call the [[Get]] method of Result(2), passing GetPropertyName(V) for the property name. 5. Return Result(4).
8.7.2 PutValue (V, W) 1. If Type(V) is not Reference, throw a ReferenceError exception. 2. Call GetBase(V). 3. If Result(2) is null, go to step 6. 4. Call the [[Put]] method of Result(2), passing GetPropertyName(V) for the property name and W for the value. 5. Return. 6. Call the [[Put]] method for the global object, passing GetPropertyName(V) for the property name and W for the value. 7. Return.
それぞれ"Get"/"Put"という仕様上の機能を呼んでいることが読み取れます。"Get"/"Put"の仕様は以下の通りです。
8.6.2.1 [[Get]] (P) When the [[Get]] method of O is called with property name P, the following steps are taken: 1. If O doesn’t have a property with name P, go to step 4. 2. Get the value of the property. 3. Return Result(2). 4. If the [[Prototype]] of O is null, return undefined. 5. Call the [[Get]] method of [[Prototype]] with property name P. 6. Return Result(5).
8.6.2.2 [[Put]] (P, V) When the [[Put]] method of O is called with property P and value V, the following steps are taken: 1. Call the [[CanPut]] method of O with name P. 2. If Result(1) is false, return. 3. If O doesn’t have a property with name P, go to step 6. 4. Set the value of the property to V. The attributes of the property are not changed. 5. Return. 6. Create a property with name P, set its value to V and give it empty attributes. 7. Return. Note, however, that if O is an Array object, it has a more elaborate [[Put]] method (15.4.5.1).
というわけで、今回のJavaScript探索はこの辺でお仕舞いに致します。