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
8 # $Id: cgi.rb 11708 2007-02-12 23:01:19Z shyouhei $
10 require "webrick/httprequest"
11 require "webrick/httpresponse"
12 require "webrick/config"
17 CGIError = Class.new(StandardError)
19 attr_reader :config, :logger
23 unless ENV.has_key?("GATEWAY_INTERFACE")
24 Apache.request.setup_cgi_env
27 if %r{HTTP/(\d+\.\d+)} =~ ENV["SERVER_PROTOCOL"]
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.
36 if config = args.shift
37 @config.update(config)
39 @config[:Logger] ||= WEBrick::BasicLog.new($stderr)
40 @logger = @config[:Logger]
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)
54 unless @header["status"]
55 phrase = HTTPStatus::reason_phrase(@status)
56 @header["status"] = "#{@status} #{phrase}"
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
78 rescue HTTPStatus::Status => ex
80 rescue Exception => ex
82 res.set_error(ex, true)
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|
92 when /^content-encoding$/i
93 Apache::request.content_encoding = val
94 when /^content-type$/i
95 Apache::request.content_type = val
100 res.cookies.each{|cookie|
101 table.add("Set-Cookie", cookie.to_s)
103 Apache.request.send_http_header
106 res.send_response(sock)
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)
116 raise HTTPStatus::MethodNotAllowed,
117 "unsupported method `#{req.request_method}'."
126 def initialize(config, env, stdin, stdout)
129 @header_part = StringIO.new
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
142 @header_part << request_line << CRLF
146 rescue Exception => ex
147 raise CGIError, "invalid CGI environment"
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
163 # we cannot get real HTTP version of client ;)
164 httpv = @config[:HTTPVersion]
165 return "#{meth} #{url} HTTP/#{httpv}"
169 add_header("CONTENT_TYPE", "Content-Type")
170 add_header("CONTENT_LENGTH", "Content-length")
172 if /^HTTP_(.*)/ =~ name
173 add_header(name, $1.gsub(/_/, "-"))
178 def add_header(envname, hdrname)
179 if value = @env[envname]
181 @header_part << hdrname << ": " << value << CRLF
187 @header_part.eof? ? @body_part : @header_part
193 [nil, @remote_port, @remote_host, @remote_addr]
197 [nil, @server_port, @server_name, @server_addr]
209 input.each{|line| yield(line) }
217 return nil unless defined?(OpenSSL)
218 if pem = @env["SSL_SERVER_CERT"]
219 OpenSSL::X509::Certificate.new(pem) unless pem.empty?
224 return nil unless defined?(OpenSSL)
225 if pem = @env["SSL_CLIENT_CERT"]
226 OpenSSL::X509::Certificate.new(pem) unless pem.empty?
231 return nil unless defined?(OpenSSL)
232 if @env["SSL_CLIENT_CERT_CHAIN_0"]
234 certs = keys.sort.collect{|k|
235 if /^SSL_CLIENT_CERT_CHAIN_\d+$/ =~ k
237 OpenSSL::X509::Certificate.new(pem) unless pem.empty?
246 return nil unless defined?(OpenSSL)
247 if cipher = @env["SSL_CIPHER"]
249 ret << @env["SSL_PROTOCOL"]
250 ret << @env["SSL_CIPHER_USEKEYSIZE"]
251 ret << @env["SSL_CIPHER_ALGKEYSIZE"]