diff --git a/lib/mongrel.rb b/lib/mongrel.rb index 15e493c4..2c63e61b 100644 --- a/lib/mongrel.rb +++ b/lib/mongrel.rb @@ -597,44 +597,103 @@ module Mongrel # to get existing CGI based systems working. It should handle everything, but please # notify me if you see special warnings. This work is still very alpha so I need # testers to help work out the various corner cases. - class CGIWrapper < ::CGI public :env_table attr_reader :options - + + # these are stripped out of any keys passed to CGIWrapper.header function + REMOVED_KEYS = [ "nph","status","server","connection","type", + "charset","length","language","expires"] + def initialize(request, response, *args) @request = request @response = response @args = *args @input = StringIO.new(request.body) - @options = {} + @head = {} + @out_called = false super(*args) end # The header is typically called to send back the header. In our case we - # collect it into a hash for later usage. Options passed to this function - # are capitalized properly (unlike CGI), sanitized and then just stored. + # collect it into a hash for later usage. + # + # nph -- Mostly ignored. It'll output the date. + # connection -- Completely ignored. Why is CGI doing this? + # length -- Ignored since Mongrel figures this out from what you write to output. + # def header(options = "text/html") - if options.class == Hash - # passing in a header so need to keep the status around and other options - @options = @options.merge(options) - else - @options["Content-Type"] = options - end + # if they pass in a string then just write the Content-Type + if options.class == String + @head['Content-Type'] = options + else + # convert the given options into what Mongrel wants + @head['Content-Type'] = options['type'] || "text/html" + @head['Content-Type'] += "; charset=" + options['charset'] if options.has_key? "charset" if options['charset'] + + # setup date only if they use nph + @head['Date'] = CGI::rfc1123_date(Time.now) if options['nph'] + + # setup the server to use the default or what they set + @head['Server'] = options['server'] || env_table['SERVER_SOFTWARE'] + + # remaining possible options they can give + @head['Status'] = options['status'] if options['status'] + @head['Content-Language'] = options['language'] if options['language'] + @head['Expires'] = options['expires'] if options['expires'] + + # drop the keys we don't want anymore + REMOVED_KEYS.each {|k| options.delete(k) } + + # finally just convert the rest raw (which puts 'cookie' directly) + # 'cookie' is translated later as we write the header out + options.each{|k,v| @head[k] = v} + end + # doing this fakes out the cgi library to think the headers are empty # we then do the real headers in the out function call later "" end + + # Takes any 'cookie' setting and sends it over the Mongrel header, + # then removes the setting from the options. If cookie is an + # Array or Hash then it sends those on with .to_s, otherwise + # it just calls .to_s on it and hopefully your "cookie" can + # write itself correctly. + def send_cookies(to) + # convert the cookies based on the myriad of possible ways to set a cookie + if @head['cookie'] + cookie = @head['cookie'] + case cookie + when Array + cookie.each {|c| to['Set-Cookie'] = c.to_s } + when Hash + cookie.each_value {|c| to['Set-Cookie'] = c.to_s} + else + to['Set-Cookie'] = options['cookie'].to_s + end + + @head.delete('cookie') + + # @output_cookies seems to never be used, but we'll process it just in case + @output_cookies.each {|c| to['Set-Cookie'] = c.to_s } if @output_cookies + end + end # The dumb thing is people can call header or this or both and in any order. # So, we just reuse header and then finalize the HttpResponse the right way. # Status is taken from the various options and converted to what Mongrel needs # via the CGIWrapper.status function. def out(options = "text/html") + return if @out_called # don't do this more than once + header(options) + @response.start status do |head, out| - @options.each {|k,v| head[k.capitalize] = v} + send_cookies(head) + + @head.each {|k,v| head[k] = v} out.write(yield || "") end end @@ -644,7 +703,7 @@ module Mongrel # message in the status we have to do a bit of parsing. def status if not @status - @status = @options["Status"] || @options["status"] + @status = @head["Status"] || @head["status"] if @status @status[0 ... @status.index(' ')] || "200" @@ -652,7 +711,7 @@ module Mongrel @status = "200" end end - end + end # Used to wrap the normal args variable used inside CGI. def args