Re-enable spec/library for full CI runs.
[rbx.git] / lib / webrick / cgi.rb
blob3ece0e0b766eaaecd3b363afe0523153f95f2eb1
2 # cgi.rb -- Yet another CGI library
4 # Author: IPR -- Internet Programming with Ruby -- writers
5 # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
6 # reserved.
8 # $Id: cgi.rb 11708 2007-02-12 23:01:19Z shyouhei $
10 require "webrick/httprequest"
11 require "webrick/httpresponse"
12 require "webrick/config"
13 require "stringio"
15 module WEBrick
16   class CGI
17     CGIError = Class.new(StandardError)
19     attr_reader :config, :logger
21     def initialize(*args)
22       if defined?(MOD_RUBY)
23         unless ENV.has_key?("GATEWAY_INTERFACE")
24           Apache.request.setup_cgi_env
25         end
26       end
27       if %r{HTTP/(\d+\.\d+)} =~ ENV["SERVER_PROTOCOL"]
28         httpv = $1
29       end
30       @config = WEBrick::Config::HTTP.dup.update(
31         :ServerSoftware => ENV["SERVER_SOFTWARE"] || "null",
32         :HTTPVersion    => HTTPVersion.new(httpv || "1.0"),
33         :RunOnCGI       => true,   # to detect if it runs on CGI.
34         :NPH            => false   # set true to run as NPH script.
35       )
36       if config = args.shift
37         @config.update(config)
38       end
39       @config[:Logger] ||= WEBrick::BasicLog.new($stderr)
40       @logger = @config[:Logger]
41       @options = args
42     end
44     def [](key)
45       @config[key]
46     end
48     def start(env=ENV, stdin=$stdin, stdout=$stdout)
49       sock = WEBrick::CGI::Socket.new(@config, env, stdin, stdout)
50       req = HTTPRequest.new(@config)
51       res = HTTPResponse.new(@config)
52       unless @config[:NPH] or defined?(MOD_RUBY)
53         def res.setup_header
54           unless @header["status"]
55             phrase = HTTPStatus::reason_phrase(@status)
56             @header["status"] = "#{@status} #{phrase}"
57           end
58           super
59         end
60         def res.status_line
61           ""
62         end
63       end
65       begin
66         req.parse(sock)
67         req.script_name = (env["SCRIPT_NAME"] || File.expand_path($0)).dup
68         req.path_info = (env["PATH_INFO"] || "").dup
69         req.query_string = env["QUERY_STRING"]
70         req.user = env["REMOTE_USER"]
71         res.request_method = req.request_method
72         res.request_uri = req.request_uri
73         res.request_http_version = req.http_version
74         res.keep_alive = req.keep_alive?
75         self.service(req, res)
76       rescue HTTPStatus::Error => ex
77         res.set_error(ex)
78       rescue HTTPStatus::Status => ex
79         res.status = ex.code
80       rescue Exception => ex 
81         @logger.error(ex)
82         res.set_error(ex, true)
83       ensure
84         req.fixup
85         if defined?(MOD_RUBY)
86           res.setup_header
87           Apache.request.status_line = "#{res.status} #{res.reason_phrase}"
88           Apache.request.status = res.status
89           table = Apache.request.headers_out
90           res.header.each{|key, val|
91             case key
92             when /^content-encoding$/i
93               Apache::request.content_encoding = val
94             when /^content-type$/i
95               Apache::request.content_type = val
96             else
97               table[key] = val.to_s
98             end
99           }
100           res.cookies.each{|cookie|
101             table.add("Set-Cookie", cookie.to_s)
102           }
103           Apache.request.send_http_header
104           res.send_body(sock)
105         else
106           res.send_response(sock)
107         end
108       end
109     end
111     def service(req, res)
112       method_name = "do_" + req.request_method.gsub(/-/, "_")
113       if respond_to?(method_name)
114         __send__(method_name, req, res)
115       else
116         raise HTTPStatus::MethodNotAllowed,
117               "unsupported method `#{req.request_method}'."
118       end
119     end
121     class Socket
122       include Enumerable
124       private
125   
126       def initialize(config, env, stdin, stdout)
127         @config = config
128         @env = env
129         @header_part = StringIO.new
130         @body_part = stdin
131         @out_port = stdout
132         @out_port.binmode
133   
134         @server_addr = @env["SERVER_ADDR"] || "0.0.0.0"
135         @server_name = @env["SERVER_NAME"]
136         @server_port = @env["SERVER_PORT"]
137         @remote_addr = @env["REMOTE_ADDR"]
138         @remote_host = @env["REMOTE_HOST"] || @remote_addr
139         @remote_port = @env["REMOTE_PORT"] || 0
141         begin
142           @header_part << request_line << CRLF
143           setup_header
144           @header_part << CRLF
145           @header_part.rewind
146         rescue Exception => ex
147           raise CGIError, "invalid CGI environment"
148         end
149       end
151       def request_line
152         meth = @env["REQUEST_METHOD"] || "GET"
153         unless url = @env["REQUEST_URI"]
154           url = (@env["SCRIPT_NAME"] || File.expand_path($0)).dup
155           url << @env["PATH_INFO"].to_s
156           url = WEBrick::HTTPUtils.escape_path(url)
157           if query_string = @env["QUERY_STRING"]
158             unless query_string.empty?
159               url << "?" << query_string
160             end
161           end
162         end
163         # we cannot get real HTTP version of client ;)
164         httpv = @config[:HTTPVersion]
165         return "#{meth} #{url} HTTP/#{httpv}"
166       end
167   
168       def setup_header
169         add_header("CONTENT_TYPE", "Content-Type")
170         add_header("CONTENT_LENGTH", "Content-length")
171         @env.each_key{|name|
172           if /^HTTP_(.*)/ =~ name
173             add_header(name, $1.gsub(/_/, "-"))
174           end
175         }
176       end
177   
178       def add_header(envname, hdrname)
179         if value = @env[envname]
180           unless value.empty?
181             @header_part << hdrname << ": " << value << CRLF
182           end
183         end
184       end
186       def input
187         @header_part.eof? ? @body_part : @header_part
188       end
189   
190       public
191   
192       def peeraddr
193         [nil, @remote_port, @remote_host, @remote_addr]
194       end
195   
196       def addr
197         [nil, @server_port, @server_name, @server_addr]
198       end
199   
200       def gets(eol=LF)
201         input.gets(eol)
202       end
203   
204       def read(size=nil)
205         input.read(size)
206       end
208       def each
209         input.each{|line| yield(line) }
210       end
211   
212       def <<(data)
213         @out_port << data
214       end
216       def cert
217         return nil unless defined?(OpenSSL)
218         if pem = @env["SSL_SERVER_CERT"]
219           OpenSSL::X509::Certificate.new(pem) unless pem.empty?
220         end
221       end
223       def peer_cert
224         return nil unless defined?(OpenSSL)
225         if pem = @env["SSL_CLIENT_CERT"]
226           OpenSSL::X509::Certificate.new(pem) unless pem.empty?
227         end
228       end
230       def peer_cert_chain
231         return nil unless defined?(OpenSSL)
232         if @env["SSL_CLIENT_CERT_CHAIN_0"]
233           keys = @env.keys
234           certs = keys.sort.collect{|k|
235             if /^SSL_CLIENT_CERT_CHAIN_\d+$/ =~ k
236               if pem = @env[k]
237                 OpenSSL::X509::Certificate.new(pem) unless pem.empty?
238               end
239             end
240           }
241           certs.compact
242         end
243       end
245       def cipher
246         return nil unless defined?(OpenSSL)
247         if cipher = @env["SSL_CIPHER"]
248           ret = [ cipher ]
249           ret << @env["SSL_PROTOCOL"]
250           ret << @env["SSL_CIPHER_USEKEYSIZE"]
251           ret << @env["SSL_CIPHER_ALGKEYSIZE"]
252           ret
253         end
254       end
255     end
256   end 
257 end