Class: Mongrel::CGIWrapper

Class | Mongrel::CGIWrapper
In: | lib/mongrel/cgi.rb lib/mongrel/cgi.rb
Parent: | ::CGI

The beginning of a complete wrapper around Mongrel‘s internal HTTP processing system but maintaining the original Ruby CGI module. Use this only as a crutch 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.

The CGIWrapper.handler attribute is normally not set and is available for frameworks that need to get back to the handler. Rails uses this to give people access to the RailsHandler#files (DirHandler really) so they can look-up paths and do other things with the files managed there.

In Rails you can get the real file for a request with:

     path = @request.cgi.handler.files.can_serve(@request['PATH_INFO'])

Which is ugly but does the job. Feel free to write a Rails helper for that. Refer to DirHandler#can_serve for more information on this.

### Methods

args args env_table env_table header header new new out out send_cookies send_cookies status status stdinput stdinput stdoutput stdoutput

### Constants

REMOVED_KEYS | = | [ "nph","status","server","connection","type", "charset","length","language","expires"] |  | these are stripped out of any keys passed to CGIWrapper.header function
REMOVED_KEYS | = | [ "nph","status","server","connection","type", "charset","length","language","expires"] |  | these are stripped out of any keys passed to CGIWrapper.header function

### Attributes

default_really_final | [RW] | Set this to false if you want calls to CGIWrapper.out to not actually send the response until you force it.
default_really_final | [RW] | Set this to false if you want calls to CGIWrapper.out to not actually send the response until you force it.
handler | [RW] |
handler | [RW] |
options | [R] |
options | [R] |

### Public Class methods

new(request, response, *args)

Takes an HttpRequest and HttpResponse object, plus any additional arguments normally passed to CGI. These are used internally to create a wrapper around the real CGI while maintaining Mongrel‘s view of the world.

[Source]

        # File lib/mongrel/cgi.rb, line 42
    42:     def initialize(request, response, *args)
    43:       @request = request
    44:       @response = response
    45:       @args = *args
    46:       @input = request.body
    47:       @head = {}
    48:       @out_called = false
    49:       @default_really_final=true
    50:       super(*args)
    51:     end

new(request, response, *args)

Takes an HttpRequest and HttpResponse object, plus any additional arguments normally passed to CGI. These are used internally to create a wrapper around the real CGI while maintaining Mongrel‘s view of the world.

[Source]

        # File lib/mongrel/cgi.rb, line 42
    42:     def initialize(request, response, *args)
    43:       @request = request
    44:       @response = response
    45:       @args = *args
    46:       @input = request.body
    47:       @head = {}
    48:       @out_called = false
    49:       @default_really_final=true
    50:       super(*args)
    51:     end

### Public Instance methods

args()

Used to wrap the normal args variable used inside CGI.

[Source]

         # File lib/mongrel/cgi.rb, line 160
    160:     def args
    161:       @args
    162:     end

args()

Used to wrap the normal args variable used inside CGI.

[Source]

         # File lib/mongrel/cgi.rb, line 160
    160:     def args
    161:       @args
    162:     end

env_table()

Used to wrap the normal env_table variable used inside CGI.

[Source]

         # File lib/mongrel/cgi.rb, line 165
    165:     def env_table
    166:       @request.params
    167:     end

env_table()

Used to wrap the normal env_table variable used inside CGI.

[Source]

         # File lib/mongrel/cgi.rb, line 165
    165:     def env_table
    166:       @request.params
    167:     end

header(options = "text/html")

The header is typically called to send back the header. In our case we 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.

