Developer of AutoPagerize / Github / Twitter / Bookmark / Photo / Old Blog
Heads up: nosniff header support coming to Chrome and Firefox https://github.com/blog/1482-heads-up-nosniff-header-support-coming-to-chrome-and-firefox
ChromeとFirefoxでnosniffってどういうことなんだろうと思って少し調べた。
IE8から、X-Content-Type-Options: nosniff があった場合は、ファイルの中身を自動判別する機能が無効になる。htmlじゃないものが自動判別でhtmlだと誤判別されて表示されることで起こるXSSを防ぐことができる。
Internet Explorer 8 のセキュリティ Part VI: Beta 2 の更新項目 http://msdn.microsoft.com/ja-jp/ie/dd218497.aspx
IE9から、X-Content-Type-Options: nosniff があった場合は、scriptが参照する応答でContent-Typeが以下の場合でない場合に読み込まない。
application/ecmascript
application/javascript
application/x-javascript
text/ecmascript
text/javascript
text/jscript
text/x-javascript
text/vbs
text/vbscript
スタイルシートはtext/cssでない場合に読み込まない。
MIME 処理の変更: X-Content-Type-Options: nosniff http://msdn.microsoft.com/ja-jp/library/ie/gg622941%28v=vs.85%29.aspx
ChromeとFirefoxのnosniffサポートという話は、IE9から加わった効果のことだった。GithubやGistのrawはtext/plainでnoniffをつけるから、Githubのアドレスを直接参照するスクリプトやスタイルシートは、ブラウザによっては動かなくなるよという話。
手元のChrome(28.0.1500.6 dev)だとjsはブロックするけど、cssはブロックしなかった。Firefox(Firefox 21 beta)はまだどちらもブロックしなかった。
GithubやGistのjsやらcssを直接参照するのは、ブラウザ次第で動かなくなるし、そもそもGithubの負荷になって迷惑になるのでやめましょう。
Github APIにアクセスするときにUser-Agentを指定する要があったので追加しました。アップデートして使ってください。

東京Node学園祭2012の発表資料です。半分くらいはelasticsearchの紹介なのと、Node.jsから使ってる部分はhttpクライアントしか使っていないので、他のプログラミング言語で使う場合でもなんとなく雰囲気は分かるんじゃないかと思います。全文検索やelasticsearchに興味があれば見てみてください。
Fulltext search with Node.js and elasticsearch
2012 11/18 tokyo nodefest 2012 @swdyh
http://swdyh.github.com/nodefest2012_es_node_slide/slide.html

今度の日曜(11/18)の東京Node学園祭2012でNode.jsとelasticsearchについての発表をします。時間が長いのでかんたんなライブコーディングもしてみるつもりです。ひさびさの発表なのと初ライブコーディングなので緊張しそうだけど、Substackさんの裏なので気楽にやろうと思います。
http://nodefest.jp/2012/session.html 14:15〜15:00 トラックB
チケットのキャンセルが出たり埋まったりを繰り返してるみたいなので、チケットなくてもまだ手に入る可能性はありそうです。
http://atnd.org/event/Nodefest2012 https://twitter.com/nodefest
いまちょうどスライドを作っていて、スライドの内容をmarkdownで書いて、redcarpetでhtmlにして、それをもとにnokogiriでshowerのhtmlツリーを作ってを書き出すみたいなよくわからないことをしながらスライドを作っています。

Yet Another npm search http://ya-npm-search.herokuapp.com/
“Yet Another npm search” is a search engine for npm packages that indexed by elasticsearch. It runs on heroku and bonsai elasticsearch addon.
command line interface:

npm install -g ya-npm-search
ya-npm-search keyword
source code:
github ya-npm-saerch-server https://github.com/swdyh/ya-npm-saerch-server
github ya-npm-search-cli https://github.com/swdyh/ya-npm-search-cli
Mac/LinuxのWiFiで位置情報を取得 http://shokai.org/blog/archives/6399
このshokaiさんのwifi_locationはRubyで書いてあったのでJavaScriptに書きなおして、Node.jsで使えるものを作りました。やっていることはほとんど同じで、airpot(Mac)かiwlist(Linux)コマンドでWiFiの電波を取得、それを使ってGoogle MapのAPIから位置情報を取得します。WiFiの電波はネットがなくてもいいけれど、位置情報の方はネットが使えないと取得できません。
Linuxは持っていなくてネットで見つけたiwlistの出力を使って書いていて、実際の動作は確認してないので、おかしかったら教えて下さい。
インストール
npm install wifi_location
使い方
var wifiLocation = require('wifi_location')
wifiLocation.wifiTowers(function(err, val) {
console.log(err, val)
})
wifiLocation.location(function(err, val) {
console.log(err, val)
})
% node
> var wifiLocation = require('wifi_location')
undefined
> wifiLocation.location(console.log)
undefined
> null { latitude: 35.7124159,
longitude: 139.774889,
address:
{ country: '日本',
country_code: 'JP',
region: '東京都',
city: '台東区',
street_number: '1' },
accuracy: 57 }
swdyh/node_wifi_location https://github.com/swdyh/node_wifi_location


