root/lang/ruby/cocproxy/proxy.rb @ 28962

Revision 22526, 7.3 kB (checked in by cho45, 5 years ago)

ぼくえんごわかんない

  • Property svn:mime-type set to text/x-ruby; charset=utf-8
  • Property svn:executable set to *
  • Property svn:keywords set to Revision
Line 
1#!/usr/bin/env ruby
2=begin TracWiki
3
4= CocProxy =
5
6めんどくさいことしない置換プロキシー
7
8== 使い方 ==
9
10 1. http://svn.coderepos.org/share/lang/ruby/cocproxy/proxy.rb
11    をダウンロード
12 2. `files' というディレクトリをつくる
13 3. 置換したいファイルをてきとうにつっこむ
14 4. ruby proxy.rb
15 5. ポートとか表示されるのでブラウザの設定を変える
16
17デフォルトだと、
18
19 1. #{File.basename(req.path_info)}",
20 2. #{req.host}#{req.path_info}",
21 3. #{req.host}/#{File.basename(req.path_info)}",
22 4. .#{req.path_info}",
23
24がスキャンされ、ヒットしたら置換される。
25
26例えば、http://example.com/test/foo/bar.css にアクセスすると
27
28 1. files/bar.css
29 2. files/example.com/test/foo/bar.css
30 3. files/example.com/bar.css
31 4. files/test/foo/bar.css
32
33が順にスキャンされ、最初に発見されたファイルが置換されブラウザに返される。
34
35== 部分置換 ==
36
37正規表現により部分的に置換することもできる。
38
39http://example.com/hoge.html というファイルの style 要素を置換したければ、
40ルールにヒットするファイルに
41
42{{{
43proxy-replace: <style type="text/css">\s*<!--([^<]+)-->\s*</style>
44}}}
45
46などと書き、さらに置換したい内容を書けば良い。
47指定した正規表現のキャプチャのうち、第一番目のキャプチャが該当ファイルの内容で
48置換される。
49
50単純にファイル内容を追記したい場合は
51
52{{{
53proxy-replace: (\z)
54}}}
55
56と書けば良い。(\z は文字列末尾にマッチする)
57
58
59また、置換後のファイルでは proxy-replace 以降、行末までは削除されるため、
60proxy-replace のあとを HTML エスケープなどする必要はない。
61
62== キャッシュ ==
63
64開発用のプロキシでリロードをしまくる性質上、
65置換対象のファイルでない限り全てのリソースを無条件にキャッシュする。
66
67もしキャッシュをクリアしたければ、?clearcache=1 をつけて、
68適当な URL にアクセスすれば良い。
69
70== さらに ==
71
72proxy.rb はカレントディレクトリの proxy-config.yaml を設定として読みこむ、
73このファイルはなくてもよいが、使用することで設定を上書きできる。
74
75また、double_screen.rb というファイルが require できればそれをロードする。
76double_screen.rb は標準出力と標準エラー出力を左右にわけて表示するもので、
77使用することで、プロキシの動作が読み易くなる。
78
79== Download / Repos. ==
80
811ファイル完結版
82
83 * http://svn.coderepos.org/share/lang/ruby/cocproxy/proxy.rb
84
85全部
86
87 * http://svn.coderepos.org/share/lang/ruby/cocproxy/
88
89
90== Principles ==
91
92 * 基本的に1つのファイルで完結すること
93 * ruby1.9 メイン
94 * 標準ライブラリで動くこと
95
96
97== Ref. ==
98
99 * http://la.ma.la/blog/diary_200607172004.htm
100
101
102== License ==
103
104Public Domain
105
106=end
107
108require 'webrick'
109require 'webrick/httpproxy'
110require 'uri'
111require 'yaml'
112require "pp"
113require "pathname"
114require "stringio"
115require "zlib"
116require "optparse"
117
118class CocProxyCommand
119        VERSION = "$Revision$"
120        DEFAULT_CONFIG = {
121                :Port        => 5432,
122                :ProxyVia    => false,
123                :Logger      => WEBrick::Log.new(nil, 0),
124                :AccessLog   => WEBrick::Log.new(nil, 0),
125                :FilterDir   => "files",
126                :Rules       => [
127                        "\#{File.basename(req.path_info)}",
128                        "\#{req.host}\#{req.path_info}",
129                        "\#{req.host}/\#{File.basename(req.path_info)}",
130                        ".\#{req.path_info}",
131                ]
132        }
133
134        def self.run(argv)
135                new(argv.dup).run
136        end
137
138        def initialize(argv)
139                @argv = argv
140                @parser = OptionParser.new do |parser|
141                        parser.banner = <<-EOB.gsub(/^\t+/, "")
142                                Usage: #$0 [options]
143                        EOB
144
145                        parser.separator ""
146                        parser.separator "Options:"
147
148                        parser.on("-c", "--config CONFIG.yaml") do |config|
149                                begin
150                                        @config = YAML.load_file(config)
151                                rescue Errno::ENOENT
152                                        puts "#{config} is not found"
153                                        exit
154                                end
155                        end
156
157                        parser.on("-p", "--port PORT", "Specify port number. This option overrides any config.") do |port|
158                                @port = port.to_i
159                        end
160
161                        parser.on("-n", "--no-cache", "Disable cache.") do |port|
162                                @nocache = true
163                        end
164
165                        parser.on("--disable-double-screen", "Disable loading double_screen.rb") do |c|
166                                @disable_double_screen = c
167                        end
168
169                        parser.on("--version", "Show version string `#{VERSION}'") do
170                                puts VERSION
171                                exit
172                        end
173                end
174        end
175
176        def run
177                @parser.order!(@argv)
178                $stdout.sync = true
179
180                unless @config
181                        begin
182                                @config = YAML.load_file("proxy-config.yaml")
183                                puts "proxy-config.yaml was found. Use it."
184                        rescue Errno::ENOENT
185                                @config = {
186                                        "server" => {
187                                        },
188                                }
189                                puts "Use default configuration."
190                        end
191                end
192
193                server_config = DEFAULT_CONFIG.update(@config["server"])
194                server_config[:Port]    = @port if @port
195                server_config[:nocache] = @nocache
196
197                unless @disable_double_screen
198                        begin
199                                require "double_screen.rb"
200                        rescue LoadError => e
201                        end
202                end
203                puts "Port : #{server_config[:Port]}"
204                puts "Dir  : #{server_config[:FilterDir]}/"
205                puts "Cache: #{!server_config[:nocache]}"
206                puts "Rules:"
207                server_config[:Rules].each_with_index do |item, index|
208                        puts "    #{index+1}. #{item}"
209                end
210
211                srv = ArrogationProxyServer.new(server_config)
212                trap(:INT) { srv.shutdown }
213                srv.start
214        end
215
216        class ArrogationProxyServer < WEBrick::HTTPProxyServer
217                def proxy_service(req, res)
218
219                        referer = req.header["referer"]
220                        dir = @config[:FilterDir]
221                        $stderr.puts req.path_info if $DEBUG
222                        $stderr.puts req.query.inspect if $DEBUG
223
224                        content    = ""
225                        local_path = ""
226
227                        @config[:Rules].each do |path|
228                                path = "#{dir}/#{eval("%Q(#{path})")}"
229                                $stderr.puts "Checking #{path.to_s}"
230                                if FileTest.file? path
231                                        puts "Hit Arrogation: #{req.path_info}"
232                                        local_path = path
233                                        content = File.open(path).binmode.read
234                                        break
235                                end
236                        end
237
238                        req.header.delete("HTTP_IF_MODIFIED_SINCE")
239
240                        case
241                        when content =~ /proxy-replace:\s*(.+)\s*/
242                                content.sub!(/proxy-replace:\s*(.+)\s*/, "")
243                                regexp = Regexp.new(Regexp.last_match[1])
244                                puts "Replace Regexp: #{regexp.source}"
245                                puts " <= #{local_path}"
246                                super
247                                case (res["Content-Encoding"] || "").downcase
248                                #when "deflate"
249                                #when "compress"
250                                when "gzip"
251                                        res["Content-Encoding"] = nil
252                                        res.body = Zlib::GzipReader.wrap(StringIO.new(res.body)) {|gz| gz.read }
253                                end
254
255                                p res
256
257                                m = res.body.match(regexp)
258                                if m && m[1]
259                                        res.body[m.begin(1)..(m.end(1)-1)] = content
260                                else
261                                        puts "In-place Regexp match failed..."
262                                end
263                                res["Content-Length"] = res.body.length
264                        when content !~ /\A\s*\Z/
265                                mime_types = WEBrick::HTTPUtils::DefaultMimeTypes.update(@config[:MimeTypes])
266                                res.header["Content-Type"] = WEBrick::HTTPUtils.mime_type(req.path_info, mime_types)
267                                res.body = content
268                                puts "Rewrote: <= #{local_path}"
269                        else
270                                @cache = {} if !@cache || req.query.key?("clearcache")
271                                r = @cache[req.request_uri.to_s]
272
273                                if r
274                                        r.instance_variables.each do |i|
275                                                res.instance_variable_set(i, r.instance_variable_get(i))
276                                        end
277                                        $stderr.puts "Cached: #{req.request_uri}"
278                                else
279                                        super
280                                        @cache[req.request_uri.to_s] = res.dup unless @config[:nocache]
281                                end
282                        end
283                        req.header["referer"] = ["http://#{req.header["host"][0]}"]
284                rescue Exception => e
285                        puts $@
286                        puts $!
287                end
288        end
289end
290
291CocProxyCommand.run(ARGV)
Note: See TracBrowser for help on using the browser.