前へ << 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 までお願いします。