Socket.ioとIE、CoffeeScriptの?とIE、indexOfとIEなどにつまずく。

Author: kaminaly

ども、2日連続でエントリしちゃうぜ!kaminalyです。
今日は、色々とハマったのでメモ。

Socket.ioを使った開発をCoffeeScriptで楽しく進めていた。
ChromeとFirefoxで検証して問題なかったが、
さすがIE、華麗に地獄を味合わせてくれた。
ガッデム!
※全部IEのせいにしたいけど本当は違う。


まずは軽いところから、
indexOfにまつわる話。

配列内を検索するのにindexOf使ったら、
IEに「そんなメソッドねーよバカ」と怒られました。
例えば、こんなの。

[code lang="js"]
var list = ["hoge","fuga"];
if(list.indexOf("hoge") !== -1){
	console.log("配列にhogeいるよねぇ〜");
}
[/code]

AS3では使えたので、自然と使ってたんです。
しかも、ChromeとFirefoxのjs実行エンジンでは使えるのに!

調べたらIEの配列にはindexOfの実装がないと。。

indexOfを鬼のように使ってしまっていたら、
Arrayのprototypeを拡張すれば、この危機は切り抜けられます。
自分は2カ所だけだったので、jQuery.inArrayを使いました。
※この時、そういえば、jQueryに配列検索する関数あったなと思い出した。
※prototypeもArrayを拡張してindexOf追加してたなぁ。とさらに思い出す。

次に、CoffeeScriptの?について。
たとえば、CoffeeScriptでこんなふうに書くと、

[code lang="coffee"]

console?.log? "hoge"

[/code]

こんな感じに吐き出してくれる。

[code lang="js"]
if (console != null) {
  if (typeof console.log === "function") {
    console.log("hoge");
  }
}
[/code]

ん〜。とても便利。

がしかし、IEでは、window.closeとか、document.getElementByIdとか
ネイティブな関数をtypeofすると”object”を返すやつがいる。
だから、たとえば次の例だと30%の確率でwindowが開く。でも、閉じてくれない。
//追記
自分が定義した関数でも”object”を返すことがあることがわかりました。
自分が見つけたパターンは、親と子のウィンドウで、
親から子、子から親にアクセスして関数をtypeofすると”object”を返しました。
//////

[code lang="js"]
var w;
if(Math.random() < 0.3){
  w = window.open("", "_blank");
}else if(Math.random() < 0.6){
  w = {};
}
if (w != null) {
  if (typeof w.close === "function") {
    w.close();
  }
}
[/code]

まぁ、これは極端な例だけど、
実際につまづいた。
これは、Coffeeのバグというか、漏れなのかな。
対処方法があるかは謎だけど。そのうちFixされればいいなぁ。

と、ここまでは、ちょっとした修正で終わると思っていたんだ。
そう、ここまでは。。。

最後にSocket.ioとIE
ラスボスが待ってた。
いや、攻略方法がわかってれば楽勝だったんだけど、
これは、解決までかなり遠回りした。

2つのエラーを回避したあと、見慣れないエラーログを見たんだ。
ソケットポリシーファイルがないから君には通信させるわけにはいかないって
見知らぬflashが言っている。

どうやら、Socet.ioのクライアントがIEでのwebsocketの通信をあきらめたあと、
次に選択したのがflashのsocket接続だったようだ。

うんうん。その話は聞いたことがあるよ。
でも、ソケットポリシーファイルの話はしらないな。
調べてみると、flashでsocket接続する時は、
crossdmain.xmlみたいなxmlを用意しないといけないと。
しかも、843番ポートにアクセスするからよろしくねとか言ってる。

apacheとnode.jsは使ったから、
今度はpytonでサーバ立ててみるか。

pythonは入ってたから、pip入れて、必要なモジュールをインストール。
環境を整えると、まえにHello Worldしたときのコードをちょこちょこっと書き換えてアップした。
うんうん。843番ポートでアクセスすると、xml返してくるね。うんうん。

で、IEでチェック。

エラー。

さらに深く調べると、httpではなくてソケットで接続するからヨロ♪って書いてあった。
ちきしょー。

pythonでソケット接続のコード書き始めたけど、
perlのflashソケットポリシー用サーバのサンプルコードを見つけたので、
perlに乗り換える。
もう既に数時間経過していたから、ためらいはなかった。

perlのサーバ起動。
・・・
エラーは消えた?
がしかし動かない?
What’s マイケルお祭りかい?

と、ここで、node.jsのログに怪しい文字列!
warn transportsにflashsocketが見つからないぜベイベ。

なるほど、transportsとflashsocketは見覚えがあった。
Socket.ioの設定関連だな。

すぐに見つかった。
Socket.IO API 解説
このページのオプション設定という項目に答えがあった。
デフォルトでflashsocketはtransportsに含まれていないらしい。
じゃあ、なんでクライアントはflashsocket選んじゃうのさoTL

環境の切り替えとか必要ないし、Socket.ioのソース読んだら、
io.configureで設定しなくてもio.setでそのまま設定できそうだったから、
シンプルに下記を足した。

ちなみにCoffeeScriptです。

[code lang="coffee"]
io.set 'transports', [
	'websocket'
	'flashsocket'
	'htmlfile'
	'xhr-polling'
	'jsonp-polling'
]
[/code]

ついにIEでも動き出したぞ。
やたー!!

どうやら、flashsocketを設定すれば、
Socket.ioがflashソケットポリシーを吐いてくれるみたいなので、
pythonやperlの件は無意味だったんだよ。
マジ泣ける。