node.js - socket.io - rails - sessionを共有する方法
railsで他言語とsessionを共有する際問題となるのが、
内部的にmarshalという形式でデータを保持しているため、
ruby以外の言語でsessionを取得しようとすると、marshalのdeserializeが出来ないとセッションデータを読み込むことが出来ません。
そのため、セッションを共有するために
・セッションデータの保持形式を marshal -> yaml or json or messagepack ....に変更
・ついでにセッションデータをクッキーではなくdb保存に変更
の2つを行います。
※今回はデータ保持にjson , セッションdbに mongodbを使います。
まずrails、node.js両者でmongoとsessionライブラリを用意。
type - orm
library - mongo_mapper
git - http://mongomapper.com/
type - session store
library - mongo_session_store
git - https://github.com/brianhempel/mongo_session_store
# /path/to/app/Gemfile gem 'mongo_mapper' gem 'bson_ext' gem 'mongo_ext' gem "mongo_session_store-rails3"
セットアップはgit参照
node.js
library - mongoose
git - https://github.com/LearnBoost/mongoose
npm install mongoose
セットアップはgit参照
次にrailsでデータ保持形式を marshal -> json に変更するためモンキーパッチを充てます。
# /path/to/app/config/initializers/session_store.rb YourApp::Application.config.session_store :mongo_mapper_store module ActionDispatch module Session class MongoMapperStore < MongoStoreBase class Session include MongoMapper::Document set_collection_name MongoSessionStore.collection_name key :_id, String key :data, String, :default => {}.to_json() timestamps! end end class MongoStore < MongoStoreBase class Session def initialize(options = { }) @_id = options[:_id] @data = options[:data] || {}.to_json @created_at = options[:created_at] @updated_at = options[:updated_at] end end end class MongoStoreBase < AbstractStore private def pack(data) data.to_json end def unpack(packed) return nil unless packed data = JSON.parse(packed) if data.has_key?('flash') data['flash'] = ActionDispatch::Flash::FlashHash.new.update(Hash[*data['flash']]) end data end end end end
変更点は
・ソースのMarshalを使っていた部分をjsonに直しただけ
・なんかエラーが出たので微修正。参考は以下
http://memo.yomukaku.net/entries/314
でrailsについてはjson形式でデータが保持されるはずです。
あとはmongooseでsessiondb読むだけです。
ついでにsocket.ioのhandshake時にsessionを書き込みます。
大体こんな感じになると思います。
※以下はcoffeescriptです
io = require('socket.io').listen(3002) connect = require('connect') sys = require('sys') util = require('util') events = require("events") mongo = require('mongoose'); mongo.connect('mongodb://localhost/your_session_db') Schema = mongo.Schema SessionSchema = new Schema _id: { type: String, required: true, unique: true } data: { type: String, default: '{}' } expires: { type: Date, index: true } Session = mongo.model('Session', SessionSchema) # config io.configure -> io.set 'authorization', (handshake,callback) -> cookie = handshake.headers.cookie sessid = parseCookie(cookie)['_yoursession_id'] Session.findOne { _id : sessid},(err,session) -> if err return callback(err,false) else handshake.session = JSON.parse(session.data) return callback(err,true) io.of('/channels').on 'connection', (sock) -> console.log sock.handshake.session
なんか不具合出るんじゃないかと不安なので、不具合出た方連絡下さい。