Ruby でパイプラインな HTTP リクエスト
投稿日:
HTTP/1.1 の同時接続数について - daily dayflower をみて「おお、こんな事出来たのか。pipelining すげぇ」と思い、Ruby でこれやるのにはどうしたらいいのか 404 Blog Not Found:HTTPサーバーのパイプライン対応 を参考に考えてみました。
どうやら、Net::HTTP は、HTTP プロトコルのパイプライン処理をできるようになってないようす。その為、Net::HTTP の拡張が必要になりました。
メインコード
404 Blog Not Found:HTTPサーバーのパイプライン対応 のパイプラインの例を ruby コード化するとこんな感じ。
require "net/http"
require "pipelinig_http.rb" # Net::HTTP の拡張
def main
host = 'b.hatena.ne.jp'
paths = ['/','/hotentry', '/news', '/entrylist?sort=hot']
Net::HTTP.start(host) do |http|
# リクエストの送信
paths.each do |path|
req = Net::HTTP::Head.new(path)
r_print req
http.just_request(req)
end
# レスポンスの取得
while res = http.fetch_response
r_print res
end
end
end
# リクエストとレスポンスのヘッダを出力する
def r_print(r)
puts "##### #{r.class.to_s} #####"
r.each{|k,v| puts "%15s: %s" % [k,v]}
puts ""
end
main if __FILE__ == $0
上記のコードで、require されてる pipelining_http.rb は、今回のために用意したファイルで、次に示す内容になっています。
パイプライン処理のための Net::HTTP の拡張 pipelining_http.rb
Net::HTTP#just_request, Net::HTTP#fetch_response は、次のように拡張される。実質、Net::HTTP#request を二つに分断した感じなってます。また今回の例では HEAD リクエストしかしていませんが、GETもPUTもできるはずです。(PUTは未確認)
class Net::HTTP
def just_request(req, body=nil)
@req_cue ||= []
@req_cue << req
unless started?
start {
req['connection'] ||= 'close'
return request(req, body, &block)
}
end
if proxy_user()
unless use_ssl?
req.proxy_basic_auth proxy_user(), proxy_pass()
end
end
req.set_body_internal body
begin_transport(req)
req.exec(@socket, @curr_http_version, edit_path(req.path))
end
def fetch_response
if @req_cue.nil? or @req_cue.empty?
end_transport(@last_request, @last_response)
return nil
end
@last_request = (req = @req_cue.shift)
begin
@last_response = (res = Net::HTTPResponse.read_new(@socket))
end while res.kind_of?(Net::HTTPContinue)
res.reading_body(@socket, req.response_body_permitted?) {
yield res if block_given?
}
res
end
end
個人的に、end_transport のあたりの処理が気に入りません。要改善。
実行結果
##### Net::HTTP::Head #####
accept: */*
##### Net::HTTP::Head #####
accept: */*
##### Net::HTTP::Head #####
accept: */*
##### Net::HTTP::Head #####
accept: */*
##### Net::HTTPOK #####
vary: Accept-Encoding
via: 1.0 squid.hatena.ne.jp:3128 (squid/2.6.STABLE14)
content-type: text/html; charset=utf-8
date: Sat, 25 Oct 2008 20:01:48 GMT
server: Apache/2.2.3 (CentOS)
set-cookie: b=$1$edWQVbYY$4h0bIVtR7RvBTX3ifNcOF1; path=/; expires=Fri, 20-Oct-28 20:01:48 GMT; domain=.hatena.ne.jp
content-length: 63501
age: 588
x-framework: Hatena/2.1
##### Net::HTTPOK #####
vary: Accept-Encoding
via: 1.0 squid.hatena.ne.jp:3128 (squid/2.6.STABLE14)
content-type: text/html; charset=utf-8
date: Sat, 25 Oct 2008 20:01:48 GMT
server: Apache/2.2.3 (CentOS)
set-cookie: b=$1$qYZ0MfS0$aCDFrIOKpp8HayJftFjt/1; path=/; expires=Fri, 20-Oct-28 20:01:48 GMT; domain=.hatena.ne.jp
content-length: 103301
age: 1163
x-framework: Hatena/2.1
##### Net::HTTPOK #####
vary: Accept-Encoding
via: 1.0 squid.hatena.ne.jp:3128 (squid/2.6.STABLE14)
content-type: text/html; charset=utf-8
date: Sat, 25 Oct 2008 20:01:48 GMT
server: Apache/2.2.3 (CentOS)
set-cookie: b=$1$00vjav3o$InTi7h2XFjwllpcFJLnv60; path=/; expires=Fri, 20-Oct-28 20:01:48 GMT; domain=.hatena.ne.jp
content-length: 52390
age: 2259
x-framework: Hatena/2.1
##### Net::HTTPOK #####
vary: Accept-Encoding
via: 1.0 squid.hatena.ne.jp:3128 (squid/2.6.STABLE14)
content-type: text/html; charset=utf-8
date: Sat, 25 Oct 2008 20:01:48 GMT
server: Apache/2.2.3 (CentOS)
set-cookie: b=$1$IZcB88yy$I2jjsa5SWRPLEBT4saLNI.; path=/; expires=Fri, 20-Oct-28 20:01:48 GMT; domain=.hatena.ne.jp
content-length: 103620
age: 2203
x-framework: Hatena/2.1
以上でした。