Monday, July 07, 2008

How to: load the session from a query string instead of a cookie

We use SWFUpload to upload some images in a login-restricted part of the site.

There is a problem however, in that we weren't able to get SWFUpload to send the normal browser cookie along with it's HTTP file uploads, so the server couldn't tell which user was logged in.

The 'normal' solution to this is to add the session key to the query string, and have the server load the session from the query string if the cookie isn't present, only ruby/rails doesn't support doing that.

a nice guy with the handle 'mcr' in #rubyonrails on irc.freenode.org worked out how to make this work, by patching ruby's cgi/session.rb

Instructions

  1. Copy cgi/session.rb out of your ruby standard library into your rails app's lib folder
  2. explicitly load the file out of lib, which will then overwrite the built in code

Needless to say this will stop working if the ruby standard library version of cgi/session changes, but I don't see that as being very likely

Patch in unified diff format:


--- /usr/lib/ruby/1.8/cgi/session.rb 2006-07-30 10:06:50.000000000 -0400
+++ lib/cgi/session.rb 2008-07-07 21:07:12.000000000 -0400
@@ -25,6 +25,9 @@
 
 require 'cgi'
 require 'tmpdir'
+require 'tempfile'
+require 'stringio'
+require 'strscan'
 
 class CGI
 
@@ -243,6 +246,20 @@
     #       undef_method :fieldset
     #   end
     #
+    def query_string_as_params(query_string)
+      return {} if query_string.blank?
+      
+      pairs = query_string.split('&').collect do |chunk|
+ next if chunk.empty?
+ key, value = chunk.split('=', 2)
+ next if key.empty?
+ value = value.nil? ? nil : CGI.unescape(value)
+ [ CGI.unescape(key), value ]
+      end.compact
+
+      ActionController::UrlEncodedPairParser.new(pairs).result
+    end
+
     def initialize(request, option={})
       @new_session = false
       session_key = option['session_key'] || '_session_id'
@@ -253,6 +270,7 @@
  end
       end
       unless session_id
+ #debugger XXX
  if request.key?(session_key)
    session_id = request[session_key]
    session_id = session_id.read if session_id.respond_to?(:read)
@@ -260,6 +278,12 @@
  unless session_id
    session_id, = request.cookies[session_key]
  end
+
+ unless session_id
+   params = query_string_as_params(request.query_string)
+   session_id = params[session_key]
+ end
+
  unless session_id
    unless option.fetch('new_session', true)
      raise ArgumentError, "session_key `%s' should be supplied"%session_key


No comments: