InPerl:WebAccess
InPerlでWebにアクセスしてあれこれやるときの便利関数群。
Webサイト/Webアプリのテストを作る/実行する時とかに便利、かも。
use YAML; # Dumpが便利。 use LWP; use HTTP::Cookies; use HTML::Form; use HTML::FormatText; use HTML::TreeBuilder; use threads; #以下の3つはサーバ実装のために必要。 use threads::shared; use IO::Socket; sub RES{ return $res; } # TEXT : HTML::FormatTextを使ってページを表示。 sub TEXT{ my $formatter = HTML::FormatText->new(); $formatter->format(HTML::TreeBuilder->new_from_content($res->content)); } # DISP : 適当なブラウザを使ってページを表示。 # ブラウザへの指示(``内)は適当に変更すること。 sub DISP{ _DISP_WRITE(); `galeon -n /tmp/DISP_temp.html`; } sub _DISP_WRITE{ open(OUT,'> /tmp/DISP_temp.html'); print OUT $res->content; close(OUT); } sub GO{ $history = 0; $req = shift; $res = $ua->request($req); while($res->header('location')){ my $req = new HTTP::Request( GET=> $res->header('location') ); $res = $ua->request($req); } unshift(@history,$res); @form = HTML::Form->parse($res); $form = $form[0]; SERVER_START(); } sub HISTORY{ my $i = 0; my $num = shift; if($num eq 'DEL'){ undef(@history); unshift(@history,$res); return; } if(defined $num){ $res = $history[$num]; $req = $res->request; @form = HTML::Form->parse($res); $form = $form[0]; $history = $num; return; SERVER_START(); } foreach $res (@history){ printf(" %1s%s: %s\n" ,($i == $history)?"*":'',$i++,$res->request->uri); } } # LINK : リンクの操作 # GO と組み合わせて使う。 # ex: GO LINK 2 # # LINK => リンク一覧の表示 # LINK 2 => 指定された番号のリンクに対応したHTTP::Requestオブジェクトを返す。 # マイナスを指定すれば逆順に数える。 # LINK uri => 直接uriを指定(相対/絶対) sub LINK{ my $uri = shift; unless( defined $uri ){ my $content = $res->content; print " ".$res->base."\n"; my $i = 0; while($content =~ /<a.*?href=([\"\'])?(.*?)\1.*?>(.*?)<\/a>/mg){ printf(" %s: %s [%s]\n",$i++,$2,$3); } return; } if($uri =~ /^\-?\d+$/){ my $i = 0; my(@list); my $content = $res->content; while($content =~ /<a.*?href=([\"\'])?(.*?)\1.*?>(.*?)<\/a>/mg){ push(@list,$2); } $uri = @list[$uri]; } if($uri){ $uri = URI->new( $uri ); $uri = $uri->abs($res->request->uri); return new HTTP::Request( GET=> $uri ); }else{ return 'no target'; } } sub DISP_FORM{ my $i = -1; my(@submits); foreach $input ($form->inputs){ $i++; next if($input->type eq 'hidden'); DISP_INPUT($input,$i); } } sub DISP_INPUT{ my $input = shift; my $i = shift; my($value,$note); if($input->type eq 'hidden'){ $note = '(hidden) '; } if($input->possible_values){ $value = sprintf("( %s )", join (' ', map { ($_ eq $input->value)? "{$_}" : $_; } $input->possible_values)); }elsif($input->type eq 'submit'){ printf("%d [[ %s ]]\n",$i,$input->value); return; }else{ $value = $input->value; } printf("%d %s%s=%s\n",$i,$note,$input->name,$value); } # FORM : フォームの操作 # GO と組み合わせて使う。 # ex: GO FORM 2,1 # # FORM => 入力可能パラメータ一覧の表示。 # FORM 2 => 指定された番号のパラメータを表示 # FORM 2, 'value' => 指定された番号のパラメータを'value'に設定 # マイナスを指定すれば逆順に数える。 # 但し、指定された番号のパラメータがsubmitだった場合は # HTTP::Requestオブジェクトを返す。 sub FORM{ my $n = shift; unless ( defined $n ){ return DISP_FORM; } if($n >= scalar($form->inputs)){ return 'no exist input $n'; } $input = ($form->inputs)[$n]; my $val = shift; unless(defined $val){ DISP_INPUT($input,$n); return ''; } if($input->type eq 'submit'){ return $input->click($form); } return $input->value($val); } my $req : shared; my $res : shared; # サーバ実装: # threadのサポートが必要。 # # http://localhost:10080/ にアクセスすると、 # GO / HISTORY で進んだ「今見ている」ページを見ることができる。 # ものすごくRFCを無視したどうかと思うWebサーバ本体 sub SERVER{ my $listener = IO::Socket::INET->new(Listen => SOMAXCONN, LocalPort => 10080, LocalAddr => "127.0.0.1", PeerAddr => "0.0.0.0", Proto => 'tcp', Reuse => 1 ) or die "create listener"; while(1){ $listener->listen or die; my $sock = $listener->accept(); unless($sock){ sleep 1; next; } while($line = $sock->getline()){ if($line =~ /X-WebAccess-InPerl-Quit:/){ close($listener); close($sock); $end = 1; } last if($line =~ /^\r\n$/); } last if($end); my $res = RES(); my $content = $res->content; my $buf = sprintf("HTTP/1.1 200 OK\r\nContent-Type: %s\r\nConnection: close\r\nContent-Length: %s\r\n\r\n%s" ,($res->header('content-type'))[0] ,length($content) ,$content); $sock->syswrite($buf,length($buf)); $sock->close; } } # ページが切り替わる度に呼ばれる。 # いちいちサーバーを落としてから立て直すのが格好悪い sub SERVER_START{ if($SERVER_THREAD){ my $sock = IO::Socket::INET->new(PeerAddr => '127.0.0.1', PeerPort => 10080, Proto => 'tcp'); $sock->syswrite("X-WebAccess-InPerl-Quit:\r\n"); $sock->close; $SERVER_THREAD->join(); } $SERVER_THREAD = threads->new(\&SERVER); } # コマンドラインであれこれやるためのUserAgentを作る。 $ua = new LWP::UserAgent; $cookie_jar = HTTP::Cookies->new; $ua->cookie_jar($cookie_jar);