PerlでAzure(2)

 順調です。
 Authorization の問題がクリアできたら、一気に進む進む。

 今のところ、Azure::Table モジュールで次の機能が出来ました。
・QUERY TABLE
・CREATE
・DELETE

 次から Entity 操作ですね。
 意外と Entity 操作もすぐできちゃうかも。

参考
http://msdn.microsoft.com/en-us/library/dd179423.aspx

 以下に現状の Azure::Table.pm を記述します。
 Perl として綺麗に書けていないから、モジュール化とかをちゃんと考えないと駄目ですね。

package Azure::Table;

use Exporter;
@ISA = qw(Exporter);
#@EXPORT = qw( new create_table list exists );

# 使用するモジュール

use Time::gmtime;
use Encode;
use MIME::Base64;
use Digest::SHA qw(hmac_sha256_base64);
use LWP::UserAgent;
use LWP::Simple;
use XML::Simple;
use Data::Dumper;

our $BLOB	= "blob.core.windows.net";
our $QUEUE	= "queue.core.windows.net";
our $TABLE	= "table.core.windows.net";

our $LAST_STATUS	= 0;
our $LAST_ERROR		= '';

sub new
{
	my $class	= shift;
    my $this	=
	{
		@_
	};

	bless $this, $class;

	$this->{target}		= defined( $this->{target} ) ? $this->{target} : $TABLE;

	return $this;
}

sub create
{
	my $this	= shift;
	my $table	= shift;
	my $version	= shift if @_;

	if ( !defined($table) || length($table) == 0 || $this->exists($table) )
	{
		$LAST_ERROR = "Tablle::create [ error ] : Table $table is already exist.";
		return 1;
	}

	my $path = Azure::Table::create_uri( $this->{'account'}, $TABLE, "/Tables" );
	my $date = &create_utc();
	my $lite = &create_sig_lite( $this->{'account'}, "/Tables", $date );
	my $auth = &create_auth( $this->{'account'}, "SharedKeyLite", $lite, $this->{'authkey'} );

	my $req	= HTTP::Request->new ( POST => $path );
	my $ua	= LWP::UserAgent->new;
	$ua->agent('Perl::Azure::Table/0.1');
	$ua->timeout(5);

	# 認証用の必須ヘッダーを記述
	$ua->default_header("Authorization"	=> $auth );
	$ua->default_header("x-ms-date"		=> $date );
#	if ( defined($version) )
#	{
#		$ua->default_header("x-ms-version"		=> $version );
#	}

	# Request 作成
	$req->content_type( "application/atom+xml" );

	my $update = &create_update();
	my $content =<< "EOC";
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<entry xmlns:d='http://schemas.microsoft.com/ado/2007/08/dataservices'
	xmlns:m='http://schemas.microsoft.com/ado/2007/08/dataservices/metadata'
	xmlns='http://www.w3.org/2005/Atom'>
	<title />
	<updated>$update</updated>
	<author>
		<name />
	</author>
	<id />
	<content type='application/xml'>
		<m:properties>
			<d:TableName>$table</d:TableName>
		</m:properties>
	</content>
</entry>
EOC

	$req->content($content);
	print "$content";

	# リクエスト送信
	my $res = $ua->request($req);

	my @tables;
	if ( $res->is_success && $res->code == 201 )
	{
		print $res->content;
	}
	else
	{
		$LAST_STATUS = $res->code;
		print $res->content;
		return 1;
	}

	return 0;
}

sub list
{
	my $this = shift;

	my $path = Azure::Table::create_uri( $this->{'account'}, $TABLE, "/Tables" );
	my $date = &create_utc();
	my $lite = &create_sig_lite( $this->{'account'}, "/Tables", $date );
	my $auth = &create_auth( $this->{'account'}, "SharedKeyLite", $lite, $this->{'authkey'} );

	my $req	= HTTP::Request->new ( GET => $path );
	my $ua	= LWP::UserAgent->new;
	$ua->agent('Perl::Azure::Table/0.1');
	$ua->timeout(5);

	# 認証用の必須ヘッダーを記述
	$ua->default_header("x-ms-date"		=> $date );
	$ua->default_header("Authorization"	=> $auth );
#	if ( defined($version) )
#	{
#		$ua->default_header("x-ms-version"		=> $version );
#	}

	# リクエスト送信
	my $res = $ua->request($req);

	my @tables;
	if ( $res->is_success )
	{
		$_ = $res->content;
		push( @tables, /<d\:TableName>(.+)<\/d\:TableName>/g );
	}
	else
	{
		$LAST_STATUS = $res->code;
	}
	return @tables;
}

