WEBrick::HTTPProxyServerでデータを逐次送信させる
HTTPProxyServerはデータをすべて取得してから一度に返す動きをしていたのでこれを逐次で返すように改造してみた。
ニコ串のソースを見てやり方はすぐ判ったのだが、これをWEBrickの枠に当てはめるのにだいぶ苦労した。
なんか動作が怪しいところもあるがyoutubeとかでデータがちょっとずつ来るのは確認できた。
#!/usr/bin/env ruby require 'webrick' require 'webrick/httpproxy' require 'net/http' module WEBrick # 返送用のソケットにアクセスできるようにする class HTTPRequest attr_reader :socket end class SequencialProxy < HTTPProxyServer def initialize(config={}, default=Config::HTTP) super(config, default) @http_version="1.0" end def proxy_service(req, res) # Proxy Authentication proxy_auth(req, res) begin self.send("do_#{req.request_method}", req, res) rescue NoMethodError raise HTTPStatus::MethodNotAllowed, "unsupported method `#{req.request_method}'." rescue => err p err.backtrace logger.debug("#{err.class}: #{err.message}") raise HTTPStatus::ServiceUnavailable, err.message end # Process contents if handler = @config[:ProxyContentHandler] handler.call(req, res) end end # とりあえずゲットだけ def do_GET(req, res) uri = req.request_uri path = uri.path.dup path << "?" << uri.query if uri.query header = setup_proxy_header(req, res) # send request # Net::HTTPだと逐次にできないのでTCPSocketでやる server=Net::BufferedIO.new( TCPSocket.new(req.request_uri.host, req.request_uri.port)) server.writeline "GET #{path} HTTP/1.0" header.each do |k, v| server.writeline "#{k}: #{v}" end server.writeline "" # 逐次で返すため先にヘッダを処理する # read header response=Net::HTTPResponse.read_new(server) # Convert Net::HTTP::HTTPResponse to WEBrick::HTTPResponse res.status = response.code.to_i choose_header(response, res) set_cookie(response, res) set_via(res) # Persistent connection requirements are mysterious for me. # So I will close the connection in every response. res['proxy-connection'] = "close" res['connection'] = "close" # header res.send_header(req.socket) # read body res.body = '' while true do begin block='' server.read(4096, block) rescue EOFError => e break ensure res.body << block # 逐次 req.socket << block if block=='' then break end end end # 既にデータは返したので元の場所では何もしない def res.send_message(socket) # dummy end end end end # プロキシサーバオブジェクトを作る s = WEBrick::SequencialProxy.new( :BindAddress => '127.0.0.1', :Port => 18080, :ProxyVia => false, # WEBrick::GenericServer callback :StartCallback => ->{ #p "StartCallback" }, :StopCallback => ->{ #p "StopCallback" }, :AcceptCallback => -> sock { #p "AcceptCallback", sock }, # WEBrick::HTTPServer callback :RequestCallback => -> req, res { #p "RequestCallback" }, ) Signal.trap('INT') do s.shutdown end s.start
現状だとProxyContentHandlerが無視されるのでまだ改造が必要だがmimeを見て動作を振り分ける方がよさげ。