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 Rack 3 #187

Merged
merged 4 commits into from
Oct 7, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ permissions:

jobs:
test:
name: Ruby ${{ matrix.ruby }} Rack ${{ matrix.rack }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-20.04 ]
rack: [ '~> 2.0', '~> 3.0' ]
ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, 3.2 ]
gemfile: [ Gemfile ]
exclude:
# Rack 3 needs >= Ruby 2.4
- { ruby: 2.2, rack: '~> 3.0' }
- { ruby: 2.3, rack: '~> 3.0' }
runs-on: ${{ matrix.os }}
env:
RACK_VERSION: ${{ matrix.rack }}
BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
steps:
- uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ gem 'minitest', '~> 5.6'
gem 'minitest-hooks', '~> 1.0'
gem 'mail', '~> 2.3', '>= 2.6.4'
gem 'nbio-csshttprequest', '~> 1.0'
gem 'rack', ENV['RACK_VERSION']
gem 'rake'
gem 'rdoc', '~> 5.0'
gem 'ruby-prof'
Expand Down
2 changes: 1 addition & 1 deletion lib/rack/contrib/access.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def ipmasks_for_path(env)
end

def forbidden!
[403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, []]
[403, { 'content-type' => 'text/html', 'content-length' => '0' }, []]
end

def ip_authorized?(request, ipmasks)
Expand Down
2 changes: 1 addition & 1 deletion lib/rack/contrib/backstage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def call(env)
if File.exist?(@file)
content = File.read(@file)
length = content.bytesize.to_s
[503, {'Content-Type' => 'text/html', 'Content-Length' => length}, [content]]
[503, {'content-type' => 'text/html', 'content-length' => length}, [content]]
else
@app.call(env)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rack/contrib/bounce_favicon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def initialize(app)

def call(env)
if env["PATH_INFO"] == "/favicon.ico"
[404, {"Content-Type" => "text/html", "Content-Length" => "0"}, []]
[404, {"content-type" => "text/html", "content-length" => "0"}, []]
else
@app.call(env)
end
Expand Down
5 changes: 4 additions & 1 deletion lib/rack/contrib/common_cookies.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ class CommonCookies
LOCALHOST_OR_IP_REGEXP = /^([\d.]+|localhost)$/
PORT = /:\d+$/

HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers
private_constant :HEADERS_KLASS

def initialize(app)
@app = app
end

def call(env)
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
headers = HEADERS_KLASS.new.merge(headers)

host = env['HTTP_HOST'].sub PORT, ''
share_cookie(headers, host)
Expand Down
4 changes: 3 additions & 1 deletion lib/rack/contrib/csshttprequest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ module Rack

# A Rack middleware for providing CSSHTTPRequest responses.
class CSSHTTPRequest
HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers
private_constant :HEADERS_KLASS

def initialize(app)
@app = app
Expand All @@ -15,7 +17,7 @@ def initialize(app)
# the CSSHTTPRequest encoder
def call(env)
status, headers, response = @app.call(env)
headers = Utils::HeaderHash.new(headers)
headers = HEADERS_KLASS.new.merge(headers)

if chr_request?(env)
encoded_response = encode(response)
Expand Down
2 changes: 1 addition & 1 deletion lib/rack/contrib/deflect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def call env
end

def deflect!
[403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, []]
[403, { 'content-type' => 'text/html', 'content-length' => '0' }, []]
end

def deflect? env
Expand Down
2 changes: 1 addition & 1 deletion lib/rack/contrib/enforce_valid_encoding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def call env
Rack::Utils.unescape(full_path).valid_encoding?
@app.call env
else
[400, {'Content-Type'=>'text/plain'}, ['Bad Request']]
[400, {'content-type'=>'text/plain'}, ['Bad Request']]
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/rack/contrib/expectation_cascade.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ class ExpectationCascade
Expect = "HTTP_EXPECT".freeze
ContinueExpectation = "100-continue".freeze

ExpectationFailed = [417, {"Content-Type" => "text/html"}, []].freeze
NotFound = [404, {"Content-Type" => "text/html"}, []].freeze
ExpectationFailed = [417, {"content-type" => "text/html"}, []]
NotFound = [404, {"content-type" => "text/html"}, []]

attr_reader :apps

Expand Down
2 changes: 1 addition & 1 deletion lib/rack/contrib/host_meta.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def initialize(app, &block)

