Oneman

Module Version: 0.33 Source [Download] [Browse] 13 Aug 2014

名前

Oneman – A simple web application framework with PSGI server

概要

# app.psgi を実行
>perl -MOneman -e oneman app.psgi
>perl -MOneman -e oneman app.psgi --host 127.0.0.1 --port 8080
>perl -MOneman -e oneman -- --workers 32 app.psgi

# スクリプトで実行
use Oneman;
oneman 'app.psgi', host => '127.0.0.1', port => 8080;

# PSGI アプリケーション (関数リファレンス) を実行
my $psgi_app = sub { [200, [], ['Hello, world!']] };
oneman $psgi_app, workers => 32;

# no EXPORT
require Oneman;
Oneman::oneman($psgi_app, workers => 32);

# WAF
use base 'Oneman';
sub app {
    my $self = shift;
    $self->write('Hello, world!');
}
my $psgi_app = __PACKAGE__->psgi_app;

# WAF + PSGI server
__PACKAGE__->psgi_app->start;

# WAF + server それぞれにオプションを指定
__PACKAGE__->build( template_dir => "$Bin/templates" )->start( workers => 32 );

説明

Onemanは、Windowsでも動作するpreforking PSGI serverです。5.8.1以上のコアモジュールのみで動作する1ファイル構成のPerlモジュールで提供されます。

HTTP/1.1 の一部(Chunked Transfer Coding、Persistent ConnectionsいわゆるKeep-Alive、他)に対応していて、Plack::Test::SuiteをPASSしています。 preforkingなマルチプロセスサーバなので、ブロッキングが発生するリクエストの処理中にも別のリクエストを受け付ける事ができます。

「oneman()」という関数にPSGIファイルを指定して実行する事でOneman PSGIサーバが動作します。

>perl -MOneman -e "oneman 'app.psgi'"
Oneman: accepting connections at http://0.0.0.0:5000/

各種パラメータをオプションで変更できます。

>perl -MOneman -e "oneman 'app.psgi', host => '127.0.0.1', port => 8080"

PSGIファイルの代わりに、PSGIアプリケーション(関数リファレンス)を指定できます。

>perl -MOneman -e "oneman sub { [ 200, [], ['Hello, world!'] ] }"

Onemanモジュールを読み込むとoneman()と他いくつかの関数がエクスポートされます。エクスポートしたくない場合は、Oneman::onemanを使用してください。

#!/usr/bin/perl
require Oneman;
Oneman::oneman( sub { [ 200, [], ['Hello, world!'] ] } );

静的コンテンツの出力にも対応しています。

use Oneman;
require AnyWebApp;
my $psgi_app = AnyWebApp->psgi_app;

use FindBin qw( $Bin );
oneman $psgi_app, static => sub { s{\A /static/ }{$Bin/static-files/}xms };

oneman()は、引数が指定されない場合はコマンドライン引数(@ARGV)を処理します。コマンドラインでオプションを指定する場合は、オプション名の先頭にハイフンを付けて、アンダースコアをハイフンに置き換えてください。

>perl -MOneman -e oneman app.psgi --host 127.0.0.1 --port 8080 --limit-request-body 20971520

コマンドラインの場合、オプションを先頭に指定すると、末尾の引数がPSGIファイルとして認識されます。

>perl -MOneman -e oneman -- --workers 32 app.psgi

“web application framework”?

OnemanはCGI/PSGI対応の簡易なWebアプリケーションフレームワークでもあります。Perlコードをそのまま埋め込めるテンプレート機能と基本的なパラメータハンドリングの機能を持っています。

動作

Oneman PSGIサーバのプロセスは「MASTER」、「BUILDER」、「WORKER」の3階層構造です。WORKERが複数動作する事により、リクエストの並列処理を行っています。また、再起動処理を簡素化するためにBUILDERの階層を設けています。

$ ps f
  PID COMMAND
 6046 -bash
 6203  \_ perl -MOneman -e oneman app.psgi         # MASTER
 6204      \_ perl -MOneman -e oneman app.psgi     # BUILDER
 6205          \_ perl -MOneman -e oneman app.psgi # WORKER
 6206          \_ perl -MOneman -e oneman app.psgi # WORKER
 6207          \_ perl -MOneman -e oneman app.psgi # WORKER
 6208          \_ perl -MOneman -e oneman app.psgi # WORKER
 6209          \_ perl -MOneman -e oneman app.psgi # WORKER

