Class: Mongrel::CGIWrapper

Inherits:
CGI
  • Object
show all
Defined in:
lib/mongrel/cgi.rb

Overview

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.

Constant Summary collapse

REMOVED_KEYS =

these are stripped out of any keys passed to CGIWrapper.header function

[ "nph","status","server","connection","type",
"charset","length","language","expires"]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(request, response, *args) ⇒ CGIWrapper

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.



40
41
42
43
44
45
46
47
48
# File 'lib/mongrel/cgi.rb', line 40

def initialize(request, response, *args)
  @request = request
  @response = response
  @args = *args
  @input = request.body
  @head = {}
  @out_called = false
  super(*args)
end

Instance Attribute Details

#handlerObject

Returns the value of attribute handler.



30
31
32
# File 'lib/mongrel/cgi.rb', line 30

def handler
  @handler
end

#optionsObject (readonly)

Returns the value of attribute options.



29
30
31
# File 'lib/mongrel/cgi.rb', line 29

def options
  @options
end

Instance Method Details

#argsObject

Used to wrap the normal args variable used inside CGI.



147
148
149
# File 'lib/mongrel/cgi.rb', line 147

def args
  @args
end

#env_tableObject

Used to wrap the normal env_table variable used inside CGI.



152
153
154
# File 'lib/mongrel/cgi.rb', line 152

def env_table
  @request.params
end

#header(options = "text/html") ⇒ Object

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.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/mongrel/cgi.rb', line 57

def header(options = "text/html")
  # if they pass in a string then just write the Content-Type
  if options.class == String
    @head['Content-Type'] = options unless @head['Content-Type']
  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

#out(options = "text/html") ⇒ Object

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.



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/mongrel/cgi.rb', line 119

def out(options = "text/html")
  return if @out_called  # don't do this more than once

  header(options)

  @response.start status do |head, out|
    send_cookies(head)
    
    @head.each {|k,v| head[k] = v}
    out.write(yield || "")
  end
end

#send_cookies(to) ⇒ Object

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.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/mongrel/cgi.rb', line 95

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

#statusObject

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.



135
136
137
138
139
140
141
142
143
144
# File 'lib/mongrel/cgi.rb', line 135

def status
  if not @status
    stat = @head["Status"]
    stat = stat.split(' ')[0] if stat

    @status = stat || "200"
  end

  @status
end

#stdinputObject

Used to wrap the normal stdinput variable used inside CGI.



157
158
159
# File 'lib/mongrel/cgi.rb', line 157

def stdinput
  @input
end

#stdoutputObject

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



162
163
164
165
# File 'lib/mongrel/cgi.rb', line 162

def stdoutput
  STDERR.puts "WARNING: Your program is doing something not expected.  Please tell Zed that stdoutput was used and what software you are running.  Thanks."
  @response.body
end