Skip to content
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
5 changes: 5 additions & 0 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ jobs:
'rails71_rack22',
'rails72_rack22',
'rails80_rack22',
'rails81_rack22',
]
jruby_version: [ '9.3.15.0', '9.4.15.0', '10.0.6.0', '10.1.1.0' ]
java_version: [ '8', '11', '17', '21', '25' ]
Expand All @@ -99,6 +100,10 @@ jobs:
jruby_version: '9.3.15.0'
- appraisal: 'rails80_rack22' # Requires Ruby 3.4 compatibility, which JRuby 9.4 does not support
jruby_version: '9.4.15.0'
- appraisal: 'rails81_rack22' # Requires Ruby 3.4 compatibility, which JRuby 9.3 does not support
jruby_version: '9.3.15.0'
- appraisal: 'rails81_rack22' # Requires Ruby 3.4 compatibility, which JRuby 9.4 does not support
jruby_version: '9.4.15.0'
fail-fast: false

env:
Expand Down
1 change: 1 addition & 0 deletions Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ version_spec = ->(prefix, desc) { "~> #{major_minor.call(prefix, desc)}.0" }
"rails71" => {racks: %w[rack22]},
"rails72" => {racks: %w[rack22]},
"rails80" => {racks: %w[rack22]},
"rails81" => {racks: %w[rack22]}
}.each do |rails_desc, c|
c[:racks].each do |rack_desc|

Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- fix: JRuby::Rack::Input#read(0) should return an empty string
- fix: close the original body when ShowStatus replaces it
- fix: detect Transfer-Encoding/Content-Length headers case-insensitively
- chore: revert `rack.version` value to be Rack 2.2 spec conformant
- fix: do not mutate (potentially frozen) response header values when writing

## 1.2.8

Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ CHECKSUMS
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73

BUNDLED WITH
2.7.2
2.6.3
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ For more information on Rack, visit http://rack.github.io/.

