トップ «前の日(02-19) 最新 次の日(02-21)» 追記

3 日坊主日記


2005-02-20

_ [Rails] session_id を Cookie ではなく query parameter として渡す(2)

昨日の手順は i_controller.rb に対してのみ有効とするものでしたが、全ての Controller に対して session_id を query parameter として渡すにはどうすればいいのか、試してみました。


結局、この問題の悩みどころは、session_id を URL のどの位置に置けばいいのか、ということにあります。

Rails 標準の URL は controller, action, id の順に / で連結した形になっています。 また、controller, action, id は後ろから順に省略してもかまいません。

 /controller/action/id
 /controller/action
 /controller/

これを public/.htaccess 上では Default rewriting rules として記述しています。

 # Default rewriting rules.
 RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)/([0-9]+)$                  ?controller=$1&action=$2&id=$3 [QSA,L]
 RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)$                           ?controller=$1&action=$2 [QSA,L]
 RewriteRule ^([-_a-zA-Z0-9]+)/$                                           ?controller=$1&action=index [QSA,L]

さて、ここに session_id を追加しようとすると、controller 名, action 名と session_id の区別ができるような形式を考えないといけません。 session_id は16進数32桁で表現されますから、正規表現では /[a-f0-9]{32}/ となります (16進数の英字は小文字でよい)。 上記の Default rewriting rules では、session_id は controller, action の正規表現 [-_a-zA-Z0-9]+, [-_a-zA-Z0-9]+ にもマッチしてしまいます。また、id の正規表現 [0-9]+ にもマッチすると考えられます (実際には id にマッチする可能性は低いですが)。

この問題は、次のいずれかで解決できます:

  • 正規表現上区別できる順序で並べる。
  • session_id, controller, action に何らかの prefix を用意する。

私は単純に並べ方で解決することにします。

前回は :action_prefix に指定することにより、controller 名と action 名の間に session_id を置きました。 今回は :controller_prefix に指定して、controller 名の前に session_id を置くことにします。

 private
   def default_url_options(options)
     { :controller_prefix => @session.session_id, :controller => controller_name }
   end

controller も指定しているのは、controller_prefix を指定するときは controller も明示しないと controller 名が URL に現れないからです (なぜこうなるのか、理由は調べられていません)。

  • dispatch.rb.patch:
 --- ..\rubima\public\dispatch.rb	2005-02-02 20:02:41.171699200 +0900
 +++ public\dispatch.rb	2005-02-19 18:13:39.888715200 +0900
 @@ -7,4 +7,4 @@
  require "dispatcher"

  ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } if defined?(Apache::RubyRun)
 -Dispatcher.dispatch
 \ No newline at end of file
 +Dispatcher.dispatch cgi = CGI.new, :session_id => (params_session_id = cgi.params["session_id"]) ? params_session_id[0] : nil
 \ No newline at end of file

このようにすると、外部から任意の文字列を session_id として設定できてしまいます。 この session_id をサニタイズする必要はあるのでしょうか。

Ruby 1.8.2 標準の CGI::Session (= Rails session) は、標準では FileStore という保存の仕組みを使います。これはひとつの session_id に対してひとつのファイルを用意して、そこにデータを保存します。このファイル名が問題になりそうです。

調べてみると、FileStore は md5 = Digest::MD5.hexdigest(id)[0,16] として得た md5 をファイル名として使います (id に session_id が入る)。よって、サニタイズの必要性は hexdigest 次第ということになります。

