| 前へ << HTTP の並行アクセス | モジュールをインストールしよう >> 次へ |
User-Agent や Referer などの書き換え、コンテンツの書き換えを目的として作り始めましたが、 現時点では中継機能しか実装されていません。proxy の雛型としてどうぞ。
% ./http-proxy.plとすると、ポート 8080 を listen しますので、ブラウザの proxy 設定を localhost:8080 にしてください。なお、keep-alive と SSL は未対応で、 メソッドは GET・POST のみ実装されています。
1: #!/usr/local/bin/perl
2:
3: # $Id: http-proxy.pl,v 1.2 2003/05/17 14:31:02 68user Exp $
4:
5: require 5.000;
6:
7: use strict;
8:
9: use IO::Socket;
10: use IO::Select;
11:
12: # 接続する proxy。直接接続なら $real_proxy=''; とすること。
13: my $real_proxy = 'proxy.hoge.co.jp:8080';
14:
15: # $real_proxy を経由しないホスト名。正規表現で記述。
16: my @noproxy_hosts = qw(localhost hoge\.co\.jp);
17:
18: # proxy 用ポート番号
19: my $listen_port = 8080;
20:
21: my $sock_waiting = IO::Socket::INET->new(Listen => SOMAXCONN,
22: LocalPort => $listen_port,
23: Proto => 'tcp',
24: Reuse => 1,
25: # 自サーバ以外からの接続を許すなら LocalAddr をコメントアウトする。
26: LocalAddr => 'localhost',
27: );
28: my $selecter = IO::Select->new;
29: $selecter->add($sock_waiting);
30:
31: my %conn_table;
32: my %conn_table_rev;
33: my %request;
34:
35: #-------------------
36: # SIGPIPE シグナルを受けても終了しないようにする。例えば、web サーバからの
37: # レスポンスをブラウザに渡そうと print したが、それより前にブラウザの中止ボタンが
38: # 押されていた、というときに SIGPIPE が飛んでくる。
39:
40: $SIG{PIPE} = sub {
41: print "Caught SIGPIPE. But continue..\n";
42: };
43:
44:
45: while (1){
46: print "Begin select\n";
47:
48: my ($clients) = IO::Select->select($selecter, undef, undef, undef);
49: if ( @$clients == 0 ){
50: next;
51: }
52:
53: foreach my $sock (@$clients){
54: print " socket_descripter=", $sock->fileno, "\n";
55:
56: if ( $sock == $sock_waiting ){
57: # ブラウザからの新しい接続あり
58: my $new_sock = $sock_waiting->accept;
59: $selecter->add($new_sock);
60: printf " New client comes (%d).\n", $new_sock->fileno;
61:
62: } else {
63:
64: if ( $conn_table{$sock} ){
65: # web サーバからのデータがあるか、web サーバとのコネクション切断
66:
67: my $buf;
68: my $read_len = sysread($sock, $buf, 10000);
69: if ( $buf ){
70: # web サーバからのデータあり
71: print " FROM Web server. Read OK ($read_len bytes).\n";
72:
73: my $fn = $conn_table{$sock};
74:
75: select($fn); $|=1; select(STDOUT);
76:
77: print $fn $buf;
78: } else {
79: # web サーバとのコネクション切断
80: print " FROM Web server. Finished.\n";
81: printf " close(%d)\n", $conn_table{$sock}->fileno;
82:
83: &remove_contable($conn_table{$sock});
84:
85: shutdown($conn_table{$sock}, 2);
86: shutdown($sock, 2);
87:
88: $conn_table{$sock} = undef;
89: }
90: } else {
91: # ブラウザからのデータあり
92: my $buf;
93: sysread($sock, $buf, 10000);
94:
95: if ( $buf ){
96: print " FROM Browser. Read OK.\n";
97: $request{$sock} .= $buf;
98:
99: if ( $request{$sock} =~ m/\r\n\r\n|\n\n/ ){
100: # 空行がある=完全なリクエストが送られてきた
101: # POST のときには、あと Content-length の分だけ
102: # 読まなければいけないが、未実装。
103:
104: if ( $request{$sock} =~ m/^(POST|GET) / ){
105: my ($host, $port, $new_req) = &parse_request($request{$sock});
106:
107: printf "MAKE SOCKET TO $host:$port\n";
108:
109: my $sock_connect = IO::Socket::INET->new(PeerAddr => $host,
110: PeerPort => $port,
111: Proto => 'tcp',
112: );
113: if ( ! $sock_connect ){
114: select($sock); $|=1; select(STDOUT);
115: print $sock "HTTP/1.0 500 cannot resolve.\r\n";
116: print $sock "\r\n";
117: print $sock "<html>\r\n";
118: print $sock "<body>\r\n";
119: print $sock "$host:$port cannot resolve.\r\n";
120: print $sock "</body>\r\n";
121: print $sock "</html>\r\n";
122:
123: &remove_contable($sock);
124: close($sock);
125: next;
126: }
127: $conn_table{$sock_connect} = $sock;
128: $conn_table_rev{$sock} = $sock_connect;
129: select($sock_connect); $|=1; select(STDOUT);
130:
131: print $sock_connect $new_req;
132: $new_req =~ s/^/ /gm;
133: print " -- request start--\n";
134: print $new_req;
135: print " -- request end--\n";
136: $selecter->add($sock_connect);
137: }
138: } else {
139: # 完全なリクエストが届かなかった (次回続きが送られてくることを期待)
140: print "Wait continue request\n";
141: }
142: } else {
143: # ブラウザからのコネクション切断。
144: print " FROM Browser. Finished.\n";
145: &remove_contable($sock);
146: }
147: }
148: }
149: }
150: }
151:
152: exit;
153:
154:
155: #---------------------------------
156: # コネクションが切断された場合、select 対象から外す。
157:
158: sub remove_contable($){
159: my ($sock) = @_;
160: print " delete ", $sock->fileno, "\n";
161: $conn_table_rev{$sock} = undef;
162: # %conn_table も undef しないとメモリリークすると思うが、
163: # 一回につき数十KB くらいなので、とりあえず放置。
164: $selecter->remove($sock);
165: $request{$sock} = undef;
166: }
167:
168: #---------------------------------
169: # ヘッダの書き換え
170:
171: sub override_header($\$) {
172: my ($header, $ref_value) = @_;
173: if ( $header =~ m/^User-Agent$/i ){
174: $$ref_value .= '';
175: }
176: }
177:
178: #---------------------------------
179: # リクエストを解析し、実際の接続先やポート番号などを求める。
180: #
181: # 引数: HTTP リクエスト
182: # 戻り値: 接続先ホスト名、接続先ポート番号、解析済リクエスト
183:
184: sub parse_request($){
185: my ($req) = @_;
186: my $host;
187: my $port;
188: my $new_req = '';
189:
190: my $is_header = 1;
191:
192: foreach ( split(/\r\n|\n/, $req, -1) ){
193: if ( m|^([A-Z]+) [a-zA-Z]+://(.*?)/(.*?) (HTTP/\d\.\d)$|i ){
194: my $proto_ver;
195: my $path;
196: my $method;
197: ($method, $host, $path, $proto_ver) = ($1, $2, $3, $4);
198:
199: if ( &is_use_proxy($host) ){
200: $new_req = "$_\r\n";
201: ($host, $port) = $real_proxy =~ m/^(.*):(\d+)$/;
202: } else {
203: if ( $host =~ s/:(\d+)$// ){
204: $port = $1;
205: } else {
206: $port = 80;
207: }
208: $new_req = "$method /$path $proto_ver\r\n";
209: }
210: } elsif ( $is_header ){
211: if ( m/^(.*?):\s*(.*)$/s ){
212: my ($header, $value) = ($1, $2);
213: &override_header($header, \$value);
214: $new_req .= "$header: $value\r\n";
215: } elsif ( $_ eq '' ){
216: # ヘッダおしまい
217: $new_req .= "\r\n";
218: $is_header = 0;
219: }
220: } elsif ( ! $is_header ){
221: $new_req .= "$_";
222: }
223: }
224:
225: return ($host, $port, $new_req);
226: }
227:
228: #----------------------------------------------
229: # 外部 proxy を経由するかどうか調べる。
230: #
231: # 引数: ホスト名
232: # 戻り値: 1…proxy を経由する
233: # 0…proxy を経由しない
234:
235: sub is_use_proxy($){
236: my ($host) = @_;
237:
238: foreach ( @noproxy_hosts ){
239: if ( $host =~ m/.*$_.*/ ){
240: return 0;
241: }
242: }
243: if ( $real_proxy ){
244: return 1;
245: } else {
246: return 0;
247: }
248: }
| 前へ << HTTP の並行アクセス | モジュールをインストールしよう >> 次へ |
ご意見・ご指摘は Twitter: @68user までお願いします。