def call(env)
if env['PATH_INFO'] == '/host-meta'
[200, {'Content-Type' => 'application/host-meta'}, [@response]]
[200, {'content-type' => 'application/host-meta'}, [@response]]
else
@app.call(env)
end
Expand Down
8 changes: 4 additions & 4 deletions lib/rack/contrib/json_body_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ module Rack
# === Parse POST and GET requests only
# use Rack::JSONBodyParser, verbs: ['POST', 'GET']
#
# === Parse POST|PATCH|PUT requests whose Content-Type matches 'json'
# === Parse POST|PATCH|PUT requests whose content-type matches 'json'
# use Rack::JSONBodyParser, media: /json/
#
# === Parse POST requests whose Content-Type is 'application/json' or 'application/vnd+json'
# === Parse POST requests whose content-type is 'application/json' or 'application/vnd+json'
# use Rack::JSONBodyParser, verbs: ['POST'], media: ['application/json', 'application/vnd.api+json']
#
class JSONBodyParser
Expand Down Expand Up @@ -63,7 +63,7 @@ def call(env)
end
rescue JSON::ParserError
body = { error: 'Failed to parse body as JSON' }.to_json
header = { 'Content-Type' => 'application/json' }
header = { 'content-type' => 'application/json' }
return Rack::Response.new(body, 400, header).finish
end
@app.call(env)
Expand All @@ -75,7 +75,7 @@ def update_form_hash_with_json_body(env)
body = env[Rack::RACK_INPUT]
return unless (body_content = body.read) && !body_content.empty?

body.rewind # somebody might try to read this stream
body.rewind if body.respond_to?(:rewind) # somebody might try to read this stream
env.update(
Rack::RACK_REQUEST_FORM_HASH => @parser.call(body_content),
Rack::RACK_REQUEST_FORM_INPUT => body
Expand Down
7 changes: 5 additions & 2 deletions lib/rack/contrib/jsonp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class JSONP
# "\342\200\251" # => "\u2029"
U2028, U2029 = ("\u2028" == 'u2028') ? ["\342\200\250", "\342\200\251"] : ["\u2028", "\u2029"]

HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers
private_constant :HEADERS_KLASS

def initialize(app)
@app = app
end
Expand All @@ -42,7 +45,7 @@ def call(env)
return status, headers, response
end

headers = HeaderHash.new(headers)
headers = HEADERS_KLASS.new.merge(headers)

if is_json?(headers) && has_callback?(request)
callback = request.params['callback']
Expand Down Expand Up @@ -108,7 +111,7 @@ def pad(callback, response)
end

def bad_request(body = "Bad Request")
[ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => body.bytesize.to_s }, [body] ]
[ 400, { 'content-type' => 'text/plain', 'content-length' => body.bytesize.to_s }, [body] ]
end

end
Expand Down
10 changes: 5 additions & 5 deletions lib/rack/contrib/lazy_conditional_get.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ def initialize app, cache={}

def call env
if reading? env and fresh? env
return [304, {'Last-Modified' => env['HTTP_IF_MODIFIED_SINCE']}, []]
return [304, {'last-modified' => env['HTTP_IF_MODIFIED_SINCE']}, []]
end

status, headers, body = @app.call env
headers = Utils::HeaderHash.new(headers)
headers = Rack.release < "3" ? Utils::HeaderHash.new(headers) : headers

update_cache unless (reading?(env) or skipping?(headers))
headers['Last-Modified'] = cached_value if stampable? headers
headers['last-modified'] = cached_value if stampable? headers
[status, headers, body]
end

Expand All @@ -96,11 +96,11 @@ def reading? env
end

def skipping? headers
headers['Rack-Lazy-Conditional-Get'] == 'skip'
headers['rack-lazy-conditional-get'] == 'skip'
end

def stampable? headers
!headers.has_key?('Last-Modified') and headers['Rack-Lazy-Conditional-Get'] == 'yes'
!headers.has_key?('last-modified') and headers['rack-lazy-conditional-get'] == 'yes'
end

def update_cache
Expand Down
5 changes: 4 additions & 1 deletion lib/rack/contrib/locale.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

module Rack
class Locale
HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers
private_constant :HEADERS_KLASS

def initialize(app)
@app = app
end
Expand All @@ -16,7 +19,7 @@ def call(env)

env['rack.locale'] = I18n.locale = locale.to_s
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
headers = HEADERS_KLASS.new.merge(headers)

unless headers['Content-Language']
headers['Content-Language'] = locale.to_s
Expand Down
2 changes: 1 addition & 1 deletion lib/rack/contrib/mailexceptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def send_notification(exception, env)

def extract_body(env)
if io = env['rack.input']
io.rewind
io.rewind if io.respond_to?(:rewind)
io.read
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rack/contrib/not_found.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def initialize(path = nil, content_type = 'text/html')
end

def call(env)
[404, {'Content-Type' => @content_type, 'Content-Length' => @length}, [@content]]
[404, {'content-type' => @content_type, 'content-length' => @length}, [@content]]
end
end
end
4 changes: 2 additions & 2 deletions lib/rack/contrib/post_body_content_type_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def initialize(app, &block)

def call(env)
if Rack::Request.new(env).media_type == APPLICATION_JSON && (body = env[POST_BODY].read).length != 0
env[POST_BODY].rewind # somebody might try to read this stream
env[POST_BODY].rewind if env[POST_BODY].respond_to?(:rewind) # somebody might try to read this stream
env.update(FORM_HASH => @block.call(body), FORM_INPUT => env[POST_BODY])
end
@app.call(env)
Expand All @@ -84,7 +84,7 @@ def call(env)
end

