diff options
author | Ryan Bigg <radarlistener@gmail.com> | 2009-04-04 16:13:18 +1000 |
---|---|---|
committer | Ryan Bigg <radarlistener@gmail.com> | 2009-04-04 16:13:18 +1000 |
commit | e878c3e44a8dba19c1188b636e25ec1bc2165116 (patch) | |
tree | 3d06b83492653b6b73422cbf37014fc5c89e9685 /actionpack/lib/action_controller | |
parent | ab55ddcc4c5cf31bcaf7720b52dc55d6d54cb150 (diff) | |
parent | 9e9469e83f6144310115124cdecc5cb65db5128e (diff) | |
download | rails-e878c3e44a8dba19c1188b636e25ec1bc2165116.tar.gz rails-e878c3e44a8dba19c1188b636e25ec1bc2165116.tar.bz2 rails-e878c3e44a8dba19c1188b636e25ec1bc2165116.zip |
Merge branch 'master' of git@github.com:lifo/docrails
* 'master' of git@github.com:lifo/docrails: (319 commits)
deletes screencast promo in prologue, its proper place is the References section
Typo fix
list -> index
in caching guide, RESTifies some examples, revised conventions here and there
Tech edit of caching guide from Gregg Pollack
Fix typo in comment: hide_actions -> hide_action
Fix two typos in a comment in config/initializers/backtrace_silencers.rb
With -> with in a title
Clear up a little confusing wording in Routing Guide.
copyedited minor details in the rack on rails guide
remove piece of UrlWriter documentation claiming that you can access named routes as its class methods
Add note about change to session options
TRUNCATE is also a MySQL DDL statement, so document this is a possible caveat when using transactions and savepoints.
Improve documentation for ActiveResource::Validations, fix typos
Fix typos in ActiveResource::Base documentation, use present tense, reword confusing sentences
Update ActiveResource::Connection documentation to use present tense
Fix typos in Active Resource README
Fix a small typo
ensure authors get warnings about broken links, and ensure end users don't
in guides generator, warn about duplicate header IDs only if WARN_DUPLICATE_HEADERS
...
Diffstat (limited to 'actionpack/lib/action_controller')
40 files changed, 366 insertions, 187 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 4fc52c1470..6348961ff4 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -22,7 +22,7 @@ module ActionController #:nodoc: attr_reader :allowed_methods def initialize(*allowed_methods) - super("Only #{allowed_methods.to_sentence} requests are allowed.") + super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") @allowed_methods = allowed_methods end @@ -408,7 +408,7 @@ module ActionController #:nodoc: # Return an array containing the names of public methods that have been marked hidden from the action processor. # By default, all methods defined in ActionController::Base and included modules are hidden. - # More methods can be hidden using <tt>hide_actions</tt>. + # More methods can be hidden using <tt>hide_action</tt>. def hidden_actions read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, []) end @@ -908,12 +908,14 @@ module ActionController #:nodoc: end options = extra_options + elsif !options.is_a?(Hash) + extra_options[:partial] = options + options = extra_options end layout = pick_layout(options) response.layout = layout.path_without_format_and_extension if layout logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout - layout = layout.path_without_format_and_extension if layout if content_type = options[:content_type] response.content_type = content_type.to_s @@ -982,6 +984,7 @@ module ActionController #:nodoc: # of sending it as the response body to the browser. def render_to_string(options = nil, &block) #:doc: render(options, &block) + response.body ensure response.content_type = nil erase_render_results @@ -1018,7 +1021,7 @@ module ActionController #:nodoc: # Clears the rendered results, allowing for another render to be performed. def erase_render_results #:nodoc: - response.body = nil + response.body = [] @performed_render = false end @@ -1101,7 +1104,6 @@ module ActionController #:nodoc: end response.redirected_to = options - logger.info("Redirected to #{options}") if logger && logger.info? case options # The scheme name consist of a letter followed by any combination of @@ -1124,6 +1126,7 @@ module ActionController #:nodoc: def redirect_to_full_url(url, status) raise DoubleRenderError if performed? + logger.info("Redirected to #{url}") if logger && logger.info? response.redirect(url, interpret_status(status)) @performed_redirect = true end @@ -1133,6 +1136,11 @@ module ActionController #:nodoc: # request is considered stale and should be generated from scratch. Otherwise, # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent. # + # Parameters: + # * <tt>:etag</tt> + # * <tt>:last_modified</tt> + # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). + # # Example: # # def show @@ -1153,20 +1161,34 @@ module ActionController #:nodoc: # Sets the etag, last_modified, or both on the response and renders a # "304 Not Modified" response if the request is already fresh. # + # Parameters: + # * <tt>:etag</tt> + # * <tt>:last_modified</tt> + # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches). + # # Example: # # def show # @article = Article.find(params[:id]) - # fresh_when(:etag => @article, :last_modified => @article.created_at.utc) + # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true) # end # # This will render the show template if the request isn't sending a matching etag or # If-Modified-Since header and just a "304 Not Modified" response if there's a match. + # def fresh_when(options) - options.assert_valid_keys(:etag, :last_modified) + options.assert_valid_keys(:etag, :last_modified, :public) response.etag = options[:etag] if options[:etag] response.last_modified = options[:last_modified] if options[:last_modified] + + if options[:public] + cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } + cache_control.delete("private") + cache_control.delete("no-cache") + cache_control << "public" + response.headers["Cache-Control"] = cache_control.join(', ') + end if request.fresh?(response) head :not_modified @@ -1178,15 +1200,26 @@ module ActionController #:nodoc: # # Examples: # expires_in 20.minutes - # expires_in 3.hours, :private => false - # expires in 3.hours, 'max-stale' => 5.hours, :private => nil, :public => true + # expires_in 3.hours, :public => true + # expires in 3.hours, 'max-stale' => 5.hours, :public => true # # This method will overwrite an existing Cache-Control header. # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities. def expires_in(seconds, options = {}) #:doc: - cache_options = { 'max-age' => seconds, 'private' => true }.symbolize_keys.merge!(options.symbolize_keys) - cache_options.delete_if { |k,v| v.nil? or v == false } - cache_control = cache_options.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"} + cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip } + + cache_control << "max-age=#{seconds}" + cache_control.delete("no-cache") + if options[:public] + cache_control.delete("private") + cache_control << "public" + else + cache_control << "private" + end + + # This allows for additional headers to be passed through like 'max-stale' => 5.hours + cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"} + response.headers["Cache-Control"] = cache_control.join(', ') end @@ -1215,13 +1248,12 @@ module ActionController #:nodoc: response.status = interpret_status(status || DEFAULT_RENDER_STATUS_CODE) if append_response - response.body ||= '' - response.body << text.to_s + response.body_parts << text.to_s else response.body = case text - when Proc then text - when nil then " " # Safari doesn't pass the headers of the return if the response is zero length - else text.to_s + when Proc then text + when nil then [" "] # Safari doesn't pass the headers of the return if the response is zero length + else [text.to_s] end end end @@ -1298,7 +1330,7 @@ module ActionController #:nodoc: rescue ActionView::MissingTemplate => e # Was the implicit template missing, or was it another template? if e.path == default_template_name - raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller + raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence(:locale => :en)}", caller else raise e end diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index 34e1c3527f..87b5029e57 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -129,24 +129,23 @@ module ActionController #:nodoc: attr_reader :path, :extension class << self - def path_for(controller, options, infer_extension=true) + def path_for(controller, options, infer_extension = true) new(controller, options, infer_extension).path end end # When true, infer_extension will look up the cache path extension from the request's path & format. - # This is desirable when reading and writing the cache, but not when expiring the cache - expire_action should expire the same files regardless of the request format. - def initialize(controller, options = {}, infer_extension=true) - if infer_extension and options.is_a? Hash - request_extension = extract_extension(controller.request) - options = options.reverse_merge(:format => request_extension) + # This is desirable when reading and writing the cache, but not when expiring the cache - + # expire_action should expire the same files regardless of the request format. + def initialize(controller, options = {}, infer_extension = true) + if infer_extension + extract_extension(controller.request) + options = options.reverse_merge(:format => @extension) if options.is_a?(Hash) end + path = controller.url_for(options).split('://').last normalize!(path) - if infer_extension - @extension = request_extension - add_extension!(path, @extension) - end + add_extension!(path, @extension) @path = URI.unescape(path) end @@ -162,13 +161,7 @@ module ActionController #:nodoc: def extract_extension(request) # Don't want just what comes after the last '.' to accommodate multi part extensions # such as tar.gz. - extension = request.path[/^[^.]+\.(.+)$/, 1] - - # If there's no extension in the path, check request.format - if extension.nil? - extension = request.cache_format - end - extension + @extension = request.path[/^[^.]+\.(.+)$/, 1] || request.cache_format end end end diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index ec40b5c4e6..07931e4a4a 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -13,7 +13,6 @@ module ActionController end if defined?(ActiveRecord) - after_dispatch :checkin_connections to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers } end @@ -115,11 +114,5 @@ module ActionController def flush_logger Base.logger.flush end - - def checkin_connections - # Don't return connection (and peform implicit rollback) if this request is a part of integration test - return if @env.key?("rack.test") - ActiveRecord::Base.clear_active_connections! - end end end diff --git a/actionpack/lib/action_controller/http_authentication.rb b/actionpack/lib/action_controller/http_authentication.rb index 2ccbc22420..b6b5267c66 100644 --- a/actionpack/lib/action_controller/http_authentication.rb +++ b/actionpack/lib/action_controller/http_authentication.rb @@ -68,8 +68,11 @@ module ActionController # # Simple Digest example: # + # require 'digest/md5' # class PostsController < ApplicationController - # USERS = {"dhh" => "secret"} + # REALM = "SuperSecret" + # USERS = {"dhh" => "secret", #plain text password + # "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password # # before_filter :authenticate, :except => [:index] # @@ -83,14 +86,18 @@ module ActionController # # private # def authenticate - # authenticate_or_request_with_http_digest(realm) do |username| + # authenticate_or_request_with_http_digest(REALM) do |username| # USERS[username] # end # end # end # - # NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password so the framework can appropriately - # hash it to check the user's credentials. Returning +nil+ will cause authentication to fail. + # NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately + # hash to check the user's credentials. Returning +nil+ will cause authentication to fail. + # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If + # the password file or database is compromised, the attacker would be able to use the ha1 hash to + # authenticate as the user at this +realm+, but would not have the user's password to try using at + # other sites. # # On shared hosts, Apache sometimes doesn't pass authentication headers to # FCGI instances. If your environment matches this description and you cannot @@ -177,26 +184,37 @@ module ActionController end # Raises error unless the request credentials response value matches the expected value. + # First try the password as a ha1 digest password. If this fails, then try it as a plain + # text password. def validate_digest_response(request, realm, &password_procedure) credentials = decode_credentials_header(request) valid_nonce = validate_nonce(request, credentials[:nonce]) - if valid_nonce && realm == credentials[:realm] && opaque(request.session.session_id) == credentials[:opaque] + if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque] password = password_procedure.call(credentials[:username]) - expected = expected_response(request.env['REQUEST_METHOD'], credentials[:uri], credentials, password) - expected == credentials[:response] + + [true, false].any? do |password_is_ha1| + expected = expected_response(request.env['REQUEST_METHOD'], request.env['REQUEST_URI'], credentials, password, password_is_ha1) + expected == credentials[:response] + end end end # Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+ - def expected_response(http_method, uri, credentials, password) - ha1 = ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) + # Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead + # of a plain-text password. + def expected_response(http_method, uri, credentials, password, password_is_ha1=true) + ha1 = password_is_ha1 ? password : ha1(credentials, password) ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':')) ::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':')) end - def encode_credentials(http_method, credentials, password) - credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password) + def ha1(credentials, password) + ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':')) + end + + def encode_credentials(http_method, credentials, password, password_is_ha1) + credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1) "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ') end @@ -213,8 +231,7 @@ module ActionController end def authentication_header(controller, realm) - session_id = controller.request.session.session_id - controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce(session_id)}", opaque="#{opaque(session_id)}") + controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}") end def authentication_request(controller, realm, message = nil) @@ -252,23 +269,36 @@ module ActionController # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4 # of this document. # - # The nonce is opaque to the client. - def nonce(session_id, time = Time.now) + # The nonce is opaque to the client. Composed of Time, and hash of Time with secret + # key from the Rails session secret generated upon creation of project. Ensures + # the time cannot be modifed by client. + def nonce(time = Time.now) t = time.to_i - hashed = [t, session_id] + hashed = [t, secret_key] digest = ::Digest::MD5.hexdigest(hashed.join(":")) Base64.encode64("#{t}:#{digest}").gsub("\n", '') end - def validate_nonce(request, value) + # Might want a shorter timeout depending on whether the request + # is a PUT or POST, and if client is browser or web service. + # Can be much shorter if the Stale directive is implemented. This would + # allow a user to use new nonce without prompting user again for their + # username and password. + def validate_nonce(request, value, seconds_to_timeout=5*60) t = Base64.decode64(value).split(":").first.to_i - nonce(request.session.session_id, t) == value && (t - Time.now.to_i).abs <= 10 * 60 + nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout end - # Opaque based on digest of session_id - def opaque(session_id) - Base64.encode64(::Digest::MD5::hexdigest(session_id)).gsub("\n", '') + # Opaque based on random generation - but changing each request? + def opaque() + ::Digest::MD5.hexdigest(secret_key) end + + # Set in /initializers/session_store.rb, and loaded even if sessions are not in use. + def secret_key + ActionController::Base.session_options[:secret] + end + end end end diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 1c05ab0bf6..fda6b639d1 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -5,7 +5,7 @@ require 'active_support/test_case' module ActionController module Integration #:nodoc: # An integration Session instance represents a set of requests and responses - # performed sequentially by some virtual user. Becase you can instantiate + # performed sequentially by some virtual user. Because you can instantiate # multiple sessions and run them side-by-side, you can also mimic (to some # limited extent) multiple simultaneous users interacting with your system. # @@ -332,11 +332,13 @@ module ActionController @cookies[name] = value end - @body = "" if body.is_a?(String) - @body << body + @body_parts = [body] + @body = body else - body.each { |part| @body << part } + @body_parts = [] + body.each { |part| @body_parts << part.to_s } + @body = @body_parts.join end if @controller = ActionController::Base.last_instantiation @@ -349,7 +351,7 @@ module ActionController @response = Response.new @response.status = status.to_s @response.headers.replace(@headers) - @response.body = @body + @response.body = @body_parts end # Decorate the response with the standard behavior of the diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb index a0db7acf72..6ec0c1b304 100644 --- a/actionpack/lib/action_controller/layout.rb +++ b/actionpack/lib/action_controller/layout.rb @@ -198,7 +198,7 @@ module ActionController #:nodoc: # is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method # object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return # weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard. - def active_layout(passed_layout = nil) + def active_layout(passed_layout = nil, options = {}) layout = passed_layout || default_layout return layout if layout.respond_to?(:render) @@ -207,21 +207,23 @@ module ActionController #:nodoc: when Proc then layout.call(self) else layout end - - find_layout(active_layout, @template.template_format) if active_layout + + find_layout(active_layout, default_template_format, options[:html_fallback]) if active_layout end private def default_layout #:nodoc: - layout = self.class.read_inheritable_attribute(:layout) unless default_template_format == :js + layout = self.class.read_inheritable_attribute(:layout) return layout unless self.class.read_inheritable_attribute(:auto_layout) find_layout(layout, default_template_format) rescue ActionView::MissingTemplate nil end - def find_layout(layout, *formats) #:nodoc: - view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", *formats) + def find_layout(layout, format, html_fallback=false) #:nodoc: + view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", format, html_fallback) + rescue ActionView::MissingTemplate + raise if Mime::Type.lookup_by_extension(format.to_s).html? end def pick_layout(options) @@ -232,7 +234,7 @@ module ActionController #:nodoc: when NilClass, TrueClass active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name) else - active_layout(layout) + active_layout(layout, :html_fallback => true) end else active_layout if action_has_layout? && candidate_for_layout?(options) @@ -258,7 +260,12 @@ module ActionController #:nodoc: template = options[:template] || default_template(options[:action]) if options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty? begin - !self.view_paths.find_template(template, default_template_format).exempt_from_layout? + template_object = self.view_paths.find_template(template, default_template_format) + # this restores the behavior from 2.2.2, where response.template.template_format was reset + # to :html for :js requests with a matching html template. + # see v2.2.2, ActionView::Base, lines 328-330 + @real_format = :html if response.template.template_format == :js && template_object.format == "html" + !template_object.exempt_from_layout? rescue ActionView::MissingTemplate true end @@ -268,7 +275,7 @@ module ActionController #:nodoc: end def default_template_format - response.template.template_format + @real_format || response.template.template_format end end end diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index 924d1aa6bd..d9b614c237 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -163,7 +163,8 @@ module ActionController if parent.is_a?(Symbol) || parent.is_a?(String) string << "#{parent}_" else - string << "#{RecordIdentifier.__send__("singular_class_name", parent)}_" + string << "#{RecordIdentifier.__send__("plural_class_name", parent)}".singularize + string << "_" end end end @@ -171,7 +172,9 @@ module ActionController if record.is_a?(Symbol) || record.is_a?(String) route << "#{record}_" else - route << "#{RecordIdentifier.__send__("#{inflection}_class_name", record)}_" + route << "#{RecordIdentifier.__send__("plural_class_name", record)}" + route = route.singularize if inflection == :singular + route << "_" end action_prefix(options) + namespace + route + routing_type(options).to_s diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 0e95cfc147..ef223f157c 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -32,7 +32,7 @@ module ActionController # <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS # constant above, an UnknownHttpMethod exception is raised. def request_method - @request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}") + @request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}") end # Returns the HTTP request \method used for action processing as a @@ -442,6 +442,7 @@ EOM end def reset_session + @env['rack.session.options'].delete(:id) @env['rack.session'] = {} end diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index 3af21967df..86abb7b2f4 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -91,7 +91,7 @@ module ActionController end def shallow_path_prefix - @shallow_path_prefix ||= "#{path_prefix unless @options[:shallow]}" + @shallow_path_prefix ||= @options[:shallow] ? @options[:namespace].try(:sub, /\/$/, '') : path_prefix end def member_path @@ -103,7 +103,7 @@ module ActionController end def shallow_name_prefix - @shallow_name_prefix ||= "#{name_prefix unless @options[:shallow]}" + @shallow_name_prefix ||= @options[:shallow] ? @options[:namespace].try(:gsub, /\//, '_') : name_prefix end def nesting_name_prefix @@ -630,7 +630,7 @@ module ActionController action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash) action_path ||= Base.resources_path_names[action] || action - map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m) + map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true }) end end end @@ -641,9 +641,9 @@ module ActionController map_resource_routes(map, resource, :destroy, resource.member_path, route_path) end - def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil) + def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} ) if resource.has_action?(action) - action_options = action_options_for(action, resource, method) + action_options = action_options_for(action, resource, method, resource_options) formatted_route_path = "#{route_path}.:format" if route_name && @set.named_routes[route_name.to_sym].nil? @@ -660,9 +660,10 @@ module ActionController end end - def action_options_for(action, resource, method = nil) + def action_options_for(action, resource, method = nil, resource_options = {}) default_options = { :action => action.to_s } require_id = !resource.kind_of?(SingletonResource) + force_id = resource_options[:force_id] && !resource.kind_of?(SingletonResource) case default_options[:action] when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements) @@ -670,7 +671,7 @@ module ActionController when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id)) when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id)) when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id)) - else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements) + else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(force_id)) end end end diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb index ccff473df0..febe4ccf29 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -40,14 +40,28 @@ module ActionController # :nodoc: delegate :default_charset, :to => 'ActionController::Base' def initialize - @status = 200 + super @header = Rack::Utils::HeaderHash.new(DEFAULT_HEADERS) + @session, @assigns = [], [] + end - @writer = lambda { |x| @body << x } - @block = nil + def body + str = '' + each { |part| str << part.to_s } + str + end - @body = "", - @session, @assigns = [], [] + def body=(body) + @body = + if body.is_a?(String) + [body] + else + body + end + end + + def body_parts + @body end def location; headers['Location'] end @@ -152,7 +166,7 @@ module ActionController # :nodoc: @writer = lambda { |x| callback.call(x) } @body.call(self, self) elsif @body.is_a?(String) - @body.each_line(&callback) + callback.call(@body) else @body.each(&callback) end @@ -162,7 +176,8 @@ module ActionController # :nodoc: end def write(str) - @writer.call str.to_s + str = str.to_s + @writer.call str str end @@ -186,7 +201,7 @@ module ActionController # :nodoc: if request && request.etag_matches?(etag) self.status = '304 Not Modified' - self.body = '' + self.body = [] end set_conditional_cache_control! @@ -195,7 +210,11 @@ module ActionController # :nodoc: def nonempty_ok_response? ok = !status || status.to_s[0..2] == '200' - ok && body.is_a?(String) && !body.empty? + ok && string_body? + end + + def string_body? + !body_parts.respond_to?(:call) && body_parts.any? && body_parts.all? { |part| part.is_a?(String) } end def set_conditional_cache_control! @@ -216,8 +235,8 @@ module ActionController # :nodoc: headers.delete('Content-Length') elsif length = headers['Content-Length'] headers['Content-Length'] = length.to_s - elsif !body.respond_to?(:call) && (!status || status.to_s[0..2] != '304') - headers["Content-Length"] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s + elsif string_body? && (!status || status.to_s[0..2] != '304') + headers["Content-Length"] = Rack::Utils.bytesize(body).to_s end end diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index a2141a77dc..c0eb61340b 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -267,7 +267,7 @@ module ActionController module Routing SEPARATORS = %w( / . ? ) - HTTP_METHODS = [:get, :head, :post, :put, :delete] + HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb index 44d759444a..d9590c88b8 100644 --- a/actionpack/lib/action_controller/routing/builder.rb +++ b/actionpack/lib/action_controller/routing/builder.rb @@ -159,7 +159,8 @@ module ActionController path = "/#{path}" unless path[0] == ?/ path = "#{path}/" unless path[-1] == ?/ - path = "/#{options[:path_prefix].to_s.gsub(/^\//,'')}#{path}" if options[:path_prefix] + prefix = options[:path_prefix].to_s.gsub(/^\//,'') + path = "/#{prefix}#{path}" unless prefix.blank? segments = segments_for_route_path(path) defaults, requirements, conditions = divide_route_options(segments, options) diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb index ebc553512f..9bfebff0c0 100644 --- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb +++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb @@ -98,7 +98,6 @@ module ActionController if Array === item i += 1 start = (i == 1) - final = (i == list.size) tag, sub = item if tag == :dynamic body += padding + "#{start ? 'if' : 'elsif'} true\n" diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index 129e87c139..4f936d51d2 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -3,7 +3,11 @@ module ActionController class Segment #:nodoc: RESERVED_PCHAR = ':@&=+$,;' SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}" - UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze + if RUBY_VERSION >= '1.9' + UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze + else + UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze + end # TODO: Convert :is_optional accessor to read only attr_accessor :is_optional @@ -314,13 +318,17 @@ module ActionController end def regexp_chunk - '(\.[^/?\.]+)?' + '/|(\.[^/?\.]+)?' end def to_s '(.:format)?' end - + + def extract_value + "#{local_name} = options[:#{key}] && options[:#{key}].to_s.downcase" + end + #the value should not include the period (.) def match_extraction(next_capture) %[ diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index dbaec00bee..9dd09c30b4 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -258,11 +258,11 @@ module ActionController #:nodoc: # Returns binary content (downloadable file), converted to a String def binary_content - raise "Response body is not a Proc: #{body.inspect}" unless body.kind_of?(Proc) + raise "Response body is not a Proc: #{body_parts.inspect}" unless body_parts.kind_of?(Proc) require 'stringio' sio = StringIO.new - body.call(self, sio) + body_parts.call(self, sio) sio.rewind sio.read diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb index bb6cb437b7..16720b915b 100644 --- a/actionpack/lib/action_controller/url_rewriter.rb +++ b/actionpack/lib/action_controller/url_rewriter.rb @@ -68,29 +68,17 @@ module ActionController # This generates, among other things, the method <tt>users_path</tt>. By default, # this method is accessible from your controllers, views and mailers. If you need # to access this auto-generated method from other places (such as a model), then - # you can do that in two ways. - # - # The first way is to include ActionController::UrlWriter in your class: + # you can do that by including ActionController::UrlWriter in your class: # # class User < ActiveRecord::Base - # include ActionController::UrlWriter # !!! + # include ActionController::UrlWriter # - # def name=(value) - # write_attribute('name', value) - # write_attribute('base_uri', users_path) # !!! + # def base_uri + # user_path(self) # end # end # - # The second way is to access them through ActionController::UrlWriter. - # The autogenerated named routes methods are available as class methods: - # - # class User < ActiveRecord::Base - # def name=(value) - # write_attribute('name', value) - # path = ActionController::UrlWriter.users_path # !!! - # write_attribute('base_uri', path) # !!! - # end - # end + # User.find(1).base_uri # => "/users/1" module UrlWriter def self.included(base) #:nodoc: ActionController::Routing::Routes.install_helpers(base) diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb index 376bb87409..e2c49c284f 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb @@ -556,7 +556,7 @@ module HTML end # Attribute value. - next if statement.sub!(/^\[\s*([[:alpha:]][\w\-]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match| + next if statement.sub!(/^\[\s*([[:alpha:]][\w\-:]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match| name, equality, value = $1, $2, $3 if value == "?" value = values.shift diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb index 6c03b55552..6349b95094 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack.rb @@ -23,14 +23,16 @@ module Rack # Return the Rack release as a dotted string. def self.release - "0.4" + "1.0 bundled" end autoload :Builder, "rack/builder" autoload :Cascade, "rack/cascade" + autoload :Chunked, "rack/chunked" autoload :CommonLogger, "rack/commonlogger" autoload :ConditionalGet, "rack/conditionalget" autoload :ContentLength, "rack/content_length" + autoload :ContentType, "rack/content_type" autoload :File, "rack/file" autoload :Deflater, "rack/deflater" autoload :Directory, "rack/directory" diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb index 8489c9b9c4..214df6299e 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/abstract/handler.rb @@ -8,8 +8,8 @@ module Rack attr_accessor :realm - def initialize(app, &authenticator) - @app, @authenticator = app, authenticator + def initialize(app, realm=nil, &authenticator) + @app, @realm, @authenticator = app, realm, authenticator end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb index 6d2bd29c2e..e579dc9632 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/md5.rb @@ -21,7 +21,7 @@ module Rack attr_writer :passwords_hashed - def initialize(app) + def initialize(*args) super @passwords_hashed = nil end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb index a40f57b7f1..a8aa3bf996 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/auth/digest/request.rb @@ -8,7 +8,7 @@ module Rack class Request < Auth::AbstractRequest def method - @env['REQUEST_METHOD'] + @env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD'] end def digest? diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb index 25994d5a44..295235e56a 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/builder.rb @@ -34,11 +34,7 @@ module Rack end def use(middleware, *args, &block) - @ins << if block_given? - lambda { |app| middleware.new(app, *args, &block) } - else - lambda { |app| middleware.new(app, *args) } - end + @ins << lambda { |app| middleware.new(app, *args, &block) } end def run(app) diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb new file mode 100644 index 0000000000..280d89dd65 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/chunked.rb @@ -0,0 +1,49 @@ +require 'rack/utils' + +module Rack + + # Middleware that applies chunked transfer encoding to response bodies + # when the response does not include a Content-Length header. + class Chunked + include Rack::Utils + + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + headers = HeaderHash.new(headers) + + if env['HTTP_VERSION'] == 'HTTP/1.0' || + STATUS_WITH_NO_ENTITY_BODY.include?(status) || + headers['Content-Length'] || + headers['Transfer-Encoding'] + [status, headers.to_hash, body] + else + dup.chunk(status, headers, body) + end + end + + def chunk(status, headers, body) + @body = body + headers.delete('Content-Length') + headers['Transfer-Encoding'] = 'chunked' + [status, headers.to_hash, self] + end + + def each + term = "\r\n" + @body.each do |chunk| + size = bytesize(chunk) + next if size == 0 + yield [size.to_s(16), term, chunk, term].join + end + yield ["0", term, "", term].join + end + + def close + @body.close if @body.respond_to?(:close) + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb index bce22a32c5..1e56d43853 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb @@ -3,21 +3,23 @@ require 'rack/utils' module Rack # Sets the Content-Length header on responses with fixed-length bodies. class ContentLength + include Rack::Utils + def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) - headers = Utils::HeaderHash.new(headers) + headers = HeaderHash.new(headers) - if !Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) && + if !STATUS_WITH_NO_ENTITY_BODY.include?(status) && !headers['Content-Length'] && !headers['Transfer-Encoding'] && (body.respond_to?(:to_ary) || body.respond_to?(:to_str)) body = [body] if body.respond_to?(:to_str) # rack 0.4 compat - length = body.to_ary.inject(0) { |len, part| len + part.length } + length = body.to_ary.inject(0) { |len, part| len + bytesize(part) } headers['Content-Length'] = length.to_s end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb new file mode 100644 index 0000000000..0c1e1ca3e1 --- /dev/null +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/content_type.rb @@ -0,0 +1,23 @@ +require 'rack/utils' + +module Rack + + # Sets the Content-Type header on responses which don't have one. + # + # Builder Usage: + # use Rack::ContentType, "text/plain" + # + # When no content type argument is provided, "text/html" is assumed. + class ContentType + def initialize(app, content_type = "text/html") + @app, @content_type = app, content_type + end + + def call(env) + status, headers, body = @app.call(env) + headers = Utils::HeaderHash.new(headers) + headers['Content-Type'] ||= @content_type + [status, headers.to_hash, body] + end + end +end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb index 3e66680092..a42b7477ae 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb @@ -36,12 +36,12 @@ module Rack mtime = headers.key?("Last-Modified") ? Time.httpdate(headers["Last-Modified"]) : Time.now body = self.class.gzip(body, mtime) - size = body.respond_to?(:bytesize) ? body.bytesize : body.size + size = Rack::Utils.bytesize(body) headers = headers.merge("Content-Encoding" => "gzip", "Content-Length" => size.to_s) [status, headers, [body]] when "deflate" body = self.class.deflate(body) - size = body.respond_to?(:bytesize) ? body.bytesize : body.size + size = Rack::Utils.bytesize(body) headers = headers.merge("Content-Encoding" => "deflate", "Content-Length" => size.to_s) [status, headers, [body]] when "identity" diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb index 56ee5e7b60..acdd3029d3 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb @@ -70,7 +70,7 @@ table { width:100%%; } return unless @path_info.include? ".." body = "Forbidden\n" - size = body.respond_to?(:bytesize) ? body.bytesize : body.size + size = Rack::Utils.bytesize(body) return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]] end @@ -89,6 +89,8 @@ table { width:100%%; } type = stat.directory? ? 'directory' : Mime.mime_type(ext) size = stat.directory? ? '-' : filesize_format(size) mtime = stat.mtime.httpdate + url << '/' if stat.directory? + basename << '/' if stat.directory? @files << [ url, basename, size, type, mtime ] end @@ -120,7 +122,7 @@ table { width:100%%; } def entity_not_found body = "Entity not found: #{@path_info}\n" - size = body.respond_to?(:bytesize) ? body.bytesize : body.size + size = Rack::Utils.bytesize(body) return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]] end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb index 7869227a36..fe62bd6b86 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb @@ -60,7 +60,7 @@ module Rack body = self else body = [F.read(@path)] - size = body.first.size + size = Utils.bytesize(body.first) end [200, { diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb index f2c976cf46..e38156c7f0 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/cgi.rb @@ -1,3 +1,5 @@ +require 'rack/content_length' + module Rack module Handler class CGI @@ -6,6 +8,8 @@ module Rack end def self.serve(app) + app = ContentLength.new(app) + env = ENV.to_hash env.delete "HTTP_CONTENT_LENGTH" diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb index f03e1615c9..6324c7d274 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/fastcgi.rb @@ -1,5 +1,6 @@ require 'fcgi' require 'socket' +require 'rack/content_length' module Rack module Handler @@ -29,6 +30,8 @@ module Rack end def self.serve(request, app) + app = Rack::ContentLength.new(app) + env = request.env env.delete "HTTP_CONTENT_LENGTH" diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb index 1f850fc77b..c65ba3ec8e 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/lsws.rb @@ -1,5 +1,6 @@ require 'lsapi' -#require 'cgi' +require 'rack/content_length' + module Rack module Handler class LSWS @@ -9,11 +10,13 @@ module Rack end end def self.serve(app) + app = Rack::ContentLength.new(app) + env = ENV.to_hash env.delete "HTTP_CONTENT_LENGTH" env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" env.update({"rack.version" => [0,1], - "rack.input" => $stdin, + "rack.input" => StringIO.new($stdin.read.to_s), "rack.errors" => $stderr, "rack.multithread" => false, "rack.multiprocess" => true, diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb index 178a1a8fe4..f0c0d58330 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/mongrel.rb @@ -1,5 +1,7 @@ require 'mongrel' require 'stringio' +require 'rack/content_length' +require 'rack/chunked' module Rack module Handler @@ -33,7 +35,7 @@ module Rack end def initialize(app) - @app = app + @app = Rack::Chunked.new(Rack::ContentLength.new(app)) end def process(request, response) diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb index fd18a8359b..9495c66374 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/scgi.rb @@ -1,5 +1,7 @@ require 'scgi' require 'stringio' +require 'rack/content_length' +require 'rack/chunked' module Rack module Handler @@ -14,7 +16,7 @@ module Rack end def initialize(settings = {}) - @app = settings[:app] + @app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app])) @log = Object.new def @log.info(*args); end def @log.error(*args); end diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb index 7ad088b36a..3d4fedff75 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/thin.rb @@ -1,9 +1,12 @@ require "thin" +require "rack/content_length" +require "rack/chunked" module Rack module Handler class Thin def self.run(app, options={}) + app = Rack::Chunked.new(Rack::ContentLength.new(app)) server = ::Thin::Server.new(options[:Host] || '0.0.0.0', options[:Port] || 8080, app) diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb index 40be79de13..829e7d6bf8 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/handler/webrick.rb @@ -1,5 +1,6 @@ require 'webrick' require 'stringio' +require 'rack/content_length' module Rack module Handler @@ -14,7 +15,7 @@ module Rack def initialize(server, app) super server - @app = app + @app = Rack::ContentLength.new(app) end def service(req, res) @@ -35,7 +36,12 @@ module Rack env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] env["QUERY_STRING"] ||= "" env["REQUEST_PATH"] ||= "/" - env.delete "PATH_INFO" if env["PATH_INFO"] == "" + if env["PATH_INFO"] == "" + env.delete "PATH_INFO" + else + path, n = req.request_uri.path, env["SCRIPT_NAME"].length + env["PATH_INFO"] = path[n, path.length-n] + end status, headers, body = @app.call(env) begin diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb index 7eb05437f0..44a33ce36e 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/lint.rb @@ -88,7 +88,9 @@ module Rack ## within the application. This may be an ## empty string, if the request URL targets ## the application root and does not have a - ## trailing slash. + ## trailing slash. This information should be + ## decoded by the server if it comes from a + ## URL. ## <tt>QUERY_STRING</tt>:: The portion of the request URL that ## follows the <tt>?</tt>, if any. May be @@ -372,59 +374,43 @@ module Rack ## === The Content-Length def check_content_length(status, headers, env) - chunked_response = false - headers.each { |key, value| - if key.downcase == 'transfer-encoding' - chunked_response = value.downcase != 'identity' - end - } - headers.each { |key, value| if key.downcase == 'content-length' - ## There must be a <tt>Content-Length</tt>, except when the - ## +Status+ is 1xx, 204 or 304, in which case there must be none - ## given. + ## There must not be a <tt>Content-Length</tt> header when the + ## +Status+ is 1xx, 204 or 304. assert("Content-Length header found in #{status} response, not allowed") { not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i } - assert('Content-Length header should not be used if body is chunked') { - not chunked_response - } - bytes = 0 string_body = true - @body.each { |part| - unless part.kind_of?(String) - string_body = false - break - end + if @body.respond_to?(:to_ary) + @body.each { |part| + unless part.kind_of?(String) + string_body = false + break + end - bytes += (part.respond_to?(:bytesize) ? part.bytesize : part.size) - } - - if env["REQUEST_METHOD"] == "HEAD" - assert("Response body was given for HEAD request, but should be empty") { - bytes == 0 + bytes += Rack::Utils.bytesize(part) } - else - if string_body - assert("Content-Length header was #{value}, but should be #{bytes}") { - value == bytes.to_s + + if env["REQUEST_METHOD"] == "HEAD" + assert("Response body was given for HEAD request, but should be empty") { + bytes == 0 } + else + if string_body + assert("Content-Length header was #{value}, but should be #{bytes}") { + value == bytes.to_s + } + end end end return end } - - if [ String, Array ].include?(@body.class) && !chunked_response - assert('No Content-Length header found') { - Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i - } - end end ## === The Body diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb index a593110139..caf60d5b19 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/response.rb @@ -16,6 +16,8 @@ module Rack # Your application's +call+ should end returning Response#finish. class Response + attr_accessor :length + def initialize(body=[], status=200, header={}, &block) @status = status @header = Utils::HeaderHash.new({"Content-Type" => "text/html"}. diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb index 5f13404dce..28258c7c89 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/showstatus.rb @@ -27,7 +27,7 @@ module Rack message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s detail = env["rack.showstatus.detail"] || message body = @template.result(binding) - size = body.respond_to?(:bytesize) ? body.bytesize : body.size + size = Rack::Utils.bytesize(body) [status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]] else [status, headers, body] diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb index eb1457a850..0ff32df181 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/urlmap.rb @@ -12,7 +12,11 @@ module Rack # first, since they are most specific. class URLMap - def initialize(map) + def initialize(map = {}) + remap(map) + end + + def remap(map) @mapping = map.map { |location, app| if location =~ %r{\Ahttps?://(.*?)(/.*)} host, location = $1, $2 diff --git a/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb b/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb index f352cb6783..0a61bce707 100644 --- a/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb +++ b/actionpack/lib/action_controller/vendor/rack-1.0/rack/utils.rb @@ -144,6 +144,19 @@ module Rack end module_function :select_best_encoding + # Return the bytesize of String; uses String#length under Ruby 1.8 and + # String#bytesize under 1.9. + if ''.respond_to?(:bytesize) + def bytesize(string) + string.bytesize + end + else + def bytesize(string) + string.size + end + end + module_function :bytesize + # Context allows the use of a compatible middleware at different points # in a request handling stack. A compatible middleware must define # #context which should take the arguments env and app. The first of which @@ -359,7 +372,7 @@ module Rack data = body end - Utils.normalize_params(params, name, data) + Utils.normalize_params(params, name, data) unless data.nil? break if buf.empty? || content_length == -1 } |