<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[てくにかるむ]]></title><description><![CDATA[「エラーをなくすことは非常に有益で時には新しい真実や事実を作り上げるよりも勝る」
ー チャールズ・ダーウィン]]></description><link>http://multix.jp/</link><generator>Ghost 0.6</generator><lastBuildDate>Tue, 10 Mar 2026 19:33:19 GMT</lastBuildDate><atom:link href="http://multix.jp/tag/web/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[SOAP::Transport::HTTP がおかしい]]></title><description><![CDATA[<p>Perl 5.20.3 + SOAP::Lite ver1.20 で XQuery 投げるとなぜか落ちる。調査すると STDIN に対して Bareward エラーが。それでこういう場当たり的なパッチが必要になった。おいおい？</p>

<pre><code class="language-brush:perl gutter:true">--- C:\Perl64\site\lib\SOAP\Transport\HTTP.pm    2016-12-17 10:45:40.000000000 +0900
+++ D:\Perl64\site\lib\SOAP\Transport\HTTP.pm    2017-01-23 12:42:33.000000000 +0900
@@ -586,6 +586,</code></pre>]]></description><link>http://multix.jp/soap-lite-failed/</link><guid isPermaLink="false">59c25a8e-8ac7-4a28-bce5-3b3bf7ed3827</guid><category><![CDATA[めもらんだむ]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Fri, 24 Feb 2017 01:24:43 GMT</pubDate><content:encoded><![CDATA[<p>Perl 5.20.3 + SOAP::Lite ver1.20 で XQuery 投げるとなぜか落ちる。調査すると STDIN に対して Bareward エラーが。それでこういう場当たり的なパッチが必要になった。おいおい？</p>

<pre><code class="language-brush:perl gutter:true">--- C:\Perl64\site\lib\SOAP\Transport\HTTP.pm    2016-12-17 10:45:40.000000000 +0900
+++ D:\Perl64\site\lib\SOAP\Transport\HTTP.pm    2017-01-23 12:42:33.000000000 +0900
@@ -586,6 +586,7 @@
         #my $content = q{};
         if ( !$chunked ) {
             my $buffer;
+            no strict 'subs';
             binmode(STDIN);
             if ( defined $ENV{'MOD_PERL'} ) {
                 while ( read( STDIN, $buffer, $length ) ) {
</code></pre>

<p>直接原因はよくわからんがとにかくこうすれば正常動作はした。STDIN が <code>strict subs</code> に引っかかるって通常ではちょっと考えにくいところだが？</p>

<hr>]]></content:encoded></item><item><title><![CDATA[textareaでTAB入力]]></title><description><![CDATA[<p><code>&lt;textarea/&gt;</code> のフォーカス内でTABキーを押すと普通は次のform部品へフォーカスが移ってしまうが、これをTAB入力に変換する。フォーカス動作はキャンセルしなければならないので keydown イベントを使う。</p>

<hr>

<pre><code class="language-brush:javascript gutter:true">// textareaでのTABキー入力
$('textarea').on('keydown', function(e){
    if (e.keyCode === 9) {
        e.preventDefault();
        var elem = e.target;
        var val = elem.value;
        var pos = elem.selectionStart;
        elem.value = val.substr(0, pos) + '\t' + val.substr(pos, val.length);
        elem.setSelectionRange(pos + 1, pos</code></pre>]]></description><link>http://multix.jp/tab-input-textarea/</link><guid isPermaLink="false">f0beb3a4-5add-4f20-85b1-c2f21b70f871</guid><category><![CDATA[めもらんだむ]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Mon, 20 Feb 2017 09:31:33 GMT</pubDate><content:encoded><![CDATA[<p><code>&lt;textarea/&gt;</code> のフォーカス内でTABキーを押すと普通は次のform部品へフォーカスが移ってしまうが、これをTAB入力に変換する。フォーカス動作はキャンセルしなければならないので keydown イベントを使う。</p>

<hr>

<pre><code class="language-brush:javascript gutter:true">// textareaでのTABキー入力
$('textarea').on('keydown', function(e){
    if (e.keyCode === 9) {
        e.preventDefault();
        var elem = e.target;
        var val = elem.value;
        var pos = elem.selectionStart;
        elem.value = val.substr(0, pos) + '\t' + val.substr(pos, val.length);
        elem.setSelectionRange(pos + 1, pos + 1);
    }
});
</code></pre>

<ul>
<li><code>e.keyCode === 9</code> <br>
TABキー押し下げを検知する。</li>
<li><code>e.preventDefault()</code> <br>
ブラウザのデフォルト動作を停止する。つまりフォーカス移動をキャンセルする。</li>
<li><code>e.target</code> <br>
イベント対象のDOMエレメントを取得する。すなわち <code>&lt;textarea/&gt;</code> 本体。jQueryの <code>$(this)[0]</code> におなじ。</li>
<li><code>elem.value</code> <br>
入力されている <code>&lt;textarea/&gt;</code> の内容。</li>
<li><code>elem.selectionStart</code> <br>
キャレットカーソルの位置。次の行でこの位置に<code>\t</code>を挿入して書き戻している。</li>
<li><code>elem.setSelectionRange(Start, End, [Direction])</code> <br>
キャレットを<code>\t</code>の後ろへ進める。選択範囲は無いので <em>Start</em> と <em>End</em> にはおなじ値を指定する。</li>
</ul>

<p>なお <em>selectionStart/selectionEnd</em> プロパティは直接書き換えることもできる。またキャレット選択範囲の後方は <em>elem.selectionEnd</em> で得られる。なので選択範囲があるなら丸ごとインデントするといったコードも書けなくはない。選択方向を指示する Direction はプラットフォームによって挙動が若干異なるので<a href="http://www.html5.jp/tag/elements/forms-textFieldSelection.html">こちら</a><sup id="fnref:1"><a href="http://multix.jp/tab-input-textarea/#fn:1" rel="footnote">1</a></sup>も参照のこと。</p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p><a href="http://www.html5.jp/tag/elements/forms-textFieldSelection.html">http://www.html5.jp/tag/elements/forms-textFieldSelection.html</a> <a href="http://multix.jp/tab-input-textarea/#fnref:1" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[IIS用FastCGI簡易ラッパー]]></title><description><![CDATA[<p><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/">前項</a>でIIS用FastCGIについて述べた件の続き。</p>

<hr>

<p>モジュールマップ登録では個々の.fcgiファイルについて1エントリが必要とされるため、拡張子.fcgi全体をひとつのモジュールマップで済ませるための簡単なラッパーを書いてみた。ものすごく単純だが用法を誤らなければ相応に使える。</p>

<pre><code class="language-brush:perl gutter:true title:fcgi.pl">use strict;  
use warnings;  
use FCGI;  
use File::Spec;  
open STDERR, "&gt;&amp;", STDOUT;  
my $request = FCGI::Request or die;  
while ( $request-&gt;Accept &gt;= 0 ) {  
    my $stderr = '';
    eval {
        local $SIG{__WARN__} = sub { $stderr .= shift };
        local $SIG{__DIE__} = sub { die($stderr = shift)</code></pre>]]></description><link>http://multix.jp/iis-fastcgi-easy-wrapper/</link><guid isPermaLink="false">d447898a-80b8-401a-aab0-e76227fcb7f8</guid><category><![CDATA[Windows]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Mon, 20 Feb 2017 09:25:55 GMT</pubDate><content:encoded><![CDATA[<p><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/">前項</a>でIIS用FastCGIについて述べた件の続き。</p>

<hr>

<p>モジュールマップ登録では個々の.fcgiファイルについて1エントリが必要とされるため、拡張子.fcgi全体をひとつのモジュールマップで済ませるための簡単なラッパーを書いてみた。ものすごく単純だが用法を誤らなければ相応に使える。</p>

<pre><code class="language-brush:perl gutter:true title:fcgi.pl">use strict;  
use warnings;  
use FCGI;  
use File::Spec;  
open STDERR, "&gt;&amp;", STDOUT;  
my $request = FCGI::Request or die;  
while ( $request-&gt;Accept &gt;= 0 ) {  
    my $stderr = '';
    eval {
        local $SIG{__WARN__} = sub { $stderr .= shift };
        local $SIG{__DIE__} = sub { die($stderr = shift) };
        my $file = File::Spec-&gt;catfile(
            $ENV{DOCUMENT_ROOT} // 'Undefined',
            $ENV{SCRIPT_NAME} // 'index.fcgi'
        );
        return print "Status: 404\n\n" unless -f $file;
        do $file;
    };
    $stderr = "$@" if $@;
    if ($stderr) {
        print "Status: 403\nContent-Type: text/plain\n\n", $stderr;
    }
}
1;  
__END__  
</code></pre>

<p>このラッパーを要求パス *.fcgi に対して適用する<sup id="fnref:1"><a href="http://multix.jp/iis-fastcgi-easy-wrapper/#fn:1" rel="footnote">1</a></sup>と、ドキュメントルート以下の FastCGIファイルについて <strong>半常駐化</strong>状態で実行されるようになる。PATH_INFO は期待どうり動作するし、 <em>既定のドキュメント</em> に index.fcgi を追加した場合にも対応する。ただしあくまでも簡易的に、だ。</p>

<p>このラッパーがなぜ半常駐化なのかというと、常駐対象になるのが、特殊変数および <code>our</code> 宣言<sup id="fnref:2"><a href="http://multix.jp/iis-fastcgi-easy-wrapper/#fn:2" rel="footnote">2</a></sup>されたパッケージ変数と <code>use</code> または <code>require</code> で読み込まれたモジュールに限定されているからだ。.fcgi ファイル自体は <code>do $file</code> の形式で読込実行されているが、これはこの命令が実行される度にファイルアクセスが発生し、コードが再コンパイル<sup id="fnref:3"><a href="http://multix.jp/iis-fastcgi-easy-wrapper/#fn:3" rel="footnote">3</a></sup>されることによる。このとき <code>do $file</code> は <code>package main</code> 名前空間内で実行されているため、複数の .fcgi で同名のグローバル変数や関数宣言が共有されることになる。</p>

<p>.fcgi 自体は毎回再コンパイルされるが、その中で使われる use モジュールについてはコンパイル済コードが再利用されるため、その使用比率が大きいコードほどメリットがある。一方で名前空間のファイル別隔離処理を端折っているため同名関数は後続のコード実行で上書きされてしまうため注意を要する。このあたりが簡易版と称する所以だ。</p>

<p>エラー処理について言えば、FastCGI 起動前に STDERR を STDOUT にリダイレクトすることで、<code>do $file</code> 実行時の構文エラーが極力 IIS Worker Process へ渡らないように細工している。<code>$SIG{}</code> の定義も同様に STDERR を漏らさないためだ。これを怠ると IIS が <em>503 Service Unavailable</em> を返すようになり、かつ、サーバ自体を再起動しなければ回復できない<sup id="fnref:4"><a href="http://multix.jp/iis-fastcgi-easy-wrapper/#fn:4" rel="footnote">4</a></sup>ことがままある。</p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>このラッパーそのものはドキュメントルートの外に配置する。また拡張子も.fcgi以外（.pl）とするべきだ。またこのラッパー自体は完全常駐状態となるため、リロードするためには IIS Worker Process を終了させなければならない。 <a href="http://multix.jp/iis-fastcgi-easy-wrapper/#fnref:1" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:2"><p>our $count //= 0; などと書けば、未定義状態の初回ロード時だけ初期化される。レキシカル変数については eval $EXPR と違って異なるスコープとなる。よって my 宣言は重複しても影響がない。 <a href="http://multix.jp/iis-fastcgi-easy-wrapper/#fnref:2" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:3"><p>require は再ロードも再コンパイルもしないが、再実行もしない。再コンパイルせずに再実行だけ行う関数はない。 <a href="http://multix.jp/iis-fastcgi-easy-wrapper/#fnref:3" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:4"><p>IISサービス（W3SVC）を再起動するだけでは回復しない。$stderr の出力時に <em>Status: 500</em> としていないのも同じ理由による。これだけ念を入れても不用意な実行時エラー（よくあるのは再入不能モジュールの実行時エラー。例えば binmode Encoding関係や Win32関係など）でサービス停止に追い込まれる当たり、IIS の FastCGI 環境は結構繊細なので、エラーに無頓着なタイプの開発者には全く向いていない。 <a href="http://multix.jp/iis-fastcgi-easy-wrapper/#fnref:4" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[IIS8+FastCGI+ActivePerl+Mojolicious::Lite]]></title><description><![CDATA[<p>最近のWindows IISでMojolicious::Liteを動かそうとすると、普通はCGIモードしか使えなくて全く実用的なパフォーマンスが確保できない。これは双方が互いを全くサポートしていないが故だが、これはそれをどうにかしてしまおうというメモ。</p>

<ul class="index"></ul>

<h3 id="iiscgi">IISのCGIセットアップ</h3>

<p>まず普通にIISでPerl CGIを動かすための手順。一応 Windows 10/Windows Server 2012 と ActivePerl 5.20(64bit) を前提にしているが、他の組み合わせでも大差はない。</p>

<p><strong>コントロールパネル＞プログラム＞プログラムと機能＞Windowsの機能の有効化または無効化</strong>と辿ってダイアログを開き、<strong>インターネットインフォメーションサービス</strong>にチェックを付ける。更にそのサブメニューを開いて<strong>World Wide Webサービス＞アプリケーション開発機能>CGI</strong>にチェックを付ける。最低限これだけを有効化すれば要は足りるからOKを押して変更を確定する。初回導入時についてはサーバ再起動は必要ない。<sup id="fnref:1"><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fn:1" rel="footnote">1</a></sup></p>

<p><img src="http://multix.jp/content/images/2015/12/2015-12-27-10-48-22.png" alt=""></p>

<p>ActivePerl(64bit)が既にインストールされており、パスが通っているならコマンドプロンプトを管理者権限で開いて以下のコマンドをタイプする。</p>

<pre><code class="language-brush:plain">ap-iis-config add all --site 1 --cgi  
</code></pre>

<p>するとIISマネージャーの<strong></strong></p>]]></description><link>http://multix.jp/iis8-fastcgi-mojolicious-lite/</link><guid isPermaLink="false">51d12cb6-9839-4b14-b74d-53400cfcb7e2</guid><category><![CDATA[Windows]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Sun, 27 Dec 2015 04:33:31 GMT</pubDate><content:encoded><![CDATA[<p>最近のWindows IISでMojolicious::Liteを動かそうとすると、普通はCGIモードしか使えなくて全く実用的なパフォーマンスが確保できない。これは双方が互いを全くサポートしていないが故だが、これはそれをどうにかしてしまおうというメモ。</p>

<ul class="index"></ul>

<h3 id="iiscgi">IISのCGIセットアップ</h3>

<p>まず普通にIISでPerl CGIを動かすための手順。一応 Windows 10/Windows Server 2012 と ActivePerl 5.20(64bit) を前提にしているが、他の組み合わせでも大差はない。</p>

<p><strong>コントロールパネル＞プログラム＞プログラムと機能＞Windowsの機能の有効化または無効化</strong>と辿ってダイアログを開き、<strong>インターネットインフォメーションサービス</strong>にチェックを付ける。更にそのサブメニューを開いて<strong>World Wide Webサービス＞アプリケーション開発機能>CGI</strong>にチェックを付ける。最低限これだけを有効化すれば要は足りるからOKを押して変更を確定する。初回導入時についてはサーバ再起動は必要ない。<sup id="fnref:1"><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fn:1" rel="footnote">1</a></sup></p>

<p><img src="http://multix.jp/content/images/2015/12/2015-12-27-10-48-22.png" alt=""></p>

<p>ActivePerl(64bit)が既にインストールされており、パスが通っているならコマンドプロンプトを管理者権限で開いて以下のコマンドをタイプする。</p>

<pre><code class="language-brush:plain">ap-iis-config add all --site 1 --cgi  
</code></pre>

<p>するとIISマネージャーの<strong>サイト＞Default Web Site＞機能ビュー＞ハンドラーマッピング</strong>に以下のスクリプトマップが登録されているはずだ。</p>

<p><img src="http://multix.jp/content/images/2015/12/2015-12-27-12-17-27.png" alt=""></p>

<p>復数のサイトが作られている場合、個別にこのスクリプトマップが必要になる。前述の<mark>--site 1</mark>の数字がサイトの登録番号を示しているので、これを変更しながら必要なだけ繰り返す。あるいはこのダイアログ画像を参考に手動でスクリプトマップを追加する。</p>

<p>なお32bit版のActivePerlでは、ISAPIを使用することもできる。Windows機能ダイアログで<strong>ISAPIフィルター</strong>と<strong>ISAPI拡張</strong>にもチェックをつけていれば、以下のコマンドでISAPIのハンドラーが作成できる。<sup id="fnref:2"><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fn:2" rel="footnote">2</a></sup></p>

<pre><code class="language-brush:plain">ap-iis-config add all --site 1 --cgi --isapi  
</code></pre>

<p>以上で <em>.pl ファイルはCGIとして認識されるようになる。</em>.cgi ファイルを扱いたい場合は手動でスクリプトハンドラーを追加しよう。<sup id="fnref:3"><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fn:3" rel="footnote">3</a></sup></p>

<h4 id="fastcgi">FastCGIを有効化する</h4>

<p>FastCGIも同様にハンドラーマッピングを追加することで有効化できるが、ひとつのスクリプトに対してひとつのモジュールマップエントリが必要になる。例えば以下のスクリプトを登録してみよう。<sup id="fnref:4"><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fn:4" rel="footnote">4</a></sup></p>

<pre><code class="language-brush:perl gutter:true title:count.fcgi">use strict;  
use warnings;  
use FCGI;

my $count = 0;  
my $request = FCGI::Request();  
while ( $request-&gt;Accept &gt;= 0 ) {  
    print "Content-type: text/html\r\n\r\n", ++$count;
}
</code></pre>

<p>このファイルは通常のURIで辿れる場所に存在しなければならない。つまり<em>C:¥inetpub¥wwwroot</em>以下以外にあるのならば、仮想ディレクトリパスが通っている必要がある。そうした上で次のようにモジュールマップを登録する。</p>

<p><img src="http://multix.jp/content/images/2015/12/2015-12-27-12-44-28.png" alt=""></p>

<p>要求パス欄は実ファイル名と一致<sup id="fnref:5"><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fn:5" rel="footnote">5</a></sup>させなければならない。モジュール欄はドロップダウンからFastCgiModuleを選択する。名前欄は同一サイト内で重複しなければ自由だ。そして実行可能ファイル欄に<strong>Perlの実行フルパス名</strong>と<strong>スクリプトのフルパス名"</strong>を半角パイプ記号で続けて記入する。なぜパイプで繋げるのかは解らないがこのように書かなければならない。これで<em>OK</em>を押すとハンドラを登録してよいか警告ダイアログが出るので<em>はい</em>で応答する。</p>

<p>正しくFastCGIハンドラが登録されていれば、このスクリプトをブラウザで開けば（例えば localhost/test/count.fcgi）カウンタが表示されるだろう。Ctrl+RやF5でリロードすればカウントアップが進むはずだ。</p>

<p>なおスクリプトを書き換えたあとは、実行中のFastCGIハンドラを再起動しなければならない。手っ取り早いのはタスクマネージャのプロセス一覧から、<em>IIS Worker Process</em> を探して <em>タスクの終了</em> をさせてしまう方法だ。こうすればすぐに再度1からカウントをやり直す。</p>

<h4 id="mojoliciouslitefastcgi">Mojolicious::LiteアプリをFastCGIで実行する</h4>

<p>Mojoliciousは、その開発当初はともかく現在はIISをサポートしていない。だが前述のもっとも単純な FastCGIスクリプトを見ればわかるように、Requestループの中で普通にCGIスクリプトを利用すれば、そのままFastCGIプロセス <em>(IIS Worker Process)</em> として永続化される。かといってループ内で Mojolicious を呼び出してもそのままでは正常に動作しない。app->start が IIS/CGIであるかどうかを正しく認識できないためだ。そこで次のようなコードにする。<sup id="fnref:6"><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fn:6" rel="footnote">6</a></sup></p>

<pre><code class="language-brush:perl gutter:true title:apps.fcgi">use strict;  
use warnings;

use Mojolicious::Lite;

plugin 'basic_auth';

my $count = 0;

get '/' =&gt; sub { $_[0]-&gt;render(text =&gt; 'Non restricted area' . ++$count); };

under sub {  
    my ($c) = shift;
    return $c-&gt;basic_auth(
        realm =&gt; sub {
            if ( @_ &amp;&amp; ( "@_" eq 'foo bar' ) ) {
                $c-&gt;req-&gt;env-&gt;{REMOTE_USER} = $_[0];
                return 1;
            }
        }
    );
};

get '/rest' =&gt; sub {  
    $_[0]-&gt;render(text =&gt; 'Hello ' . $_[0]-&gt;req-&gt;env-&gt;{REMOTE_USER} . ++$count . join(",", @ARGV));
};

if ($ENV{APP_POOL_CONFIG}) {  
    use FCGI;
    my $request = FCGI::Request();
    while ( $request-&gt;Accept &gt;= 0 ) {
        app-&gt;start("cgi");
    }
}
else {  
    app-&gt;start();
}
</code></pre>

<p>これはIIS固有の環境変数が存在するならば、FCGI::Requestループ実行に切り替えるようにしている。そうでなければ、つまりコマンドプロンプトで実行すれば、従来通りの挙動となる。こうしておいたうえで、FastCGIハンドラには次のように指定する。</p>

<pre><code class="language-brush:plain">C:¥Perl64¥bin¥perl.exe|C:¥test¥apps.fcgi cgi -m production  
</code></pre>

<p>ここでCGI形式で実行することを強要し、かつproductionモードを強制する。このモード指定がないと developmentモードで実行されることになり、標準出力に非HTTPプロトコルな出力が混じってしまうので、結果IISはInternal Server Errorを返すことになってしまう。</p>

<p>apps.fcgiのある場所に logディレクトリを掘っておくとそこにログが吐き出されるので、モード指定がなくても一見問題が解決できるように見える。だが実運用段階ではこのログを定期的に削除する必要に迫られるので、ここではその方法は取らない。ログファイルは IIS Worker Process が握っているので、このプロセスを終了させないとログファイルを削除することができないのだ。他には IISの環境変数に MOJO_MODE を追加して回避する手段があるが、今回は設定が一箇所ですむ手法を取り上げた。<sup id="fnref:7"><a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fn:7" rel="footnote">7</a></sup></p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>機能を無効化した際はアンインストールのために再起動が必要になる。 <a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fnref:1" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:2"><p>64bit版ではISAPIはサポートされなくなった。もっともこれを使う機会じたいが滅多にないので、困る／困ったという話もおよそ聞いたことがない。 <a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fnref:2" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:3"><p>Strawberry Perlや Cygin Perl、その他CGIで使えるものはなんであれこのように手動で登録する。 <a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fnref:3" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:4"><p>mod_rewrite的な仮想パス機能ではないので、/などを指定することはできない。 <a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fnref:4" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:5"><p>FCGIモジュールは標準インストールされていないので事前に <em>ppm install FCGI</em> を実行しておく必要がある。 <a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fnref:5" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:6"><p>このサンプルはBASIC認証プラグインのテストも兼ねている。このプラグインは <em>ppm install Mojolicious-Plugin-BasicAuth</em> でインストールできる。ブラウザで apps.fcgi/rest を開くと認証ダイアログが出るのでユーザ名:foo パスワード:bar を入力してみて欲しい。 <a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fnref:6" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:7"><p>環境変数切り替えでは一見、復数のモードを切り替えて使い分けられるように見える。だがUnix環境と違ってIISマネージャをいちいち立ち上げないとその操作すら覚束ないのでメリットがない。結局必要なだけ個別のモジュールハンドラを登録してURIで使い分けたほうが簡単だったりする。 <a href="http://multix.jp/iis8-fastcgi-mojolicious-lite/#fnref:7" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[日本国内限定IPv4アクセス制限の仕込み方]]></title><description><![CDATA[<p>Webサービスとかやってると世界中と繋がっているのだなと思うことは多々ある。だがサービス内容によってはその範囲を狭めたいこともあるし、それ以前にお断りということもある。ではどうやって日本国内からのIPアクセスとそれ以外を区別しよう？</p>

<ul class="index"></ul>

<hr>

<h4 id="ipapnic">IPアドレスリストをAPNICから入手する</h4>

<p>世界中に分配されているIPv4アドレスは総元締めのIANAから、世界を分割支配する5つの団体（ARIN/APNIC/LACNIC/AfriNIC/RIPE NCC）に分配され、そこからさらに各国別の元締め（日本ならJPNIC）に再分配されている。このIP分配リストは各団体のFTPサイト等で公開されているので、これを入手して日本にアサインされたIPリストだけを抽出すればACL（アクセス制限リスト）の元を作ることが出来る。今回の場合はJPNICに割り当てられたIPリストを得るために、APNICのFTPサイトから更新リストを入手する。</p>

<p>この更新リスト<sup id="fnref:1"><a href="http://multix.jp/delegated-apnic-acl/#fn:1" rel="footnote">1</a></sup>はほぼ毎日アップデートされている。ファイルサイズは1.8MBほどで約4万行弱ある。また世界中からダウンロードに来ることもあって、日に何回もアクセスにゆくと回数制限に引っ掛かって数時間ブロックされるということもある。故に基本的には1日1回、cronを使用してダウンロードすることになる。</p>

<p>一方で毎日更新されているとはいえ、日本にアサインされたIPが日々こまめに変化しているというわけでもない。だいたい週に1〜2回程度、僅かな追加と削除<sup id="fnref:2"><a href="http://multix.jp/delegated-apnic-acl/#fn:2" rel="footnote">2</a></sup>が発生している程度だ。そこでダウンロードと共に前回のリストと比較して変化があったらメール通知を行い、さらにACLの更新処理を行うという仕組みを作ることにする。</p>

<p>なお以下の仕掛けはすべてrootユーザが行い、メインのワークエリアとしては<code>/etc/rc.d</code>を使用するものとする。</p>

<pre><code class="language-brush:bash gutter:true title:/etc/cron.daily/apnic.list_update">#!/bin/sh

### /etc/</code></pre>]]></description><link>http://multix.jp/delegated-apnic-acl/</link><guid isPermaLink="false">0bd8198c-10c5-4c3d-af4d-0aed7c19e87c</guid><category><![CDATA[てくにかるむ]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Wed, 13 May 2015 09:08:40 GMT</pubDate><content:encoded><![CDATA[<p>Webサービスとかやってると世界中と繋がっているのだなと思うことは多々ある。だがサービス内容によってはその範囲を狭めたいこともあるし、それ以前にお断りということもある。ではどうやって日本国内からのIPアクセスとそれ以外を区別しよう？</p>

<ul class="index"></ul>

<hr>

<h4 id="ipapnic">IPアドレスリストをAPNICから入手する</h4>

<p>世界中に分配されているIPv4アドレスは総元締めのIANAから、世界を分割支配する5つの団体（ARIN/APNIC/LACNIC/AfriNIC/RIPE NCC）に分配され、そこからさらに各国別の元締め（日本ならJPNIC）に再分配されている。このIP分配リストは各団体のFTPサイト等で公開されているので、これを入手して日本にアサインされたIPリストだけを抽出すればACL（アクセス制限リスト）の元を作ることが出来る。今回の場合はJPNICに割り当てられたIPリストを得るために、APNICのFTPサイトから更新リストを入手する。</p>

<p>この更新リスト<sup id="fnref:1"><a href="http://multix.jp/delegated-apnic-acl/#fn:1" rel="footnote">1</a></sup>はほぼ毎日アップデートされている。ファイルサイズは1.8MBほどで約4万行弱ある。また世界中からダウンロードに来ることもあって、日に何回もアクセスにゆくと回数制限に引っ掛かって数時間ブロックされるということもある。故に基本的には1日1回、cronを使用してダウンロードすることになる。</p>

<p>一方で毎日更新されているとはいえ、日本にアサインされたIPが日々こまめに変化しているというわけでもない。だいたい週に1〜2回程度、僅かな追加と削除<sup id="fnref:2"><a href="http://multix.jp/delegated-apnic-acl/#fn:2" rel="footnote">2</a></sup>が発生している程度だ。そこでダウンロードと共に前回のリストと比較して変化があったらメール通知を行い、さらにACLの更新処理を行うという仕組みを作ることにする。</p>

<p>なお以下の仕掛けはすべてrootユーザが行い、メインのワークエリアとしては<code>/etc/rc.d</code>を使用するものとする。</p>

<pre><code class="language-brush:bash gutter:true title:/etc/cron.daily/apnic.list_update">#!/bin/sh

### /etc/rc.d/apnic.list_update
###
### delegated-apnic-latest を取得して変化があれば
### メール通知およびrc.iptablesの再実行を行うスクリプト
###
### /etc/cron.daily からsymlinkを張るか、そこに配置すること
###

new=`mktemp`  
errors=`mktemp`  
DIR="/etc/rc.d"  
list="$DIR/delegated-apnic-latest"

test -f $list || touch $list  
wget -q -O - "http://ftp.apnic.net/stats/apnic/delegated-apnic-latest" &gt; $new 2&gt; $errors

if [ $? -eq 0 ]; then  
    sort_new=`mktemp`
    sort_old=`mktemp`
    diff_out=`mktemp`

    # 日本国内IPについてのみ比較する
    /bin/grep '^apnic|JP|ipv4|' $new &gt; $sort_new
    /bin/grep '^apnic|JP|ipv4|' $list &gt; $sort_old
    diff --ignore-matching-lines="^#" $sort_new $sort_old &gt; $diff_out
    if [ $? -ne 0 ]; then
        (
         echo '-------------------- old delegated-apnic-latest --------------------'
         grep -P '^\d' $list
         echo
         echo '-------------------- new delegated-apnic-latest --------------------'
         grep -P '^\d' $new
         echo '---------------------- difference ----------------------'
         cat $diff_out

         # メールは rootにだす（ /etc/aliases に従う）
        ) | mail -s "delegated-apnic-latest updated $(hostname)" root
        cp -f $new $list

        # iptables の -s に渡せる形式に変換
        $DIR/jponly.pl &lt; $list &gt; $DIR/jponly.list

#        # rc.iptables を再実行する
#        \time $DIR/rc.iptables

         # iptables-save/restoreで更新する
         $DIR/update_mangle.pl
    fi
    rm -f $sort_new $sort_old $diff_out
else  
    cat $errors | mail -s "delegated-apnic-latest update check error $(hostname)" root
fi  
rm -f $new $errors  
</code></pre>

<p>このスクリプトは1日1回、APNICにお伺いを立てて（日本に割り当てられた）IPv4アドレスリストが変化していたら<code>/etc/rc.d/delegated-apnic-latest</code>ファイルを更新する。通知メールはrootに向けて投げるため、<code>/etc/aliases</code>に実際の通知先メールアドレスを書いておこう。</p>

<p>取得したリストの中身は概ね以下のような行の羅列である。</p>

<pre><code class="language-brush:plain">apnic|CN|ipv4|223.214.0.0|131072|20100803|allocated  
apnic|JP|ipv4|223.216.0.0|262144|20100712|allocated  
apnic|CN|ipv4|223.220.0.0|131072|20100723|allocated  
apnic|KR|ipv4|223.222.0.0|65536|20100721|allocated  
apnic|JP|ipv4|223.223.0.0|32768|20100803|allocated  
apnic|IN|ipv4|223.223.128.0|8192|20100809|allocated  
</code></pre>

<p>バーチカルラインで区切られた各カラムは次の意味を持つ。</p>

<ul>
<li>IP管理組織</li>
<li>2文字の国コード</li>
<li>IP種別</li>
<li>割り当てブロック先頭のIPアドレス</li>
<li>割り当てブロックに含まれるIP数（2の冪乗に一致）</li>
<li>割り当てられた日付</li>
<li>現在のステータス</li>
</ul>

<p>注意するのは5カラム目のIP数がIPマスクでもCIDRでもないという点で、ACLとして使用するにはこれをCIDRに変換する必要がある。これを行っているのが43行目の<code>jponly.pl</code>だ。</p>

<hr>

<h4 id="ipcidr">日本国内IPのCIDRを抽出する</h4>

<p>このフィルタスクリプトは、国コードがJPかつステータスがallocatedの行を抽出し、そのIP数からCIDRを計算して出力する。</p>

<pre><code class="language-brush:perl gutter:true title:/etc/rc.d/jponly.pl">#!/usr/bin/perl
use strict;  
use utf8;

###
### /etc/rc.d/jponly.pl
###
### delegated-apnic-latest から日本国内IPのCIDRを抽出する
###

my %CIDR;  
for my $bit ( 0..32 ) {  
    my $len = 2 ** ( 32 - $bit );
    $CIDR{$len} = $bit;
}
while ( &lt;&gt; ) {  
    chomp;
    if ( m{^apnic\|JP\|ipv4\|(\d+(?:\.\d+){3})\|(\d+)\|\d+\|allocated} ) {
        my $addr = $1;
        my $cidr = $CIDR{ $2 || 32 };
        printf "%s/%u\n", $addr, $cidr;
    }
}
exit 0;  
__END__  
</code></pre>

<p>これを通して生成した<code>/etc/rc.d/jponly.list</code>は1行1ブロックのIP/CIDRリストとなる。</p>

<pre><code class="language-brush:plain">223.216.0.0/14  
223.223.0.0/17  
</code></pre>

<hr>

<h4 id="iptablesacl">iptablesでのACL</h4>

<p>これを用いて例えばiptablesのmangleテーブル・Jponlyチェイン（フィルタ）に書き込むには次のようにする。</p>

<pre><code class="language-brush:bash futter:true">#!/bin/bash
JPONLY=/etc/rc.d/jponly.list

# mangleテーブルでフィルタを作る
iptables -t mangle -N Jponly  
cat $JPONLY | while read; do  
  iptables -t mangle -A Jponly -s $REPLY -j MARK --set-mark 2/2
done

# 上記のフィルタをINPUT/FORWARDに適用してパケットにmarkビットを立てる
iptables -t mangle -A INPUT -j Jponly  
iptables -t mangle -A FORWARD -j Jponly

# 他のチェインの中で、markビットが立っているならACCEPT、なければDROPする例
iptables -A Accept -m mark --mark 2/2 -j ACCEPT  
iptables -A Accept -j DROP  
</code></pre>

<p>mangleとmarkビットマスクを使うことで、このJponlyフィルタを容易に再利用できるようにしている。この他にホワイトリストチェイン・ブラックリストチェインを併用する場合も、markビットマスクを利用することで柔軟なACLフィルタを作り出すことが出来る。</p>

<hr>

<h4 id="">フィルタチェインの高速更新</h4>

<p><code>apnic.list_update</code>の49行目で呼び出している<code>update_mangle.pl</code>は、iptablesで設定した既存のJponlyチェインをアップデートするスクリプトだ。これはiptables-save・iptables-restoreコマンドを使用して高速に更新処理を行う。初回こそ前項のようにiptablesコマンドで丁寧に設定する必要があるが、これは数秒〜数十秒の処理時間が掛かってしまう。そこで2回目以降は該当チェインだけを書き換えることでダウンタイムを実用上問題ない時間（ミリ秒オーダー）まで切り詰める。<sup id="fnref:3"><a href="http://multix.jp/delegated-apnic-acl/#fn:3" rel="footnote">3</a></sup></p>

<pre><code class="language-brush:perl gutter:true title:/etc/rc.d/update_mangle.pl">#!/usr/bin/perl
use strict;

my $JPONLY  = '/etc/rc.d/jponly.list';  
my $UPDATE  = '/etc/rc.d/iptables.bak';  
my $SAVE    = '/sbin/iptables-save -c';  
my @RESTORE = ('/sbin/iptables-restore', '-c');

my $SEARCH   = qr{-A Jponly -s};  
my $TEMPLATE = "[0:0] -A Jponly -s %s -j MARK --set-xmark 0x2/0x2 \n";

my $BEFORE;  
my $AFTER = [];  
my @INSERT;

my $PIPE;  
open $PIPE, '&lt;', $JPONLY or die;  
while (&lt;$PIPE&gt;) {  
    chomp;
    push @INSERT, sprintf $TEMPLATE, $_;
}

close $PIPE;  
open $PIPE, '-|', $SAVE or die;  
while (&lt;$PIPE&gt;) {  
    unless ($BEFORE) {
        if ($_ =~ $SEARCH) {
            $BEFORE = $AFTER;
            push @$BEFORE, @INSERT;
            next;
        }
    }
    elsif ($_ =~ $SEARCH) {
        next;
    }
    push @$AFTER, $_;
}
close $PIPE;

open $PIPE, '&gt;', $UPDATE or die;  
print $PIPE @$BEFORE;  
print $PIPE @$AFTER;  
close $PIPE;

system @RESTORE, $UPDATE;

1;  
__END__  
</code></pre>

<hr>

<h4 id="apacheacl">ApacheでのACL</h4>

<p>前述の<code>jponly.list</code>からApache用のACLを作り出すには次のようにする。</p>

<pre><code class="language-brush:bash">#!/bin/bash
JPONLY=/etc/rc.d/jponly.list

( cat $JPONLY | while read; do
  echo "Allow from $REPLY"
done ) &gt; /etc/httpd/conf/jponly.conf  
</code></pre>

<p>こうして生成したACLを、必要な場所でIncludeディレクティブを使用して読み込む。なおリストが更新されたらhttpdプロセスをreload(あるいはgraceful)しなければ実際には反映されない。<sup id="fnref:4"><a href="http://multix.jp/delegated-apnic-acl/#fn:4" rel="footnote">4</a></sup></p>

<pre><code class="language-brush:plain">Order Allow,Deny  
Allow from localhost  
Allow from 192.168.0.0/16  
Include conf/jponly.conf  
Deny from All  
</code></pre>

<hr>

<h4 id="nginxacl">nginxでのACL</h4>

<p>nginxでACLを掛けるには、geoモジュールを使用する。まずApacheの場合と同様にInclude可能なファイルを作成する。</p>

<pre><code class="language-brush:bash">#!/bin/bash
JPONLY=/etc/rc.d/jponly.list

( cat $JPONLY | while read; do
  echo "$REPLY 2;"
done ) &gt; /etc/nginx/jponly.conf  
</code></pre>

<p>これをhttpブロックのgeoモジュールで読み込み、$acl変数に反映させる。これをlocationブロック内で参照して条件分岐する。なおリスト更新後はnginxプロセスをreloadしなければ実際には反映されない。</p>

<pre><code class="language-brush:plain">http {  
    geo $acl {
        default        0;
        127.0.0.1      1;
        192.168.0.0/16 1;
        include jponly.conf;
    }
    server {
        location /jponly/ {
            root /var/www/html_jponly;
            if ($acl = 0) {
                return 403;
            }
        }
        location / {
            root /var/www/html_public;
        }
    }
}
</code></pre>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>このリストにはIPv6も記載されているし、日本以外のアジア諸国もすべて入っている。特定の国のIPだけ弾きたい、といったニーズならばそれに応じたフィルタを書けば同じように対応できる。 <a href="http://multix.jp/delegated-apnic-acl/#fnref:1" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:2"><p>あるときA国で使われていたIPが、気がついたら何時の間にかB国に再割当てされていたということは割とある。 <a href="http://multix.jp/delegated-apnic-acl/#fnref:2" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:3"><p>差分更新処理は端折っているため、実行すると各IPブロックのカウンターはすべてゼロ初期化される。 <a href="http://multix.jp/delegated-apnic-acl/#fnref:3" title="return to article">↩</a></p></li>

<li class="footnote" id="fn:4"><p>GeoIPモジュール <a href="http://dev.maxmind.com/geoip/">http://dev.maxmind.com/geoip/</a> を使ったほうが手間は少ないし処理も高速だし設定もスッキリするが、DBファイルをアップデートしたらサービスリロードが必要になるのは変わらない。 <a href="http://multix.jp/delegated-apnic-acl/#fnref:4" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[ホーム画面/Webクリップアイコン]]></title><description><![CDATA[<p>スマートフォンでWebページをホーム画面に登録したり、Windows 8.xでタイル画面にWebクリップをピン留めするときに使われるアイコンの設定方法に関するメモ。</p>

<ul class="index"></ul>

<hr>

<h4 id="ios">iOSの場合</h4>

<p>iOSの場合、特に何もしていなければホーム画面登録時に以下のファイルをサーバのドキュメントルートへ探しに行く。</p>

<pre><code class="language-table">|ファイル名|表示サイズ|クライアント|
|apple-touch-icon-152x152.png|152px|Retina版のiPad、iPad mini用、iOS 7以降|
|apple-touch-icon-144x144.png|144px|Retina版のiPad、iPad mini用、iOS 6以前|
|apple-touch-icon-120x120.png|120px|Retina版のiPhone、iPod touch用、iOS 7以降|
|apple-touch-icon-114x114.png|114px|Retina版のiPhone、iPod touch用、iOS 6以前|
|apple-touch-icon-76x76.png|76px|非Retina版のiPad 2、iPad miniの用、iOS</code></pre>]]></description><link>http://multix.jp/webclip-icon/</link><guid isPermaLink="false">f37cc7e2-ced3-427d-946a-b4987a71b3b8</guid><category><![CDATA[めもらんだむ]]></category><category><![CDATA[Web]]></category><dc:creator><![CDATA[朝日薫]]></dc:creator><pubDate>Mon, 11 May 2015 02:34:37 GMT</pubDate><content:encoded><![CDATA[<p>スマートフォンでWebページをホーム画面に登録したり、Windows 8.xでタイル画面にWebクリップをピン留めするときに使われるアイコンの設定方法に関するメモ。</p>

<ul class="index"></ul>

<hr>

<h4 id="ios">iOSの場合</h4>

<p>iOSの場合、特に何もしていなければホーム画面登録時に以下のファイルをサーバのドキュメントルートへ探しに行く。</p>

<pre><code class="language-table">|ファイル名|表示サイズ|クライアント|
|apple-touch-icon-152x152.png|152px|Retina版のiPad、iPad mini用、iOS 7以降|
|apple-touch-icon-144x144.png|144px|Retina版のiPad、iPad mini用、iOS 6以前|
|apple-touch-icon-120x120.png|120px|Retina版のiPhone、iPod touch用、iOS 7以降|
|apple-touch-icon-114x114.png|114px|Retina版のiPhone、iPod touch用、iOS 6以前|
|apple-touch-icon-76x76.png|76px|非Retina版のiPad 2、iPad miniの用、iOS 7以降|
|apple-touch-icon-72x72.png|72px|非Retina版のiPad 2、iPad miniの用、iOS 6以前|
|apple-touch-icon.png|57px/60px|デフォルト|
</code></pre>

<p>iOS7未満ではグローエフェクトをバイパスする<code>-precomposed</code>付きの<code>apple-touch-icon-precomposed.png</code>等も探しに行くようになっていたが、iOS7以降は廃止された。<sup id="fnref:1"><a href="http://multix.jp/webclip-icon/#fn:1" rel="footnote">1</a></sup></p>

<p>真面目にアイコンを用意しようとするとこれらを全部用意しなければならないが、サーバに<mark>404 File not found</mark>エラーログが残ることを気にしないならば<code>apple-touch-icon.png</code>さえ存在していれば実用上の問題はない。</p>

<p>もともとの画像サイズの規定値は上記の通りだが、サイズが合ってなければ自動的にリサイズされるし、また使用されている大多数のiOSデバイスは既にRetinaディスプレイ化されているため、生真面目に最小公倍数で作成する必要も取り立ててない。ざっくり512pxや600pxあたりで作画してしまってもよいだろう。</p>

<p>その他気を付けるべきこととして以下の制約がある。</p>

<ul>
<li>画像ファイルは標準的な24bit PNG形式とする。</li>
<li>True Color 形式とし、Indexカラーにしてはならない。</li>
<li>インタレース化してはいけない。リサイズによって表示が乱れる。</li>
<li>SSLは問題ないが、クライアント認証に対応していない。</li>
<li>Expires等で有効期限が指示されている場合、期限が切れると自動的に再アクセス・再生成される。ファイルサイズが大きいと処理の重さが気になるかもしれない。</li>
</ul>

<hr>

<h4 id="android">Androidの場合</h4>

<p>Android系の場合、Webクリップ画像についてはiOSに準じたものでよい。それを<code>&lt;head&gt;</code>セクション内の<code>&lt;link&gt;</code>要素で指定することができる。</p>

<pre><code class="language-brush:html">&lt;link rel="apple-touch-icon" href="apple-touch-icon.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="72x72" href="apple-touch-icon-72x72.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="76x76" href="apple-touch-icon-76x76.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="114x114" href="apple-touch-icon-114x114.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="120x120" href="apple-touch-icon-120x120.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="144x144" href="apple-touch-icon-144x144.png"/&gt;  
&lt;link rel="apple-touch-icon" sizes="152x152" href="apple-touch-icon-152x152.png"/&gt;  
</code></pre>

<p><code>&lt;sizes&gt;</code>属性で優先するアイコンサイズを指定することができるが、これは機種やAndroidバージョンによって千差万別<sup id="fnref:2"><a href="http://multix.jp/webclip-icon/#fn:2" rel="footnote">2</a></sup>となるため「これは必須」と明言することができない。従ってこれもアバウトに大きめのサイズをひとつ用意するのが実利的だろう。</p>

<hr>

<h4 id="windows">Windowsタイル画面へのピン留め</h4>

<p>Windows8.x では（正確にはInternet Explorer 11の機能）スタートタイル画面にWebクリップを貼ろうとすると、デフォルトではまずドキュメントルートの<code>browserconfig.xml</code>を探しに行く。このブラウザ構成ファイルのフォーマットは次のようになっている。</p>

<pre><code class="language-brush:xml">&lt;?xml version="1.0" encoding="utf-8"?&gt;  
&lt;browserconfig&gt;  
  &lt;msapplication&gt;
    &lt;tile&gt;
      &lt;square70x70logo src="images/smalltile.png"/&gt;
      &lt;square150x150logo src="images/mediumtile.png"/&gt;
      &lt;wide310x150logo src="images/widetile.png"/&gt;
      &lt;square310x310logo src="images/largetile.png"/&gt;
      &lt;TileColor&gt;#009900&lt;/TileColor&gt;
    &lt;/tile&gt;
  &lt;/msapplication&gt;
&lt;/browserconfig&gt;  
</code></pre>

<p>このカスタム構成ファイルは<code>&lt;head&gt;</code>中の<code>&lt;meta&gt;</code>要素で明示的に指定することができる。</p>

<pre><code class="language-brush:html">&lt;meta name="msapplication-config" content="ieconfig.xml" /&gt;  
</code></pre>

<p>このカスタム構成ファイルが見つからない場合は、通常のFaviconがタイル画像として採用される。</p>

<p>使用される画像のサイズは下表のとおりだが、ワイドサイズのみ長方形でそれ以外は正方形として規定されている。</p>

<pre><code class="language-table">|タイルのサイズ|標準のタイルのサイズ|最小画像サイズ|推奨される画像のサイズ|
|:-|:-:|:-:|:-:|
|小サイズ|70 x 70|56 x 56|128 x 128|
|普通サイズ|150 x 150|120 x 120|270 x 270|
|ワイドサイズ|310 x 150|248 x 120|558 x 270|
|大サイズ|310 x 310|248 x 248|558 x 558|
</code></pre>

<p>カスタム構成ファイルには画像更新間隔や通知テキストを追記することもでき、ウェザーサイトのように一定時間間隔で画像を書き換えさせることもできる。詳しくはMSDNの該当ページを参照されたい。<sup id="fnref:3"><a href="http://multix.jp/webclip-icon/#fn:3" rel="footnote">3</a></sup></p>

<hr>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>iOS7以降はフラットデザインとなったため、角丸処理の他は行う必要がなくなったことで廃止された。 <a href="http://multix.jp/webclip-icon/#fnref:1" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:2"><p>環境によっては画像URLは絶対パスで指定しないと正常認識されないケースもあるようだ。 <a href="http://multix.jp/webclip-icon/#fnref:2" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:3"><p><a href="https://msdn.microsoft.com/ja-jp/library/dn455106(v=vs.85).aspx">https://msdn.microsoft.com/ja-jp/library/dn455106(v=vs.85).aspx</a> <a href="http://multix.jp/webclip-icon/#fnref:3" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item></channel></rss>