Skip to content

Instantly share code, notes, and snippets.

@gardejo
Created May 12, 2010 14:18
Show Gist options
  • Save gardejo/398635 to your computer and use it in GitHub Desktop.
Save gardejo/398635 to your computer and use it in GitHub Desktop.
PSGI-Doc-JA (trial)

README For PSGI-Doc-JA

(English follows Japanese)

PSGI-Doc-JAはウェブサーバーとPerlベースのウェブアプリケーションとのインターフェースであるPSGI仕様に関して日本語訳を提供するためのプロジェクトです。

I. 翻訳の入手

PSGI-Doc-JAはgitで管理されているので、誰でもチェックアウト可能です。 現在はgithub上にて公開されています。

<del>http://github.com/jpa/PSGI-Doc-JA</del>

註: 現在はJPA(Japan Perl Association)のリポジトリではなく、翻訳協力者の個人リポジトリにて公開されています。

http://github.com/gardejo/PSGI-Doc-JA

II. 追加修正

誤訳等による修正や、新バージョンによる追加はgithubのpull request等を通して連絡してください。

META.ymlファイルにバージョンを指定するのを忘れないでください。このファイルにバージョン情報がないと正しくデータが出力されません。Perl組み込み(コアモジュール・ドキュメント)に関しては perl-X.XXXのように同梱されているPerlのバージョンを指定してください。

また、文章はすべてUTF-8で統一してください。

III. 翻訳協力者(敬称略)

守屋雅樹 (gardejo)

IV. PSGIディストリビューションのREADME.md

これはPythonのWSGIやRubyのRack風のPSGI(Perl Web Server Gateway Interface)です。

PSGIプロトコル仕様(英語)は現在草案の段階にあります。私達はこれを最終版にする前にフィードバックを審査しているところです。FAQも参照してください。

さらなるリンクはPSGI/Plackウェブサイト(英語)を参照してください。

README For PSGI-Doc-JA

PSGI-Doc-JA is a project to translate PSGI documents in Japanese.

I. Where to get the source

PSGI-Doc-JA is currently hosted on github. Anybody can clone it:

<del>http://github.com/jpa/PSGI-Doc-JA</del>

NOTE: PSGI-Doc-JA is not currently hosted on JPA (Japan Perl Association)'s repository, but instead on translator's personal repository.

http://github.com/gardejo/PSGI-Doc-JA

II. Updating the documentation

Whether by mistake, omission, or new updates, you can make your changes and ping us via github's pull request feature

You must specify the module's version number in a META.yml file.

When you do so, please use UTF-8 only, please.

III. Translators

Masaki Moriya (gardejo)

IV. README.md in PSGI distribution

This is PSGI, Perl Web Server Gateway Interface -- ala Python's WSGI and Ruby's Rack.

PSGI protocol specification is currently in draft. We're reviewing feedbacks before making it final. See also FAQ

See PSGI/Plack website for more links.

NAME

PSGI::FAQ - Frequently Asked Questions and answers

QUESTIONS

General

How do you pronounce PSGI?

We read it simply P-S-G-I.

So what is this?

PSGI is an interface between web servers and perl-based web applications akin to what CGI does for web servers and CGI scripts.

Why do we need this?

Perl has CGI as a core module that somewhat abstracts the difference between CGI, mod_perl and FastCGI. However, most web application framework developers (e.g. Catalyst and Jifty) usually avoid using it to maximize the performance and to access low-level APIs. So they end up writing adapters for all of those different environments, some of which may be well tested while others are not.

PSGI allows web application framework developers to only write an adapter for PSGI. End users can choose from among all the backends that support the PSGI interface.

You said PSGI is similar to CGI. How is the PSGI interface different from CGI?

The PSGI interface is intentionally designed to be very similar to CGI so that supporting PSGI in addition to CGI would be extremely easy. Here's a highlight of the key differences between CGI and PSGI:

  • In CGI, servers are the actual web servers written in any languages but mostly in C, and script is a script that can be written in any language such as C, Perl, Shell scripts, Ruby or Python.

    In PSGI, servers are still web servers, but they're perl processes that are usually embedded in the web server (like mod_perl) or a perl daemon process called by a web server (like FastCGI), or an entirely perl based web server. And PSGI application is a perl code reference.

  • In CGI, we use STDIN, STDERR, and environment variables to read parameters and the HTTP request body and to send errors from the application.

    In PSGI, we use the $env hash references and the psgi.input and psgi.errors streams to pass that data between servers and applications.

  • In CGI, applications are supposed to print HTTP headers and body to STDOUT to pass it back to the web server.

    In PSGI, applications are supposed to return a HTTP status code, headers, and body (as an array ref or a filehandle-like object) to the application as an array reference.

My framework already does CGI, FCGI and mod_perl. Why do I want to support PSGI?

If your web application framework already supports most server environments, performance is good, and the backends are well tested, there may not be a direct benefit for you to support PSGI immediately -- though you would be able to remove any code that overlaps with PSGI backends. But if only CGI environment is currently supported, supporting PSGI in addition should be extremely easy, and the benefit you and your framework users will enjoy is huge: being able to run your application on new PSGI compatible servers, being able to use utilitles like plackup and Plack::Test and plenty of Plack::Middleware components.

I'm writing a web application. What's the benefit of PSGI for me?

If the framework you're using supports PSGI, that means your application can run on any of existing and future PSGI implementations. You can provide a .psgi file that returns PSGI application, the end users of your application should be able to configure and run your application in a bunch of different ways.

But I'm writing a web application in CGI and it works well. Should I switch to PSGI?

If you're writing a web application with a plain CGI.pm and without using any web frameworks, you're limiting your application in the plain CGI environments, along with mod_perl and FastCGI with some tweaks. If you're the only one developer and user of your application then that's probably fine.

