HTTPClient製作 on Perl

息抜きついでに

今はC++でちょっと大きめのHTTPClient書いてるんですが、息抜き+Perlの勉強としてPerlでHTTPClient書いてみました。

Perlは難しいですけど、ネットワーク関連などは色々整っててC/C++より断然書きやすいと感じました。

特にテキストの整形なんかは評判通り楽チンです


まずはPerl談義

#use strict;

よく入門書やサイトなんかでソースの上に#use strictって書かれてますが、大抵は恒例の「おまじない」扱いです

ってことで調べたら出てきました

strict is your friend!!!!!
strict プラグマこそが、我々のミスを見つけてくれる、プログラマーの強力な味方です。

(http://iandeth.dyndns.org/mt/ian/archives/000617.html)

だそうです(汗

とりあえず曖昧なPerlでの変数宣言を少し厳密にしてくれるプラグマのようです。

#use warning? or option of -w?

#use strictについて調べているとソースの中に#use warningを入れるのを奨励する人と、-wを付けるのを勧める人がいました。

これもググってみると案外簡単に答えが見つかりました。以前議論があったのかな?

なぜuse warnings;を使うべきで、-wでないのか。-wの方がuse warnings;より短いではないか。

理由は二つある。

  • wは.plには有効でも.pmには有効ではない

これが一番の理由である。-wはあくまでも実行時のスイッチなので、スクリプト、すなわち実行ファイルで指定しないと有効にならない。
(http://blog.livedoor.jp/dankogai/archives/51068305.html)

非常に分かりやすいですね。なるほど、関数がパッケージにまたがってしまうと、-wは効力を持たなくなってしまうが、

#use warningだとまたがっても安心だと。

ふむぅ・・・。

[eq/ne] or [==/!=]?

下にあるサイトで書かれてるサイトのソースを参考にして色々遊びながら書いてるんですが、文字列比較で条件分岐するとき、

len1 eq len2

ってあったんですが、僕は過去に「==」で比較をしてるPerl文を見たことがあったんですよ、絶対に。

そこで調べてみると

ん〜、やっぱり文字列比較は 「 eq / ne 」 で、
数字の比較は 「 == / != 」を使わないとですね。。。

(http://ano421.blog59.fc2.com/blog-entry-378.html)

みたいです。

結構初心者のハマるところみたいなので早めに知れてて良かったかなと。

eqの方は文字列に型変換して評価してるみたいですね。

なんだかんだで完成

とりあえず汚いソースから

#!C:\Perl\bin\perl

use warnings;
use Socket;

#start analysis of argments.
if ($ARGV[0] eq "-HEAD" || $ARGV[0] eq "-head"){
	$method = "HEAD";
}elsif($ARGV[0] eq "-GET" || $ARGV[0] eq "-get"){
	$method = "GET";
}else{
	print "method is GET or HEAD only.\n";
	exit;
}

if($ARGV[1] =~ m|^http://([-_\.a-zA-Z0-9]+)/?(.*)$|){
	$host = $1;
	$path = $2;
}else{
	print "URL encode is \"http://host/path\"\n";
	exit;
}

if( $#ARGV == 2){
	if( $ARGV[2] =~ m|^(-_\.a-zA-Z0-9]+):(\d+)$|){
		$proxy = $1;
		$port = $2;
		$connect_host = $proxy;
	}else {
		print "Proxy encode is \"host:port\"\n";
		exit;
	}
	$connect_host = $proxy;
}else{
	$connect_host = $host;
	$port = getservbyname('http', 'tcp');
}
#end analysis of argments.
#start processing of conection

$iaddr = inet_aton($connect_host) or die "$connect_host is not exist.\n";

$sock_addr = pack_sockaddr_in($port, $iaddr);

socket(SOCKET, PF_INET, SOCK_STREAM, 0) or die "Can't create a socket.\n";

connect(SOCKET, $sock_addr) or die "Can't connect to $port of #connect_host\n";

select(SOCKET); $|=1; select(STDOUT);

#end processing of connection.
#start sending request of HTTP

if( defined $proxy){
	print SOCKET "$method http://$host/$path HTTP/1.0\r\n";
}else{
	print SOCKET "$method /$path HTTP/1.0\r\n";
	print "\$host = $host\n\$path = $path\n";
}

print SOCKET "User-Agent: ShinlinsuiTalker/0.10\r\n";
print SOCKET "\r\n";

#end sending request of HTTP
#start gettin data from server

if($method eq "GET"){
	open(FILE,">$path");
	binmode(FILE);
	while(<SOCKET>){
		m/^\r\n$/ and last;
	}
	
	while(<SOCKET>){
		print FILE $_;
	}
	close(FILE);
}else{
	while(<SOCKET>){
		print $_;
	}
}

色々と納得いかないところがあるのですがひとまず。これからも改良していくつもりです。

このソースではファイルネームにファイルパスをそのまま使ってますが、実はファイルネームの取得をしたかったのです

そこで

$ARGV[1] =~ m/[a-zA-Z0-9]+\.[a-zA-Z0-9]+/;

$filename = $+;

とかして取得するつもりだったのですが、上手く動かない。

正規表現学びたてなので正規表現のところに不備があるのでしょう。

しかし正規表現のチェックをかけると、ちゃんとpreg用のチェッカーでも動作するんですが・・・

正規表現を使ったあと、またすぐに正規表現を使う、というのがエラーの原因になってたりとか・・・ないよねー・・・

うーん・・・

解決できたらまた書きます

追記

ソース中のコメント、英語で入れてますが適当です。

一時期使ってた環境でマルチバイト文字が通らなかったため、英語で適当に書いてたらくせになりました。

わざわざ日本語に切り替える手間も省けますし、意味も大体分かりますしその習慣が残っているわけです。

ただ、間違った英語を使い続けるのもあれですので、英語に間違いがあったときもコメント頂けると幸いです。