0

async.jsで非同期処理をまとめる

複数の非同期処理が両方終わるまで待って結果をまとめて受け取るとか、順番に実行とかできる。


2つHTTPリクエスト同時に使って結果をまとめて受け取る例

var async = require('async');
var request = require('request');

async.parallel(
[function(exit){
request.get('http://shokai.org', function(err, res, body){
if(err) exit('HTTP Request Error');
if(res.statusCode != 200) exit('HTTP Status ('+res.statusCode+')');
exit(null, body);
});
}, function(exit){
request.get('http://masui.sfc.keio.ac.jp', function(err, res, body){
if(err) exit('HTTP Request Error');
if(res.statusCode != 200) exit('HTTP Status ('+res.statusCode+')');
exit(null, body);
});
}], function(err, results){
if(err) throw err;
console.log(results[0].match(/<title>(.+)<\/title>/i)[1]);
console.log(results[1].match(/<title>(.+)<\/title>/i)[1]);
}
);
配列の中に2つのfunctionが入っていて、両者でexitという名前で使っている第一引数がエラー、第二引数が結果のコールバックを呼ぶとasync.parallelの第二匹数に指定した関数にまとまる。

parallelの他にseriesとwaterfallがある。


実行結果

shokai.org - 橋本商会
Masui Lab.


参考

0

スケール設定してないRubyで書かれたHerokuアプリを止める方法

いままでPassenger+Apacheとか、あらかじめforkさせて複数プロセス待機させておくとかしていたから気づかなかった。
Herokuにお金を払ってwebサーバー複数プロセス起動させているなら問題ないと思います。


1リクエストずつしか処理できない

HerokuでRailsやSinatraを使うときは、Rubyで書かれたwebサーバー(thinとか)が1プロセスだけでそのまま動いているのだが、webrick/mongrel/thinあたりは1リクエストずつ順番にしか処理できない。


つまりこれとかがそうなんだけど、
http://shokai.herokuapp.com/base64img
リクエスト処理中のアプリ自身にリクエストを送ってしまうと固まってしまう。引数にURLを渡されて別のサイトにHTTPリクエストするのを想定して作られているアプリとかに有り得るミスだと思う。

上は画像のURLを渡されるとbase64encodeしたimgタグを作ってくれるページなんだけど、ここにhttp://shokai.herokuapp.comとか入れると固まるのだった。
(今は固まらないようにしてある)

リクエストうける→自分自身にリクエストしてbase64encodeしようとする→1リクエストずつしか処理できないので、待たされる→最初のレスポンスを返せない!!
となる。


対策

上の画像をbase64エンコードするアプリの例
get '/base64img' do
halt 400, 'bad request' if params[:url] =~ /^https?:\/\/#{env['HTTP_HOST']}#{env['SCRIPT_NAME']}/

## 処理続き〜〜
end

こんな感じでリクエスト送る前にチェックしてhaltすると良いと思う。

でもこれだけだとbitly等の短縮URLを使うとすり抜けられる。
どのHTTPライブラリを使うかにもよるけど、自動的に30x系リダイレクト処理をしてくれるライブラリを使う場合はいちいちHTTPヘッダのLocationの指す先を調べないと、HTTPリクエスト処理中の自分自身にHTTPリクエストしてしまう。


ちなみにNode.jsだと複数リクエスト同時に処理できるので全然問題無かった。

0

画像を安全に埋め込みたい

チャットに画像のURLが貼られた時に、安全にimgタグを埋め込みたいという議論があったので書いておく。

一番単純な実装だと、

var s = "はろー http://shokai.org.ex/example.jpg てすとてすと";
s.replace(/(https?:\/\/.+)\.(jpe?g|gif|png)/g, "<img src=\"$1.$2\">$1.$2</img>");
のようにすると
"はろー <img src="http://shokai.org.ex/example.jpg">http://shokai.org.ex/example.jpg</img> てすとてすと"

になるのだが、よく考えると http://tumblr.com/logout#.png みたいなのを貼られるとimgタグが描画された瞬間にログアウトしてしまう。

正規表現がショボいのも悪いけど、最近はURL末尾に画像の拡張子が付いていなくても画像が返ってくるサービスもある。そういうのが埋め込めない。

問題

いくつか問題というか解決策がある。どれが正しいのかは知らない。
  1. GETでアクセスしただけでログアウトしてしまうサービスが悪い
  2. imgタグを描画する前に、本当にURLがimageなのか調べるべき。でもJavaScriptだけではcontent-type調べられない。
  3. ブラウザはサーバーに期待するmime-typeを付けてリクエストする。imgタグからリクエストするなら X-Mime-Condition: image/* とか
3が一番いいけど、2が現実的かな。


api.geta6.net

2について、geta6が2つ作ってくれた。

http://api.geta6.com/imagent
URL先のページや画像をPhantomJS+xvfbでキャプチャして画像で返してくれるAPIで、これを使うとなんでもimgタグに埋め込んで大丈夫になる。
画像だったら画像がそのまま返ってくるし、webページも画像になって返ってくる。
resizeやcropもできる。

http://api.geta6.com/headers
URLを渡すとmime-typeがJSONPで取得できるので、JavaScriptで判別してimg/embed/audio/iframe/aなど適当なタグで埋め込む。


content-type.herokuapp.com


Node.jsの勉強がてら、簡単なのを作ってみた。
http://content-type.herokuapp.com/


http://content-type.herokuapp.com/type/image/IMAGE_URL のようにすると、typeがimageでなければ埋め込まれない。
<img src="http://content-type.herokuapp.com/type/image/http://twiticon.herokuapp.com/shokai/bigger">


画像じゃない時はエラー画像が表示される。
<img src="http://content-type.herokuapp.com/type/image/http://tumblr.com/logout#.png">

あと、 http://content-type.herokuapp.com/type/http://twiticon.herokuapp.com/shokai でmime-typeが取得できるのでこれを使って適当にタグを埋め込むJSライブラリを作ると便利かもしれない。

Herokuのmemcache

上のをNodeで作ってて誤算だったのは、HerokuのNode.jsではMemcacheが使えなかったことだった。
HerokuのMemcacheはSASLという認証が必要なんだけど、SASLをサポートしているNode.jsのmemcacheライブラリがmemjsしかない。でもmemjsもSASL通らなかった。

しょうがないのでmemory-cacheというオンメモリキャッシュを使った。

0

USキーボード vs JISキーボード

Macの話。どちらの配列も普段から使っているけど、俺はJISの方が好き。

結論としてはおっさんは初めて使ったキーボードがUSなのだろうから、USキーボードに愛着があるんだろうしそのままUS使えばいい。
日本語打たないならUSの方がいいかも。

command+tabでのウィンドウ切り替え

USキーボードではtabの上に~があり、これで逆順に回せる


USは数字キーの真上にF1等のキーがあるので押しやすい

対応する同じ番号のFキーがある


USは英数、かなキーが無いのでcommand+spaceで入力方法を切り替える

モード変換をトグルで切り替えるのは正気の沙汰じゃないですよ、無駄が多い。
keyremap4macbookで左右のcommandそれぞれにその機能を割り当てられるので、USキーボードでも問題ない
でもcommand+wが誤爆する事があって悲しい事になる
ほとんどのアプリはcommand+wに警告を出してくれない


USキーボードは+と-、[と]などが左右に並んでいる

わかりやすい
キーボードショートカットもUSの方が押しやすいアプリが多いらしい?
俺は慣れた


JISキーボードはキーが左に寄っているので、トラックパッドに手が乗ってしまう

最近のMacのトラックパッドは手のひらは検出しないようになってるので問題ない


JISキーボードはキートップに日本語と英語両方印字してあってゴチャゴチャしている

むしろ耳なし芳一っぽくてかわいい、バグが寄ってこなくなりそう


USキーボードの方がspaceキーが大きい

JISでも十分に大きい
spaceキーが大きいと、右commandキーをかなキーに割り振っている時に押しづらい


USキーボードはEnterキーが小さい

ッターンできない


USキーボードは記号が右に集中してて小指が疲れる

全体的に横に長いので小指が仕事しまくってつらい
特に\と|が、JISだと上段なので薬指でいけるんだけど、USでは小指一択になる


JISキーボードは左手小指でcontrolキーを押せるが、USキーボードだとcapslockになっている

Macの環境設定で変更できるのでUSキーボードでも問題ない


昔のMacのJISキーボードはcommandキーが左側にしか無かったらしい

今は左右にある


みんなJISキーボードに慣れてる

おっさんは最初に触ったキーボードがUSなので、US派になる
おっさん以外は、最初に触ったキーボードがJISキーボードのはず
国内メーカーのパソコンも店に並んでるMacもみんなJISキーボード


JISキーボードは右側の記号のキーが小さくて押しづらい

最近はそうでもない
少なくともMacbook Air11インチ、Macbook Pro17インチだと全キー同じサイズになってる
ネットブックとか、昔のノートパソコンとかだと右のほうのキーが露骨に小さかったけど・・

0

Node.jsでMemcachedを使う

インストール

https://github.com/elbart/node-memcacheを使う。

npm install memcache


プログラム書いた

2秒expireで書き込み→すぐ読む→3秒待って読む、という例。

memcache-test.js
var memcache = require('memcache');
var client = new memcache.Client(11211, 'localhost');
client.connect();

var get = function(){
client.get('name', function(err, val){
if(!err) console.log(val);
else console.error('get error');
});
};

client.set('name', 'shokai', function(err, res){
if(err){
console.error('set error');
process.exit(1);
}
get();
setTimeout(function(){
console.log('wait 3 sec');
get();
}, 3000);
}, 2);


実行

node memcache-test.js


結果

shokai
wait 3 sec
null

値がまだ書き込まれていないかexpireしている時はnullが返ってくる。
返り値のerrorは、setはnullが、getはundefinedが入っていた。
setのresultには保存失敗した場合はSTORED返ってきた。errorの方だけ見ればいいかも。

接続エラー

memcahedが起動していなかったり、client.connect()せずにsetするとこういうエラーがでる。
最初connect忘れていてなんだこのエラーは!?と思った。
node_modules/memcache/lib/memcache.js:110
this.conn.write(query + crlf);
^
TypeError: Cannot call method 'write' of null