One day you want to deploy your application in a shared hosting environment for your clients, or run your server in the standalone mode rather than as a CGI script, or distribute your application as open source software. Limiting your application in the CGI environment by using CGI.pm will bite you then.

You can start using one of PSGI compatible frameworks (either full-stack ones or micro ones), or use Plack::Request if you are anti frameworks, to make your application PSGI aware, to be more future proof.

Even if you ignore PSGI today and write applications in plain CGI, you can always later switch to PSGI with the CGI::PSGI wrapper.

What should I do to support PSGI?

If you're a web server developer, write a PSGI implementation that calls a PSGI application. Also join the development on Plack, the PSGI toolkit and utilities, to add a server adapter for your web server.

If you're a web application framework developer, write an adapter for PSGI. Now you're freed from supporting all different server environments.

If you're a web application developer (or a web application framework user), choose the framework that supports PSGI, or ask the author to support it. :) If your application is a large scale installable application that doesn't use any existing frameworks (e.g. WebGUI or Movable Type) you're considered as a framework developer instead from the PSGI point of view. So, writing an adapter for PSGI on your application would make more sense.

Is PSGI faster than (my framework)?

Again, PSGI is not an implementation, but there's a potential for a very fast PSGI implementation that preloads everything and runs fully optimized code as a preforked standalone with XS parsers and sendfile(2) kernel call, an event-based tiny web server written in C and embedded perl that supports PSGI, or a plain-old CGI.pm based backend that doesn't load any modules at all and runs pretty quickly without eating so much memory under the CGI environment.

There are HTTP::Server::PSGI::Prefork and Coro based PSGI servers which are pretty fast, and they include adapters for Plack so you can run with the plackup utility.

Users of your framework can choose which backend is the best for their needs. You, as a web application framework developer, don't need to think about lots of different users with different needs.

Plack

What is Plack? What is the difference between PSGI and Plack?

PSGI is a specification, so there's no software or module called PSGI. End users will need to choose one of the PSGI server implementations to run PSGI applications on. Plack is a set of PSGI utilities and contains the reference PSGI server HTTP::Server::PSGI, as well as Web server adapters for CGI, FastCGI and mod_perl.

