ソケット通信でHTTP/1.1を使う際にまず押さえておくこと
ソケット通信というのは非常に便利である。HTTPヘッダーを好きなように書いて、検証もできるし、リクエストも飛ばすことができる。
しかしながら、HTTPのルールに従わず、間違ったリクエストを送ると、送られたサーバが急に応答不能な状況に陥りかねないので、気をつけましょうという話し。
たとえば、PHPであればfsockopenという関数がある(PHP便利で(ry)。
if ($fp = fsockopen('localhost', 80)) { $header = "POST /test.php HTTP/1.1\r\n"; $header .= "Host: www.example.com" . "\r\n\r\n";
まずこんな感じでソケットオープンと、ボディに含めるヘッダー情報を加えていく。
そして、以下の文も加えて、リクエストを送りつけてやる。
fwrite($fp, $header); fclose($fp);
ファイルポインタも閉じたし、これで問題ないと。
しかしながら、これはファイルポインタは閉じているものの、これは間違った送り方をしている。
なぜ、ダメか。そこでRFCを引用するとこんなことが書いてある。
HTTP/1.1 applications that do not support persistent connections MUST
include the "close" connection option in every message.
引用:http://www.ietf.org/rfc/rfc2616.txt
簡単に言えば、「HTTP/1.1使うんだったら、コネクションをクローズするオプションを毎回つけなさい」ということだ。RFCにはこう書かれているが、そんな読んでから実装できるぐらいなら、苦労しないというのが人生だ。
よって、最初に書いたスクリプトには「クローズするオプションがない」ので、仕様に従っておらず、なんかおかしい挙動してるぞということになる。
なので、以下のヘッダーを\$headerに加えないといけない。
$header .= "Connection: Close\r\n\r\n";
「Connection: Close」と。「Close」を忘れちゃいかんということだ。
※また、「a double line break」とPHPマニュアルのコメントに書かれているが、「\r\n\r\n」もヘッダーの末尾にしないといけないので、それも気を付けておこう。
終わりに
個人的にはリクエストを簡単に送信するスクリプトを作って、それをループで一定回数まわし、複数サーバから特定のサーバに対して擬似abみたいなことをよくやるので、DevOpsとかに関係なく知っておくべき知識だと思う。
なんかリクエスト送った先でnetstatで調べたら、fcloseしてるのにkeepaliveとかタイマーやたら長いセッションあるなと思ったら、こういう部分を疑ってもよいかもという気がする。
このネタ掘り下げたら色々ありそうだから、誰か続き書いてくれないかしらん。