ものがたり(旧)

atsushieno.hatenablog.com に続く

WCF question (2) ChannelDispatcherって嘘っぱちなんじゃないの?という疑惑

id:atsushieno:20091023:p1 でQ1を書いたんだけど、ここを見ている人で解ける人は誰もいなそうなので、すっかり忘れていた疑問その2。頭の中を整理するのと備忘録的な目的とで書いておきます。

ChannelDispatcherというクラスがあります。ChannelDispatcherはServiceHostに1つ以上存在するもので、1つ以上のendpointをもっています。その名前が正しければ、channelをdispatchする、あるいはchannelからdispatchするものなんだと思いますが、実際にこいつが受け取るのは、ただひとつ関連づけられたIChannelListenerから生成されるIReplyChannelの実装あるいはIInputChannelの実装 から受け取ったMessageです。ChannelDispatcherは、Messageを受け取ったら、それを然るべきEndpointDispatcherにdispatchします。

EndpointDispatcherにはmessage filterがあり、たとえばActionMessageFilterでSOAP Actionなどをもとにメッセージを拾うかどうかを判断したり、EndpointMessageFilterでリクエスURIからメッセージを披露かどうかを判断したりします。複数のEndpointDispatcherがひとつのMessageにmatchする場合、それらのendpoint dispatchersのうちFilterPriorityが高い方が適用され、もしFilterPriorityで優劣がつけられない場合はエラーになります。

さて、実は、ServiceHostでは、複数のChannelDispatcherが、同一のlisten URIを対象とするEndpointDispatcherを定義することができます。そして実はこれは実際に行われています。WebHttpBindingを使用して作成されたIChannelListenerのlisten URIと、ServiceMetadataExtensionが内部的に定義するIMetadataExchangeのためのIChannelListenerのlisten URIは、デフォルトで同一になります(たとえばhttp://localhost:8080 でlistenしていたら、http://localhost:8080)。ただ、WebHttpBindingに関連づけられて作成されたEndpointDispatcherのMessageFilterには、(おそらくその内部で生成されるWebHttpBehaviorのApplyDispatchBehavior()によって)FilterPriority=1が設定されます。

さて問題。これらのChannelDispatcherの間で生成されたendpoint dispatchersの間で、Messageを正しく受信して処理することは出来るでしょうか?

まず(この質問を理解できる人のうちの)多くの人が、ChannelDispatcherが同じものだろうが違うものだろうが、複数のendpoint dispatchersの間でのMessageのfilteringは正しく出来るんじゃないか、と思うでしょう。しかしMessageを受信するのは誰でしょうか? ChannelDispatcherに結びつけられたIChannelListenerです。そしておそらくHttpChannelListener的な実装には、その元になった*それぞれのBindingに*MaxReceivedMessageSizeなどのプロパティが設定されており、もっと重要なことを言えば、MessageVersionが設定されているはずです。ひとつのChannelDispatcherで受け取ったMessageを、他のChannelDispatcherで処理することは、原則的に無理だと考えなければならないのです。

それにもかかわらず、EndpointDispatcherのMessageFilterが「このリクエストを扱うかどうか」を判断する対象はMessageです。MessageはChannelDispatcherを決定しない限り受け取ることができません。(HTTPリクエストをまるごとキャッシュしますか? それは場合によっては数MB単位のものになりえますが。)

先に書いた通り、WebHttpBindingによって生成されたEndpointDispatcherは、WSDLリクエスト用のChannelDispatcherのEndpointDispatcherより、高い優先度が設定されています。しかし、上記のchichen and eggの状態を解決せずに正しいendpoint dispatcherに辿り着くことは、出来ないはずです。

そうなると、僕が思いつく解決策は、ChannelDispatcher、EndpointDispatcherそしてHttpChannelListener的な実装との間で、内部的にしか実装できない優先順位解決ロジックをでっち上げることだけです。

しかし、こんな基本的な優先度の問題を解決できないとしたら、ChannelDispatcherって一体何のために存在しているんでしょう? どうも設計に失敗しているのではないかという疑惑が生じてしまいます。

追記: 一応参考リンク