Plack also has useful APIs and helpers on top of PSGI, such as Plack::Request to provide a nice object-oriented API on request objects, plackup that allows you to run an PSGI application from the command line and configure it using app.psgi (a la Rack's Rackup), and Plack::Test that allows you to test your application using standard HTTP::Request and HTTP::Response pair through mocked HTTP or live HTTP servers. See Plack for details.

What kind of server backends would be available?

In Plack, we already support most web servers like Apache2, and also the ones that supports standard CGI or FastCGI, but also try to support special web servers that can embed perl, like Perlbal or nginx. We think it would be really nice if Apache module mod_perlite and Google AppEngine supported PSGI too, so that you could run your PSGI/Plack based perl app in the cloud.

Ruby is Rack and JavaScript is Jack. Why is it not called Pack?

Well Pack indeed is a cute name, but Perl has a built-in function pack so it's a little confusing, especially when speaking instead of writing.

What namespaces should I use to implement PSGI support?

Do not use the PSGI:: namespace to implement PSGI backends or adapters.

The PSGI namespace is reserved for PSGI specifications and reference unit tests that implementors have to pass. It should not be used by particular implementations.

If you write a plugin or an extension to support PSGI for an (imaginary) web application framework called Camper, name the code such as Camper::Engine::PSGI.

If you write a web server that supports PSGI interface, then name it however you want. You can optionally support Plack::Handler's abstract interface or write an adapter for it, which is:

my $server = Plack::Handler::FooBar->new(%opt);
$server->run($app);

By supporting this new and run in your server, it becomes plackup compatible, so users can run your app via plackup. You're recommended to, but not required to follow this API, in which case you have to provide your own PSGI app launcher.

I have a CGI or mod_perl application that I want to run on PSGI/Plack. What should I do?

You have several choices:

CGI::PSGI

If you have a web application (or framework) that uses CGI.pm to handle query parameters, CGI::PSGI can help you migrate to PSGI. You'll need to change how you create CGI objects and how to return the response headers and body, but the rest of your code will work unchanged.

CGI::Emulate::PSGI and CGI::Compile

If you have a dead old CGI script that you want to change as little as possible (or even no change at all), then CGI::Emulate::PSGI and CGI::Compile can compile and wrap them up as a PSGI application.

Compared to CGI::PSGI, this might be less efficient because of STDIN/STDOUT capturing and environment variable mangling, but should work with any CGI implementation, not just CGI.pm, and CGI::Compile does the job of compiling a CGI script into a code reference just like mod_perl's Registry does.

Plack::Request and Plack::Response

If you have an HTTP::Engine based application (framework), or want to write an app from scratch and need a better interface than CGI, or you're used to Apache::Request, then Plack::Request and Plack::Response might be what you want. It gives you a nice Request/Response object API on top of the PSGI env hash and response array.

NOTE: Don't forget that whenever you have a CGI script that runs once and exits, and you turn it into a persistent process, it may have cleanup that needs to happen after every request -- variables that need to be reset, files that need to be closed or deleted, etc. PSGI can do nothing about that (you have to fix it) except give you this friendly reminder.

HTTP::Engine

Why PSGI/Plack instead of HTTP::Engine?

HTTP::Engine was a great experiment, but it mixed the application interface (the request_handler interface) with implementations, and the monolithic class hierarchy and role based interfaces make it really hard to write a new backend. We kept the existing HTTP::Engine and broke it into three parts: The interface specification (PSGI), Reference server implementations (Plack::Handler) and Standard APIs and Tools (Plack).

Will HTTP::Engine be dead?

It won't be dead. HTTP::Engine will stay as it is and still be useful if you want to write a micro webserver application rather than a framework.

Do I have to rewrite my HTTP::Engine application to follow PSGI interface?

No, you don't need to rewrite your existing HTTP::Engine application. It can be easily turned into a PSGI application using HTTP::Engine::Interface::PSGI.

Alternatively, you can use Plack::Request and Plack::Response which gives compatible APIs to HTTP::Engine::Request and HTTP::Engine::Response:

use Plack::Request;
use Plack::Response;

sub request_handler {
    my $req = Plack::Request->new(shift);
    my $res = Plack::Response->new;
    # ...
    return $res->finalize;
}

And this request_handler is a PSGI application now.

What's the benefit of converting my HTTP::Engine app to run on PSGI?

As of today most web server implementations and middlewares implemented by Plack are mostly available on HTTP::Engine as well, so there might not be direct immediate benefit of switching to PSGI. But PSGI is more future proof, and there are high hope that in the near future we'll have a pretty fast server environments (think of Passenger for Ruby Rack) and/or plenty of useful middlewares that HTTP::Engine doesn't have today.

See the question My framework already does CGI, FCGI and mod_perl. Why do I want to support PSGI? for more details.

API Design

Keep in mind that most design choices made in the PSGI spec are to minimize the requirements on backends so they can optimize things. Adding a fancy interface or allowing flexibility in the PSGI layers might sound catchy to end users, but it would just add things that backends have to support, which would end up getting in the way of optimizations, or introducing more bugs. What makes a fancy API to attract web application developers is your framework, not PSGI.

Why a big env hash instead of objects with APIs?

The simplicity of the interface is the key that made WSGI and Rack successful. PSGI is a low-level interface between backends and web application framework developers. If we define an API on what type of objects should be passed and which method they need to implement, there will be so much duplicated code in the backends, some of which may be buggy.

For instance, PSGI defines $env->{REMOTE_ADDR} as a string. What if the PSGI spec required it to be an instance of Net::IP? Backend code would have to depend on the Net::IP module, or have to write a mock object that implements ALL of Net::IP's methods. Backends depending on specific modules or having to reinvent lots of stuff is considered harmful and that's why the interface is as minimal as possible.

Making a nice API for the end users is a job that web application frameworks (adapter developers) should do, not something PSGI needs to define.

Why is the application a code ref rather than an object with a ->call method?

Requiring an object in addition to a code ref would make EVERY backend's code a few lines more tedious, while requiring an object instead of a code ref would make application developers write another class and instanciate an object.

In other words, yes an object with a call method could work, but again PSGI was designed to be as simple as possible, and making a code reference out of class/object is no brainer but the other way round always requires a few lines of code and possibly a new file.

Why are the headers returned as an array ref and not a hash ref?

Short: In order to support multiple headers (e.g. Set-Cookie).

Long: In Python WSGI, the response header is a list of (header_name, header_value) tuples i.e. type(response_headers) is ListType so there can be multiple entries for the same header key. In Rack and JSGI, a header value is a String consisting of lines separated by "\n".

We liked Python's specification here, and since Perl hashes don't allow multiple entries with the same key (unless it's tied), using an array reference to store [ key => value, key => value ] is the simplest solution to keep both framework adapters and backends simple. Other options, like allowing an array ref in addition to a plain scalar, make either side of the code unnecessarily tedious.

No iterators support in $body?

We learned that WSGI and Rack really enjoy the benefit of Python and Ruby's language beauty, which are iterable objects in Python or iterators in Ruby.

Rack, for instance, expects the body as an object that responds to the each method and then yields the buffer, so

body.each { |buf| request.write(buf) }

would just magically work whether body is an Array, FileIO object or an object that implements iterators. Perl doesn't have such a beautiful thing in the language unless autobox is loaded. PSGI should not make autobox as a requirement, so we only support a simple array ref or file handle.

Writing an IO::Handle-like object is pretty easy since it's only getline and close. You can also use PerlIO to write an object that behaves like a filehandle, though it might be considered a little unstable.

See also IO::Handle::Util to turn anything iterators-like into IO::Handle-like.

How should server determine to switch to sendfile(2) based serving?

First of all, an application SHOULD always set a IO::Handle-like object (or an array of chunks) that responds to getline and close as a body. That is guaranteed to work with any servers.

Optionally, if the server is written in perl or can tell a file descriptor number to the C-land to serve the file, then the server MAY check if the body is a real filehandle (possibly using Plack::Util's is_real_fh function), then get a file descriptor with fileno and call sendfile(2) or equivalent zero-copy data transfer using that.

Otherwise, if the server can't send a file using the file descriptor but needs a local file path (like mod_perl or nginx), the application can return an IO::Handle-like object that also responds to path method. This type of IO-like object can easily be created using IO::File::WithPath, IO::Handle::Util or Plack::Util's set_io_path function.

Middlewares can also look to see if the body has path method and does something interesting with it, like setting X-Sendfile headers.

To summarize:

  • When to serve static files, applications should always return a real filehandle or IO::Handle object. That should work everywhere, and can be optimized in some environments.

  • Applications can also set IO::Handle like object with an additional path method, then it should work everywhere again, and can be optimized in even more environments.

What if I want to stream content or do a long-poll Comet?

The most straightforward way to implement server push is for your application to return a IO::Handle-like object as a content body that implements getline to return pushed content. This is guaranteed to work everywhere, but it's more like pull than push, and it's hard to do non-blocking I/O unless you use Coro.

If you want to do server push, where your application runs in an event loop and push content body to the client as it's ready, you should return a callback to delay the response.

# long-poll comet like a chat application
my $app = sub {
    my $env = shift;
    unless ($env->{'psgi.streaming'}) {
        die "This application needs psgi.streaming support";
    }
    return sub {
        my $respond = shift;
        wait_for_new_message(sub {
            my $message = shift;
            my $body = [ $message->to_json ];
            $respond->([200, ['Content-Type', 'application/json'], $body]);
        });
    };
};

wait_for_new_message can be blocking or non-blocking: it's up to you. Most of the case you want to run it non-blockingly and should use event loops like AnyEvent. You may also check psgi.nonblocking value to see that it's possible and fallback to a blocking call otherwise.

Also, to stream the content body (like streaming messages over the Flash socket or multipart XMLHTTPRequest):

my $app = sub {
    my $env = shift;
    unless ($env->{'psgi.streaming'}) {
        die "This application needs psgi.streaming support";
    }
    return sub {
        my $respond = shift;
        my $writer = $respond->([200, ['Content-Type', 'text/plain']]);
        wait_for_new_message(sub {
            my $message = shift;
            if ($message) {
                $writer->write($message->to_json);
            } else {
                $writer->close;
            }
        });
    };
};

Which framework should I use to do streaming though?

We have servers that support non-blocking (where psgi.nonblocking is set to true), but the problem is that framework side doesn't necessarily support asynchronous event loop. For instance Catalyst has write method on the response object:

while ($cond) {
    $c->res->write($some_stuff);
}

This should work with all servers with psgi.streaming support even if they are blocking, and it should be fine if they're running in multiple processes (psgi.multiprocess is true).

Catalyst::Engine::PSGI also supports setting an IO::Handle-like object that supports getline, so using IO::Handle::Util

my $io = io_from_getline sub {
     return $data; # or undef when done()
};
$c->res->body($io);

And that works fine to do streaming, but it's blocking (pull) rather than asynchronous server push, so again you should be careful not to run this application on non-blocking (and non-multiprocess) server environments.

We expect that more web frameworks will appear that is focused on, or existent frameworks will add support for, asynchronous and non-blocking streaming interface.

Is psgi.streaming interface a requirement for the servers?

It is specified as SHOULD, so unless there is a strong reason not to implement the interface, all servers are encouraged to implement this interface.

However, if you implement a PSGI server using an Perl XS interface for the ultimate performance or integration with web servers like Apache or nginx, or implement a sandbox like environment (like Google AppEngine or Heroku) or distributed platform using tools like Gearman, you might not want to implement this interface.

That's fine, and in that case applications relying on the streaming interface can still use Plack::Middleware::BufferedStreaming to fallback to the buffered write on unsupported servers.

Why CGI-style environment variables instead of HTTP headers as a hash?

Most existing web application frameworks already have code or a handler to run under the CGI environment. Using CGI-style hash keys instead of HTTP headers makes it trivial for the framework developers to implement an adapter to support PSGI. For instance, Catalyst::Engine::PSGI is only a few dozens lines different from Catalyst::Engine::CGI and was written in less than an hour.

Why is PATH_INFO URI decoded?

To be compatible with CGI spec (RFC 3875) and most web servers' implementations (like Apache and lighttpd).

I understand it could be inconvenient that you can't distinguish foo%2fbar from foo/bar in the trailing path, but the CGI spec clearly says PATH_INFO should be decoded by servers, and that web servers can deny such requests containing %2f (since such requests would lose information in PATH_INFO). Leaving those reserved characters undecoded (partial decoding) would make things worse, since then you can't tell foo%2fbar from foo%252fbar and could be a security hole with double encoding or decoding.

For web application frameworks that need more control over the actual raw URI (such as Catalyst), we made the REQUEST_URI environment hash key REQUIRED. The servers should set the undecoded (unparsed) original URI (containing the query string) to this key. Note that REQUEST_URI is completely raw even if the encoded entities are URI-safe.

For comparison, WSGI (PEP-333) defines both SCRIPT_NAME and PATH_INFO be decoded and Rack leaves it implementation dependent, while fixing most of PATH_INFO left encoded in Ruby web server implementations.

http://www.python.org/dev/peps/pep-0333/#url-reconstruction http://groups.google.com/group/rack-devel/browse_thread/thread/ddf4622e69bea53f

SEE ALSO

WSGI's FAQ clearly answers lots of questions about how some API design decisions were made, some of which can directly apply to PSGI.

http://www.python.org/dev/peps/pep-0333/#questions-and-answers

MORE QUESTIONS?

If you have a question that is not answered here, or things you totally disagree with, come join the IRC channel #plack on irc.perl.org or mailing list http://groups.google.com/group/psgi-plack. Be sure you clarify which hat you're wearing: application developers, server implementors or middleware developers. And don't criticize the spec just to criticize it: show your exact code that doesn't work or get too messy because of spec restrictions etc. We'll ignore all nitpicks and bikeshed discussion.

AUTHOR

Tatsuhiko Miyagawa <miyagawa@bulknews.net>

COPYRIGHT AND LICENSE

version: 1.03
distribution: PSGI

NAME / 名前

PSGI - Perl Web Server Gateway Interface Specification / Perlウェブサーバーゲートウェイインターフェース仕様

ABSTRACT / 摘要

この文書は、ウェブサーバーと、Perlウェブアプリケーションまたはフレームワークとの間の、標準的なインターフェースを規定するものです。このインターフェースは、ウェブアプリケーションの移植性を増進し、ウェブアプリケーションフレームワーク開発者による重複した取り組みを低減するために設計されています。

PSGIがさらにもう一つの(Yet Another)ウェブアプリケーションフレームワークでないことに留意してください。PSGIはウェブアプリケーションフレームワークのコードをウェブサーバー環境から切り離すための仕様です。PSGIはまた、ウェブアプリケーションのAPIでもありません。ウェブアプリケーション開発者(エンドユーザー)は、PSGIインターフェースを直接用いて自身のウェブアプリケーションを実行するのではなく、その代わりにPSGIをサポートするフレームワークを使用することが推奨されます。

TERMINOLOGY / 用語

サーバー(Servers)

サーバー(Server)とは、HTTPリクエストを受け容れ、そのリクエストをウェブアプリケーションにディスパッチし、HTTPレスポンスをクライアントに返すウェブサーバーです。PSGI仕様の文脈に於いて、サーバーとは、HTTPサーバー内部で実行されているPerlのプロセス(例えばApache内のmod_perl)や、ウェブサーバーから呼ばれるデーモンプロセス(例えばFastCGIデーモン)や、ピュアPerlなHTTPサーバーに該当します。

アプリケーション(Applications)

アプリケーション(Applications)とは、HTTPリクエストを受け容れ、HTTPレスポンスを返すウェブアプリケーションです。PSGI仕様に於いて、アプリケーションとはコードリファレンスに該当します。

ミドルウェア(Middleware)

ミドルウェア(Middleware)とは、PSGIアプリケーション(コードリファレンス)であり、かつサーバー(Server)です。ミドルウェア(Middleware)は、サーバー(server)から呼ばれる際にはアプリケーション(application)のように見えて、他のアプリケーション(application)を順番に呼ぶことができます。これはPSGIアプリケーションを拡張するプラグイン(plugin)だと考えられます。

フレームワーク開発者(Framework developers)

フレームワーク開発者(Framework developers)とは、ウェブアプリケーションの作者です。その人達は、PSGIのインプットを受け取り、ウェブアプリケーションを実行し、サーバー(server)へPSGIレスポンスを返すアダプター(またはエンジン)を記述する必要があります。

ウェブアプリケーション開発者(Web application developers)

ウェブアプリケーション開発者(Web application developers)とは、ウェブアプリケーションフレームワークの上部でコードを書く人達です。これらの開発者は、PSGIを直接取り扱う必要が全くありません。

SPECIFICATION / 仕様

アプリケーション(Application)

PSGIアプリケーションはPerlのコードリファレンスです。これはきっかり1つの引数を環境に引き渡し、きっかり3つの値が入った配列リファレンスを返します。

my $app = sub {
    my $env = shift;
    return [
        '200',
        [ 'Content-Type' => 'text/plain' ],
        [ "Hello World" ], # またはIO::Handleのようなオブジェクト
    ];
};

環境(Environment)

環境(environment)は以下で詳述するCGIのようなヘッダーを含んだハッシュリファレンスでなければ「なりません」。アプリケーション(application)は環境を変更する必要がありません。環境は、それらが正常に空である場合を除いて、(PEP 333RackJSGIから借用した)3つのキーを含んでいなければ「なりません」

値としてブール値を求める環境のキー(environment key)は、その値をPerlのブール値でない記法に合わせなければ「なりません」。これはつまり、空文字と明示的な0の両方ともに正当な偽値であることを意味します。ブール値のキーが存在しない場合、アプリケーションはこれを偽値であるものとして取り扱うでしょう

(ピリオドで終わる名前が付けられていない)全てのCGIキーの値は、スカラー文字列でなくては「なりません」

詳細は以下を参照してください。

  • REQUEST_METHOD: "GET"や"POST"のようなHTTPリクエストメソッドです。これは空文字であっては「いけません」し、常に必須とされます。

  • SCRIPT_NAME: アプリケーションに相当する、リクエストURLのパス(path)の開始部分です。これはアプリケーションに対して仮想的な"場所(location)"を知らせます。アプリケーションがサーバーのルートURIに相当する場合、これは空文字であるでしょう。

    このキーが空でない場合は、それはフォワードスラッシュ/で始まっていなければ「なりません」

  • PATH_INFO: リクエスト対象の仮想的な"場所(location)"をアプリケーションに対して明示する、リクエストURLのパス(path)のリマインダーです。リクエストURLがアプリケーションルートを指し示していて、かつ、トレイリングスラッシュ(訳註: URLの末尾に付けるスラッシュのこと)がない場合、これは空文字であるでしょう。"/www.ietf.org/rfc/rfc3875" in RFC 3875http:に準拠するため、この値はサーバーによってURIデコードされているべきです。

    このキーが空でない場合は、それはフォワードスラッシュ/で始まっていなければ「なりません」

  • REQUEST_URI: デコードされていない、生のリクエストURL行です。 これはHTTPのGET /... HTTP/1.x行に現れている生のURIパスとクエリー部(query part)であり、URIスキーム(URI scheme)とホスト名(host name)は含んでいません。

    PATH_INFOと違って、この値はサーバーによってデコードされていては「いけません」PATH_INFOの代わりにこのキーを使うことを選んだならば、URLをアプリケーションハンドラーにマッピングするためにパスを適切にデコードするのは、アプリケーションの責務となります。

  • QUERY_STRING: リクエストURLの?があれば、それに後続する部分です。このキーは空で「ありえます」が、空であっても常に存在していなければ「なりません」

  • SERVER_NAME, SERVER_PORT: SCRIPT_NAMEPATH_INFOが兼ね備えられている場合、これらのキーはURLを完全なものにするために使えます。しかし、リクエストURLを再構築するためには、HTTP_HOSTが存在する場合にはSERVER_NAMEよりも優先して使うべきだということに留意してください。SERVER_NAMESERVER_PORT は空文字であっては「いけません」 し、常に必須とされます。

  • SERVER_PROTOCOL: クライアントからのリクエストの送信に用いられたプロトコルのバージョンです。一般的に、これは"HTTP/1.0"または"HTTP/1.1"のようなもので、アプリケーションがどのようにHTTPリクエストヘッダーを取り扱うかを決定するために用いられるでしょう。

  • CONTENT_LENGTH: 整数として表現される、コンテントのバイト長です。このキーの存否は、リクエスト内のHTTPのContent-Lengthヘッダーの存否に相当します。

  • CONTENT_TYPE: クライアントにより明示される、リクエストのMIME型です。このキーの存否は、リクエスト内のHTTPのContent-Typeヘッダーの存否に相当します。

  • HTTP_* キー群: これらのキーは、クライアントから供給されたHTTPリクエストヘッダーに相当します。これらのキーの存否は、リクエスト内の適切なHTTPのContent-Lengthヘッダーの存否に相当します。

    同一のキーを持つ複数のヘッダー行が存在する場合、サーバーは"/www.ietf.org/rfc/rfc2616" in RFC 2616http:に則って、それらが,によって結合されて1行として送られたかのように取り扱うでしょう。

この他に、PSGI環境は以下のPSGI特有のキーも含めなければ「なりません」:

  • psgi.version: このPSGIのバージョンを[1,0]のように表す、配列リファレンスです。第1の数字はメジャーバージョンで、第2の数字はマイナーバージョンです。

  • psgi.url_scheme: リクエストURLに依拠する、httpまたはhttpsという文字列です。

  • psgi.input: インプットストリーム(input stream)です。詳細は以下を参照してください。

  • psgi.errors: エラーストリーム(error stream)です。詳細は以下を参照してください。

  • psgi.multithread: アプリケーションが同一プロセスの他のスレッドから同時に呼び出される場合に真でなくては「ならず」、そうでない場合に偽であるという、ブール値です。

  • psgi.multiprocess: 同等のアプリケーションオブジェクトが他のプロセスから同時に呼び出される場合に真でなくては「ならず」、そうでない場合に偽であるという、ブール値です。

  • psgi.run_once: アプリケーションが、それが含まれるプロセスの一生の内、一度しか呼ばれないことをサーバーが予期している(が保証していない!)場合に真になるという、ブール値です。通常は、CGI(または同様のもの)ベースのサーバーでのみ真になります。

  • psgi.nonblocking: サーバーが非ブロッキングイベントループでアプリケーションを呼び出している場合に真になるという、ブール値です。

  • psgi.streaming: サーバーがコールバックスタイルの遅延レスポンス(delayed response)とライターオブジェクト(writer object)をサポートしている場合に真になるという、ブール値です。

PSGI環境は、以下の追加の拡張キーを含めることが「できます」:これらは任意のものであるので、アプリケーションとミドルウェアコンポーネントは、 使用前に環境にこれらが存在していることを検査しなければ「なりません」

  • psgix.logger: メッセージをロギングするための、コードリファレンスです。このコードリファレンスは、ロギングされるメッセージを表すハッシュリファレンスとして、1つの引数を渡されます。このハッシュリファレンスは、最低でもlevelmessageの2つのキーを含んでいなければ「ならず」levelは以下の文字列の中の一つでなければ「なりません」: debug, warn, info, errorおよび fatalmessageはプレーンな文字列か、文字列化されたスカラー変数スカラー変数でなければ「なりません」

  • psgix.session: セッションデータを保存・復元するための、ハッシュリファレンスです。このハッシュリファレンスを更新する際には、ミドルウェアコンポーネントが永続されていなければ「なりません」し、成功しているリクエストが復元されなければ「なりません」。セッションデータの永続化と復元の方法と、リクエスト元のクライアントを同定する方法のいずれも、実装固有のものです。

    psgix.session.options: セッションデータの操作方法をリクエスト後にミドルウェアコンポーネントへ伝えるための、ハッシュリファレンスです。受け容れ可能なキー・値は、実装固有のものです。

サーバーまたはアプリケーションは、自身のデータを環境に格納することもできます。これらのキーは、最低でも1つのドットを含んでいなければ「なりません」し、一意な接頭辞が付けられていなければなりません。

psgi.接頭辞はPSGIコア仕様が使うために予約されており、psgix.接頭辞は公式にブレスされた拡張のために予約されています。これらの接頭辞は、他のサーバーやアプリケーションによって使われては「なりません」

環境は、HTTP_CONTENT_TYPEまたはHTTP_CONTENT_LENGTHと名付けられたキーを含んでは「なりません」

SCRIPT_NAME またはPATH_INFOの1つはセットされていなければなりません。REQUEST_URI/である場合、PATH_INFO/であるべきで、SCRIPT_NAMEは空であるべきです。SCRIPT_NAME/であっては 「いけません」 が、空であることは「ありえます」

インプットストリーム(Input Stream)

psgi.inputにあるインプットストリームは、HTTP POSTまたはPUTの生のデータをストリーミングする、IO::Handleのようなオブジェクトです。これがファイルハンドルである場合、バイナリーモードでオープンされていなければ「なりません」。インプットストリームは、readメソッドに応答しなければ「ならず」seekメソッドを実装しても「よい」です。

Perlの組み込みファイルハンドルまたはIO::Handleベースのオブジェクトは、PSGIサーバー内でもあるがままに動くべきです。アプリケーション開発者は、このストリームの型やクラスを検査すべきでは「ありません」。その代わりに、readメソッドをこのオブジェクトでただ単に呼ぶ「べき」です。

アプリケーション開発者は、インプットストリームを読み込むために、Perlの組み込みread関数やイテレーター(<$fh>)を使う「べきではありません」。その代わりに、アプリケーション開発者は、ダックタイプが許可されているものとして、readをメソッドとして($fh->read)呼ぶべきです。

自身が触れられないアップストリームコードの内で組み込みのread()関数によってインプットストリームが使われることを知っているフレームワーク開発者は、この問題に対処するために、PerlIOまたはタイされたハンドルを使う「べき」です。

インプットストリームオブジェクトには、readメソッドが提供されていることが期待されます:

read
$input->read($buf, $len [, $offset ]);

実際に読み込んだ文字数を、ファイルの末尾であれば0を、エラーが発生していればundefを返します。

インプットストリームオブジェクトには、選択的なseekメソッドが実装されていることもあります。

seek
$input->seek($pos, $whence);

成功時には1を、そうでなければ0を返します。

メソッドの厳密な動作方法の詳細は、IO::Handleのドキュメンテーションをご覧ください。

エラーストリーム(Error Stream)

psgi.errors にあるエラーストリームは、エラーを印字するための、IO::Handleのようなオブジェクトです。エラーストリームは、printメソッドが実装されていなければなりません。

インプットストリームと同様に、Perlの組み込みファイルハンドルまたはIO::Handleベースのオブジェクトは、PSGIサーバー内でもあるがままに動くべきです。アプリケーション開発者は、このストリームの型やクラスを検査「すべきであはありません」。その代わりに、printメソッドをこのオブジェクトでただ単に呼ぶ「べき」です。

print
$errors->print($error);

成功時に真を返します。

レスポンス(Response)

アプリケーションは、3要素の配列リファレンスか、遅延/ストリーミングレスポンスのためのコードリファレンスのいずれかによって、レスポンスを返さなければ「なりません」

レスポンスの配列リファレンスは、以下の要素から成り立ちます:

ステータス(Status)

HTTPステータスコードです。これは100以上の整数でなければ「なりません」 し、RFC 2616に記述されているHTTPステータスコードでなければ「なりません」

ヘッダー(Headers)

ヘッダーは、(ハッシュリファレンスではなく)キー/値のペアから成る配列リファレンス「でなくてはなりません」。これはつまり、偶数個の要素を含んでいなければ「ならない」ということを意味しています。

ヘッダーには、Statusと名付けられたキーと、(※訳註: onrはnorの誤記? ご連絡済み http://twitter.com/gardejo/status/13853380264 )改行文字を名前に含むキーのいずれも、含んでは「なりません」-_で終わるどのキーも含んでは「なりません」

全てのキーは、アルファベット文字とアラビア数字と_-のみで成り立っていなければ「なりません」。全てのキーは、文字で始まっていなければ「なりません」。ヘッダーの値はスカラー文字列でなくてはなりません。値の文字列は、ASCII chr(32)(ホワイトスペース)のためのものを除いては、ASCII chr(37)(訳註: %)を含んでは「なりません」

同一のキー名が配列リファレンス内で複数回出現した場合には、それらのヘッダー行はクライアントに対して分割されて送られなければ「なりません」(例えば、Set-Cookie行)。

コンテント型(Content-Type)

Status が1xxや204や304であるためにcontent typeが存在して「ならない」場合を除いて、Content-Typeは存在していなければ「なりません」

コンテント長(Content-Length)

Statusが1xxや204や304である場合には、Content-Lengthは存在していては「なりません」

ステータスが1xxや204や304であるか、Content-Lengthヘッダーが存在していない場合には、PSGIサーバーはボディー(Body)を参照してコンテント長を計算するでしょう。この値はその後、アプリケーションによって返されるヘッダーのリストによって、付け加えられることがあります。

ボディー(Body)

レスポンスボディーは、アプリケーションによって、配列リファレンスかハンドルのいずれかの形で返されなければ「なりません」

  • ボディーが配列リファレンスである場合、それはボディーを作成する行の配列に相当することが期待されます。

    my $body = [ "Hello\n", "World\n" ];

    配列リファレンスの各要素の末尾の改行文字が「必須ではない」ことに注意してください。サーバーは、クライアントへ対して、それぞれの要素をあるがままに書き込まなくては「ならず」、行が改行文字で終わっているか否かを気にするべきでは「ありません」

    1つの値から成る配列リファレンスは、正当です。つまり、[ $html ]は正当なレスポンスボディーです。

  • ボディーは、Perlの組み込みファイルハンドルか、IO::Handleのようなオブジェクトのいずれかのハンドルであることもできます。

    open my $body, "</path/to/file";
    open my $body, "<:via(SomePerlIO)", ...;
    my $body = IO::File->new("/path/to/file");
    
    my $body = SomeClass->new(); # getline()とclose()メソッドを実装しているモッククラス

    サーバーは、ボディーの型やクラスを検査すべきでは「ありません」 。その代わりに、ボディーをイテレートするためのgetlineと、それが完了したときのためのcloseを、ただ単に呼ぶべきです。

    サーバーは、filenoおよびScalar::Util::reftypeを使って、ボディーが真のファイルハンドルであるか否かを検査「してもよい」です。ボディーが真のファイルハンドルである場合、サーバーは最適化のためにsendfile(2)のような技巧を凝らすことが「できます」

    ボディーオブジェクトは、pathメソッドに応答することも「できます」。このメソッドは、サーバーによってアクセス可能なファイルを示すパスを返すことが期待されています。これにより、ファイルを受け持つファイルデスクリプター番号(訳註: i-node番号など)の代わりに、この情報をサーバーが使用できるようになります。

    サーバーは、getlineメソッドを用いて$bodyからコンテントを読み込む場合には、$/特殊変数へバッファーサイズをセットしなければ「なりません」(訳註: $/は入力レコードセパレーター。デフォルトは\n。整数や、整数が入った変数へのリファレンスの場合には、当該バイト分を1レコードとして読み込みます)。これは、$/に整数へのリファレンス($/ = \8192)を設定することで行われます。

    ボディーのファイルハンドルがPerl組み込みのファイルハンドルであるIO::Handleオブジェクトである場合、この値が守られるでしょう。同じAPIを提供するオブジェクトは、上記と同様にこの特殊変数を守ることも「できます」が、そのような挙動は必須ではありません。

遅延レスポンス(Delayed Response)とストリーミングボディー(Streaming Body)

PSGIインターフェースにより、アプリケーションおよびサーバーは、上述の3要素の配列リファレンスの代わりに、コールバックスタイルのレスポンスを提供することもできます。これにより、遅延レスポンス(delayed response)と(サーバープッシュ型の)ストリーミングボディー(streaming body)を実現できます。

このインターフェースは、PSGIサーバーによって実装されていなければ「なりません」し、そのサーバーによってpsgi.streaming環境が真に設定されていなければ「なりません」

遅延レスポンスを有効にするためには、アプリケーションがレスポンスとしてコールバック関数を返さなければ「なりません」。アプリケーションは、psgi.streaming環境が真にセットされているかを検査「できます」し、真でなければ直接レスポンスへフォールバックさせることもできます。

このコールバックは、他のサブルーチンリファレンス(これは今後レスポンダー(responder)として参照されるようになります)をただ一つの引数として呼ばれるでしょう。レスポンダー(responder)は、標準的な3要素の配列リファレンスによるレスポンスを順番に呼ばなけばなりません。実例の最適な説明は以下の通りです:

my $app = sub {
    my $env = shift;

    # ネットワークからコンテントを取得している間、レスポンスを遅延させる
    return sub {
        my $responder = shift;

        fetch_content_from_server(sub {
            my $content = shift;
            $responder->([ 200, $headers, [ $content ] ]);
        });
    };
};

アプリケーションは、レスポンダー(responer)が呼ばれた場合、3番目の要素(ボディー)を無視「できます」。ボディーが無視される場合、レスポンダー(responder)は、writeメソッドとcloseメソッドを実装したさらにもう一つの(yet another)オブジェクトを返さなければ 「なりません」。これもまた実例が最も良い説明です。

my $app = sub {
    my $env = shift;

    # レスポンスが直ちに開始され、コンテントがストリーミングされる
    return sub {
        my $responder = shift;
        my $writer = $responder->([ 200, [ 'Content-Type', 'application/json' ]]);

        wait_for_events(sub {
            my $new_event = shift;
            if ($new_event) {
                $writer->write($new_event->as_json . "\n");
            } else {
                $writer->close;
            }
        });
    };
};

この遅延レスポンス(delayed response)とストリーミングAPIは、非ブロッキングI/Oベースのサーバーストリーミングや、ロングポール(long-poll)によるComet送信技術を実装したい場合に便利ですが、ブロッキングサーバーに於いて、バッファリングされていない書き込みを実装するために使うこともできます。

ミドルウェア(Middleware)

ミドルウェア(middleware)コンポーネントはPSGIアプリケーションを取得し、それを実行します。サーバーの観点からすれば、ミドルウェアコンポーネントはPSGIアプリケーションに該当します。ミドルウェアコンポーネントによって起動されるアプリケーションの観点からすれば、ミドルウェアはサーバーに該当します。これらは一般的に、PSGI環境ハッシュ上での幾つかの種類の前処理や、レスポンスの後処理を実装するために行われます。

以下に、PSGIアプリケーションに対して特別なHTTPヘッダーであるX-PSGI-Usedを付け加えるという、簡単な実例を掲げます。

# $appは単純なPSGIアプリケーションです
my $app = sub {
    my $env = shift;
    return [ '200', [ 'Content-Type' => 'text/plain' ], [ "Hello World" ] ];
};

# $xheader は$appを包み込むミドルウェアの一部です
my $xheader = sub {
    my $env = shift;
    my $res = $app->($env);
    push @{$res->[1]}, 'X-PSGI-Used' => 1;
    return $res;
};

ミドルウェアは、サーバーの観点から見てPSGIアプリケーションであるように、正確に振る舞わなければ「なりません」。ミドルウェアは、これまでに述べたストリーミングインターフェースをサポートしないことを決定「できます」が、理解できないレスポンスタイプを素通ししなければ「なりません」

CHANGELOGS / 更新履歴

バージョン1.1: 2010年2月某日

  • 拡張として、随意選択のPSGIキーを追加しました: psgix.loggerおよびpsgix.session

  • psgi.streamingはPSGIサーバーによって実装「されうる」のではなく、実装「されなければならない」ようになりました。

  • psgi.run_onceと、psgi.nonblockingと、psgi.streamingのPSGIキーは、PSGIサーバーによって設定「されなければならない」ようになりました。

  • ライターメソッド(writer methods)からpoll_cbを取り除きました。

ACKNOWLEDGEMENTS / 謝辞

この仕様のいくらかの部分は下記の仕様から借用されています。

私はこれらの素晴らしい文書の作者に感謝したいと思います。

AUTHOR / 作者

Tatsuhiko Miyagawa <miyagawa@bulknews.net>

CONTRIBUTORS / 貢献者

以下の人々は、コードのコミットや、パッチの送付や、バグの報告や、質問や、有用な助言の提案や、粗探しや、IRCでのチャットや、私のブログへのコメントで、PSGI仕様とPlack実装へ貢献してくれました(順不同):

Tokuhiro Matsuno
Kazuhiro Osawa
Yuval Kogman
Kazuho Oku
Alexis Sukrieh
Takatoshi Kitano
Stevan Little
Daisuke Murase
mala
Pedro Melo
Jesse Luehrs
John Beppu
Shawn M Moore
Mark Stosberg
Matt S Trout
Jesse Vincent
Chia-liang Kao
Dave Rolsky
Hans Dieter Pearcey
Randy J Ray
Benjamin Trott
Max Maischein
Slaven Rezić
Marcel Grünauer
Masayoshi Sekimura
Brock Wilcox
Piers Cawley
Daisuke Maki
Kang-min Liu
Yasuhiro Matsumoto
Ash Berlin
Artur Bergman
Simon Cozens
Scott McWhirter
Jiro Nishiguchi
Masahiro Chiba
Patrick Donelan
Paul Driver
Florian Ragwitz

COPYRIGHT AND LICENSE / 著作権とライセンス

訳註: 原文にはこのセクションの内容が記述されていません。

Copyright Tatsuhiko Miyagawa, 2009.

このドキュメントは、クリエイティブ・コモンズ・ライセンス「by-sa」によってライセンスされています。

The original text: This document is licensed under the Creative Commons license by-sa.

@gardejo
Copy link
Author

gardejo commented May 17, 2010

I, gardejo, had executed a git remote add http://git.coderepos.org/share/lang/perl/PSGI-Doc-JA.git command.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment