net/http.rb

このライブラリについて

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

使用例

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

require 'net/http'
Net::HTTP.version_1_2   # おまじない
Net::HTTP.start('www.example.com', 80) {|http|
  response = http.get('/index.html')
  puts response.body
}

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

require 'net/http'
Net::HTTP.version_1_2   # おまじない
Net::HTTP.get_print 'www.example.com', '/index.html'

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

require 'net/http'
Net::HTTP.version_1_2   # おまじない
Net::HTTP.start('www.example.com', 80) {|http|
  response = http.post('/cgi-bin/somecgi.rb',
                       'querytype=subject&target=ruby')
}

(参照: フォームの値の区切り文字について)

プロクシ経由のアクセス

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

require 'net/http'
Net::HTTP.version_1_2   # おまじない

$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 自身を返すので 上のコードのように書いておけばプロクシなしの場合にも対応できます。

リダイレクトに対応する

以下のメソッド fetch はリダイレクトに対応しています。 limit 回数以上リダイレクトしたらエラーにします。

require 'uri'
require 'net/http'
Net::HTTP.version_1_2    # おまじない

def fetch( uri_str, limit = 10 )
  # 適切な例外クラスに変えるべき
  raise ArgumentError, 'http redirect too deep' if limit == 0

  response = Net::HTTP.get_response(URI.parse(uri_str))
  case response
  when Net::HTTPSuccess     then response
  when Net::HTTPRedirection then fetch(response['location'], limit - 1)
  else
    response.error!
  end
end

print fetch('http://www.ruby-lang.org')

Ruby 1.6.7 未満には URI クラスが標準添付されていないので、別途調達するか、 あるいはいいかげんながら以下のようにしてください。

require 'net/http'
Net::HTTP.version_1_2   # おまじない

def fetch( uri_str, limit = 10 )
  # 適切な例外クラスに変えるべき
  raise ArgumentError, 'http redirect too deep' if limit == 0

  response = Net::HTTP.get_response(*split_uri(uri_str))
  case response
  when Net::HTTPSuccess     then response
  when Net::HTTPRedirection then fetch(response['location'], limit - 1)
  else
    response.error!
  end
end

def split_uri( uri_str )
  m = %r<http://([^/]+)>.match(uri_str) or raise ArgumentError, "cannot parse URI: #{uri_str}"
  host = m[1].strip
  path = m.post_match
  path = '/' if path.empty?
  return host, path
end

print fetch('http://www.ruby-lang.org')

Basic 認証

require 'net/http'
Net::HTTP.version_1_2   # おまじない

req = Net::HTTP::Get.new('/need-auth.cgi')
req.basic_auth 'account', 'password'
Net::HTTP.start('www.example.com') {|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)... }

つまり Ruby 1.6 でも Net::HTTP.version_1_2 を呼べば 1.2 の挙動になりますし、 大半のメソッドは呼べます (Ruby 1.8 でもメソッドが増えているので全てではありません)。 Ruby 1.8 でも Net::HTTP.version_1_1 を呼べば元の挙動にできるので、後方互換性を 保つことができます。

ただし、この機能はスレッドセーフではありません。 つまり、複数スレッドでそれぞれに version_1_1 や version_1_2 を呼んだ場合、 次に生成する Net::HTTP オブジェクトがどちらのバージョンになるかは保証できません。 アプリケーション全体でどちらかのバージョンに固定する必要があります。

なおどちらを使うかですが、これから書くなら断然 version_1_2 です。 require 'net/http' 直後に Net::HTTP.version_1_2 を呼んで 1.1 のことは忘れてください。

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 )

Proxy 経由で http サーバに接続するためのクラスを作成し返します。 このクラスは Net::HTTP を継承しているので Net::HTTP と全く 同じように使えます。指定されたプロクシを常に経由して 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, dest = nil )
post( path, data, header = nil ) {|str| .... }

サーバ上の path にあるエンティティに対し文字列 data を 送ります。

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

header は get メソッドと同じです。

dest を与えた場合には、レスポンスは << メソッドを使って dest に書きこまれます。 dest には << メソッドが定義されたオブジェクト、通常 String オブジェクトか Array オブジェクトを与えます。この dest は戻り値の HTTPResponse オブジェクトの body にもなります。

ブロックと一緒に呼びだされたときはエンティティボディを少しづつ文字列として ブロックに与えます。このとき戻り値の HTTPResponse オブジェクトは有効な body を 持ちません。