MASTERはインターフェースをlistenしてからBUILDERを起動します。起動したBUILDERはPSGIアプリケーションをコンパイル(require)してからWORKERを起動します。
MASTERにkill -HUPを送る事で、BUILDER以下を再起動します。

$ kill -HUP 6203
$ ps f
  PID COMMAND
 6046 -bash
 6203  \_ perl -MOneman -e oneman app.psgi
 6211      \_ perl -MOneman -e oneman app.psgi
 6212          \_ perl -MOneman -e oneman app.psgi
 6213          \_ perl -MOneman -e oneman app.psgi
 6214          \_ perl -MOneman -e oneman app.psgi
 6215          \_ perl -MOneman -e oneman app.psgi
 6216          \_ perl -MOneman -e oneman app.psgi

再起動の際、MASTER がインターフェースをlistenしたままBUILDERがPSGIアプリケーションを再度コンパイルするため、PSGIサーバを停止させる事なくPSGIアプリケーションを更新できます。
ただし、これが有効なのはoneman()の引数にPSGIファイルを指定した場合のみです。(Perlコードリファレンスを指定した場合、それはすでにコ ンパイル済みであり、最初からMASTERで保持してしまうためです。)

オプション

host

host => '127.0.0.1'

Oneman PSGIサーバが特定のIPアドレスをlistenするように指定します。デフォルトではサーバが持つ全てのインターフェースをlistenします。

port

port => 8080

Oneman PSGIサーバがlistenするポート番号を指定します。デフォルトは5000です。

workers

workers => 32

動作させるWORKERの数を指定します。デフォルトは5です。

timeout

timeout => 15

クライアントからデータを読み込む時の、受信バッファが空の状態を待つ秒数を指定します。デフォルトは5です。

リクエストボディの読み込み中は、1バイトでも読み込みできればリセットされるため、指定秒数以内の間隔であれば何度途切れてもタイムアウトになりません。

limit_request_body

limit_request_body => 20971520

リクエストボディとして許容する最大バイト数を指定します。デフォルトは10485760(10MiB)です。

max_memory_buffers

max_memory_buffers => 2097152

リクエストボディをメモリにバッファリングする最大バイト数を指定します。デフォルトは1048576(1MiB)です。

リクエストボディが指定サイズを超える場合は、一時ファイル上にバッファリングします。
また、ここで指定するサイズは、リクエストラインの許容サイズおよびリクエストヘッダ全体の許容サイズとしても使用されます。

static

static => sub { s{\A /static/ }{$Bin/static-files/}xms }

静的ファイルのレスポンスをOnemanに指示、および、そのパスの指定を行うための関数のリファレンスを指定します。

指定した関数は、リクエスト毎にPATH_INFOが$_に格納された状態で呼び出されます。関数が真を戻すと、Onemanは$_の内容をサーバ上の物理パスとするファイルをレスポンスします。
例のようにs///演算子を使用する事で、指示のための真偽値と物理パスへの変換を同時に行う事ができます。

mime_type

mime_type => { psgi => 'application/psgi' }

静的ファイルのレスポンスを行う時の、ファイル拡張子と対応するMIMEタイプのペアを持つハッシュのリファレンスを指定します。

拡張子は小文字で定義してください。指定ファイルの拡張子に大文字を含む場合も小文字に変換してマッチングします。Oneman内でもいくつかの拡張子とそのMIMEタイプのペアをあらかじめ定義しており、ここでの指定拡張子に一致しない場合に使用されます。

keepalive

keepalive => 0

Keep-Aliveを無効化する場合に偽値を指定します。デフォルトは真値(有効)です。
コマンドラインでは–no-keepaliveで指定できます。

keepalive_timeout

keepalive_timeout => 15

Keep-Aliveが有効の場合に、次のリクエストを待つ秒数を指定します。デフォルトは1です。

Webアプリケーションフレームワーク

Oneman Webアプリケーションフレームワーク(以下WAF)はユーザ固有のアプリケーションクラスの基底クラスとして動作します。run()メソッドによりアプリケーションオブジェクトを生成し、アプリケーションクラスで定義するapp()メソッドを呼び出します。
run()メソッドを実行するスクリプトはCGIとして動作します。

#/usr/bin/perl

MyWebApp->run;

package MyWebApp;
use base 'Oneman';
sub app {
    my ($self) = @_;
    $self->write('Hello, world!');
}

