=begin

= net/http.rb

== このライブラリについて

汎用データ転送プロトコル HTTP を扱うライブラリです。
実装は [RFC2616] ((<URL:http://www.ietf.org/rfc/rfc2616.txt>)) に
基いています。

== 使用例

=== ウェブサーバからドキュメントを得る (GET)

    require 'net/http'
    Net::HTTP.version_1_1   # declear to use 1.1 features.
    Net::HTTP.start( 'some.www.server', 80 ) {|http|
        response, body = http.get('/index.html')
        print body
    }

また以下は同じ意味で短く書いたものです。

    require 'net/http'
    Net::HTTP.get_print 'some.www.server', '/index.html'

=== フォームの情報を送信する (POST)

    require 'net/http'
    Net::HTTP.version_1_1   # declear to use 1.1 features.
    Net::HTTP.start( 'some.www.server', 80 ) {|http|
        response, body = http.post( '/cgi-bin/any.rhtml',
                                'querytype=subject&target=ruby' )
    }

=== プロクシ経由のアクセス

Net::HTTP のクラスメソッド Net::HTTP.Proxy は、常にプロクシ経由で
接続するような動作をする、新しいクラスを作成して返します。このクラスは
Net::HTTP を継承しているので Net::HTTP と全く同じように使えます。

    require 'net/http'
    Net::HTTP.version_1_1   # declear to use 1.1 features.

    $proxy_addr = 'your.proxy.addr'
    $proxy_port = 8080
          :
    Net::HTTP::Proxy($proxy_addr, $proxy_port).start( 'some.www.server' ) {|http|
        # always connect to your.proxy.addr:8080
          :
    }

また Net::HTTP.Proxy は第一引数が nil だと Net::HTTP 自身を返すので
上のコードのように書いておけばプロクシなしの場合にも対応できます。

=== リダイレクトに対応する

    require 'net/http'
    Net::HTTP.version_1_1   # declear to use 1.1 features.

    host = 'www.ruby-lang.org'
    path = '/'
    begin
      Net::HTTP.start( host, 80 ) {|http|
	response , = http.get(path)
        print response.body
      }
    rescue Net::ProtoRetriableError => err
      if m = %r<http://([^/]+)>.match( err.response['location'] ) then
	host = m[1].strip
	path = m.post_match
	retry
      end
    end

この例では URL からホスト名を得るのにいいかげんな方法を使っていますが、
将来 URI クラスが標準添付になればもっと簡単になるはずです。

=== Basic 認証

    require 'net/http'
    Net::HTTP.version_1_1   # declear to use 1.1 features.

    Net::HTTP.start( 'auth.some.domain' ) {|http|
        response, body = http.get( '/need-auth.cgi',
                'Authorization' => 'Basic ' + ["#{account}:#{password}"].pack('m').strip )
        print body
    }

バージョン 1.2 (Ruby 1.7 以降に添付) では次のように書けます。

    require 'net/http'
    Net::HTTP.version_1_2   # declear to use 1.2 features.

    req = Net::HTTP::Get.new('/need-auth.cgi')
    req.basic_auth 'account', 'password'
    Net::HTTP.start( 'auth.some.domain' ) {|http|
        response = http.request(req)
        print response.body
    }

== 新しい仕様への変更と移行措置について

Ruby 1.6 に入っているのが http.rb 1.1 で 1.7 以降が 1.2 ですが、
この間ではかなり大きく仕様が変わります。そこで突然に仕様を変更
するのでなく、両方の実装を並存させる時期を設けることにしました。

メソッド HTTP.version_1_2、HTTP.version_1_1 を呼ぶと
そのあとに生成される Net::HTTP オブジェクトはそれぞれの
バージョンの仕様で動作するようになります。以下は使用例です。

    # example
    Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }

    Net::HTTP.version_1_1
    Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }

    Net::HTTP.version_1_2
    Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }

この機能はスレッドセーフではありません。

== class Net::HTTP

=== クラスメソッド

: new( address, port = 80, proxy_addr = nil, proxy_port = nil )
    新しい HTTP オブジェクトを生成します。address は HTTP サーバーの FQDN で、
    port は接続するポート番号です。このメソッドではまだ接続はしません。

    proxy_addr を与えるとプロクシを介して接続するオブジェクトを生成します。

