AnyproxyがNode.js製のLocal Proxyとしてなかなかよい

Introduction · AnyProxy

Node.js で動く proxy で

  • https を扱えて(証明書生成もできる)
  • Web UI があって
  • header も body も全部 Node.js で書き換え可能

なやつ。Mitmptoxy の Node.js 版と言えば分かる人は分かると思う。

でまぁ、そんな目新しい話はないんだけど、昔話と今回の狙いを残しておこうと思う。

かつてCocproxyがあった

azu/node-cocproxy: Convention over Configuration Proxy written in Node.js.

いろんなバージョンがある1が、基本的には

  • ホスト名と同じ名前のディレクトリ、パスを掘ってあげて
  • そこにコンテンツを置いておき
  • proxy を起動し
  • ブラウザの通信にその proxy を利用するように設定

以上を満たすと local に該当リソースがあったら利用し、なければ remote のリソースを取得して返すという動作をしてくれる。これを利用することで例えば開発環境を手元で全部再現しなくても Web サイトのフロントエンドなどだけを準備、改善していくことが可能だった。

もちろん今でも HTTPS になっていないサイトに対しては普通に利用できる。手元でだけ別な CSS を当てるといったことが簡単に行えるので、もし知らなかったという人は試してみると面白いと思う。

いわゆるLocal Proxyのユーザー層は複数ある

Local Proxy という言葉はそこまで定着してるわけでもないんだけど、初出はこの辺かな?

ここまで出来る!LocalProxyベースの開発手法紹介

まぁ本当に便利。

ただこの手の Proxy はフロントエンド開発な人たちとは別にセキュリティ業界の人も重宝していて、どっち方面のツール、情報なのかを意識できるようになっておくと、見つけた記事が何を狙っているものなのかを判断しやすくなってよいと思う。Web のフロントエンド開発を目指して OWASP ZAP とか見つけても欲しい関連情報はなかなか辿れないはず。

HTTPS時代、Local Proxyは不便になった

CocProxy は目的に向かって挙動が整っていたのですぐにローカルでコンテンツを書き換え始めることができてとても便利だったが、最大の泣き所は HTTPS に対応できないことである。

そもそも Secure 通信は Hijack できないのがポイントなわけで、Proxy を挟んで中身を書き換えられると困るわけだけど、開発時にはまさにそれをやりたいという矛盾。

ま、解決策はあって、サーバが自分で証明書を持っておいて、そいつをクライアントに信用させればよい。いわゆるオレオレ証明書である。

これをやるのに Mitmproxy を使っていたこともあるんだけど、Cocproxy のような動作をさせるには自分でスクリプトを追加する必要がある。そのうえ一時期ゆっくりだった開発が active になり、独自に追加するスクリプトで利用する API がゴリゴリ変わってこの手のツールが次々死んでいくという事態になっている。

強くて気の利いたツールにこだわる

自分だけの話ならわざと Mitmproxy の古いバージョンを使うとかいろいろ手はあるんだけど、フロントエンド向けに定番ぽいものがあった方がよいかと思って Node.js 製のものを探していたところ Anyproxy を見つけた。まさに最近の Mitmproxy が実現していることがほぼ再現されていて、かつ独自のスクリプトは JavaScript で書ける。

本当はまさに Cocproxy みたいな気の利いた設計の専用 Proxy で HTTPS 時代に対応できているものがあればそれがいちばんよかったんだけど、結構頑張ってみたけどそういうものを見つけることはできなかった。

ということで次善の手として上に挙げた

  • https を扱えて(証明書生成もできる)
  • Web UI があって
  • header も body も全部 Node.js で書き換え可能

を基準に Anyproxy にした。ケータイに証明書をインストールするには QR コードをパシャっとやれば済む親切設計。これなら openssl 叩けとか言わなくて済む。

また 2019-03 時点で GitHub Star 5000 超えだし、repository の owner は alibaba なので、吹いて飛ぶようなこともないだろう。

OSごとの証明書を信用させる方法の違いに注意

具体的に自分がまさにやらかしたことを書くと、かつてやったことのあった iOS での方法が OS のバージョンアップで変わっていたことに気づかずにだいぶ時間をロスしてしまった。

  1. 初出はRubyでその後gem化され、Node.jsもPlackもNginxもある 

Shift_JISの範囲外の文字を検出、見える化したい

まだちょっと悩んでるけど一応できたのでメモ。

若者のiconv離れ

とあるデータ加工作業があって、その中で Shift_JIS の範囲外の文字を置き換えていくという作業を行う必要があった。具体的には (株) とか「はしごだか」とかああいう文字ね。

で、これまでその辺はある程度お任せにしていて、必要になったら(実はここがミソ)自分が手で iconv をこんな風に

iconv -f sjis -t sjis filename > /dev/null

叩くことでダメな文字を指摘していた。iconv は nkf のようにいい具合に skip して処理しないので、Shift_JIS の範囲内で変換できない文字のところで以下のようにエラーを吐いて止まる。

iconv: filename:{line}:{col}: cannot convert

iconv なんてイマドキそんなに使わないのでは?と思いつつ古のワザをくり出していたのだけど、何せこの表示、見にくい。具体的には

普段テキストエディタで作業していない人には全然分かりやすくない

こんなことしなくても作業の練度が上がるとダメな文字が入ってくるパターンを学習して「だいたいこんな感じ」でいい具合に弾けるようになる。ところがこの学習の効いていない人に引き継ぐと、途端に難しくなってしまう。じゃあ iconv 使ってもらうかというとそれは厳しいなぁと思う次第。

Rubyで書けるのではと思ったら意外に難しかった

他にも加工を支援する必要があるのである程度のカタマリで rake task を用意していくことにした。[^1]そこで Ruby の Encoding でも不正な文字列が入っていると変換に失敗するので似たようなことはできるのではないかと思ってやり始めてみたら意外に奥が深かった。

結論から書くとこんな感じ。

  • 変換エラーで止まる際はどこの文字かヒントがない(バイト列は表示される)ので、 iconv よりも分かりにくい
  • Encoding#valid_encoding? は変換表に基づいて valid? の判断をしていないので、1文字ずつなめて valid でない文字の前もヒントととして表示してあげるといったことはできない
  • encode(enc, undef: :replace, replace: '??') みたいにするとダメな文字だけを特定しやすい文字に置き換えることはできる
    • ただし、全部同じ文字に置き換えられてしまうので、結局元のデータを修正する時は目視が必要
    • しかも問答無用で Windows-31J → Shift_JIS 変換を行ってはならない

全部ハマった。最後のものはどういうことかというと、

open(file, 'rb:Windows-31J').encode('Shift_JIS', undef: :replace, replace: '??')

で目的が達成できるかと思ったら、〜 がすべて不正な文字扱いになってしまう。

正しい Shift_JIS として記録されているのに Windows-31J として読み込んでしまうと 〜 を Shift_JIS の範囲外の文字として扱ってしまう[^2]。これを encode('Shift_JIS') してしまうと不正な文字だらけになってしまって意味がない。

結局こうなった

kanazawa.js v1.7でJSTDD Introと銘打って話します

来る 3/31(土)に kanazawa.js v1.7 が開催されます。参加申し込みはこちら!

kanazawa.js v1.7 - Back to Basics - : ATND

今回はなんと Mozilla さんと共同開催でかつ node.js 方面からも刺客(講師)が来るという豪勢な内容になっています。

