Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/tags/rel_1-2-1/railties/lib/webrick_server.rb

Revision 5847, 4.7 kB (checked in by david, 2 years ago)

Fixed that webrick would strip leading newlines and hang connection (closes #4156) [psross]

Line 
1 # Donated by Florian Gross
2
3 require 'webrick'
4 require 'cgi'
5 require 'stringio'
6 require 'dispatcher'
7
8 include WEBrick
9
10 class CGI #:nodoc:
11   def stdinput
12     @stdin || $stdin
13   end
14  
15   def env_table
16     @env_table || ENV
17   end
18  
19   def initialize(type = "query", table = nil, stdin = nil)
20     @env_table, @stdin = table, stdin
21
22     if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
23       Apache.request.setup_cgi_env
24     end
25
26     extend QueryExtension
27     @multipart = false
28     if defined?(CGI_PARAMS)
29       warn "do not use CGI_PARAMS and CGI_COOKIES"
30       @params = CGI_PARAMS.dup
31       @cookies = CGI_COOKIES.dup
32     else
33       initialize_query()  # set @params, @cookies
34     end
35     @output_cookies = nil
36     @output_hidden = nil
37   end
38 end
39
40 # A custom dispatch servlet for use with WEBrick. It dispatches requests
41 # (using the Rails Dispatcher) to the appropriate controller/action. By default,
42 # it restricts WEBrick to a managing a single Rails request at a time, but you
43 # can change this behavior by setting ActionController::Base.allow_concurrency
44 # to true.
45 class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
46   REQUEST_MUTEX = Mutex.new
47
48   # Start the WEBrick server with the given options, mounting the
49   # DispatchServlet at <tt>/</tt>.
50   def self.dispatch(options = {})
51     Socket.do_not_reverse_lookup = true # patch for OS X
52
53     params = { :Port        => options[:port].to_i,
54                :ServerType  => options[:server_type],
55                :BindAddress => options[:ip] }
56     params[:MimeTypes] = options[:mime_types] if options[:mime_types]
57
58     server = WEBrick::HTTPServer.new(params)
59     server.mount('/', DispatchServlet, options)
60
61     trap("INT") { server.shutdown }
62    
63     server.start
64   end
65
66   def initialize(server, options) #:nodoc:
67     @server_options = options
68     @file_handler = WEBrick::HTTPServlet::FileHandler.new(server, options[:server_root])
69     # Change to the RAILS_ROOT, since Webrick::Daemon.start does a Dir::cwd("/")
70     # OPTIONS['working_directory'] is an absolute path of the RAILS_ROOT, set in railties/lib/commands/servers/webrick.rb
71     Dir.chdir(OPTIONS['working_directory']) if defined?(OPTIONS) && File.directory?(OPTIONS['working_directory'])
72     super
73   end
74
75   def service(req, res) #:nodoc:
76     unless handle_file(req, res)
77       begin
78         REQUEST_MUTEX.lock unless ActionController::Base.allow_concurrency
79         unless handle_dispatch(req, res)
80           raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
81         end
82       ensure
83         unless ActionController::Base.allow_concurrency
84           REQUEST_MUTEX.unlock if REQUEST_MUTEX.locked?
85         end
86       end
87     end
88   end
89
90   def handle_file(req, res) #:nodoc:
91     begin
92       req = req.dup
93       path = req.path.dup
94
95       # Add .html if the last path piece has no . in it
96       path << '.html' if path != '/' && (%r{(^|/)[^./]+$} =~ path)
97       path.gsub!('+', ' ') # Unescape + since FileHandler doesn't do so.
98
99       req.instance_variable_set(:@path_info, path) # Set the modified path...
100
101       @file_handler.send(:service, req, res)     
102       return true
103     rescue HTTPStatus::PartialContent, HTTPStatus::NotModified => err
104       res.set_error(err)
105       return true
106     rescue => err
107       return false
108     end
109   end
110
111   def handle_dispatch(req, res, origin = nil) #:nodoc:
112     data = StringIO.new
113     Dispatcher.dispatch(
114       CGI.new("query", create_env_table(req, origin), StringIO.new(req.body || "")),
115       ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS,
116       data
117     )
118
119     header, body = extract_header_and_body(data)
120
121     set_charset(header)
122     assign_status(res, header)
123     res.cookies.concat(header.delete('set-cookie') || [])
124     header.each { |key, val| res[key] = val.join(", ") }
125    
126     res.body = body
127     return true
128   rescue => err
129     p err, err.backtrace
130     return false
131   end
132  
133   private
134     def create_env_table(req, origin)
135       env = req.meta_vars.clone
136       env.delete "SCRIPT_NAME"
137       env["QUERY_STRING"] = req.request_uri.query
138       env["REQUEST_URI"]  = origin if origin
139       return env
140     end
141    
142     def extract_header_and_body(data)
143       data.rewind
144       data = data.read
145
146       raw_header, body = *data.split(/^[\xd\xa]{2}/on, 2)
147       header = WEBrick::HTTPUtils::parse_header(raw_header)
148      
149       return header, body
150     end
151    
152     def set_charset(header)
153       ct = header["content-type"]
154       if ct.any? { |x| x =~ /^text\// } && ! ct.any? { |x| x =~ /charset=/ }
155         ch = @server_options[:charset] || "UTF-8"
156         ct.find { |x| x =~ /^text\// } << ("; charset=" + ch)
157       end
158     end
159
160     def assign_status(res, header)
161       if /^(\d+)/ =~ header['status'][0]
162         res.status = $1.to_i
163         header.delete('status')
164       end
165     end
166 end
Note: See TracBrowser for help on using the browser.