: start( address, port = 80, proxy_addr = nil, proxy_port = nil )
: start( address, port = 80, proxy_addr = nil, proxy_port = nil ) {|http| .... }
    以下と同じです。

        Net::HTTP.new(address, port, proxy_addr, proxy_port).start(&block)

: get( address, path, port = 80 )
    ホスト address の port 番ポートに接続して path の表現する
    エンティティを取得、文字列で返します。

: get_print( address, path, port = 80 )
    ホスト address の port 番ポートに接続して path の表現する
    エンティティを取得したうえ、$stdout に << で出力します。

: Proxy( address, port = 80 )
    常に指定されたプロクシに接続するクラスを作成し返します。
    このクラスは Net::HTTP を継承しているので Net::HTTP と全く
    同じように使えます。

    address が nil のときは Net::HTTP クラスをそのまま返します。

        # example
        proxy_class = Net::HTTP::Proxy( 'proxy.foo.org', 8080 )
                        :
        proxy_class.start( 'www.ruby-lang.org' ) {|http|
            # connecting proxy.foo.org:8080
                        :
        }

: proxy_class?
    自身が (Proxy メソッドによって作成された) プロクシ用のクラスならば真。

: port
    HTTP のデフォルトポート (80)。

=== メソッド

: start
: start {|http| .... }
    TCP コネクションを張り、HTTP セッションを開始します。
    すでにセッションが開始していたら例外 IOError を発生します。

    イテレータとして呼ばれた時はブロックの間だけセッションを接続し、
    ブロック終了とともに自動的にセッションを閉じます。

: active?
    HTTP セッションが開始されていたら真。

: address
    接続するアドレス

: port
    接続するポート番号

: open_timeout
: open_timeout=(n)
    接続時に待つ最大秒数。この秒数たってもコネクションが
    開かなければ例外 TimeoutError を発生します。

: read_timeout
: read_timeout=(n)
    読みこみ (read(1) 一回) でブロックしてよい最大秒数。
    この秒数たっても読みこめなければ例外 TimeoutError を発生します。

: finish
    HTTP セッションを終了します。セッション開始前にこのメソッドが
    呼ばれた場合は例外 IOError を発生します。

: proxy?
    プロクシを介して接続するなら真。

: proxy_address
    プロクシ経由で接続する HTTP オブジェクトならプロクシのアドレス。
    そうでないなら nil。

: proxy_port
    プロクシ経由で接続する HTTP オブジェクトならプロクシのポート。
    そうでないなら nil。

: get( path, header = nil )
: get( path, header = nil ) {|str| .... }
    サーバ上の path にあるエンティティを取得します。また header が nil
    でなければ、リクエストを送るときにその内容を HTTP ヘッダとして書き
    こみます。header はハッシュで、「ヘッダ名 => 内容」のような形式で
    なければいけません。

    返り値は、バージョン 1.1 では HTTPResponse とエンティティボディ文字列の
    二要素の配列です。1.2 では HTTPResponse ただひとつのみです。この場合、
    エンティティボディは response.body で得られます。

    ブロックとともに呼ばれた時はエンティティボディを少しづつブロックに
    与えます。

    1.1 では 3xx (再試行可能なエラー)に対しても例外を発生します。この場合
    HTTPResponse は例外オブジェクトから err.response で得ることができます。
    一方 1.2 では全く例外を発生しません。

        # version 1.1 (bundled with Ruby 1.6)
        response, body = http.get( '/index.html' )

        # version 1.2 (bundled with Ruby 1.7 or later)
        response = http.get( '/index.html' )

        # compatible in both version
        response , = http.get( '/index.html' )
        response.body
        
        # using block
        File.open( 'save.txt', 'w' ) {|f|
            http.get( '/~foo/', nil ) do |str|
              f.write str
            end
        }