def bad_request(body = 'Bad Request')
[ 400, { 'Content-Type' => 'text/plain', 'Content-Length' => body.bytesize.to_s }, [body] ]
[ 400, { 'content-type' => 'text/plain', 'content-length' => body.bytesize.to_s }, [body] ]
end
end
end
4 changes: 2 additions & 2 deletions lib/rack/contrib/profiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ def print(printer, result)
end

def headers(printer, env, mode)
headers = { 'Content-Type' => CONTENT_TYPES[printer.name] }
headers = { 'content-type' => CONTENT_TYPES[printer.name] }
if printer == ::RubyProf::CallTreePrinter
filename = ::File.basename(env['PATH_INFO'])
headers['Content-Disposition'] =
headers['content-disposition'] =
%(attachment; filename="#{filename}.#{mode}.tree")
end
headers
Expand Down
3 changes: 2 additions & 1 deletion lib/rack/contrib/relative_redirect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def initialize(app, &block)
# does not start with a slash, make location relative to the path requested.
def call(env)
status, headers, body = @app.call(env)
headers = Rack::Utils::HeaderHash.new(headers)
headers_klass = Rack.release < "3" ? Rack::Utils::HeaderHash : Rack::Headers
headers = headers_klass.new.merge(headers)

if [301,302,303, 307,308].include?(status) and loc = headers['Location'] and !%r{\Ahttps?://}o.match(loc)
absolute = @absolute_proc.call(env, [status, headers, body])
Expand Down
3 changes: 2 additions & 1 deletion lib/rack/contrib/response_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ def initialize(app, cache, &block)
# subdirectory of cache. Otherwise, cache the body of the response as the value with the path as the key.
def call(env)
status, headers, body = @app.call(env)
headers = Rack::Utils::HeaderHash.new(headers)
headers_klass = Rack.release < "3" ? Rack::Utils::HeaderHash : Rack::Headers
headers = headers_klass.new.merge(headers)

if env['REQUEST_METHOD'] == 'GET' and env['QUERY_STRING'] == '' and status == 200 and path = @path_proc.call(env, [status, headers, body])
if @cache.is_a?(String)
Expand Down
8 changes: 6 additions & 2 deletions lib/rack/contrib/response_headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@

module Rack
# Allows you to tap into the response headers. Yields a Rack::Utils::HeaderHash
# of current response headers to the block. Example:
# (Rack 2) or a Rack::Headers (Rack 3) of current response headers to the block.
# Example:
#
# use Rack::ResponseHeaders do |headers|
# headers['X-Foo'] = 'bar'
# headers.delete('X-Baz')
# end
#
class ResponseHeaders
HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers
private_constant :HEADERS_KLASS

def initialize(app, &block)
@app = app
@block = block
end

def call(env)
response = @app.call(env)
headers = Utils::HeaderHash.new(response[1])
headers = HEADERS_KLASS.new.merge(response[1])
@block.call(headers)
response[1] = headers
response
Expand Down
6 changes: 4 additions & 2 deletions lib/rack/contrib/static_cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ module Rack


class StaticCache
HEADERS_KLASS = Rack.release < "3" ? Utils::HeaderHash : Headers
private_constant :HEADERS_KLASS

def initialize(app, options={})
@app = app
Expand All @@ -67,7 +69,7 @@ def initialize(app, options={})
end
end
root = options[:root] || Dir.pwd
@file_server = Rack::File.new(root)
@file_server = Rack::Files.new(root)
@cache_duration = options[:duration] || 1
@versioning_enabled = options.fetch(:versioning, true)
if @versioning_enabled
Expand All @@ -87,7 +89,7 @@ def call(env)
end

status, headers, body = @file_server.call(env)
headers = Utils::HeaderHash.new(headers)
headers = HEADERS_KLASS.new.merge(headers)

if @no_cache[url].nil?
headers['Cache-Control'] ="max-age=#{@duration_in_seconds}, public"
Expand Down
2 changes: 1 addition & 1 deletion rack-contrib.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Gem::Specification.new do |s|

s.required_ruby_version = '>= 2.2.2'

s.add_runtime_dependency 'rack', '~> 2.0'
s.add_runtime_dependency 'rack', '< 4'

s.homepage = "https://github.com/rack/rack-contrib/"
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "rack-contrib", "--main", "README"]
Expand Down
2 changes: 1 addition & 1 deletion test/spec_rack_access.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
describe "Rack::Access" do

before do
@app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ['hello']] }
@app = lambda { |env| [200, { 'content-type' => 'text/plain' }, ['hello']] }
@mock_addr_1 = '111.111.111.111'
@mock_addr_2 = '192.168.1.222'
@mock_addr_localhost = '127.0.0.1'
Expand Down