| JRuby-Rack Series | Status | Rack | JRuby | Java | Rails | Servlet API (min → mostly supported) | Notes |
|----------------------------------------------------------------|---------------|-----------|------------|------|-----------|--------------------------------------|-------|
| [**1.2**](https://github.com/jruby/jruby-rack/tree/1.2-stable) | EOL @ 2026-09 | 2.2 | 9.3 → 10.1 | 8+ | 5.0 → 8.0 | 3.0 → 4.0 (Java EE 6 → 7) | |
| [**1.2**](https://github.com/jruby/jruby-rack/tree/1.2-stable) | EOL @ 2026-09 | 2.2 | 9.3 → 10.1 | 8+ | 5.0 → 8.1 | 3.0 → 4.0 (Java EE 6 → 7) | |
| [**1.1**](https://github.com/jruby/jruby-rack/tree/1.1-stable) | EOL @ 2024-05 | 1.x → 2.2 | 1.6 → 9.4 | 6+ | 2.1 → 5.2 | 2.5 → 4.0 (Java EE 5 → 7) | |
| [**1.0**](https://github.com/jruby/jruby-rack/tree/1.0.10) | EOL @ 2011-11 | 0.9 → 1.x | 1.1 → 1.9 | 5+ | 2.1 → 3.x | 2.5 (Java EE 5) | |

Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails50_rack22.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ end

group :development do
gem "appraisal", require: nil
gem "rexml"
end

group :test do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails52_rack22.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ end

group :development do
gem "appraisal", require: nil
gem "rexml"
end

group :test do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails60_rack22.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ end

group :development do
gem "appraisal", require: nil
gem "rexml"
end

group :test do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails61_rack22.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ end

group :development do
gem "appraisal", require: nil
gem "rexml"
end

group :test do
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rails70_rack22.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ source "https://rubygems.org"
group :default do
gem "rack", "~> 2.2.0"
gem "rails", "~> 7.0.0"
gem "rdoc", "!= 8.0.0"
end

group :development do
gem "appraisal", require: nil
gem "rexml"
end

group :test do
Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rails71_rack22.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ source "https://rubygems.org"
group :default do
gem "rack", "~> 2.2.0"
gem "rails", "~> 7.1.0"
gem "rdoc", "!= 8.0.0"
end

group :development do
gem "appraisal", require: nil
gem "rexml"
end

group :test do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails72_rack22.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ end

group :development do
gem "appraisal", require: nil
gem "rexml"
end

group :test do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails80_rack22.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ end

group :development do
gem "appraisal", require: nil
gem "rexml"
end

group :test do
Expand Down
19 changes: 19 additions & 0 deletions gemfiles/rails81_rack22.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This file was generated by Appraisal

source "https://rubygems.org"

group :default do
gem "rack", "~> 2.2.0"
gem "rails", "~> 8.1.0"
end

group :development do
gem "appraisal", require: nil
gem "rexml"
end

group :test do
gem "rake", "~> 13.4", require: nil
gem "rspec"
gem "logger"
end
12 changes: 4 additions & 8 deletions src/main/java/org/jruby/rack/ext/Response.java
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,7 @@ protected void writeStatus(final RackResponseEnvironment response) {
}

@JRubyMethod(name = "write_headers")
public IRubyObject write_headers(final ThreadContext context, final IRubyObject response)
throws IOException {
public IRubyObject write_headers(final ThreadContext context, final IRubyObject response) {
writeHeaders(response.toJava(RackResponseEnvironment.class));
return context.nil;
}
Expand Down Expand Up @@ -406,7 +405,7 @@ public IRubyObject yield(ThreadContext context, IRubyObject[] args) {

@Override
public IRubyObject yield(ThreadContext context, IRubyObject value) {
value.callMethod(context, "chomp!", newLine);
value = value.callMethod(context, "chomp", newLine);
response.addHeader(name, value.toString());
return value;
}
Expand Down Expand Up @@ -439,18 +438,17 @@ public IRubyObject write_body(final ThreadContext context, final IRubyObject res
}

protected void writeBody(final RackResponseEnvironment response) throws IOException {
final ThreadContext context = currentContext();
Channel bodyChannel = null; IRubyObject body = this.body;
try {
if ( body.respondsTo("call") && ! body.respondsTo("each") ) {
final ThreadContext context = currentContext();
final IRubyObject outputStream =
JavaUtil.convertJavaToRuby(context.runtime, response.getOutputStream());
this.body.callMethod(context, "call", outputStream);
return;
}

if ( body.respondsTo("to_path") ) { // send_file
final ThreadContext context = currentContext();
final IRubyObject path = body.callMethod(context, "to_path");
callMethod("send_file", path, JavaUtil.convertJavaToRuby(context.runtime, response));
return;
Expand All @@ -465,7 +463,6 @@ protected void writeBody(final RackResponseEnvironment response) throws IOExcept
bodyChannel = ((RubyIO) body).getChannel();
}
else {
final ThreadContext context = currentContext();
final IRubyObject channel = body.callMethod(context, "to_channel");
bodyChannel = channel.toJava(Channel.class);
}
Expand All @@ -480,8 +477,6 @@ protected void writeBody(final RackResponseEnvironment response) throws IOExcept
// NOTE: we no longer handle "to_inputstream" since in 1.7 "to_channel" covers those ...

final OutputStream output = response.getOutputStream();
final ThreadContext context = currentContext();
IOException error = null;
if ( doDechunk() ) {
final IRubyObject output_stream = JavaUtil.convertJavaToRuby(context.runtime, output);
callMethod(context, "write_body_dechunked", output_stream);
Expand Down Expand Up @@ -706,6 +701,7 @@ private void transferChannel(final ReadableByteChannel channel, final OutputStre

ThreadContext currentContext() { return getRuntime().getCurrentContext(); }

@SuppressWarnings("UnusedReturnValue")
static IRubyObject invoke(
final ThreadContext context, final IRubyObject self,
final String method, final BlockBody body) {
Expand Down
20 changes: 1 addition & 19 deletions src/main/java/org/jruby/rack/util/IOHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,24 +73,6 @@ public static String inputStreamToString(final InputStream stream)
return str.toString();
}

public static ByteList readURL(final Ruby runtime, final URL url)
throws IOException {
if ( url == null ) return null;

final int chunk = 256;

final InputStream stream = url.openStream();
final ByteList bytes = new ByteList(chunk);

try {
while ( true ) bytes.append(stream, chunk);
}
catch (EOFException e) { /* read whole stream */ }
finally { stream.close(); }

return bytes;
}

public static String rubyMagicCommentValue(final String script, final String prefix)
throws IOException {
if ( script == null ) return null;
Expand All @@ -100,7 +82,7 @@ public static String rubyMagicCommentValue(final String script, final String pre
String line, comment = null; Pattern pattern = null;
while ( (line = reader.readLine()) != null ) {
// we only support (magic) comments at the beginning :
if ( line.length() == 0 || line.charAt(0) != '#' ) break;
if (line.isEmpty() || line.charAt(0) != '#' ) break;

if (pattern == null) {
pattern = Pattern.compile(prefix + "\\s*(\\S+)");
Expand Down
4 changes: 4 additions & 0 deletions src/spec/ruby/jruby/rack/integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@
it_should_behave_like 'a rails app'
end

describe 'rails 8.1', lib: :rails81 do
it_should_behave_like 'a rails app'
end

def expect_to_have_monkey_patched_chunked
@runtime.evalScriptlet "require 'rack/chunked'"
script = %{
Expand Down
7 changes: 7 additions & 0 deletions src/spec/ruby/jruby/rack/response_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ class << value
response.write_headers(response_environment)
end

it "writes frozen header values containing newlines without raising" do
response.to_java.getHeaders.update({ "Set-Cookie" => "cookie1\ncookie2".freeze })
expect(servlet_response).to receive(:addHeader).with("Set-Cookie", "cookie1")
expect(servlet_response).to receive(:addHeader).with("Set-Cookie", "cookie2")
response.write_headers(response_environment)
end

it "adds an int header when values is a fixnum" do
update_response_headers "Expires" => 0
expect(response_environment).to receive(:addIntHeader).with("Expires", 0)
Expand Down
75 changes: 75 additions & 0 deletions src/spec/ruby/rack/handler/servlet_lint_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#--
# This source code is available under the MIT license.
# See the file LICENSE.txt for details.
#++

require File.expand_path('../../spec_helper', File.dirname(__FILE__))

require 'rack'
require 'rack/lint'
require 'uri'
require 'rack/handler/servlet'

# End-to-end conformance check: the env built from the servlet request and the
# handling of the (Lint wrapped) response must satisfy the loaded Rack version's
# SPEC - Rack::Lint raises when either side of the contract is broken.
describe 'Rack::Handler::Servlet (Rack::Lint)' do

before :each do
@servlet_context = mock_servlet_context
allow(@rack_context).to receive(:getServerInfo).and_return 'Mock-Container/1.0'
@servlet_request = org.springframework.mock.web.MockHttpServletRequest.new(@servlet_context)
@servlet_response = org.springframework.mock.web.MockHttpServletResponse.new
@servlet_env = org.jruby.rack.servlet.ServletRackEnvironment.new(
@servlet_request, @servlet_response, @rack_context
)

@servlet_request.setMethod('GET')
@servlet_request.setContextPath('')
@servlet_request.setRequestURI('/some/path')
@servlet_request.setQueryString('a=1&b=2')
@servlet_request.addParameter('a', '1')
@servlet_request.addParameter('b', '2')
@servlet_request.setServerName('lint.example.com')
@servlet_request.setServerPort(8080)
@servlet_request.setProtocol('HTTP/1.1')
@servlet_request.setRemoteAddr('127.0.0.1')
@servlet_request.setContent(''.to_java_bytes)
@servlet_request.addHeader('Accept', 'text/plain')
end

let(:inner_app) do
lambda do |env|
env['rack.input'].read # exercises the Lint wrapped input contract
[ 200, { 'Content-Type' => 'text/plain', 'Content-Length' => '2' }, [ 'OK' ] ]
end
end

let(:servlet) { Rack::Handler::Servlet.new Rack::Lint.new(inner_app) }

shared_examples 'lint-compatible env' do

it "creates a SPEC compatible env and writes the response" do
response = servlet.call(@servlet_env)
expect(response.to_java.getStatus).to eql 200

response_env = org.jruby.rack.servlet.ServletRackResponseEnvironment.new(@servlet_response)
response.to_java.respond(response_env)
expect(@servlet_response.getStatus).to eql 200
expect(@servlet_response.getContentAsString).to eql 'OK'
end

end

describe 'with (default) env' do
it_behaves_like 'lint-compatible env'
end

describe 'with servlet env' do
before { Rack::Handler::Servlet.env = :servlet }
after { Rack::Handler::Servlet.env = nil }

it_behaves_like 'lint-compatible env'
end

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class ApplicationController < ActionController::Base
# Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
allow_browser versions: :modern
end
2 changes: 2 additions & 0 deletions src/spec/stub/rails81/app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module ApplicationHelper
end
42 changes: 42 additions & 0 deletions src/spec/stub/rails81/config/application.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require_relative "boot"

require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
# require "active_job/railtie"
# require "active_record/railtie"
# require "active_storage/engine"
require "action_controller/railtie"
# require "action_mailer/railtie"
# require "action_mailbox/engine"
# require "action_text/engine"
require "action_view/railtie"
# require "action_cable/engine"
# require "rails/test_unit/railtie"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Rails81
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 8.1

# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example.
config.autoload_lib(ignore: %w[assets tasks])

# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")

# Don't generate system test files.
config.generators.system_tests = nil
end
end
3 changes: 3 additions & 0 deletions src/spec/stub/rails81/config/boot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

require "bundler/setup" # Set up gems listed in the Gemfile.
Loading
Loading