カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

ソケット通信で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とかタイマーやたら長いセッションあるなと思ったら、こういう部分を疑ってもよいかもという気がする。

このネタ掘り下げたら色々ありそうだから、誰か続き書いてくれないかしらん。