1 # This code is based on the original CGIWrapper from Mongrel
2 # Copyright (c) 2005 Zed A. Shaw
3 # Copyright (c) 2009 Eric Wong
4 # You can redistribute it and/or modify it under the same terms as Ruby.
6 # Additional work donated by contributors. See CONTRIBUTORS for more info.
12 # The beginning of a complete wrapper around Unicorn's internal HTTP
13 # processing system but maintaining the original Ruby CGI module. Use
14 # this only as a crutch to get existing CGI based systems working. It
15 # should handle everything, but please notify us if you see special
16 # warnings. This work is still very alpha so we need testers to help
17 # work out the various corner cases.
18 class Unicorn::CGIWrapper < ::CGI
19 undef_method :env_table
20 attr_reader :env_table
23 # these are stripped out of any keys passed to CGIWrapper.header function
24 NPH = 'nph'.freeze # Completely ignored, Unicorn outputs the date regardless
25 CONNECTION = 'connection'.freeze # Completely ignored. Why is CGI doing this?
26 CHARSET = 'charset'.freeze # this gets appended to Content-Type
27 COOKIE = 'cookie'.freeze # maps (Hash,Array,String) to "Set-Cookie" headers
28 STATUS = 'status'.freeze # stored as @status
29 Status = 'Status'.freeze # code + human-readable text, Rails sets this
31 # some of these are common strings, but this is the only module
32 # using them and the reason they're not in Unicorn::Const
33 SET_COOKIE = 'Set-Cookie'.freeze
34 CONTENT_TYPE = 'Content-Type'.freeze
35 CONTENT_LENGTH = 'Content-Length'.freeze # this is NOT Const::CONTENT_LENGTH
36 RACK_INPUT = 'rack.input'.freeze
37 RACK_ERRORS = 'rack.errors'.freeze
39 # this maps CGI header names to HTTP header names
42 'type' => CONTENT_TYPE,
43 'server' => 'Server'.freeze,
44 'language' => 'Content-Language'.freeze,
45 'expires' => 'Expires'.freeze,
46 'length' => CONTENT_LENGTH,
49 # Takes an a Rackable environment, plus any additional CGI.new
50 # arguments These are used internally to create a wrapper around the
51 # real CGI while maintaining Rack/Unicorn's view of the world. This
52 # this will NOT deal well with large responses that take up a lot of
53 # memory, but neither does the CGI nor the original CGIWrapper from
55 def initialize(rack_env, *args)
59 @headv = Hash.new { |hash,key| hash[key] = [] }
64 # finalizes the response in a way Rack applications would expect
66 # @head[CONTENT_LENGTH] ||= @body.size
67 @headv[SET_COOKIE] += @output_cookies if @output_cookies
68 @headv.each_pair do |key,value|
69 @head[key] ||= value.join("\n") unless value.empty?
72 # Capitalized "Status:", with human-readable status code (e.g. "200 OK")
73 parseable_status = @head.delete(Status)
74 @status ||= parseable_status.split(/ /)[0].to_i rescue 500
76 [ @status || 500, @head, [ @body.string ] ]
79 # The header is typically called to send back the header. In our case we
80 # collect it into a hash for later usage. This can be called multiple
81 # times to set different cookies.
82 def header(options = "text/html")
83 # if they pass in a string then just write the Content-Type
85 @head[CONTENT_TYPE] ||= options
87 HEADER_MAP.each_pair do |from, to|
88 from = options.delete(from) or next
92 @head[CONTENT_TYPE] ||= "text/html"
93 if charset = options.delete(CHARSET)
94 @head[CONTENT_TYPE] << "; charset=#{charset}"
97 # lots of ways to set cookies
98 if cookie = options.delete(COOKIE)
99 set_cookies = @headv[SET_COOKIE]
102 cookie.each { |c| set_cookies << c.to_s }
104 cookie.each_value { |c| set_cookies << c.to_s }
106 set_cookies << cookie.to_s
109 @status ||= options.delete(STATUS) # all lower-case
111 # drop the keys we don't want anymore
113 options.delete(CONNECTION)
115 # finally, set the rest of the headers as-is, allowing duplicates
116 options.each_pair { |k,v| @headv[k] << v }
119 # doing this fakes out the cgi library to think the headers are empty
120 # we then do the real headers in the out function call later
124 # The dumb thing is people can call header or this or both and in
125 # any order. So, we just reuse header and then finalize the
126 # HttpResponse the right way. This will have no effect if called
127 # the second time if the first "outputted" anything.
128 def out(options = "text/html")
130 @body.size == 0 or return
131 @body << yield if block_given?
134 # Used to wrap the normal stdinput variable used inside CGI.
136 @env_table[RACK_INPUT]
139 # The stdoutput should be completely bypassed but we'll drop a
140 # warning just in case
142 err = @env_table[RACK_ERRORS]
143 err.puts "WARNING: Your program is doing something not expected."
144 err.puts "Please tell Eric that stdoutput was used and what software " \
145 "you are running. Thanks."