https://userscripts.org/scripts/source/139785.user.js
FirefoxのGreasemonkeyとChromeで動きます。 インストールすると、一覧に”via”というリンクがつくようになって、それをマウスオーバーすると転載元ページへのリンクになります。転載元がない場合もあって、そのときは消えてなくなります。
Chromeで直接Webからインストールできない場合は、一旦ファイルをダウンロードして、右上のレンチみたいなアイコン -> ツール -> 拡張機能、を選んで、拡張機能のページが開いたらそこにダウンロードしたファイルをドラッグしてドロップしてください。
urlパラメータにgifのurlを渡す
http://stopgif.herokuapp.com/?url=http%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F6%2F6a%2FSorting_quicksort_anim.gif
indexパラメータでコマを指定
http://stopgif.herokuapp.com/?url=http%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F6%2F6a%2FSorting_quicksort_anim.gif&index=20
altパラメータでエラー時に出す画像を指定(リダイレクト)
http://stopgif.herokuapp.com/?url=http%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F6%2F6a%2FSorting_quicksort_anim.gif&alt=http%3A%2F%2Fexample.com%2Fbar.gif
Rubyで書いていて、gifの処理はRMagick、Web API部分はsinatra、サーバはheroku ceder。とりあえず動く状態にした段階で遅いんだけど、open-uriの部分をEventMachineのにしたり、キャッシュをちゃんとすればもっと速くなると思う。
swdyh/stopgif GitHub https://github.com/swdyh/stopgif
HTMLのimgタグでアニメーションを禁止にできたら、自分の問題は解決なんだけど、そういうのはないんだろうか。jsgifを使ってなんとかするというのも解決方法のひとつだったと思うけど、外部の画像を処理するときにsame origin policyに引っかかって面倒そうだったのでやらなかった。
node_redisを使うときにエラーをハンドリングしていないと、接続に失敗したときにプロセスごと死んでしまう。このときのエラーはtry catchだと補足できないので、onメソッドでerrorイベントを補足するようにしないといけない。errorイベントは特殊なイベントで、このイベントを補足しておくと、他になにもしなくてもプロセスが死ぬことがなくなって再接続まで自動でやってくれる。
var util = require('util')
var redis = require('redis')
var c = redis.createClient()
c.on('error', function(err) {
util.log('redis_err ' + String(err))
})
connect-redisを使ってセッションをredisに保存する場合、RedisStoreのclientがredisクライアントなので、そこにエラー処理を書いておく。
var util = require('util')
var express = require('express')
var RedisStore = require('connect-redis')(express)
var rs = new RedisStore()
rs.client.on('error', function(err) {
util.log('redis_err ' + String(err))
})
var app = express.createServer()
app.use(express.cookieParser())
app.use(express.session({ secret: 'xxx', store: rs }))
app.get('/', function(req, res) {
req.session.count = req.session.count || 1
res.send(String(req.session.count++))
})
app.listen(9999)
nodeのプロセスを動かしてからredis-serverを再起動してみると、エラーが補足さることと再接続されることが確認できる。
サーバ起動時にredis-serverもnodeも動かすようにしていたら、redis-serverのが起動が遅くてnodeの方が死んでいたみたいなことがあった。redisを使うときはいつでもエラーイベントを補足するようにした方がいい。
mranney/node_redis GitHub https://github.com/mranney/node_redis
Rubyのaws-sdkを使って、定期的にディレクトリごとs3にアップロードしていて、手元のファイルとs3のファイルが同じかどうかをetagでチェックしていたら、etagがファイルのmd5の場合とそうじゃない場合があって困ったので調べてみた。
Generally the ETAG is the MD5 of the object. If the object was uploaded using multipart upload then this is the MD5 all of the upload-part-md5s.
http://docs.amazonwebservices.com/AWSRubySDK/latest/AWS/S3/S3Object.html#etag-instance_method
ふつうはアップロードしたもののmd5になるけれど、multipart uploadを使ったときは部分ごとのmd5を合わせたもののmd5になる、ということだった。
自分の場合でファイルのmd5とそうじゃないのがあったのは、アップロードするときに使っていたaws-sdkのS3Object#writeが16MBより大きい場合、5MBごとにmultipart uploadをするからだった。この16MBや5MB、それからmultipart uploadをするかどうかはオプションで指定できた。それでmultipart uploadをしなければ一応解決なんだけどmultipart uploadのが速いので、multipart uploadの場合のetagを実際に計算してみた。
分割したデータのmd5(hex表記じゃないそのままのもの)を全部つなげたデータのmd5のhex表記-分割数
こんな感じに計算すればよかった。分割部のmd5はhexじゃないのを使わないといけなくて、ここが分からなくてはまった。Rubyのコードだとこんな感じ。
md5s = []
while !io.eof?
md5s << Digest::MD5.digest(io.read(part_size))
end
r = Digest::MD5.hexdigest(md5s.join('')) + '-' + md5s.size.to_s
gemにしてコマンドラインで使えたり、Rubyのライブラリとして使えるようにしものを作っておいた。
swdyh/s3etag GitHub https://github.com/swdyh/s3etag
インストール
% gem install s3etag
コマンドラインで使う
% s3etag
s3etag file
-t, --threshold threshold
-p, --max-parts max-parts
-s, --min_part_size min_part_size
% s3etag text.txt
91fdac689d4861c9ae7a0afa21a1f6b8-18
% s3etag -s 10000000 text.text
32aafcd9748824e559b4dbd6b908f6fa-10
Rubyのライブラリとして使う
require 's3etag'
p S3Etag.calc(:data => 'a' * 1000)
p S3Etag.calc(:data => 'a' * 1000, :threshold => 100, :min_part_size => 100)
p S3Etag.calc(:file => 'test.txt')
p S3Etag.calc(:file => '.text.text', :threshold => 100, :min_part_size => 100)

madはmarkdownベースのmanみたいなもの。もともとmarkdownはそのままでも読みやすいんだけど、強調とかコードに色がついて読みやすくなっている。mad-pagesにNode.jsのドキュメントとhttpステータスとかが用意されていて、数は多くないけれどなかなか便利。
Going mad(1) - TJ Holowaychuk http://tjholowaychuk.com/post/21100445420/going-mad-1
mad(1) node.js pages - TJ Holowaychuk http://tjholowaychuk.com/post/23368504100/mad-1-node-js-pages
visionmedia/mad https://github.com/visionmedia/mad
visionmedia/mad-pages https://github.com/visionmedia/mad-pages
インストール
% hub clone visionmedia/mad
% cd mad
% make install
% mad --update
madの一覧表示
% mad -l
madを見る
% mad node.fs
% mad http.status
% mad ansi
madのデータは/usr/local/share/madにあって自分で追加してもいい。あと表示色は/usr/local/etc/mad.confでやMAD_CONFIGで指定したファイルで変えられる。色の指定はmad ansiを見るのが便利。そもそも、1mとかじゃなくてboldとかで指定できたら良さそうだけど。
あと、てきとうだけどmad用のzsh補完関数を書いた。zsh補完関数の書き方、全然わからないし意味わからないからいつも困るけど、あるとやっぱり便利だから見よう見まねでなんとか書いてる。
https://gist.github.com/2933058
#compdef mad
_arguments \
'(-U --update-self)'{-U,--update-self}'[update mad(1) itself]' \
'(-u --update)'{-u,--update}'[update remote mad-pages]' \
'(-v --version)'{-v,--version}'[output mad version]' \
'(-h --help)'{-h,--help}'[output this help information]' \
'(-l --list)'{-l,--list}'[list mad-pages]' \
'-[read from stdin]' \
'*:: :->' && return 0
if (( CURRENT == 1 )); then
`mad -l | grep '^ \w' | sed 's/^/compadd /'`
_files -g "*.(md|markdown)"
return
fi

