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

root/branches/2-1-caching/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     server.start
63   end
64
65   def initialize(server, options) #:nodoc:
66     @server_options = options
67     @file_handler = WEBrick::HTTPServlet::FileHandler.new(server, options[:server_root])
68     # Change to the RAILS_ROOT, since Webrick::Daemon.start does a Dir::cwd("/")
69     # OPTIONS['working_directory'] is an absolute path of the RAILS_ROOT, set in railties/lib/commands/servers/webrick.rb
70     Dir.chdir(OPTIONS['working_directory']) if defined?(OPTIONS) && File.directory?(OPTIONS['working_directory'])
71     super
72   end
73
74   def service(req, res) #:nodoc:
75     unless handle_file(req, res)
76       begin
77         REQUEST_MUTEX.lock unless ActionController::Base.allow_concurrency
78         unless handle_dispatch(req, res)
79           raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
80         end
81       ensure
82         unless ActionController::Base.allow_concurrency
83           REQUEST_MUTEX.unlock if REQUEST_MUTEX.locked?
84         end
85       end
86     end
87   end
88
89   def handle_file(req, res) #:nodoc:
90     begin
91       req = req.dup
92       path = req.path.dup
93
94       # Add .html if the last path piece has no . in it
95       path << '.html' if path != '/' && (%r{(^|/)[^./]+$} =~ path)
96       path.gsub!('+', ' ') # Unescape + since FileHandler doesn't do so.
97
98       req.instance_variable_set(:@path_info, path) # Set the modified path...
99
100       @file_handler.send(:service, req, res)     
101       return true
102     rescue HTTPStatus::PartialContent, HTTPStatus::NotModified => err
103       res.set_error(err)
104       return true
105     rescue => err
106       return false
107     end
108   end
109
110   def handle_dispatch(req, res, origin = nil) #:nodoc:
111     data = StringIO.new
112     Dispatcher.dispatch(
113       CGI.new("query", create_env_table(req, origin), StringIO.new(req.body || "")),
114       ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS,
115       data
116     )
117
118     header, body = extract_header_and_body(data)
119
120     set_charset(header)
121     assign_status(res, header)
122     res.cookies.concat(header.delete('set-cookie') || [])
123     header.each { |key, val| res[key] = val.join(", ") }
124    
125     res.body = body
126     return true
127   rescue => err
128     p err, err.backtrace
129     return false
130   end
131  
132   private
133     def create_env_table(req, origin)
134       env = req.meta_vars.clone
135       env.delete "SCRIPT_NAME"
136       env["QUERY_STRING"] = req.request_uri.query
137       env["REQUEST_URI"]  = origin if origin
138       return env
139     end
140    
141     def extract_header_and_body(data)
142       data.rewind
143       data = data.read
144
145       raw_header, body = *data.split(/^[\xd\xa]{2}/on, 2)
146       header = WEBrick::HTTPUtils::parse_header(raw_header)
147      
148       return header, body
149     end
150    
151     def set_charset(header)
152       ct = header["content-type"]
153       if ct.any? { |x| x =~ /^text\// } && ! ct.any? { |x| x =~ /charset=/ }
154         ch = @server_options[:charset] || "UTF-8"
155         ct.find { |x| x =~ /^text\// } << ("; charset=" + ch)
156       end
157     end
158
159     def assign_status(res, header)
160       if /^(\d+)/ =~ header['status'][0]
161         res.status = $1.to_i
162         header.delete('status')
163       end
164     end
165 end
Note: See TracBrowser for help on using the browser.