[Source]

        # File lib/mongrel/cgi.rb, line 60
    60:     def header(options = "text/html")
    61:       # if they pass in a string then just write the Content-Type
    62:       if options.class == String
    63:         @head['Content-Type'] = options unless @head['Content-Type']
    64:       else
    65:         # convert the given options into what Mongrel wants
    66:         @head['Content-Type'] = options['type'] || "text/html"
    67:         @head['Content-Type'] += "; charset=" + options['charset'] if options.has_key? "charset" if options['charset']
    68:
    69:         # setup date only if they use nph
    70:         @head['Date'] = CGI::rfc1123_date(Time.now) if options['nph']
    71:
    72:         # setup the server to use the default or what they set
    73:         @head['Server'] = options['server'] || env_table['SERVER_SOFTWARE']
    74:
    75:         # remaining possible options they can give
    76:         @head['Status'] = options['status'] if options['status']
    77:         @head['Content-Language'] = options['language'] if options['language']
    78:         @head['Expires'] = options['expires'] if options['expires']
    79:
    80:         # drop the keys we don't want anymore
    81:         REMOVED_KEYS.each {|k| options.delete(k) }
    82:
    83:         # finally just convert the rest raw (which puts 'cookie' directly)
    84:         # 'cookie' is translated later as we write the header out
    85:         options.each{|k,v| @head[k] = v}
    86:       end
    87:
    88:       # doing this fakes out the cgi library to think the headers are empty
    89:       # we then do the real headers in the out function call later
    90:       ""
    91:     end

header(options = "text/html")

The header is typically called to send back the header. In our case we 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.

[Source]

        # File lib/mongrel/cgi.rb, line 60
    60:     def header(options = "text/html")
    61:       # if they pass in a string then just write the Content-Type
    62:       if options.class == String
    63:         @head['Content-Type'] = options unless @head['Content-Type']
    64:       else
    65:         # convert the given options into what Mongrel wants
    66:         @head['Content-Type'] = options['type'] || "text/html"
    67:         @head['Content-Type'] += "; charset=" + options['charset'] if options.has_key? "charset" if options['charset']
    68:
    69:         # setup date only if they use nph
    70:         @head['Date'] = CGI::rfc1123_date(Time.now) if options['nph']
    71:
    72:         # setup the server to use the default or what they set
    73:         @head['Server'] = options['server'] || env_table['SERVER_SOFTWARE']
    74:
    75:         # remaining possible options they can give
    76:         @head['Status'] = options['status'] if options['status']
    77:         @head['Content-Language'] = options['language'] if options['language']
    78:         @head['Expires'] = options['expires'] if options['expires']
    79:
    80:         # drop the keys we don't want anymore
    81:         REMOVED_KEYS.each {|k| options.delete(k) }
    82:
    83:         # finally just convert the rest raw (which puts 'cookie' directly)
    84:         # 'cookie' is translated later as we write the header out
    85:         options.each{|k,v| @head[k] = v}
    86:       end
    87:
    88:       # doing this fakes out the cgi library to think the headers are empty
    89:       # we then do the real headers in the out function call later
    90:       ""
    91:     end

out(options = "text/html", really_final=@default_really_final) {||| "")| ...}

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.

We also prevent Rails from actually doing the final send by adding a second parameter "really_final". Only Mongrel calls this after Rails is done. Since this will break other frameworks, it defaults to a different setting for rails (false) and (true) for others.

[Source]

         # File lib/mongrel/cgi.rb, line 127
    127:     def out(options = "text/html", really_final=@default_really_final)
    128:       if @out_called || !really_final
    129:         # don't do it more than once or if it's not the really final call
    130:         return
    131:       end
    132:
    133:       header(options)
    134:
    135:       @response.start status do |head, body|
    136:         send_cookies(head)
    137:
    138:         @head.each {|k,v| head[k] = v}
    139:         body.write(yield || "")
    140:       end
    141:
    142:       @out_called = true
    143:     end

out(options = "text/html", really_final=@default_really_final) {||| "")| ...}

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.

We also prevent Rails from actually doing the final send by adding a second parameter "really_final". Only Mongrel calls this after Rails is done. Since this will break other frameworks, it defaults to a different setting for rails (false) and (true) for others.

[Source]

         # File lib/mongrel/cgi.rb, line 127
    127:     def out(options = "text/html", really_final=@default_really_final)
    128:       if @out_called || !really_final
    129:         # don't do it more than once or if it's not the really final call
    130:         return
    131:       end
    132:
    133:       header(options)
    134:
    135:       @response.start status do |head, body|
    136:         send_cookies(head)
    137:
    138:         @head.each {|k,v| head[k] = v}
    139:         body.write(yield || "")
    140:       end
    141:
    142:       @out_called = true
    143:     end

