LWPでtimeout指定が効かない

自作実装のActivityPubサーバーから、フォロワーさんの所属しているリモートサーバーに投稿を送信する時に、お相手のリモートサーバーがなんらかの事情で落ちてるケースがある。
レスポンスは500番代のエラーとなる。
perlで定番のLWP UserAgentを使ってリクエストを投げてるんだけど。
400番代のエラーと違って、サーバーに問題が発生している500番のエラーの場合、返事が戻ってくるのに時間がかかる、待たされる。投稿するためのPOSTもアカウント情報取得のためのGETもなかなか戻ってこない。
デフォルトだと、リクエストを投げてお相手の反応が返ってくるまで180秒(3分)待つことになっている。
LWP::UserAgent - Web ユーザエージェントクラス
https://perldoc.jp/docs/modules/libwww-perl-6.04/LWP/UserAgent.pod
timeout( $secs )
秒単位のタイムアウト値を取得または設定します。 デフォルトのtimeout()の値は180秒、つまり3分です。
サーバへの接続においてtimeout秒反応がないと、リクエストは中断します。 つまり、トランザクションが完了してrequest()メソッドが実際に返るまでの 時間を意味します。
ということなのでtimeoutに10秒とか設定してみてたんだけど、どうもそれが効いてない。
検索したら、やっぱりtimeoutの指定は効かないことがあるらしい。
LWP::UserAgentのタイムアウトがうまく効かなかった事象の調査 (序章)
https://papix.hatenablog.com/entry/2020/12/25/180640
もう少し粘って検索してみると
lWP::UserAgentの「:content_cb」(コールバック)のサブルーチンでSIGNALを設定してalarmで対応できるという記事を発見。
LWP::UserAgent get callback with timeout
https://stackoverflow.com/questions/29071348/lwpuseragent-get-callback-with-timeout
リクエストをサブルーチンで処理することになるのが、素人のわたし的に難しそう…影響範囲がわからない。
思いつきでデフォルトを180秒にしたわけじゃあるまいし、なにか理由がありそう。それをここで指定しちゃうと、全部に影響するわけだしなあ、と。
てことで、それならリモートに投稿を送信するリクエストのサブルーチン限定にしてしまえば大丈夫っぽいんじゃないかと。
サブルーチン丸ごとSIGNALのALRMを設定してevalで捕まえることにした。
結果オーライとか、やっつけ仕事だけは昔から得意だし。
my $res = "";
eval{
local $SIG{ALRM} = sub{die "timeout";};
alarm $self->{timeout};
$res = $self->post_actpb({url=>$u, content=>$args->{json}});
alarm 0;
};
if( $@ ){
printf qq{ERROR deliver %s ::: %s}, $u, $@;
}
設定を15秒にしてみたら、意図通りにtimeoutをキャッチして、待ち時間が少なくなった。
ちゃんとしたActivityPubサーバーのMastodonなんかだと、リクエストの送受信は裏側でやってるんで、アクセスしてるユーザーが待たされることはないはず。
わたしの自作ActivityPubサーバーは最低限で、いろいろ手抜きしていて表示するだけのために3分以上待たされるんだよなあ。自業自得というか。
サーバーのお守り代わりの画像
(松戸市立博物館)
» ローカル環境で電子書籍を作る、Macアプリ・Windows版ツール 「かんたんEPUB3作成easy_epub」
perlの再帰でlocalの使い途

