WEBrick::HTTPProxyServer で無理矢理リクエストヘッダを書き換えるプロクシサーバ
概要
全然需要なさそうだけど、個人的に必要だったので少し前に作ったもの。
レスポンスヘッダとか受け取ったコンテンツを書き換えるプロクシは一杯あるんだけど、リクエストヘッダまで書き換えるプロクシっていうのはあんまり無くて、自分が知っているものとしては Proxomitron 系ぐらい。
でも Proxomitron は設定ファイルを作るのがかなりの手間なので、シンプルなリクエストヘッダ書き換え用のプロクシサーバが欲しいと思って、Ruby + WEBrick::HTTPProxyServer で書いてみた。
参考にしたのは HTTPリクエストヘッダを付加したい - kkkkkkkk で、特異メソッドをオーバーライドしたりしてかなり強引なやり方なんだけど、とりあえず動く。
使い方
オプション "-r" で追加するリクエストヘッダを指定する。"one-field:value1,another-field:value2" みたいな感じで指定できる。
以下みたいにすると、常に uid を送り続ける Docomo 端末を偽装することができる。モバイルサイトの動作確認用とかに使えるかも。
ruby my_proxy.rb -r "user-agent:DoCoMo/2.0 P906i(c100;TB;W24H15),x-dcmguid:1234567"
あとは -b で bind するアドレスを指定したり、-p でポートを指定したりする。
ソース
長いけど MyProxyServer#run については WEBrick のソースコードから持ってきている部分がほとんど。
- my_proxy.rb
#!/usr/bin/env ruby require 'webrick' require 'webrick/httpproxy' require 'optparse' include WEBrick # HTTPリクエストヘッダを付加したい - kkkkkkkk <http://d.hatena.ne.jp/kkkkkkkk/20060707/p1> class MyProxyServer < HTTPProxyServer def run(sock) while true res = HTTPResponse.new(@config) req = HTTPRequest.new(@config) def req.read_header(socket) super begin @header = HTTPUtils::parse_header(@raw_header) @config[:RequestHeaders].each do |k,v| @header[k] = [v] end rescue => ex raise HTTPStatus::BadRequest, ex.message end end server = self begin timeout = @config[:RequestTimeout] while timeout > 0 break if IO.select([sock], nil, nil, 0.5) timeout = 0 if @status != :Running timeout -= 0.5 end raise HTTPStatus::EOFError if timeout <= 0 || sock.eof? req.parse(sock) res.request_method = req.request_method res.request_uri = req.request_uri res.request_http_version = req.http_version res.keep_alive = req.keep_alive? server = lookup_server(req) || self if callback = server[:RequestCallback] || server[:RequestHandler] callback.call(req, res) end server.service(req, res) rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex res.set_error(ex) rescue HTTPStatus::Error => ex @logger.error(ex.message) res.set_error(ex) rescue HTTPStatus::Status => ex res.status = ex.code rescue StandardError => ex @logger.error(ex) res.set_error(ex, true) ensure if req.request_line req.fixup() res.send_response(sock) server.access_log(@config, req, res) end end break if @http_version < "1.1" break unless req.keep_alive? break unless res.keep_alive? end end end # default settings header_list = [] address = 'localhost' port = 8080 # option parse opt = OptionParser.new opt.on('-r HEADERS', Array ) {|v| header_list = v } opt.on('-b ADDRESS', String ) {|v| address = v } opt.on('-p PORT', Integer) {|v| port = v } opt.parse! # create request headers request_headers = {} header_list.each do |header| field, value = header.split(':') request_headers[field] = value end # construct proxy server server = MyProxyServer.new( :BindAddress => address, :Port => port, :Logger => WEBrick::Log::new($stderr, WEBrick::Log::DEBUG), :ProxyVia => false, :RequestHeaders => request_headers ) # start server Signal.trap('INT') { server.shutdown; } server.start