dest とブロックを同時に与えてはいけません。同時に与えた場合は例外 ArgumentError を投げます。

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
}
request( request [, data] )
request( request [, data] ) {|response| .... }

HTTPResquest オブジェクト request を送信します。POST/PUT の時は data も 与えられます (POST/PUT 以外で data を与えると ArgumentError を発生します)。

ブロックとともに呼びだされたときはボディを読みこまずに HTTPResponse オブジェクトをブロックに与えます。

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

class Net::HTTPRequest

HTTP リクエストを抽象化するクラス。Net::HTTPRequest は抽象クラス なので実際には下位クラスの Net::HTTP::Get, Post, Head, Put を使用 してください。

クラスメソッド

new(path)

HTTP リクエストオブジェクトを生成します。リクエストする path を文字列で与える。

メソッド

self[ key ]

key ヘッダフィールドの文字列。 key は大文字小文字を区別しません。

self[ key ] = val

key ヘッダフィールドに val をセットします。 key は大文字小文字を区別しません。

add_header(key, val)

key ヘッダフィールドに val をセットします。

basic_auth( account, password )

Authrization: ヘッダを basic auth 用にセットします。

body
body=(str)

サーバに送るリクエストのエンティティボディを文字列で設定します。

body_stream
body_stream=

サーバに送るリクエストのエンティティボディを IO オブジェクトなどの ストリームで設定します。read(size) メソッドが定義されている必要があります。

each {|name, val| .... }

ヘッダ名とその値に対するくりかえし。ヘッダ名は小文字で統一されます。

delete(key)

key ヘッダフィールドを削除します。

content_length

Content-Length: ヘッダの値 (整数)。

content_range

Content-Range: ヘッダの値 (Range)。

method

リクエストの HTTP メソッドを文字列で返します。

path

リクエストする path を文字列で返します。

range

Range: ヘッダの示す範囲を Range オブジェクトで返します。

range = r
set_range( i, len )

範囲を指定してエンティティを取得するためのヘッダ Range: をセットします。 r は Range オブジェクト、i, len は始点と長さです。

request_body_permitted?

リクエストにエンティティボディを一緒に送ることが許されている HTTP メソッド (POST など)の場合真を返します。

response_body_permitted?

サーバからのレスポンスにエンティティボディを含むことが許されている HTTP メソッド (GET, POST など)の場合真を返します。

class Net::HTTPResponse

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

メソッド

self[ key ]

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

self[ key ] = val

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

body

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

canonical_each {|name,value| .... }

ヘッダフィールドの正式名とその値のペアに対して繰り返します。

code

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

each {|name,value| .... }

すべてのヘッダフィールド名とその値のペアに対するくりかえし。

key?( key )

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

http_version

サーバがサポートしている HTTP のバージョンを文字列で返す。

message

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

read_body( dest = '' )

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

read_body {|str| .... }

エンティティボディを少しづつ取得して順次ブロックに与えます。

例外

get、head、post メソッドで発生する HTTP プロトコル関連の例外として、 以下に挙げるものがあります。 ここに挙げる例外クラスの親クラスはすべて Net::ProtocolError クラスで、 response メソッドによってエラーの原因となったレスポンスオブジェクトを 得ることができます。

フォームの値の区切り文字について

POSTで application/x-www-form-urlencoded として複数のフォームの値を送る場合、 現在広く行なわれているのは、 name0=value0&name1=value1 のようにアンパサンド `&' で区切るやりかたです。 この方法は、RFC1866 Hypertext Markup Language - 2.0 で初めて公式に登場し、 HTML 4.01 Specification の 17.13.4 Form content types[外部] でもそのように書かれています。

ところが、同じ HTML 4.01 Specification の B.2.2 Ampersands in URI attribute values[外部] では、この `&' がSGMLの文字実体参照で用いられることが指摘されており、 CGIやサーバの実装者に対し `&' の代わりに セミコロン `;' をサポートすることを奨めています。

しかし、実際には `;' を解釈しないCGIやサーバもまだまだ見受けられるため このリファレンスマニュアルでは例として `&' を用いました。

なお Ruby 標準の cgi.rb では & と ; の両方サポートしていますので、 cgi.rb を使って CGI スクリプトを書く場合はこれらの違いを気にする 必要はありません。