Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Rack 2.0 #137

Merged
merged 1 commit into from
Nov 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,7 @@ rvm:
- 2.4.1
- 2.3.4
- 2.2.7
- 2.1.10
- 1.9.3

gemfile:
- Gemfile
- test/gemfiles/minimum_versions

matrix:
include:
- rvm: 1.8.7
gemfile: test/gemfiles/1.8.7-compatible
- rvm: 1.8.7
gemfile: test/gemfiles/1.8.7-minimum_versions
6 changes: 0 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@ have before they can be landed:
place, but if you diverge from how things are done at the moment the patch
won't get accepted as-is.

* Support Ruby 1.8.7. We're going to keep supporting 1.8 as long as Rack
proper does. Some existing middleware requires 1.9, but all new
middleware, and existing 1.8-compatible middleware, must continue to be
1.8 compatible. We use [Travis
CI test runs](https://travis-ci.org/rack/rack-contrib) to validate this.

* Require no external dependencies. Some existing middleware depends on
additional gems in order to function; we feel that this is an
anti-pattern, and so no patches will be accepted which add additional
Expand Down
2 changes: 0 additions & 2 deletions lib/rack/contrib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ def self.release
end
end

autoload :AcceptFormat, "rack/contrib/accept_format"
autoload :Access, "rack/contrib/access"
autoload :BounceFavicon, "rack/contrib/bounce_favicon"
autoload :Cookies, "rack/contrib/cookies"
Expand All @@ -34,7 +33,6 @@ def self.release
autoload :Profiler, "rack/contrib/profiler"
autoload :ResponseHeaders, "rack/contrib/response_headers"
autoload :Runtime, "rack/contrib/runtime"
autoload :Sendfile, "rack/contrib/sendfile"
autoload :Signals, "rack/contrib/signals"
autoload :SimpleEndpoint, "rack/contrib/simple_endpoint"
autoload :TimeZone, "rack/contrib/time_zone"
Expand Down
66 changes: 0 additions & 66 deletions lib/rack/contrib/accept_format.rb

This file was deleted.

18 changes: 9 additions & 9 deletions lib/rack/contrib/jsonp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def call(env)
end

headers = HeaderHash.new(headers)

if is_json?(headers) && has_callback?(request)
callback = request.params['callback']
return bad_request unless valid_callback?(callback)
Expand All @@ -50,32 +50,32 @@ def call(env)

# No longer json, its javascript!
headers['Content-Type'] = headers['Content-Type'].gsub('json', 'javascript')

# Set new Content-Length, if it was set before we mutated the response body
if headers['Content-Length']
length = response.to_ary.inject(0) { |len, part| len + bytesize(part) }
length = response.to_ary.inject(0) { |len, part| len + part.bytesize }
headers['Content-Length'] = length.to_s
end
end

[status, headers, response]
end

private

def is_json?(headers)
headers.key?('Content-Type') && headers['Content-Type'].include?('application/json')
end

def has_callback?(request)
request.params.include?('callback') and not request.params['callback'].to_s.empty?
end

# See:
# http://stackoverflow.com/questions/1661197/valid-characters-for-javascript-variable-names
#
#
# NOTE: Supports dots (.) since callbacks are often in objects:
#
#
def valid_callback?(callback)
callback =~ VALID_CALLBACK
end
Expand All @@ -98,7 +98,7 @@ def pad(callback, response, body = "")
# a string and should therefore not be present any other places.
body << s.to_s.gsub(U2028, '\u2028').gsub(U2029, '\u2029')
end

# https://github.com/rack/rack-contrib/issues/46
response.close if response.respond_to?(:close)

Expand Down
117 changes: 3 additions & 114 deletions lib/rack/contrib/nested_params.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
require 'cgi'
require 'strscan'
require 'rack/utils'

module Rack
# Rack middleware for parsing POST/PUT body data into nested parameters
Expand All @@ -20,124 +19,14 @@ def initialize(app)

def call(env)
if form_vars = env[FORM_VARS]
env[FORM_HASH] = parse_query_parameters(form_vars)
Rack::Utils.parse_nested_query(form_vars)
elsif env[CONTENT_TYPE] == URL_ENCODED
post_body = env[POST_BODY]
env[FORM_INPUT] = post_body
env[FORM_HASH] = parse_query_parameters(post_body.read)
env[FORM_HASH] = Rack::Utils.parse_nested_query(post_body.read)
post_body.rewind if post_body.respond_to?(:rewind)
end
@app.call(env)
end

## the rest is nabbed from Rails ##

def parse_query_parameters(query_string)
return {} if query_string.nil? or query_string.empty?

pairs = query_string.split('&').collect do |chunk|
next if chunk.empty?
key, value = chunk.split('=', 2)
next if key.empty?
value = value.nil? ? nil : CGI.unescape(value)
[ CGI.unescape(key), value ]
end.compact

UrlEncodedPairParser.new(pairs).result
end

class UrlEncodedPairParser < StringScanner
attr_reader :top, :parent, :result

def initialize(pairs = [])
super('')
@result = {}
pairs.each { |key, value| parse(key, value) }
end

KEY_REGEXP = %r{([^\[\]=&]+)}
BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}

# Parse the query string
def parse(key, value)
self.string = key
@top, @parent = result, nil

# First scan the bare key
key = scan(KEY_REGEXP) or return
key = post_key_check(key)

# Then scan as many nestings as present
until eos?
r = scan(BRACKETED_KEY_REGEXP) or return
key = self[1]
key = post_key_check(key)
end

bind(key, value)
end

private
# After we see a key, we must look ahead to determine our next action. Cases:
#
# [] follows the key. Then the value must be an array.
# = follows the key. (A value comes next)
# & or the end of string follows the key. Then the key is a flag.
# otherwise, a hash follows the key.
def post_key_check(key)
if scan(/\[\]/) # a[b][] indicates that b is an array
container(key, Array)
nil
elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
container(key, Hash)
nil
else # End of key? We do nothing.
key
end
end

# Add a container to the stack.
def container(key, klass)
type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
value = bind(key, klass.new)
type_conflict! klass, value unless value.is_a?(klass)
push(value)
end

# Push a value onto the 'stack', which is actually only the top 2 items.
def push(value)
@parent, @top = @top, value
end

# Bind a key (which may be nil for items in an array) to the provided value.
def bind(key, value)
if top.is_a? Array
if key
if top[-1].is_a?(Hash) && ! top[-1].key?(key)
top[-1][key] = value
else
top << {key => value}
end
push top.last
return top[key]
else
top << value
return value
end
elsif top.is_a? Hash
key = CGI.unescape(key)
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
top[key] ||= value
return top[key]
else
raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
end
end

def type_conflict!(klass, value)
raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
end
end

end
end
Loading