run()の代わりにpsgi_app()メソッドを使用する事で、アプリケーション側の変更を行う事なくPSGIアプリケーションとして動作させる事ができます。

require MyWebApp;
my $psgi_app = MyWebApp->psgi_app;

psgi_app()で得られる関数リファレンスはOneman PSGIサーバの起動用クラスでblessされており、start()メソッドにより直接HTTPサーバとして動作させる事ができます。
HTTPサーバの各種パラメータ変更のためのstart()メソッド用オプションは、oneman()と同じ仕様です。

require MyWebApp;
MyWebApp->psgi_app->start( port => 8080, workers => 32 );

テンプレート処理

Oneman WAFは、PerlコードをHTML内に直接記述できるテンプレート処理機能を持っています。これは前身のWaftとほぼ同じで、HTML内に<%と%>で囲んだPerlコードを埋め込む事ができます。execute()メソッドによりテンプレートをコンパイルし、実行できます。

テンプレート内は自動的にstrict, warnings, utf8プラグマが有効になります。パッケージはテンプレート毎にユニークです。

my $template = << 'END';
<% for ( 1 .. 10 ) { %>
    Hello, world!<br />
<% } %>
END

$self->execute(\$template);

<%= … %>で囲まれた部分は、その評価結果を出力する事ができます。出力時には自動的にHTMLエスケープが行われます。

<%= '<br />' x 2 %> <!-- $self->write( html_escape('<br />' x 2) ); と等価 -->

&lt;br /&gt;&lt;br /&gt;

<%text= … %>は、さらにタブ文字の展開と改行タグの挿入が行われます。

<%text= "Oneman\n\t<1>Web Application Framework\n\t<2>PSGI server" %>

Oneman<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;1&gt;Web Application Framework<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;2&gt;PSGI server

<%jsstr= … %>は、JavaScript用のエスケープを行います。

var str = '<%jsstr= "</script>\n" %>';

var str = '\x3C\/script\x3E\n';

<%json= … %>は、JSONエンコードを行います。

var data = <%json= { key => 'value', 123 => [456, "789", undef] } %>;

var data = {"key":"value","123":[456,"789",null]};

execute()メソッドにファイルのパスを指定すると、それをテンプレートとして処理します。パスはオプションtemplate_dirで指定したディレクトリが基点です。
テンプレートはメソッドと同様に引数を受け取る事ができます。

# template.html
<%
my @values = @_; %>
<h1>Oneman</h1><%
for ( @values ) { %>
    <h2><%= $_ %></h2><%
}
$self->execute('template.html', 'WAF', 'PSGI server');

<h1>Oneman</h1>
    <h2>WAF</h2>
    <h2>PSGI server</h2>

compile()メソッドは、テンプレートのコンパイルのみを行い、関数リファレンスを戻します。

my $coderef = $self->compile($template);
$coderef->($self, 'WAF', 'PSGI server'); # もしくは $self->$coderef(...

パラメータハンドリング

Oneman WAFは、クライアントから受信したパラメータを自動的に読み込んでUTF-8デコードを行い、アプリケーションオブジェクト内に格納します。パラメータはハッシュで保持されており、リファレンスをp()メソッドで取得できます。

sub app {
    my ($self) = @_;
    $self->write( $self->p->{name} );
}

同一名のパラメータを複数受信した場合、Oneman WAFはそれらを1つのキーで扱います。

電話番号:
<input type="text" name="tel" value="090" /> -
<input type="text" name="tel" value="1234" /> -
<input type="text" name="tel" value="xxxx" />

my $p = $self->p;
$self->write( $p->{tel} );        # 090
$self->write( $p->{'tel', 0} );   # 090
$self->write( $p->{'tel', 2} );   # xxxx

p()メソッドにパラメータ名を指定すると、複数の値を配列で取得できます。また、パラメータ名に加えて値を指定する事で上書きされます。

$self->write( join '-', $self->p('tel') ); # 090-1234-xxxx
$self->p('tel', '080', '5678', 'xxxx');
$self->write( join '-', $self->p('tel') ); # 080-5678-xxxx

write_hidden_tags()メソッドはパラメータをHTML内に展開します。

$self->write_hidden_tags;

<input type="hidden" name="tel" value="080" /><input type="hidden" name="tel" value="5678" /><input type="hidden" name="tel" value="xxxx" />

url()メソッドはパラメータをURLに付加します。

$self->url('/', ['tel']); # /?tel=080&tel=5678&tel=xxxx

メソッド