Class: Unicorn::CGIWrapper

Inherits:
CGI
  • Object
show all
Defined in:
lib/unicorn/cgi_wrapper.rb

Overview

The beginning of a complete wrapper around Unicorn’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 us if you see special warnings. This work is still very alpha so we need testers to help work out the various corner cases.

Constant Summary collapse

NPH =

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

'nph'.freeze
CONNECTION =

Completely ignored, Unicorn outputs the date regardless

'connection'.freeze
CHARSET =

Completely ignored. Why is CGI doing this?

'charset'.freeze
'cookie'.freeze
STATUS =

maps (Hash,Array,String) to “Set-Cookie” headers

'status'.freeze
Status =

stored as @status

'Status'.freeze
'Set-Cookie'.freeze
CONTENT_TYPE =
'Content-Type'.freeze
CONTENT_LENGTH =

this is NOT Const::CONTENT_LENGTH

'Content-Length'.freeze
RACK_INPUT =
'rack.input'.freeze
RACK_ERRORS =
'rack.errors'.freeze
HEADER_MAP =

this maps CGI header names to HTTP header names

{
  'status' => Status,
  'type' => CONTENT_TYPE,
  'server' => 'Server'.freeze,
  'language' => 'Content-Language'.freeze,
  'expires' => 'Expires'.freeze,
  'length' => CONTENT_LENGTH,
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rack_env, *args) ⇒ CGIWrapper

Takes an a Rackable environment, plus any additional CGI.new arguments These are used internally to create a wrapper around the real CGI while maintaining Rack/Unicorn’s view of the world. This this will NOT deal well with large responses that take up a lot of memory, but neither does the CGI nor the original CGIWrapper from Mongrel…



57
58
59
60
61
62
63
64
# File 'lib/unicorn/cgi_wrapper.rb', line 57

def initialize(rack_env, *args)
  @env_table = rack_env
  @status = nil
  @head = {}
  @headv = Hash.new { |hash,key| hash[key] = [] }
  @body = StringIO.new("")
  super(*args)
end

Instance Attribute Details

#bodyObject (readonly)

Returns the value of attribute body.



23
24
25
# File 'lib/unicorn/cgi_wrapper.rb', line 23

def body
  @body
end

#env_tableObject (readonly)

Returns the value of attribute env_table.



22
23
24
# File 'lib/unicorn/cgi_wrapper.rb', line 22

def env_table
  @env_table
end

Instance Method Details

#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. This can be called multiple times to set different cookies.



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/unicorn/cgi_wrapper.rb', line 83

def header(options = "text/html")
  # if they pass in a string then just write the Content-Type
  if String === options
    @head[CONTENT_TYPE] ||= options
  else
    HEADER_MAP.each_pair do |from, to|
      from = options.delete(from) or next
      @head[to] = from.to_s
    end

    @head[CONTENT_TYPE] ||= "text/html"
    if charset = options.delete(CHARSET)
      @head[CONTENT_TYPE] << "; charset=#{charset}"
    end

    # lots of ways to set cookies
    if cookie = options.delete(COOKIE)
      set_cookies = @headv[SET_COOKIE]
      case cookie
      when Array
        cookie.each { |c| set_cookies << c.to_s }
      when Hash
        cookie.each_value { |c| set_cookies << c.to_s }
      else
        set_cookies << cookie.to_s
      end
    end
    @status ||= options.delete(STATUS) # all lower-case

    # drop the keys we don't want anymore
    options.delete(NPH)
    options.delete(CONNECTION)

    # finally, set the rest of the headers as-is, allowing duplicates
    options.each_pair { |k,v| @headv[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. This will have no effect if called the second time if the first “outputted” anything.



129
130
131
132
133
# File 'lib/unicorn/cgi_wrapper.rb', line 129

def out(options = "text/html")
  header(options)
  @body.size == 0 or return
  @body << yield if block_given?
end

#rack_responseObject

finalizes the response in a way Rack applications would expect



67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/unicorn/cgi_wrapper.rb', line 67

def rack_response
  # @head[CONTENT_LENGTH] ||= @body.size
  @headv[SET_COOKIE].concat(@output_cookies) if @output_cookies
  @headv.each_pair do |key,value|
    @head[key] ||= value.join("\n") unless value.empty?
  end

  # Capitalized "Status:", with human-readable status code (e.g. "200 OK")
  @status ||= @head.delete(Status)

  [ @status || 500, @head, [ @body.string ] ]
end

#stdinputObject

Used to wrap the normal stdinput variable used inside CGI.



136
137
138
# File 'lib/unicorn/cgi_wrapper.rb', line 136

def stdinput
  @env_table[RACK_INPUT]
end

#stdoutputObject

return a pointer to the StringIO body since it’s STDOUT-like



141
142
143
# File 'lib/unicorn/cgi_wrapper.rb', line 141

def stdoutput
  @body
end