send_cookies(to)

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.

[Source]

         # File lib/mongrel/cgi.rb, line 98
     98:     def send_cookies(to)
     99:       # convert the cookies based on the myriad of possible ways to set a cookie
    100:       if @head['cookie']
    101:         cookie = @head['cookie']
    102:         case cookie
    103:         when Array
    104:           cookie.each {|c| to['Set-Cookie'] = c.to_s }
    105:         when Hash
    106:           cookie.each_value {|c| to['Set-Cookie'] = c.to_s}
    107:         else
    108:           to['Set-Cookie'] = options['cookie'].to_s
    109:         end
    110:
    111:         @head.delete('cookie')
    112:       end
    113:
    114:       # @output_cookies seems to never be used, but we'll process it just in case
    115:       @output_cookies.each {|c| to['Set-Cookie'] = c.to_s } if @output_cookies
    116:     end

send_cookies(to)

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.

[Source]

         # File lib/mongrel/cgi.rb, line 98
     98:     def send_cookies(to)
     99:       # convert the cookies based on the myriad of possible ways to set a cookie
    100:       if @head['cookie']
    101:         cookie = @head['cookie']
    102:         case cookie
    103:         when Array
    104:           cookie.each {|c| to['Set-Cookie'] = c.to_s }
    105:         when Hash
    106:           cookie.each_value {|c| to['Set-Cookie'] = c.to_s}
    107:         else
    108:           to['Set-Cookie'] = options['cookie'].to_s
    109:         end
    110:
    111:         @head.delete('cookie')
    112:       end
    113:
    114:       # @output_cookies seems to never be used, but we'll process it just in case
    115:       @output_cookies.each {|c| to['Set-Cookie'] = c.to_s } if @output_cookies
    116:     end

status()

Computes the status once, but lazily so that people who call header twice don‘t get penalized. Because CGI insists on including the options status message in the status we have to do a bit of parsing.

[Source]

         # File lib/mongrel/cgi.rb, line 148
    148:     def status
    149:       if not @status
    150:         stat = @head["Status"]
    151:         stat = stat.split(' ')[0] if stat
    152:
    153:         @status = stat || "200"
    154:       end
    155:
    156:       @status
    157:     end

status()

Computes the status once, but lazily so that people who call header twice don‘t get penalized. Because CGI insists on including the options status message in the status we have to do a bit of parsing.

[Source]

         # File lib/mongrel/cgi.rb, line 148
    148:     def status
    149:       if not @status
    150:         stat = @head["Status"]
    151:         stat = stat.split(' ')[0] if stat
    152:
    153:         @status = stat || "200"
    154:       end
    155:
    156:       @status
    157:     end

stdinput()

Used to wrap the normal stdinput variable used inside CGI.

[Source]

         # File lib/mongrel/cgi.rb, line 170
    170:     def stdinput
    171:       @input
    172:     end

stdinput()

Used to wrap the normal stdinput variable used inside CGI.

[Source]

         # File lib/mongrel/cgi.rb, line 170
    170:     def stdinput
    171:       @input
    172:     end

stdoutput()

The stdoutput should be completely bypassed but we‘ll drop a warning just in case

[Source]

         # File lib/mongrel/cgi.rb, line 175
    175:     def stdoutput
    176:       STDERR.puts "WARNING: Your program is doing something not expected.  Please tell Zed that stdoutput was used and what software you are running.  Thanks."
    177:       @response.body
    178:     end

stdoutput()

The stdoutput should be completely bypassed but we‘ll drop a warning just in case

[Source]

         # File lib/mongrel/cgi.rb, line 175
    175:     def stdoutput
    176:       STDERR.puts "WARNING: Your program is doing something not expected.  Please tell Zed that stdoutput was used and what software you are running.  Thanks."
    177:       @response.body
    178:     end

[Validate]