var util = require('util')
var events = require('events')
var express = require('express')
var redis = require('redis')
var app = express.createServer()
var notifyEmitter = new events.EventEmitter()
var redisClientSubscriber = redis.createClient()
redisClientSubscriber.on('message', function(ch, d) {
util.log(JSON.stringify(['redisSubscriber', ch, d]))
notifyEmitter.emit('myevent', 'redis node-sse-example:myevent ' + d)
})
redisClientSubscriber.subscribe('node-sse-example:myevent')
setInterval(function() {
var len = notifyEmitter.listeners('myevent').length
util.log(JSON.stringify(['listeners.length', len]))
if (len > 0) {
notifyEmitter.emit('myevent', 'interval!')
util.log(JSON.stringify(['emit myevent']))
}
}, 10 * 1000)
app.get('/', function(req, res) {
var r = '<html><head><script>\n'
var f = function() {
var es = new EventSource('/sse')
es.onmessage = function(m) {
document.body.innerHTML += m.data + new Date() + '<br />'
}
es.onerror = function(e) {
console.log('err:', e)
}
}
r += 'var f = ' + f.toString() + '\n' + 'f()'
r += '\n</script></head><body><h1>sever sent events test</h1></body></html>'
res.send(r)
})
app.get('/sse', function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no' // disable nginx proxy buffering
})
res.write('\n')
var f = function(d) {
res.write('data: ' + d + ' \n\n')
}
notifyEmitter.on('myevent', f)
req.on('close', function() {
notifyEmitter.removeListener('myevent', f)
})
})
app.listen(9301)
console.log('see http://localhost:9301/')
console.log("publish by redis 'redis-cli publish node-sse-example:myevent foo'\n")
Add-on SDKのsimple-prefsモジュールを使って簡易な設定画面を作ってみる。SDKのドキュメントは情報が少ないので、Add-ons Blogの記事も見るわかりやすい。
Just Landed: Simple Prefs API | Mozilla Add-ons Blog
http://blog.mozilla.org/addons/2011/12/08/just-landed-simple-prefs-api/
simple-prefs-on SDK Documentation
https://addons.mozilla.org/en-US/developers/docs/sdk/latest/packages/addon-kit/simple-prefs.html
設定画面の作り方は、package.jsonに設定項目の情報を書いておくだけ。アドオンの詳細画面に設定項目が追加される。
package.json
{
"id": "jid1-7wBt2LagLajA6Q",
"name": "fx-addon-prefs-example",
"fullName": "fx-addon-prefs-example",
"author": "swdyh",
"version": "0.0.1",
"preferences": [{
"name": "somePreference",
"title": "Some preference title",
"description": "Some short description for the preference",
"type": "string",
"value": "this is the default string value"
}]
}

この設定画面で設定された値はユーザが変更するたびに自動でFirefoxに保存されるので、あとは設定が変更されたときの処理をイベントリスナーで書いておけば終わり。
main.js
var prefs = require('simple-prefs')
exports.main = function() {
prefs.on('stringPreference', function(name) {
console.log('pref changed.', name, prefs.prefs[name])
})
}
integerやboolでも使い方はstringと同じ。boolはチェックボックスになる。
{
"type": "bool",
"name": "boolPreference",
"value": true,
"title": "Boolean Pref"
},
{
"type": "integer",
"name": "intPreference",
"value": 42,
"title": "Integer Pref"
},

ドキュメントには今のところ、integer、bool、stringの3種類と書いてあるけれど、ソースを見るとさらにいくつかtypeが利用できそうだった。ファイルやディレクトリの選択、色の選択など。
VALID_PREF_TYPES = ['bool', 'boolint', 'integer', 'string', 'color', 'file',
'directory', 'control']
https://github.com/mozilla/addon-sdk/blob/master/python-lib/cuddlefish/options_xul.py
それからcontrolというのは、ボタンが配置されるだけで、他のもののように設定値が保存されることはないけれど、そのボタンが押された時になにか処理を実行するということができる。
{
"type": "control",
"name": "controlPreference",
"label": "controlPref",
"title": "Control Pref"
}

ボタンが押されたときの処理を追加するのは、設定値の変更と同じようにイベントリスナーを追加しておけばいい。
main.js
var prefs = require('simple-prefs')
exports.main = function() {
prefs.on('controlPreference', function(name) {
console.log('pressed controlPreference')
})
}
simple-prefsという名前の通り、単純な設定であればこれを使うとすごく楽にできる。これでできない範囲のものは、自分でhtmlなりxulなりで書くことになると思う。AutoPagerizeはすでにHTMLで設定画面を作ってあったのとtextareaを使いたいので、controlを使ってそれを表示するボタンだけを設置することにした。
New Vulnerability ucnv http://www.cltvt.org/exhibiton/2012/04/08/729.html
Page 1 of 2