そんな中で JavaScript での TDD について話すことになりました。まぁぶっちゃけ二つ返事で返しちゃったんですが、あとでプログラムを見てからアウェイ感にやられております。メインセッション扱いですが、聞く方はポカーンじゃないですかね、これ(笑

正直言うと不安はあります。

  1. 嘘を言うんじゃないか
  2. もっと良い話をできる人は他にたくさんいる

1 については、なんだかんだで自分はそんなにきちんと TDD を学んでおらず、我流ですし、ペアプロの機会にも恵まれませんので、自分を見直すことが難しい状況です。文字で読める情報はなんとか追ってはいますが、誤解を与える可能性が消えません。

まずここがいちばん怖い。

2 についてはまぁ、JavaScript x TDD x kanazawa という組み合わせになってしまうと話せる人はたぶんそれほど多くはないので、東京で話すことに比べれば気は楽です。

ということで、1 に関する不安を減らすために、

当日話す内容のスライドのレビューをしてくれる方を募集します。

3/11(日) 0:00 ( 3/10 24:00 ) までに「レビューしたる」と wtnabe at gmail dot com までお送りいただくか、メールアドレスを公開されている方は @wtnabe によこせと言ってもらえれば喜んで PDF を送りつけて差し上げたいと思います。

まぁ現時点で公開しちゃってもいいっちゃいいんですけど、一応叩いてもらってから当日に公開しようと思っています。

あ、出席予定の方は面白みがなくなるのでご遠慮ください(笑)

今さらSinatra最低限メモ

今度は Sinatra. 次もすぐに動かせるように公式のドキュメントを圧縮する感じで、自分の欲しい情報だけ。アプリを書く以前のレベルで、まずはページを表示できる状態を目指す。

実は昨日の kanazawa.js の成果その2

環境

今回の環境は

  • MacOSX 10.5
  • Ruby 1.8.7
  • Sinatra 1.1.3
  • Ruby 1.9.2
  • Sinatra 1.2.0

で確認した。Sinatra のバージョンが合ってないことに特に意味はない。

インストール

(ry

動かし方

1ファイルの場合は

$ ruby app.rb

でもよい。例の感じで中に

require 'rubygems' unless defined? ::Gem
require 'sinatra'

get '/' do
  'Hello, World'
end

で終了。デフォルトで port 4567 で起きる。

ちなみにこれだけで response header とこの文字列だけの response body が返る。PHPer 的には print や echo で出力したくなるけどそれは間違い。これは

get '/' do
  return 'Hello, World'
end

の return を省略してるだけ。return が response になる。rack の header を省略できると思えばいいはず。

設定とか分離したい

rack の流儀に則って config.ru を用意(別名でもいいけど)。

config.ru

require 'rubygems' unless defined? ::Gem
require File.dirname( __FILE__ ) + '/app'

run Sinatra::Application

app.rb

require 'sinatra'

get '/' do
  'Hello, World !'
end

この場合は rackup する。1

$ rackup

デフォルトで port 9292 になる。rack の流儀で変更可能。

$ rackup --port 80

※ ただし Un*x なシステムでは 1024 以下は特権ポート。のはず。

Handler を明示すれば option を与えられるけど、Handler を与えない方針なので以上。

自動でReload

mod_php や Rails の development 環境に慣れてしまったのでこれがないとやってられない。方法は3つ。

  • Rack::Reloader
  • Shotgun
  • Sinatra::Reloader

What happened to reloading in Sinatra 0.9.2? - Sinatra: Frequently Asked Questions

Rack::Reloader

普通の Rack Middleware

use Rack::Reloader

すればオッケー。でもちゃんと動かないらしい。動いてるように見えるけど、そうとは限らないということか。却下。

Shotgun

rtomayko/shotgun - GitHub

shotgun の名前がインパクトがあってよく覚えてたんだけど、今 Sinatra のサイトで読むといちばん遅いのと、fork を使うので JRuby や Windows で動かないとのこと。あやや。

インストールはもちろん

gem install shotgun

これは普通のアプリの起動にも使えるし、

shotgun app.rb

rackup 代わりにもそのまま使える。

shotgun [config.ru]

今度は port はデフォルトで 9393 になる。

Sinatra::Reloader

gem install sinatra-reloader

個人的には予想以上に依存が多い。

if development?
  require 'sinatra/reloader'
  Sinatra.register Sinatra::Reloader
end

で使える。Rack Middleware の要領で use ってやったら怒られた。Sinatra Extension と Rack Middleware は違うのね。

あと当たり前だけど

reloaderに関する書き換えを行うときは再起動した方がいいよ。

ディレクトリ構成とsetによる設定

project/
 ├── app.rb
 ├── config.ru
 ├── public/
 │     └── javascripts/
 │             ├── LAB.js
 │             ├── application.js
 │             ├── jquery-1.5.1.min.js
 │             └── jquery.Router.js
 └── views/
        ├── hello.erubis
        ├── index.erubis
        └── layout.erubis

Rails流とちょっと違う。

この public, views はそれぞれ以下のように設定できる。

README.rdoc

set :public, File.dirname( __FILE__ ) + '/static'
set :views, File.dirname( __FILE__ ) + '/templates'

要するにアプリケーションのファイルを基準にして自分で決めたきゃ決めろと。この設定方法は Capistrano に似てるなぁ。

何が設定できるかはここを見るといい。

Sinatra: Configuring Settings

root も変更できる。

テンプレートの選択とrender

基本系

get '/' do
  erb :index
end

こんな感じ。これで

  • index.erb を探して
  • erb でレンダリング

という意味になる。拡張子の変更はできるのかなぁ。できないような気がする。中で render メソッドを読んでるんだけど、ちゃんと追いかけると何か分かるかも。

render は最後に

erb :index

って書き方だけだと index.erb を表示用に使うよ、っていう宣言に見えちゃうんだけど、これはれっきとした render メソッドの呼び出しなので、処理はこの前に書くこと。と言うか

erb :index とかは最後に書け

の方が分かりやすいか。

erubis でHTMLのエスケープをデフォルトに

Rails 3 以降デビューなのでこれがないとやってられない。

require 'erubis'

set :erubis, :escape_html => true

get '/' do
  erubis :index
end

こんだけ。

オブジェクトの割り当て

これは Rails の View に似ていて(同じ?)、

  • インスタンス変数は直接参照可能
  • ローカル変数は明示的に :locals で割り当て

という方法を採る。

app.rb

get '/' do
  erubis :index, :locals => {:key => value}
end

views/index.erubis

<%= key %>

こんな感じで value が表示できる。

layoutファイル

layout ファイルは

views/layout.#{ENGINE}

になるはず。layout ファイルと個々のテンプレートで種類を変えることができるのかどうか分からない。たぶんしない方がいいと思う。

erubis の「エスケープしない」出力

layout.erubis

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title><%= @title %></title>
  </head>
  <body>
    <h1><%= @title %></h1>

    <%== yield %>

  </body>
</html>

こういう風にしとくと個々の

erubis :index

などの中身に HTML をそのまま書くことができる。

<%== %>

が決め手。

参考

  1. Handler を直接 config.ru に書く方法もあるんだけど、それだと環境を変えるためにはいちいち書き直さなきゃいけないから嬉しくないと思う。 

run-parts(periodic)で mail の送信先を変更する

いやなんかね。cron の管理が面倒で面倒で。とにかく登場するファイルの数を減らしたいわけです。

crontabファイルとスクリプトの氾濫

cron の設定管理は意外に難しい。crontab ファイル一つにすべて書くと見にくくなるし、だからと言って Linux で cron.d/ 以下に分けて書くと何時にどういうジョブが走るのか、全体の見通しが悪くなる。システム全体に関わるジョブを実行ユーザーが root じゃないからという理由で crontab コマンドを使って設定するなんてもってのほか。

また、cron.d/ 方式は crontab と実際に実行するスクリプトがそれぞれ別個に増えていく方式なので、煩雑さに拍車がかかる。

というかそもそも一つ一つのスクリプトについていちいち細かく実行時間を設定したくない。

解決案 run-parts(periodic) の活用

ではどうするのがよいのだろうと思い悩んだが、

  • 特別負荷が高くなく
  • 順番に依存しない処理

であれば、daily, weekly, monthly のように run-parts(periodic) を活用するのがよいように思う。この方法は

あるディレクトリ以下に実行するスクリプトを直接置くだけ1

という気軽さがあるし、crontab ファイルは増えないので実行スクリプトの管理だけに集中することができる。

run-parts(periodic)活用時の MAILTO 問題

crontab は独自に MAILTO や HOME などの変数を解釈してくれるし、スクリプトを実行するユーザーも個別に指定できる。これを活用することで permission の問題に対応したり結果の送信先を柔軟に変更できるのだが、run-parts(periodic) を利用した場合は残念ながら cron が run-parts を実行するユーザーが誰であるかに関係なく root 宛に mail が届く。2

この送信先を変更したければ

run-parts DIR | sendmail ADDR

として明示的にメールの送信先を変更するしかないみたい。

またこの方法で変更した場合に届くメールは当然 cron からの通知ではないので subject が自動で設定されたりはしない。man sendmail.

cf.

  1. ただしファイル名に注意。ルールに合わないファイル名のスクリプトは無視される。 

  2. みたい。今回は run-parts でしか試してないけど。 

眼鏡の鼻当て修理

眼鏡歴に反して、今まで泥酔して眼鏡をひん曲げた以外に眼鏡を壊したことはなかったのだけど、今している眼鏡は一昨年末にワイヤーが切れるなど、比較的手の掛かるものになっている。やはり舶来ものは壊れるのか。

今回は鼻当ての部分がいきなり取れた。というかねじ止めしている部分で折れたという表現が適切なんだけど、折れるとか割れるという表現を使うと鼻に当たる部分が折れたり割れたりしていそうでなんだか大げさに聞こえる。まぁそれはともかく、これを修理したんだけど、

鼻当ての修理ってどこでもタダでできるんですか。

知らなかった。今回は近くのメガネトップで直したんだけどにこやかに無料で交換できるものと500円掛かるものがございますが?と聞かれた。今までと同じものでいいですと答えたら無料で交換できるものだったのでそれでお願いした。ここの店で買ったわけじゃないんだけど、こういうのも元の眼鏡の値段に入っているんだろな。

でも実は微妙な違和感が残っている。というのも自慢じゃないが私の顔は歪んでいるので鼻に当たる部分と耳に当たる部分の角度のチューニングは繊細な匠の技を要するのだ。基本的にはデフォルトの状態で自分の視点で言って左前方向に眼鏡がずれていくので、これを止めるために鼻の左側と右耳の後ろに負荷が掛かる。この力をうまく逃がしつつ眼鏡がずれていくのを防がなければいけない。おまけに私はどうもメガネなどの圧力が顔に掛かることに対して敏感で、

ちょっとでもバランスが悪いと痛くなってしまう

のだ。今まではある店に何度か通いながら調整してもらっていたのだが、今はその店はない。はてどうしたものか。

CVSユーザのためのSubversionガイド

Subversion for CVS Users by Mike Mason の wakatono 氏による翻訳。

cygwin の問題解決?

Updated: cygwin-1.5.6-1 に、

Don't send SIGHUP on CTRL_LOGOFF_EVENT to processes running in invisible Windows stations (like services). (Corinna Vinschen)

という記述を発見。

これで 1.5 になってから悩まされていた問題は解決か? 現在の dll は 1.5.5 なので明日早速上げてみよう。ちなみに現在の最新は 1.5.7。これで解決すれば ssh on SFU の実験は必要ないな。ついでに 1.5.7 では bash のハングなんかの問題も解決する、と。うむ。

Unison は rsync より使えそうかも

Unison のマニュアルをちまちまと訳し始めたのだが、Unison は rsync の苦手な「ある特定のファイルやディレクトリだけをコピー対象とする」ことが容易にできそうだ。この点だけでも rsync を Unison で置き換えるメリットは大きいと思う。

Phoenix にしてみた

brussy テーマで説教講座 EntranceHole を開いてみたところ

ブックマークを吐き出すことに成功したので、Phoenix 0.5 + Brassy Theme にしてみた。

よい感じ。Safari 風なような、成金趣味のような。

今度は Ruby 開発者まつもとゆきひろ氏へのインタビューを

募集しています。

http://slashdot.jp/articles/03/03/07/0413248.shtml?topic=86

期限とかあるのかな?

聞いてみたいことは、、、やっぱ Ruby の入り口かなぁ。でもこれは開発者よりも Ruby Hacker に聞くべきか。あとは ActivePerl みたいな distributer というか Porter の登場を期待していますか? とか。個人的には期待しているのですが。やっぱ Perl も ActivePerl の登場は大きかったと思うし。

About

例によって個人のなんちゃらです