にたまごほうれん草アーカイブ

はてなダイアリーで書いてた「にたまごほうれん草」という日記のアーカイブです。現在は「にたまごほうれん草ブログ」を運営中です。

HTTPのバージョン違いによるクライアント作成時のハマりどころ

C言語のソケットプログラミングの勉強にと、HTTPクライアントを書いていたら、HTTPのバージョンによる動作の違いに少しハマりました*1。以下のような簡単なリクエストを送信し、結果を受信して標準出力に書き出すだけのプログラムです。

GET / HTTP/1.1
Host: www.google.co.jp:80

そして、実際に動作させてみるとページのデータは取得できるものの、受信ループから抜けられない、という結果になりました*2。しばらく頭をひねったのですが、HTTPのバージョンを1.0に変更するか、リクエスト行に「Connection: close」を追加することで解決しました。道理で世の中のサンプルプログラムの多くはHTTP/1.0で書かれているわけです。
これは、HTTP/1.1でリクエストごとに接続が終了しないように変更されたためで、切断するにはサーバかクライアントのどちらかが「Connection: close」を指定する必要があります。HTTP/1.0では、クライアント・サーバがKeep-Aliveをサポートしている場合以外は必ずリクエストごとに切断されるようです。
要するに、今回の本質的な原因は、「バージョンの数字が大きい方がエライと思い込んで勝手に書き換えた」ことでしょう。先にHTTPの勉強しておくべきでした。
以下、動作サンプル用のRubyコードです。HTTPのバージョンの数字やConnectionの項目を切り替えてご確認どうぞ。

# Pull down Google's web page
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.co.jp' )
socket.connect( sockaddr )
socket.write( "GET / HTTP/1.1\r\n" )
socket.write( "Host: www.google.co.jp\r\n" )
#socket.write( "Connection: close\r\n" )
socket.write( "\r\n" )
results = socket.read
puts results

追記

受信データを出力するだけじゃなく利用する場合はchunkedエンコーディングについても理解する必要があります。

*1:C言語に限った話ではありません

*2:www.yahoo.co.jpではコネクションが切断された。サイトによって挙動が異なることでますますわからなかった