public/.htaccess と vendor/webrick_server.rb は、前回と比べると session_id の位置が違うだけです。

  • dot.htaccess.patch:
 --- ..\rubima\public\.htaccess	2005-02-19 16:49:45.659849600 +0900
 +++ public\.htaccess	2005-02-19 18:48:36.763873600 +0900
 @@ -47,6 +47,11 @@
  # Add missing slash
  RewriteRule ^([-_a-zA-Z0-9]+)$                                            /$1/ [R]

 +# for mobile
 +RewriteRule ^([a-f0-9]{32})/([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)/([0-9]+)$   ?session_id=$1&controller=$2&action=$3&id=$4 [QSA,L]
 +RewriteRule ^([a-f0-9]{32})/([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)$            ?session_id=$1&controller=$2&action=$3 [QSA,L]
 +RewriteRule ^([a-f0-9]{32})/([-_a-zA-Z0-9]+)/$                            ?session_id=$1&controller=$2&action=index [QSA,L]
 +
  # Default rewriting rules.
  RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)/([0-9]+)$                  ?controller=$1&action=$2&id=$3 [QSA,L]
  RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)$                           ?controller=$1&action=$2 [QSA,L]
  • webrick_server.rb.patch:
 --- c:\usr\lib\ruby\gems\1.8\gems\rails-0.9.5\lib\webrick_server.rb	2005-01-28 00:25:17.376188800 +0900
 +++ vendor\webrick_server.rb	2005-02-19 18:14:47.796361600 +0900
 @@ -1,5 +1,5 @@
  # Donated by Florian Gross
 -
 +puts "loading vendor webrick_server."
  require 'webrick'
  require 'cgi'
  require 'stringio'
 @@ -123,8 +123,16 @@

    def self.parse_uri(path)
      component, id = /([-_a-zA-Z0-9]+)/, /([0-9]+)/
 +    session_id = /([a-f0-9]{32})/

      case path.sub(%r{^/(?:fcgi|mruby|cgi)/}, "/")
 +      when %r{^/#{session_id}/#{component}/?$} then
 +        { :session_id => $1, :controller => $2, :action => "index" }
 +      when %r{^/#{session_id}/#{component}/#{component}$} then
 +        { :session_id => $1, :controller => $2, :action => $3 }
 +      when %r{^/#{session_id}/#{component}/#{component}/#{id}$} then
 +        { :session_id => $1, :controller => $2, :action => $3, :id => $4 }
 +
        when %r{^/#{component}/?$} then
          { :controller => $1, :action => "index" }
        when %r{^/#{component}/#{component}$} then

2007-02-20

_ [] 市立図書館

行くたび続編が置いてある。うれしいなあ。

読んだ順: スカイ・クロラ スカイ・クロラ The Sky Crawlers ナ・バ・テア ナ・バ・テア None But Air フラッタ・リンツ・ライフ―Flutter into Life フラッタ・リンツ・ライフ―Flutter into Life

そしてさっき見つけた: ダウン・ツ・ヘヴン ダウン・ツ・ヘヴン Down to Heaven

_ 航空用語

読む前に基本用語は押さえておきたい。

これでは動作を想像するのに足りない。wikipedia で補完 [ja.wikipedia.org]:

紅子さんが解説していたな。

プッシュ/トラックとは別の切り口かな。

_ 散香ペーパクラフト

 まず、小説の時代は、ほぼ現代(2000年くらい)。ただ、
 第二次大戦以降の歴史は現実とは違います。パラレル・ワールド
 だと思って下さい。

 おそらく材料工学の関係で、ジェットエンジンが実現しなかった
 ので、飛行機は相変わらずレシプロエンジンで飛んでいます。

なるほど。

_ ウォーカロン

女王の百年密室 女王の百年密室

ウォーカロンという言葉は (消してみた ;) に出てくる。

てことでよろしく。PQR

本日のツッコミ(全2件) [ツッコミを入れる]

# ogijun [うあ、これ未読の人にネタバレかも..]

# moriq [ご指摘ありがとうございます。]


2009-02-20

_ [Rails][MySQL] FOUND_ROWS

昔作ったRails appを2.1系まで上げていじっていたところ FOUND_ROWS() の値がおかしくなってることに気づいた 。

Customer Load (0.969000)  SELECT SQL_CALC_FOUND_ROWS * FROM customers WHERE (point >= '100' and shop_id = '2') ORDER BY code LIMIT 50
Customer Columns (0.015000)  SHOW FIELDS FROM `customers` # こいつめ
SQL (0.000000)  SELECT FOUND_ROWS()

testを書いてなかった……

Columns の取得は find_by_sql(sql) の中で instantiate(record) するときに行われる。 仕方ないので find_by_sql(sql) の実装を展開して対応してみた。

  def self.find_all_with_calc_found_rows(conditions = nil, orderings = nil, limit = nil, joins = nil)
    sql  = "SELECT SQL_CALC_FOUND_ROWS * FROM #{table_name} "
    sql << "#{joins} " if joins
    add_conditions!(sql, conditions)
    sql << "ORDER BY #{orderings} " unless orderings.nil?
    add_limit!(sql, :limit => limit)

    # ret = find_by_sql(sql)
    row = connection.select_all(sanitize_sql(sql), "#{name} Load")
    @found_rows = connection.select_one("SELECT FOUND_ROWS()")["FOUND_ROWS()"].to_i
    row.collect! { |record| instantiate(record) }
  end

  def self.found_rows
    @found_rows
  end