: head( path, header = nil )
    サーバ上の path にあるエンティティのヘッダのみを取得します。
    また header が nil でなければリクエストを送るときにその内容を
    HTTP ヘッダとして書きこみます。header はハッシュで、
    「ヘッダ名 => 内容」のような形式でなければいけません。

    HTTPResponse オブジェクトを返します。

    1.1 では 3xx (再試行可能なエラー)に対しても例外を発生します。この場合
    HTTPResponse は例外オブジェクトから err.response で得ることができます。
    一方 1.2 では全く例外を発生しません。

        response = nil
        Net::HTTP.start( 'some.www.server', 80 ) {|http|
            response = http.head( '/index.html' )
        }
        p response['content-type']

: post( path, data, header = nil )
: post( path, data, header = nil ) {|str| .... }
    サーバ上の path にあるエンティティに対し文字列 data を
    送ります。レスポンスは << メソッドを使って dest に書き
    こまれます。header は get メソッドと同じです。
    HTTPResponse オブジェクトと dest の配列を返します。

    イテレータとして呼びだされたときはエンティティボディを少しづつ
    ブロックに与えます。

    1.1 では 3xx (再試行可能なエラー)に対しても例外を発生します。この場合
    HTTPResponse は例外オブジェクトから err.response で得ることができます。
    一方 1.2 では全く例外を発生しません。

        # version 1.1
        response, body = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )

        # version 1.2
        response = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )

        # compatible in both version
        response , = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )

        # using block
        File.open( 'save.html', 'w' ) {|f|
            http.post( '/cgi-bin/search.rb',
                       'query=subject&target=ruby' ) do |str|
              f.write str
            end
        }

: request_get( path, header = nil )
: request_get( path, header = nil ) {|response| .... }
    path にあるエンティティを取得します。HTTPResponse
    オブジェクトを返します。

    ブロックとともに呼び出されたときは、ブロック実行中は接続を
    維持したまま HTTPResponse オブジェクトをブロックに渡します。

    このメソッドは HTTP プロトコルに関連した例外は発生させません。

        # example
        response = http.request_get( '/index.html' )
        p response['content-type']
        puts response.body          # body is already read

        # using block
        http.request_get( '/index.html' ) {|response|
            p response['content-type']
            response.read_body do |str|   # read body now
              print str
            end
        }

: request_post( path, data, header = nil )
: request_post( path, data, header = nil ) {|response| .... }
    path にあるエンティティを取得します。HTTPResponse
    オブジェクトを返します。

    ブロックとともに呼び出されたときは、ボディを読みこむ前に
    HTTPResponse オブジェクトをブロックに渡します。

    このメソッドは HTTP プロトコルに関連した例外は発生させません。

        # example
        response = http.post2( '/cgi-bin/nice.rb', 'datadatadata...' )
        p response.status
        puts response.body          # body is already read

        # using block
        http.post2( '/cgi-bin/nice.rb', 'datadatadata...' ) {|response|
            p response.status
            p response['content-type']
            response.read_body do |str|   # read body now
              print str
	    end
        }


== class Net::HTTPResponse

HTTP レスポンスのクラスです。
引数がヘッダフィールド名である場合、大文字小文字を区別しません。

=== メソッド

: self[ key ]
    key ヘッダフィールド(文字列)です。たとえばキー 'content-length'
    に対しては '2048' のような文字列が得られます。
    key は大文字小文字を区別しません。

: self[ key ] = val
    key ヘッダフィールドを value に設定します。
    key は大文字小文字を区別しません。

: key?( key )
    key というヘッダフィールドがあれば真。
    key は大文字小文字を区別しません。

: each {|name,value| .... }
    すべてのヘッダフィールド名とその値のペアに対するくりかえし。

: canonical_each {|name,value| .... }
    ヘッダフィールドの正式名とその値のペアに対して繰り返します。

: code
    HTTP のリザルトコードです。例えば '302' などです。

: message
    HTTP サーバがリザルトコードに付加して返すメッセージです。
    例えば 'Not Found' などです。

: read_body( dest = '' )
    エンティティボディを取得し dest に << メソッドを使って書きこみます。
    同じ HTTPResponse オブジェクトに対して二回以上呼ばれた場合、
    二回目からはなにもせずに一回目の返り値をそのまま返します。

: read_body {|str| .... }
    エンティティボディを少しづつ取得して順次ブロックに与えます。

: body
    エンティティボディです。read_body を呼んでいればその引数 dest、
    呼んでいなければエンティティボディを文字列として読みこんで返します。

=end
