Plackとhtmlのvideoタグを一緒に使う場合に気をつける事


時代もhtml5になって、音声とか動画であればブラウザの標準機能だけで再生できる時代になって久しいですが、認証をつけたいとかの理由でvideoタグに使うファイルの配信をPlackでやるときの注意点みたいなの。


# htmlのvideoタグの動作

src属性に指定したコンテンツを一括で取りにいって再生しているのかと思いきや違って以下の動作をする。

src属性のファイルに対してRangeヘッダをつけたリクエストを発行する
↓
`206(Partial Content)` が返ってくる（事を期待する）
↓
そのあと再度src属性のファイルを全部取ってくる


（206については[ここらへん](http://qiita.com/inokappa/items/1e20ddfcf2b9e8d5ad04)を参照。）



最初のリクエストでファイルの長さとかファイルの種類を取得して、その上で再生をするという仕組みではないかと思われる。 (要出典)


Plackではrangeリクエストが対応していないようで、一回目のヘッダにrangeが含まれるリクエストを投げても200を返される事となり、その結果videoタグで再生されない。


nginxなどのフルスタックなサーバであるとここらへんはしっかり実装されており、documentrootなどの静的なファイルを置く場所に置いて配信すると普通にvideoタグは動作する。


# いや、rangeに対応させる手段が無い訳ではない

どっちなんだよという話ですが。
Rangeに対応させるPlackのmidlewareは[Plack::Middleware::Static::Range](http://search.cpan.org/~audreyt/Plack-Middleware-Static-Range-0.01/)というのが存在する。

で、意気揚々と`cpanm Plack::Middleware::Static::Range`と打つ訳ですがエラーが出る訳です。

```
cpanm (App::cpanminus) 1.7001 on perl 5.020000 built for darwin-2level
Work directory is /Users/agile/.cpanm/work/1428416540.25616
You have make /usr/bin/make
You have LWP 6.06
You have /usr/bin/tar: bsdtar 2.8.3 - libarchive 2.8.3
You have /usr/bin/unzip
Searching Plack::Middleware::Static::Range on cpanmetadb ...
--> Working on Plack::Middleware::Static::Range
Fetching http://www.cpan.org/authors/id/A/AU/AUDREYT/Plack-Middleware-Static-Range-0.01.tar.gz
-> OK
Unpacking Plack-Middleware-Static-Range-0.01.tar.gz
Entering Plack-Middleware-Static-Range-0.01
Checking configure dependencies from META.yml
Checking if you have ExtUtils::MakeMaker 6.42 ... Yes (6.98)
Configuring Plack-Middleware-Static-Range-0.01
Running Makefile.PL
Cannot determine license info from lib/Plack/Middleware/Static/Range.pm
Warning: prerequisite PerlIO::subfile 0 not found.
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for Plack::Middleware::Static::Range
Writing MYMETA.yml and MYMETA.json
-> OK
Checking dependencies from MYMETA.json ...
Checking if you have parent 0 ... Yes (0.228)
Checking if you have ExtUtils::MakeMaker 6.42 ... Yes (6.98)
Checking if you have Plack::Middleware 0 ... Yes (undef)
Checking if you have PerlIO::subfile 0 ... No
Checking if you have HTTP::Message 0 ... Yes (6.06)
==> Found dependencies: PerlIO::subfile
Searching PerlIO::subfile on cpanmetadb ...
--> Working on PerlIO::subfile
Fetching http://www.cpan.org/authors/id/N/NW/NWCLARK/PerlIO-subfile-0.08.tar.gz
-> OK
Unpacking PerlIO-subfile-0.08.tar.gz
Entering PerlIO-subfile-0.08
Checking configure dependencies from META.yml
Configuring PerlIO-subfile-0.08
Running Makefile.PL
You need perl 5.7.1 or later, configured to use perlio (and not to use sfio)
Yes, this module is currently only suitable for the development branch of
perl

Note
  1: The features required for layers have only recently been added to
     development perl, so if your doesn't have the header perliol.h it's
     too old
  2: The perlio layers API has changed (at least once) between the initial
     version and 5.7.1, and may well change again. If so, it is quite
     possible that this module will fail to compile.
-> N/A
-> FAIL Configure failed for PerlIO-subfile-0.08. See /Users/agile/.cpanm/work/1428416540.25616/build.log for details.
-> FAIL Installing the dependencies failed: Module 'PerlIO::subfile' is not installed
-> FAIL Bailing out the installation for Plack-Middleware-Static-Range-0.01.
Expiring 7 work directories.
 
```


# Plack::App::File::Rangeの仕組み

`Plack::Middleware::Static::Range`内で使われている[Plack::App::File::Range](https://metacpan.org/source/AUDREYT/Plack-Middleware-Static-Range-0.01/lib/Plack/App/File/Range.pm#L31)内でこういうコードがあります。

```
open my $fh, "<:raw:subfile(start=$start,end=".($end+1).")", $file	
```

openの第三引数に `:moge` みたいな形式で指定するのはPerlIOの機能を使うためのものです。

ある程度のPerl使いなら`:encoding(sjis)` とか `:utf8` とか指定した事があるかと思いますが、それを自作するための仕組みがPerlIOなわけです。

で、上記では`:subfile`という標準でないPerlIOのレイヤーを使っている訳ですが、その実態は[PerlIO::subfile](https://metacpan.org/release/PerlIO-subfile)です。

`PerlIO::subfile`のSYNOPSISを見ると一目瞭然ですが

```
use PerlIO::subfile;
open FOO, "<:subfile(start=123,end=+43)", "bigfile" or die $!;
print while <FOO>; # Just prints the specified subsection of the file
seek (FOO, 0, SEEK_SET) # Takes you to offset 123 in bigfile.
```

あるファイルの取得したい範囲を`:subfile`レイヤーで指定すると、その範囲のみを取得するファイルハンドルを作成してくれるというPerlIOレイヤーなわけです。ファイルハンドルを返せばファイルハンドルの内容をcontentにしてくれるPlackの仕組みと相性がいい訳ですね。


# エラーの原因

PerlIOを使っていると分かったところで、先ほどのエラーログを見返してみるとこう出力されている訳です。

```
  1: The features required for layers have only recently been added to
     development perl, so if your doesn't have the header perliol.h it's
     too old
```

PerlIOは開発版のperlじゃねぇと使えねぇよバーーーーカwwwwwww

開発版のperlを本番環境で置く事は無いと思うのでつかえないというわけです。
おしまい。


# まとめ
Plackでは現在のところ、Rangeヘッダに対応していない。正し開発版のperlを使えば出来る事は出来る。


これをやりたかった背景として、取りためた動画やラジオをブラウザのみで見れるようにしたいというもくろみがあったのですが、この前ふとenclosureなRSSを書いてPodcastに読ませてみたら普通に音声を再生できたのでもういらなくなったという消極的理由もあったりはする。