#ふとしたきっかけがあったのでrfc.httpの持続的接続の実装を少し進めてみたんだけれど、サーバ側で切断された場合を検出するのが難しいな。
#素直にソケットを使いまわしてrequest -> responseするだけだと、サーバ側切断は(1)request中にエラーになる(2)responseを読もうとしてEOFが返るか、で始めて検出できる。
#HEADやGETの場合はそれを捕まえて接続しなおしてリトライをかけたいところだけど、他のエラーとの区別が面倒だし、冪等でないリクエストだとリトライすべきかどうかをアプリケーションに投げないとならないのでそれもややこしい。
#リクエスト途中で切られちゃった場合はどうしようもないけど、2回目以降のリクエストを出す前に接続が生きてるかどうかだけでもチェックできれば、その時点で繋ぎ直せるので話は簡単になる。ではどうやって生きてるかどうかチェックするか。
#ソケットの読み出し口をselect()してreadableだったら、というのはひとつありかも。サーバが向こう側を閉じたならreadableになって読むとEOFが返るし、まだ閉じてなければreadableでないはずだから。
#マルチスレッド前提で、送るのと読むのを別スレッドで回してキューで同期とるような形なら、切断は直ちに読み出しスレッドで検出できるんだけど、rfc.httpのレイヤでスレッド前提にしちゃうのもきつい。
#こんにちは。
#rfc.httpの実装ありがとうございました。すごく個人的な意見なのですが、rfc.httpのhttp-requestはサーバーの応答そのものじゃなく<http-responce>みたいなオブジェクトを返してもらった方がいいかなと思いました。receiverが必要な場合はhttp-requestを使ってsocketを取得して、ただ応答が欲しい場合は省略記法としてhttp-getを使う、という使い回しがいいかなと。あとこれもすごくオブジェクト指向的なんですが、接続の確立(<http-connection>を作る)とリクエスト(<http-connection>へリクエストを送る)は分離されていた方が個人的にはしっくりくると感じました。いかがでしょうか…?
#(1)http-getなどは「簡易API」で、正式なAPIは(まだunofficialですが) make-http-connection で <http-connection> を作り、http-request でリクエストを送る、というものです。
#(2) http-requestがレスポンスオブジェクトを返すというのはアリですが、リクエストを送った側は速やかに応答を読み出さないとならない (レスポンスを保存しておいて必要な時に読む、とか、いらないから読まない、とかいうことは避けるべき) で、そういう時系列的なお約束をAPIの外に持たなければならないのはいまいち。
#例えば持続的接続を使って同じサーバにふたつリクエストを送った場合、レスポンスオブジェクトは応答の順番に読み出さなければならず、またpipeliningをしてた場合、2度目のレスポンスで失敗したら最初のリクエストからリトライする必要があります。<http-response>を返すのは一見抽象度が高いようですが、こういう「お約束」をどこにも表現できないため、案外使いにくい、というのが私の経験です。
#こういう時間的な依存関係を表現するにはモナドのような関数的なAPIの方が相性が良いですね。
#マルチスレッド前提にしないのはどういう理由でしょう? 無効にしているかもしれないから?
#ですねえ。MinGW等、まだサポートされてないプラットフォームもあります。
#あと、アプリケーションによっては途中でforkしたい場合があるかもしれず、rfc.httpを使っただけで勝手にスレッドが作られるとちょいと都合が悪い、というような場合も。
#ある程度言語の実行モデルを「スレッドはいつもある。forkはしない (かわりに安全に子プロセスをキックする何かを提供する)」のように決め打ちにしちゃうと色々綺麗になるだろうとは思うんですが。
#まだ正式じゃなかったんですね。
#なるほど。HTTPリクエストをサブミットするとfutureがすぐ返ってきて、裏で別スレッドがリクエストをサブミットしてレスポンスをfutureにセットしてくれる、みたいなやつが最近便利だなと思っていて、しかしこれだとマルチスレッド前提なんですよねー。
#時代はそっちの流れなのかなあ。割り切っちゃえばいいんだけど。抽象化の壁を破ってOSべったりのところもいじれるようにしておこうと思うと、そこで高機能な抽象化ルーチンの前提を崩すような操作をされたらどうするのか、あたりの方針が難しい。
#futureで抽象化するのはありですね。それなら時間的依存関係とかを壁の向こう側で済ませることができる。
#部分継続使えばレスポンスを受け取るコールバック形式からfuture形式に変換できると思うけど。
#一応意図としては、rfc.httpは低レベルAPIで「部品」を提供しといて、いろんなネットワークプロトコルを統一的に扱う高レベルAPIを作るってことを考えてるんで、高レベルAPIの方でそういう抽象化を提供できるかも。
#複数のHTTPリクエストを並行して投げたいときというのは結構多いようなきがしていて、futureだとレスポンスにアクセスせずに次々にリクエストを投げれば並行に処理されるというのが(適当に書いても)期待できるというのも嬉しい。
#HTTPのpersistent connectionなどはそもそも複雑なものだからマルチスレッド前提にしてしまってもいいのではないかというのが僕の印象ですかね。
#あとマルチスレッドがOSの低レベルなものと相性が悪いのは、fork()以外に思いつかないのですが、なにかあります?
#あとはシグナルくらいかな。(シグナルそのものが使えない、というより、シグナルを活用するようなモデルがマルチスレッドとはちょっと思想が違う)
#マルチスレッドがサポートされていないときは、HTTPリクエストをサブミットするとfutureが返ってくるのだけど(APIは同じ)、実際の処理は同じスレッドで同期的に行っている、という見せ方もありえるかも。
#コルーチンか何かでやるということ? 違いを完全に隠蔽するのは難しそうだが…
#サブミットしたらHTTPレスポンスが返ってくるまで処理が戻らない
#マルチスレッドだとすぐ制御が返ってくるのだけど
#ああ、futureが帰ってきたときはもう既に処理が済んでる、ということですか。
#そうです
#Persistent connectionがシングルスレッドでは実装しづらいということへの解決策にはならないんですが。
#APIは同じだけど、アプリとしてはMTの有り無しで書き方を変えないとならないケースが出てきそうだが…それは他の場合でも同じかなあ。
#でてきそうですね。だいたいの場合同じコードがMTなしなら遅い(HTTPリクエストが同期になるので)が動く、みたいな感じではないかと思ったんですがあまり根拠はない。
#futureで返すとして、例えばダウンロードのprogressをモニタしたい、なんて要求をうまくレイヤリングする手はありますか?
#今のrfc.httpのreceiverプロトコルはそのへんも視野に入れてるんだけど、どうも複雑過ぎる感じがしてしっくりこない。
#レイヤリングというのは、「progressのモニタリング」というspecificな機能を追加するんじゃなくて、そういった機能を誰かが追加したいと思ったときに簡単に追加できるような仕組みって意図です。
#よく見えてないのですが future がフックポイント「も」持つようにするということですか?
#futureでどうすべきかというのが私にも見えてないってことです。今のrfc.httpの(非公開)APIでは関数をレイヤリングすればそういう機能を追加できるんですが。
#うーむ、どういう機能追加がありえるのかあまり話がみえていないかも
#いまのAPIみてみます
#関数をレイヤリングすれば実装できるものなら、リクエストをサブミットするときに手続きを一緒に渡して、その手続きをHTTPリクエストを実際にサブミットするスレッドから呼び出すというわけにはいかないのでしょうか?
#まあそういうことになるかも。ただその場合、コールバックが別スレッドから呼ばれるって点が若干話をややこしくするかもしれない。
#レスポンスの受け取った部分から順になにかをするというのはfutureでの抽象化はうまくあてはまらないですね。プログレスを通知する程度のことならコールバックを渡してそれを適当に別スレッドで呼んでもらってよさそうだけど、ダウンロードが完了したときにすでにやりたい処理が完了しているならfuture使う意味なさそう。完了するまでの状態を隠蔽するのがまさにfutureですもんね。
#APIを低層と高層に分けて、低層APIはコールバックで何とかして、高層APIでfutureを提供するってのがいいんじゃないかって気がしはじめてる。rfc.httpは低層をカバーすることを意図してるんだけど、そこに高層も入れるか、それともopen-uriみたいなもっと高層のAPIでfuture版を用意するか、ってのはまだ判断つかない。
#そういう気がしてきました。低層APIを使ってその高層API実装できる。
#goって、fork()やらsignalやらにも触れるんでしょうか。それともスレッドとメッセージ使えって感じ?
#goは書いたことないです。上で便利だなーと思っていたのはJavaでの経験で…
#JavaはJVMの層で(良い意味でも悪い意味でも)レガシーとは断絶があるからなあ。
#JavaだとMTは必ずあってforkはないし、java.util.concurrentがよくできてるから自然とfutureで抽象化みたいになっちゃう
#Goだとfork-and-exec相当のものしか見あたりませんね。forkだけというのが見つからない。 http://golang.org/pkg/os/#ForkExec #File*の配列を陽に渡すのは正しいな。
#シグナルはchannelとして抽象化されてるみたい。シグナルが到着し次第なにかするにはgoroutineをつくってそのchannelで待ってればいいということかな http://golang.org/pkg/os/signal #それも妥当だなあ。同期シグナルはどうするんだろうと思うけど、言語レベルでは扱わないという割り切りかも。GaucheだってSEGVハンドルできないし。
#勘違いしているかもしれませんが、rfc.httpでfutureをサポートするんじゃなくて、future-applyみたいなのを作って、時間がかかる proc を (future-apply proc args) みたいに呼び出すと、別スレッドで呼び出してくれれば、もっと汎用的でいいんじゃないかと思ったんですがどうでしょう。
#あ、futureを別のモジュールでサポートするというのは前提です (たぶんcontrol.futureとか)。rfc.httpがそれに依存すべきかどうか、という話です。
#任意の手続きをfuture化する手続きがあれば、rfc.http自身はそれに依存しなくてもいいんじゃないかなと思ったのです。
#実装上の問題があるのかしらん。
#APIとして
#「http-requestはfutureを返す」としてしまったら、依存することになりますよね。
#http-reqeustがコールバックを受け取るようになってて、そのコールバックがfutureを作る、とするなら依存しなくて、それが上で「rfc.httpは下層で、上層でfutureを使う」といったのはそういう意図です。
#あ、rfc.http自体を下層/上層に分けるという話ではないのですね。
#下層の話題のついでに、少し話が変わりますが、以前 http://tabesugi.net/memo/2009/7a.html#041747 を読んでいて思ったのですが、HTTPのリクエストとレスポンスのpull型パーザがあると便利だなと思いました。 #select使って通信を多重化しているときに使いたくなります。
#もしrfc.http自体にAPIを2レイヤ用意するなら、上位レイヤでfutureを使ってスレッド必須、下位レイヤはコールバックでスレッド無しでも使える、というふうになるかと思いますが、未定。プロトコルを越えた上位モジュールはもともと用意するつもりなので、それならrfc.httpは下位レイヤと割り切っていいかとは思っていますが。