sub exists
{
	my $this	= shift;
	my $table	= shift;

	@tables = $this->list();
	foreach ( @tables )
	{
		return 1 if $_ eq $table ;
	}
	return 0;
}

sub delete
{
	my $this	= shift;
	my $table	= shift;

	if ( ! $this->exists($table) )
	{
		print "No such table $table in Tables.\n";
		return 0;
	}

	my $path = Azure::Table::create_uri( $this->{'account'}, $TABLE, "/Tables('$table')" );
	my $date = &create_utc();
	my $lite = &create_sig_lite( $this->{'account'}, "/Tables('$table')", $date );
	my $auth = &create_auth( $this->{'account'}, "SharedKeyLite", $lite, $this->{'authkey'} );

	my $req	= HTTP::Request->new ( DELETE => $path );
	my $ua	= LWP::UserAgent->new;
	$ua->agent('Perl::Azure::Table/0.1');
	$ua->timeout(5);

	# 認証用の必須ヘッダーを記述
	$ua->default_header( "x-ms-date"		=> $date );
	$ua->default_header( "Authorization"	=> $auth );

	# Request 作成
	$req->content_type( "application/atom+xml" );

	# リクエスト送信
	my $res = $ua->request($req);

	print $res->content;

	my @tables;
	if ( $res->is_success )
	{
		print "Succeeded to delete table $table.\n";
	}
	else
	{
		$LAST_STATUS = $res->code;
		return 1;
	}

	return 0;
}


# URI 作成
sub create_uri
{
	my $account	= shift;
	my $target	= shift;
	my $path	= shift;

    return sprintf("https://%s.%s%s", $account, $target, $path);
}

# GMT 時間取得
sub create_utc
{
	my $gm    = gmtime();
	my $week  = (qw(Sun Mon Tue Wed Thu Fri Sat Sun))[ $gm->wday() ];
	my $month = (qw(Jan Feb Mar Apl May Jun Jul Aug Sep Oct Nov Dec))[ $gm->mon() ];
	my $year  = ( $gm->year() < 1900 ) ? $gm->year() + 1900 : $gm->year();
	my $date  = sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT", $week, $gm->mday(), $month, $year,
		$gm->hour(), $gm->min(), $gm->sec() );

	return $date;
}

# Atom Update 用時間取得
sub create_update
{
	my $gm    = gmtime();
	my $week  = (qw(Sun Mon Tue Wed Thu Fri Sat Sun))[ $gm->wday() ];
	my $month = (qw(Jan Feb Mar Apl May Jun Jul Aug Sep Oct Nov Dec))[ $gm->mon() ];
	my $year  = ( $gm->year() < 1900 ) ? $gm->year() + 1900 : $gm->year();
	my $date  = sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", $year, $gm->mon() + 1, $gm->mday(),
		$gm->hour(), $gm->min(), $gm->sec() );

	return $date;
}

# SharedLiteKey のシグネイチャー作成
sub create_sig_lite
{
	my $account	= shift;
    my $path    = shift;
    my $date    = shift;

    return sprintf("%s\n/%s%s", $date, $account, $path);
}

# 認証コード作成
sub create_auth
{
	my $account = shift;
    my $type    = shift;
    my $sign    = shift;
    my $key     = shift;

	$key = decode_base64($key);

    my $octets  = encode('utf8', $sign);
    my $code    = hmac_sha256_base64($octets, $key);
    $code      .= '=' while length($code) % 4;

    return sprintf("%s %s:%s", $type, $account, $code);
}

1;
__END__

 サンプルコードで自前の Azure Table にアクセスすると、こんな感じで結果が返ります。

D:\Develop\Azure\Perl>get_req.pl
ArticleEntity SampleEntity
Table ArticleEntity exists.
Failed to create SampleEntity table.
Status code : 0
Succeeded to delete table SampleEntity.
Succeed to delete SampleEntity table.