perlでディレクトリを辿ってファイルをリストする。
というのは File::Find というモジュールがあるのでそれを使えば一発で解決するんだけど。自分のモジュールに組み込む方法がよくわからず。
File::Find - ディレクトリツリーを辿る
https://perldoc.jp/docs/modules/File-Find-1.19/File/Find.pod
読んでもなんか使い勝手が違う、というかナニソレwanted?
てことで自分のモジュールで使えるものを自作。
「static」というディレクトリ以下にある、ホームページ用のhtmlファイルをリストアップする。というもの。以前から同じことをやってるスクリプトからの使い回しのサブルーチンの再帰。
use vars qw( $HTML );
〜〜〜〜〜
〜〜〜〜〜
sub parse_static{
my $self = shift;
my $args = shift;
my @dirs = ();
my $d = ($args->{dir} || 'static');
opendir(DIR, $d ) || die;
@dirs = grep(!/^\.\.?/, readdir(DIR));
closedir(DIR);
foreach my $f (sort @dirs){
if(-d $d . '/' . $f ){
$self->parse_static({dir=> sprintf(qq{%s/%s}, $d ,$f) });
}
else{
push(@{$HTML}, $d . '/' . $f) if $f =~ m!\.x?html!;
}
}
return $HTML;
}
1)ディレクトリ一覧を取得して
2)ファイルなら配列に放り込んで
3)ディレクトリなら(1)に戻る
というありがちなスクリプト。
…なんだけど、放り込む配列は use vars を使ってのグローバル変数。そうグローバル変数!なのが前から気に入らなかった。
もっと「かっこいい書き方」があるんじゃないかと。わたしのような野良、素人にとって「かっこいい」かどうかがポイント。
検索してみた。やっぱりこれも以前からちょっと気になってた my と local の違いがきっと魔法の種だと思ったらビンゴだった。
Perl で再帰呼出し時のスタック間データ共有
https://amachang.hatenablog.com/entry/20061010/1160506848
知りたかったのが、まさにこれ。かっちょええよなあ。
ダイナミックスコープとかレキシカルスコープとか意味は分からない、グローバル変数を局所化するとかも分からない。
けど、匂いでわかるかっこよさ。さっそくこのままいただいた(多謝!
sub parse_static{
my $self = shift;
my $args = shift;
my @dirs = ();
my $d = ($args->{dir} || 'static');
local $ongoldenpond::html = $ongoldenpond::html;
opendir(DIR, $d ) || die;
@dirs = grep(!/^\.\.?/, readdir(DIR));
closedir(DIR);
foreach my $f (sort @dirs){
if(-d $d . '/' . $f ){
$self->parse_static({dir=> sprintf(qq{%s/%s}, $d ,$f) });
}
else{
push(@{$ongoldenpond::html}, $d . '/' . $f) if $f =~ m!\.x?html!;
}
}
return $ongoldenpond::html;
}
おかげで、このサブルーチンの中だけで記述が完結することができた。
パッケージ名ongoldenpondというのは、わたしがここんとこどっぷりハマってる 個人ホームページ(On Golden Pond) 用のオレオレMovableTypeのスクリプトだから。
このブログ『ひまつぶし雑記帖』にはperlの小ネタもあるから、その手のも集めて、改めて個人ホームページに掲載するかなあ。
前にも書いたように、ここは日常雑記のために記法というか、書き込んだものを変換するけど、perlやhtmlのコードをそのまま掲載しても見づらいだけになってしまう。今さら変換規則を変えると過去25年以上の分全部に影響するんで、HTMLやperlのコードをそのまま掲載できる個人ホームページの方が見やすくなる。
ちょっと整理してみるか。
「よんでますよアザゼルさん」
アザゼル篤史とベルゼブブ優一
たぶん、今、こんなものTV放映したら大騒ぎだろう(最上の褒め言葉
» ローカル環境で電子書籍を作る、Macアプリ・Windows版ツール 「かんたんEPUB3作成easy_epub」
サブルーチンの確認

perlで自作したおひとり様ActivityPubサーバーはその後も増築改築をちまちま続けていて、例によってその場での思いつき、やっつけ仕事の現物合わせ仕様で、わけわかめ状態となっている。
Activityを約束事どおりに対応するモジュール(pmファイル)で、いったい何をしてるのか。
もうすでに忘れてる自分がいるので、サブルーチンの洗い出し用にテキトーなスクリプトをでっちあげた。という覚え書きが今回のエントリ。
#!/usr/bin/perl
use strict;
use utf8;
use Encode;
my $f = shift(@ARGV);
if(! -e $f ){
printf qq{Not Found pm %s\n}, $f;
exit;
}
open(IN, $f) || die;
my $name; my @buf=; close(IN);
foreach (@buf){
if( m!^sub (.+) *\{! ){
$name->{$1}++;
}
}
printf qq{sum : %s\n}, scalar( keys %{$name} );
foreach my $sub (sort keys %{$name}){
my $pm = join('', @buf);
my $cnt = $pm =~ s!$sub!!g;
printf qq{%s :\n}, $sub;
my @called; my $zzz;
if( $cnt > 1 ){
my $subname;
foreach (@buf){
if(m!^sub (.+) *\{!){
$subname = $1;
next;
}
if( m!\$self\-\>$sub! && !$zzz->{$subname}++ ){
push(@called, $subname);
}
}
}
printf qq{\t%s\n}, join("\t", sort @called);
}
・サブルーチンの総数
・サブルーチンの名前
・サブルーチンを呼び出しているサブルーチン
ぐらい見えれば、そこそこ役に立つかなあ、と。
文字列検索でひっかけてるだけで、signなんかはサブルーチン呼び出しじゃない部分にもヒットする。本当は動かしながらcaller()でチェックするのが確実…だけど、ざっくり見るだけのためにあちこちにcaller()を仕込むのはうっとーしいんで却下。
使い捨てのつもりで書いたスクリプトだけど、思ったよりちゃんと見えるようにしてくれたので自画自賛&エントリとして書き起こし
10/4、ポケモンGOの対人戦GBLでACEに到達。
初期レートが1984で、ACE到達時のレートは2007。GBLで遊んでいて、ずっと継続してACEにたどり着いてたんだけど、前期初めてACEに到達できず、今期もやべえかなあ、と思ってたので、ほっとひと息。
まる6年続いているゲームで、まだ全然飽きないのがすげーす。
» ローカル環境で電子書籍を作る、Macアプリ・Windows版ツール 「かんたんEPUB3作成easy_epub」
小ネタ:pdftotextで文字データを抽出

元データがPDFで、圏点(傍点)やダッシュをPDFから検出する必要にせまられた。
目視確認などありえないんで、テキストデータとして取り出して検索しよう、というのが今回のエントリ…というかエントリにするまでもない内容なんだけど、たぶんそのうち忘れるんで、メモ。
PDFから文字情報だけをひっぱりだすのに定番の「pdftotext」というツールを使う。
perlだけでもできそうなんだけど(PDF::API2あたり)ちょっと時間も押してるんで、外部ツールを間に挟むことにした。
まずは肝腎のpdftotextのインストール。これがpoppler-utilsというパッケージだなんて検索しないとわからなかった(pdftotextというパッケージがあるもんだと思ってた)
以下のコマンドラインでインストール
sudo apt-get install poppler-utils
unix系のツールの例に漏れず、これもいろんなオプションが用意されてるけど、今回必要なのは文字情報だけ。レイアウトデザイン情報やなんかは不要。
rawオプションをつけて利用する。
たとえば「kappa.pdf」の文字情報を抜き出すのは
pdftotext -raw kappa.pdf
こうすると「kappa.txt」に文字情報を吐き出す。ただ、これだとひと文字ずつだあーっと出力されるので、ここからがテキストデータを扱わせたら最強のperlの出番。
pdftotextの出力には改ページがつくので、そこで改行すれば「それっぽい段落」ごとに見えるひとに優しいテキストファイルとなる。
open(P, '-|', 'pdftotext -raw kappa.pdf - ' ) || die;
binmode STDOUT => ":utf8";
open(OUT, '>' , 'kappa.txt') || die;
binmode OUT => ":utf8";
while(){
my $line = Encode::decode('utf8', $_);
$line =~ s!\r?\n!!;
$line =~ s!\x{C}!\n!;
print OUT $line;
}
close(OUT);
close(P);
特筆すべきようなスクリプトじゃないんだけど。
perlは外部コマンドの「標準出力」をパイプで受け取って加工整形できる。
open(P, '-|', 'pdftotext -raw kappa.pdf - ' )
openの
・第2引数で、標準出力を受け取りますよ、という指定
・第3引数はpdftotextの結果を標準出力に出すからね、という指定
これだけでそれっぽい段落にわけたテキストファイルを作ってくれる。
そうしたら、あとは出力されたテキストファイルからダッシュや圏点(傍点)っぽいものをperlで検知するだけ。これはワードで作ったPDFで「、」が圏点となっている。inDesignで作られたデータだと「0」(ゴマ)「4」(ドット)となる。
直接触ってもいいんだけど、一度テキストファイルに吐き出したほうがなにかわからないことが起こった時に便利なので、こういう仕様、段取り。
このスクリプトのおかげで抜け漏れは捕捉できるんでずいぶんラクになった。
以前、目視確認とか無駄なだけだし、んなもんツール作ってやればいいじゃん、とか言ったら、そしたら仕事がなくなる、目視確認手作業修正は必須だ、と言われて心底、呆れた。
カネをもらった上で、人間の作業=ミスが入り込む原因になる工程を入れるって、いろいろ悪質すぎる。
ITといってもピンキリで、こんなのが入り込んでるから要注意。
そもそも、その程度の仕事なんて、なくなっても問題ないし、特に困らない。
» ローカル環境で電子書籍を作る、Macアプリ・Windows版ツール 「かんたんEPUB3作成easy_epub」
電子書籍でエラーになる絵文字の検知

今回のエントリは。
経緯
・電子書籍でエラーになるのが絵文字だった。
・元データから絵文字を検知する必要が出てきた。
結論
・perlを使えば絵文字のチェックも簡単だった。
てことなんだけど、調べてみたらunicodeまわりが魔窟でびっくり。またハマりそうなのでメモ。
以下は、何を今さらという話が無駄に長くて、既知のかたには役に立たない内容です。
電子書籍に求められる基準はほぼ以下のふたつ
・Epubcheckでエラーはない。
・kindle previewerでエラーはない。
epubcheckでエラーがなければEPUB3の電子書籍として問題はないし、kindle perviewerでエラーをチェックするのは最大の配信サイトであるAmazonでエラーがあったら商売上よろしくないから。
上記2つのチェックをクリアしたのに、一部の配信サイトでエラーになるという指摘があって、追いかけてみると、問題になったのが「絵文字」
「😀」←こういうやつ
原稿に絵文字があったんで、あれ?これってイケるんだっけと思って作ってみて、上記2つのチェックにかけてもエラーはなかった。
文字コードがShiftJISの時代ならともかく、今どきはutf8。
utf8のおかげで文字化けなんかを気にする必要はほぼなくなった。機種依存、環境依存文字に神経質にならなくていいのはストレスフリーで、いい時代になったもんだとのんきに感慨深い今日この頃だったのに…。
文字として問題はないんだけど、配信サイトごとにビューワーが違ってたりして、そのビューワーが絵文字に対応してるかどうかのことだと思う。
「EPUB | CSS組版ブログ」
https://blog.antenna.co.jp/CSSPage2/archives/category/epub
↑絵文字なんかについて、縦書きのepub3が始まった頃に議論があったらしい。
(電子書籍元年当時の話が読めるのでオススメ)
「横倒しのまま」にするのか「正立させる」のか。文字の意味的に方向があるものは正立させる?とか。そのあたりのすり合わせが問題になってたっぽい(という理解でいいのかなあ)
これは縦書きの場合、見た目けっこう致命的なので、たぶん、絵文字の対応を見送ったビューワー=配信サイトがあったんじゃないかと思う(憶測)
あるいは、そもそも、絵文字は環境ごとで見え方も違うのでそこが問題なのかもしれない(憶測)
そんなこんなの名残りもあって、面倒なものは却下、ということで今でもエラーにする配信サイトがある、んだろう(めっちゃ憶測)
epubcheckでもkindle previewerでもエラーにならない文字を自前で検知する必要にせまられる事態となった。
電子書籍のボリュームを目で追ってその中から絵文字を見つける、目視で探すなどありえない。必ず漏れが出る。
なもんで、絵文字の文字コードを調べてみて改めて今さらびっくりのunicodeだ。
ちなみに、文字を表示させるためには、以下の2本立てになっている。
・unicodeの文字コード表で文字を指定特定していて
・文字コードをエンコードすることで文字を表現する
これ、けっこう勘違いするんだけど、utf8というのは文字コードではなくてunicodeの文字コードをエンコードする方法の名前のこと。
以下のサイトを読んでまたびっくりすることをおすすめする。
「文字コード再入門 ─ Unicodeでのサロゲートペア、結合文字、正規化、書記素クラスタを理解しよう!」
https://en-ambi.com/itcontents/entry/2020/04/28/103000/
「書記素クラスタ - daiizfeel 2022」
https://isobe-yaki.hateblo.jp/entry/2023/07/20/194803
てのはともかく、読んでも難しいんで、なにか大変なことになってるんだなあ、でOK。
そもそもutf8でエンコードされた、日本語などは、ひと文字を表すのに、そのコードは3バイト使う。
ひとつの文字がアルファベットのように1バイトではないので
たとえば「日本語の文字」の6文字は、1つの文字に3バイトのコードが必要で、文字列の長さをバイト数でいうと18となる。
文字数いくつだっけ?て時に、バイト数の18ではなくて「正しく」6と数えるためにエンコードされている必要がある。
unicodeのコードをエンコードして初めて「文字として認識される」
このエンコードする方法がutf16だったりutf8とか呼ばれて、エンコードすることで初めて「文字として認識された文字を表示する=扱うことができる」という変な日本語。
perlの場合、utf8フラグというのを使うことで問題解決する。
my $str = '日本語の文字'
my $utf8 = Encode::decode('utf8',$str)
print length($str)
→18文字
print length($utf8)
→6文字
perlはこのおかげで、文字の扱いについて、普段はまずほとんど、こんな面倒くさいことを意識する必要はなくて、なもんで、すっかりわけがわからず混乱してしまったのが昨日今日の話、だ(とほほ
unicodeとutf8についてのおさらいができたところで、やっと本題。
じゃあ、絵文字を検知するには絵文字が使ってるコードを見つければいいだけじゃん。簡単だろ、と思ったらまたひと悶着。
ていうか、ここからが今回の本丸、一丁目一番地(死語)
utf8フラグだけでは解決しない「結合文字」というのがあった。
「1つの文字を表示しているのは、1つのunicodeのコードポイント」とは限らない。
ひとつの文字を表示、扱うのに、複数のunicodeのコードポイントを使ってるケースがあって、見た目のひと文字とそれを表すコードは1対1ではない、ということ。
perlでutf8フラグがついていてもutf8の文字コード(←unicodeコード表によるコードではなくてエンコード後の文字コード)2つ結合した文字はlength()では意図通りに取得できない。ひと文字として扱ってほしいのに、たとえば二文字としてカウントされてしまってお手上げ。
実のところ、書記素クラスタというのはこういう結合文字も含めた呼び方で、書記素クラスタに対応すれば「結合文字」も「ひと文字」として扱うことができる。書記素クラスタというのがなんだか「とても面倒くさい」というのはこのあたり。
こうなってくると、電子書籍でエラーをなくすために、なんでこんなことまで調べにゃならんのか。そもそもレアケースだし、配信サイト個別のクレーム対応でいいんじゃないのか。と思わないでもないけど。乗りかかった船だししょうがない。
話がそれた。
perlはこれも解決してくれる。て、テキストを扱わせたらperl最強じゃね?
「絵文字を含む文字列を分割~解説編~」
https://www.lemorin.jp/perl/3b_split_char.html
↑こちらのサイトに知りたいことのすべてがあった(多謝
my $line = Encode::decode('utf8', $_);
my $len = length($line);
print $len . "\n";
my @x = $line =~ m!\X!g;
print scalar(@x) . "\n";
length()では「見た目のひと文字」じゃなくて困ってたところ、perlでは「いい感じに」「ちゃんと」文字として認識できる文字のための正規表現「\X」が用意されてた。
「perlの正規表現」
https://perldoc.perl.org/perlrebackslash#Misc
ということで、やっと本エントリの締めとなります(長っ!
問題のない文字ダネを削除して残った文字を正規表現「\X」で配列に取り出して、「見た目のひと文字」をコードポイントにバラして16進表現して絵文字のコードに検索をかける。検索がヒットしたら、それはイコール絵文字。
「HTMLの絵文字 文字コード表」
https://gray-code.com/html_css/list-of-emoji/
↑絵文字のコードはこちらの一覧から拝借しました。
こちらで掲載されているコードを絵文字判定の対象としました。
これでやっと絵文字検知。
前から言ってるんだけど、人間の目視確認、手作業修正(目grep、手marge)なんて1mmも信用できない。特におれ。なので機械に頼めるなら機械に任せるのが正解。
↓こちらも参考になりました
「Perl 5.26 & Unicode 9.0 で変わる書記素クラスタ(grapheme cluster)のお話」
https://shogo82148.github.io/blog/2017/08/25/unicode9-grapheme-cluster/
「UTF-8の文字コード表」
https://orange-factory.com/dnf/utf-8.html
本エントリとはまったく関係ないけど。
この埼玉県八潮市の資料館はびっくりの充実。行く前は小学校の教室ぐらいなもんだろ、と思っててすみませんでした。
交通アクセスにちょっと難があって、気楽にとはいえないけど、行けるかたはぜひぜひ
https://www.city.yashio.lg.jp/kurashi/shisetsuguide/shiryokan/index.html
[08/25 16:58:54] いろいろ間違えてたので改稿
「文字につかうバイト数」と「コードポイント」を混用していた、という乱暴な理解だった(恥
» ローカル環境で電子書籍を作る、Macアプリ・Windows版ツール 「かんたんEPUB3作成easy_epub」
縦書き電子書籍のための縦中横

青空文庫に限らず、縦書きのEPUB電子書籍を作る時に半角の英数字を縦にするための小ネタが今回のエントリ…なにを今さら、というネタで、縦書き文章で半角英数字の縦中横なんて当然。
それでもネタとして取り上げるのはビミョーに考えることがあって、たぶん、どんなことを縦中横にするのか、どうやってるのかがいろいろあるから。特にWEB経由の文章は半角英数字がてんこ盛り。全部対応するのはやってられないけど。
・一文字の英数字は全角の英数字にして縦表示にしたい。
・数字の3桁以上は縦中横にすると無理矢理感が強い。この場合はそのまま転がしておきたい。
↑このへんがこだわりどころ。
そのために使ってるのが以下のもの
置換する時にゴソゴソする必要があったのでメモ…「1 while〜」のあたり、たぶん5分後には忘れる
・perl
sub _tcy{
my $self = shift;
my $str = shift;
return $str if $str =~ m![0-9][0-9][0-9]+!;
$str =~ s!([a-zA-Z0-9\!\?][a-zA-Z0-9\!\?]+)!sprintf(qq{%s}, $1)!eg;
return $str;
}
sub h2z{
my $self = shift;
my $str = shift;
$str =~ tr/a-zA-Z0-9\!\?/a-zA-Z0-9!?/;
return $str;
}
sub add_tag{
my $self = shift;
my $str = shift;
$str =~ s!^([a-zA-Z0-9\!\?])([^a-zA-Z0-9\!\?])!$self->h2z($1) . $2!e;
$str =~ s!([^a-zA-Z0-9\!\?])([a-zA-Z0-9\!\?])$!$1 . $self->h2z($2)!e;
1 while $str =~ s!([^a-zA-Z0-9\!\?])([a-zA-Z0-9\!\?])([^a-zA-Z0-9\!\?])!$1 . $self->h2z($2) . $3!eg;
$str =~ s!([a-zA-Z0-9\!\?][a-zA-Z0-9\!\?]+)!$self->_tcy($1)!eg;
return $str;
}
・style sheet
.tcy {
-webkit-text-combine: horizontal;
-epub-text-combine: horizontal;
}
» ローカル環境で電子書籍を作る、Macアプリ・Windows版ツール 「かんたんEPUB3作成easy_epub」