diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 09d36cc67..393f4959d 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -21,7 +21,7 @@ jobs: matrix: jruby_version: [ '9.4.14.0', '10.0.2.0' ] java_version: [ '8', '11', '17', '21', '25' ] - rack_version: [ '~> 2.2.0' ] + rack_version: [ '~> 2.2.0', '~> 3.1.0', '~> 3.2.0' ] exclude: - jruby_version: '10.0.2.0' java_version: '8' # JRuby 10 requires Java 21 @@ -61,9 +61,10 @@ jobs: appraisal: [ 'rails61_rack22', 'rails70_rack22', - 'rails71_rack22', - 'rails72_rack22', - 'rails80_rack22', + 'rails71_rack22', 'rails71_rack31', + 'rails72_rack22', 'rails72_rack31', + 'rails80_rack22', 'rails80_rack31', 'rails80_rack32', + 'rails81_rack31', 'rails81_rack32', ] jruby_version: [ '9.4.14.0', '10.0.2.0' ] java_version: [ '8', '11', '17', '21', '25' ] @@ -76,6 +77,15 @@ jobs: java_version: '17' # JRuby 10 requires Java 21 - appraisal: 'rails80_rack22' jruby_version: '9.4.14.0' # Rails 8 requires Ruby 3.4 compatibility, which JRuby 9.4 does not support + - appraisal: 'rails80_rack31' + jruby_version: '9.4.14.0' # Rails 8 requires Ruby 3.4 compatibility, which JRuby 9.4 does not support + - appraisal: 'rails80_rack32' + jruby_version: '9.4.14.0' # Rails 8 requires Ruby 3.4 compatibility, which JRuby 9.4 does not support + - appraisal: 'rails81_rack31' + jruby_version: '9.4.14.0' # Rails 8 requires Ruby 3.4 compatibility, which JRuby 9.4 does not support + - appraisal: 'rails81_rack32' + jruby_version: '9.4.14.0' # Rails 8 requires Ruby 3.4 compatibility, which JRuby 9.4 does not support + fail-fast: false env: diff --git a/Appraisals b/Appraisals index dd04ccfa3..4acf91754 100644 --- a/Appraisals +++ b/Appraisals @@ -5,9 +5,10 @@ version_spec = ->(prefix, desc) { "~> #{desc.split(prefix).last.insert(1, ".")}. { "rails61" => %w[rack22], "rails70" => %w[rack22], - "rails71" => %w[rack22], - "rails72" => %w[rack22], - "rails80" => %w[rack22] + "rails71" => %w[rack22 rack31], + "rails72" => %w[rack22 rack31], + "rails80" => %w[rack22 rack31 rack32], + "rails81" => %w[rack31 rack32], }.each do |rails_desc, rack_descs| rack_descs.each do |rack_desc| diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1270c3b..3753881f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Officially support Javax Servlet API 4.0 (JEE 8) - Officially support JRuby 10.0 +- Support Rack 3.0 -> 3.2 (default vendored Rack is still 2.2) Breaking compatibility changes - Drop support for JRuby 9.3 diff --git a/README.md b/README.md index 48158b501..034c9e87c 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ For more information on Rack, visit http://rack.github.io/. | JRuby-Rack Series | Status | Rack | JRuby | Java | Rails | Target Servlet API | Notes | |------------------------------------------------------------|------------|-----------|------------|------|-----------|---------------------|--------------------------------------------| -| 2.0 (_planned_) | Dev | 2.2 | 9.4 → 10.0 | 8+ | 6.1 → 8.0 | 5.0+ (Jakarta EE 9) | Pre 5.0 servlet APIs non functional. | -| 1.3 (master, _unreleased_) | Dev | 2.2 | 9.4 → 10.0 | 8+ | 6.1 → 8.0 | 4.0 (Java EE 8) | Servlet 2.5 → 3.1 likely to work fine. | +| 2.0 (_planned_) | Dev | 2.2 → 3.2 | 9.4 → 10.0 | 8+ | 6.1 → 8.1 | 5.0+ (Jakarta EE 9) | Pre 5.0 servlet APIs non functional. | +| 1.3 (master, _unreleased_) | Dev | 2.2 → 3.2 | 9.4 → 10.0 | 8+ | 6.1 → 8.1 | 4.0 (Java EE 8) | Servlet 2.5 → 3.1 likely to work fine. | | [1.2](https://github.com/jruby/jruby-rack/tree/1.2-stable) | Maintained | 2.2 | 9.3 → 9.4 | 8+ | 5.0 → 7.2 | 3.0 (Java EE 6) | Servlet 3.1 → 4.0 OK with some containers. | | [1.1](https://github.com/jruby/jruby-rack/tree/1.1-stable) | EOL | 1.x → 2.2 | 1.6 → 9.4 | 6+ | 2.1 → 5.2 | 2.5 (Java EE 5) | Servlet 3.0 → 4.0 OK with some containers. | | 1.0 | EOL | 0.9 → 1.x | 1.1 → 1.9 | 5+ | 2.1 → 3.x | 2.5 (Java EE 5) | | @@ -357,7 +357,7 @@ package and push the .jar every time a commit changes a source file). ```shell VERSION=rails72 cd src/spec/stub - rm -rf $VERSION && BUNDLE_GEMFILE=~/Projects/community/jruby-rack/gemfiles/${VERSION}_rack22.gemfile bundle exec rails new $VERSION --minimal --skip-git --skip-docker --skip-active-model --skip-active-record --skip-test --skip-system-test --skip-dev-gems --skip-bundle --skip-keeps --skip-asset-pipeline --skip-ci --skip-brakeman --skip-rubocop + rm -rf $VERSION && BUNDLE_GEMFILE=~/Projects/community/jruby-rack/gemfiles/${VERSION}_rack32.gemfile bundle exec rails new $VERSION --minimal --skip-git --skip-docker --skip-active-model --skip-active-record --skip-test --skip-system-test --skip-dev-gems --skip-bundle --skip-keeps --skip-asset-pipeline --skip-ci --skip-brakeman --skip-rubocop ``` * Manual changes to make to support testing * In `config/production.rb` comment out the default `config.logger` value so jruby-rack applies its own `RailsLogger`. diff --git a/examples/README.md b/examples/README.md index c58c11fe8..41e54e13c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,7 +4,7 @@ This directory includes samples using JRuby-Rack to build Rack web applications into Java app servers. - All use [Warbler](https://github.com/jruby/warbler) to do so for easy of packaging. -- Require JRuby `9.4` and a compatible JVM (Java `8` -> `25`). +- Require JRuby `10.x` and a compatible JVM (Java `21` -> `25`). ### Building/running @@ -30,11 +30,11 @@ As an executable jar within Jetty: | Example | Component | Embedded Route | Deployed War Route | |---------|-------------------------------|-----------------------------------|------------------------------------------| -| Rails 7 | Status Page | http://localhost:8080/up | http://localhost:8080/rails7/up | -| Rails 7 | Snoop Dump | http://localhost:8080/snoop | http://localhost:8080/rails7/snoop | -| Rails 7 | Embedded JSP (non-functional) | http://localhost:8080/jsp/ | http://localhost:8080/rails7/jsp/ | -| Rails 7 | Simple Form submission | http://localhost:8080/simple_form | http://localhost:8080/rails7/simple_form | -| Rails 7 | Body Posts | http://localhost:8080/body | http://localhost:8080/rails7/body | +| Rails 8 | Status Page | http://localhost:8080/up | http://localhost:8080/rails7/up | +| Rails 8 | Snoop Dump | http://localhost:8080/snoop | http://localhost:8080/rails7/snoop | +| Rails 8 | Embedded JSP (non-functional) | http://localhost:8080/jsp/ | http://localhost:8080/rails7/jsp/ | +| Rails 8 | Simple Form submission | http://localhost:8080/simple_form | http://localhost:8080/rails7/simple_form | +| Rails 8 | Body Posts | http://localhost:8080/body | http://localhost:8080/rails7/body | | Sinatra | Demo Index | http://localhost:8080/ | http://localhost:8080/sinatra | | Sinatra | Info | http://localhost:8080/info | http://localhost:8080/sinatra/info | | Sinatra | Snoop Dump | http://localhost:8080/env | http://localhost:8080/sinatra/env | diff --git a/examples/camping/Gemfile b/examples/camping/Gemfile index e02a3bba6..873078950 100644 --- a/examples/camping/Gemfile +++ b/examples/camping/Gemfile @@ -1,12 +1,12 @@ source 'https://rubygems.org' -ruby '~> 3.1.0' +ruby '~> 3.4.0' gem 'camping', '< 3' -gem 'rack', '~> 2.2.0' +gem 'rack', '~> 3.2.0' group :development do if !ENV['WARBLER_SRC']; gem 'warbler' else gem 'warbler', path: '../../../warbler' end if !ENV['JRUBY_RACK_SRC']; gem 'jruby-rack' else gem 'jruby-rack', path: '../../target' end - gem 'jruby-jars', '~> 9.4.0' + gem 'jruby-jars', '~> 10.0.0' end diff --git a/examples/rails7/config/environments/production.rb b/examples/rails7/config/environments/production.rb deleted file mode 100644 index 1949e9d1d..000000000 --- a/examples/rails7/config/environments/production.rb +++ /dev/null @@ -1,79 +0,0 @@ -require "active_support/core_ext/integer/time" - -Rails.application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # Code is not reloaded between requests. - config.enable_reloading = false - - # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both threaded web servers - # and those relying on copy on write to perform better. - # Rake tasks automatically ignore this option for performance. - config.eager_load = true - - # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false - config.action_controller.perform_caching = true - - # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment - # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). - # config.require_master_key = true - - # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. - # config.public_file_server.enabled = false - - # Compress CSS using a preprocessor. - # config.assets.css_compressor = :sass - - # Fall back to assets pipeline if a precompiled asset is missed. - config.assets.compile = true - - # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.asset_host = "http://assets.example.com" - - # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache - # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX - - # Assume all access to the app is happening through a SSL-terminating reverse proxy. - # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. - # config.assume_ssl = true - - # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - config.force_ssl = false - - # Skip http-to-https redirect for the default health check endpoint. - # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } - - # Log to STDOUT by default - config.logger = ActiveSupport::Logger.new(STDOUT) - .tap { |logger| logger.formatter = ::Logger::Formatter.new } - .then { |logger| ActiveSupport::TaggedLogging.new(logger) } - - # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] - - # "info" includes generic and useful information about system operation, but avoids logging too much - # information to avoid inadvertent exposure of personally identifiable information (PII). If you - # want to log everything, set the level to "debug". - config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") - - # Use a different cache store in production. - # config.cache_store = :mem_cache_store - - # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation cannot be found). - config.i18n.fallbacks = true - - # Don't log any deprecations. - config.active_support.report_deprecations = false - - # Enable DNS rebinding protection and other `Host` header attacks. - # config.hosts = [ - # "example.com", # Allow requests from example.com - # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` - # ] - # Skip DNS rebinding protection for the default health check endpoint. - # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } -end diff --git a/examples/rails7/config/initializers/permissions_policy.rb b/examples/rails7/config/initializers/permissions_policy.rb deleted file mode 100644 index 7db3b9577..000000000 --- a/examples/rails7/config/initializers/permissions_policy.rb +++ /dev/null @@ -1,13 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Define an application-wide HTTP permissions policy. For further -# information see: https://developers.google.com/web/updates/2018/06/feature-policy - -# Rails.application.config.permissions_policy do |policy| -# policy.camera :none -# policy.gyroscope :none -# policy.microphone :none -# policy.usb :none -# policy.fullscreen :self -# policy.payment :self, "https://secure.example.com" -# end diff --git a/examples/rails7/config/master.key b/examples/rails7/config/master.key deleted file mode 100644 index a73c9f66c..000000000 --- a/examples/rails7/config/master.key +++ /dev/null @@ -1 +0,0 @@ -1bf39111dcca7a9dd0f154a78f40624c \ No newline at end of file diff --git a/examples/rails7/config/puma.rb b/examples/rails7/config/puma.rb deleted file mode 100644 index 03c166f4c..000000000 --- a/examples/rails7/config/puma.rb +++ /dev/null @@ -1,34 +0,0 @@ -# This configuration file will be evaluated by Puma. The top-level methods that -# are invoked here are part of Puma's configuration DSL. For more information -# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. - -# Puma starts a configurable number of processes (workers) and each process -# serves each request in a thread from an internal thread pool. -# -# The ideal number of threads per worker depends both on how much time the -# application spends waiting for IO operations and on how much you wish to -# to prioritize throughput over latency. -# -# As a rule of thumb, increasing the number of threads will increase how much -# traffic a given process can handle (throughput), but due to CRuby's -# Global VM Lock (GVL) it has diminishing returns and will degrade the -# response time (latency) of the application. -# -# The default is set to 3 threads as it's deemed a decent compromise between -# throughput and latency for the average Rails application. -# -# Any libraries that use a connection pool or another resource pool should -# be configured to provide at least as many connections as the number of -# threads. This includes Active Record's `pool` parameter in `database.yml`. -threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) -threads threads_count, threads_count - -# Specifies the `port` that Puma will listen on to receive requests; default is 3000. -port ENV.fetch("PORT", 3000) - -# Allow puma to be restarted by `bin/rails restart` command. -plugin :tmp_restart - -# Specify the PID file. Defaults to tmp/pids/server.pid in development. -# In other environments, only set the PID file if requested. -pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/examples/rails7/.gitignore b/examples/rails8/.gitignore similarity index 100% rename from examples/rails7/.gitignore rename to examples/rails8/.gitignore diff --git a/examples/rails7/Gemfile b/examples/rails8/Gemfile similarity index 73% rename from examples/rails7/Gemfile rename to examples/rails8/Gemfile index 563d65d33..87f5600fd 100644 --- a/examples/rails7/Gemfile +++ b/examples/rails8/Gemfile @@ -1,13 +1,13 @@ source 'https://rubygems.org' -ruby '~> 3.1.0' +ruby '~> 3.4.0' -gem 'rails', '~> 7.2.0' -gem 'rack', '~> 2.2.0' +gem 'rails', '~> 8.1.0' +gem 'rack', '~> 3.2.0' gem 'sprockets-rails' group :development do if !ENV['WARBLER_SRC']; gem 'warbler' else gem 'warbler', path: '../../../warbler' end if !ENV['JRUBY_RACK_SRC']; gem 'jruby-rack' else gem 'jruby-rack', path: '../../target' end - gem 'jruby-jars', '~> 9.4.0' + gem 'jruby-jars', '~> 10.0.0' end diff --git a/examples/rails7/Rakefile b/examples/rails8/Rakefile similarity index 100% rename from examples/rails7/Rakefile rename to examples/rails8/Rakefile diff --git a/examples/rails7/app/assets/config/manifest.js b/examples/rails8/app/assets/config/manifest.js similarity index 100% rename from examples/rails7/app/assets/config/manifest.js rename to examples/rails8/app/assets/config/manifest.js diff --git a/examples/rails7/app/assets/images/rails.png b/examples/rails8/app/assets/images/rails.png similarity index 100% rename from examples/rails7/app/assets/images/rails.png rename to examples/rails8/app/assets/images/rails.png diff --git a/examples/rails7/app/assets/stylesheets/application.css b/examples/rails8/app/assets/stylesheets/application.css similarity index 100% rename from examples/rails7/app/assets/stylesheets/application.css rename to examples/rails8/app/assets/stylesheets/application.css diff --git a/examples/rails7/app/controllers/application_controller.rb b/examples/rails8/app/controllers/application_controller.rb similarity index 100% rename from examples/rails7/app/controllers/application_controller.rb rename to examples/rails8/app/controllers/application_controller.rb diff --git a/examples/rails7/app/controllers/body_controller.rb b/examples/rails8/app/controllers/body_controller.rb similarity index 74% rename from examples/rails7/app/controllers/body_controller.rb rename to examples/rails8/app/controllers/body_controller.rb index 620fc9fc0..5a7416d7e 100644 --- a/examples/rails7/app/controllers/body_controller.rb +++ b/examples/rails8/app/controllers/body_controller.rb @@ -9,6 +9,7 @@ def index private def body_size bytes = 0 + request.body.rewind # Need to rewind to re-read body with Rack 3 - and rewindable bodies are not mandatory... while str = request.body.read(1024) bytes += str.size end diff --git a/examples/rails7/app/controllers/cache_headers_controller.rb b/examples/rails8/app/controllers/cache_headers_controller.rb similarity index 100% rename from examples/rails7/app/controllers/cache_headers_controller.rb rename to examples/rails8/app/controllers/cache_headers_controller.rb diff --git a/examples/rails7/app/controllers/images_controller.rb b/examples/rails8/app/controllers/images_controller.rb similarity index 100% rename from examples/rails7/app/controllers/images_controller.rb rename to examples/rails8/app/controllers/images_controller.rb diff --git a/examples/rails7/app/controllers/jsp_controller.rb b/examples/rails8/app/controllers/jsp_controller.rb similarity index 100% rename from examples/rails7/app/controllers/jsp_controller.rb rename to examples/rails8/app/controllers/jsp_controller.rb diff --git a/examples/rails7/app/controllers/jsp_forward_controller.rb b/examples/rails8/app/controllers/jsp_forward_controller.rb similarity index 100% rename from examples/rails7/app/controllers/jsp_forward_controller.rb rename to examples/rails8/app/controllers/jsp_forward_controller.rb diff --git a/examples/rails7/app/controllers/jsp_include_controller.rb b/examples/rails8/app/controllers/jsp_include_controller.rb similarity index 100% rename from examples/rails7/app/controllers/jsp_include_controller.rb rename to examples/rails8/app/controllers/jsp_include_controller.rb diff --git a/examples/rails7/app/controllers/simple_form_controller.rb b/examples/rails8/app/controllers/simple_form_controller.rb similarity index 100% rename from examples/rails7/app/controllers/simple_form_controller.rb rename to examples/rails8/app/controllers/simple_form_controller.rb diff --git a/examples/rails7/app/controllers/snoop_controller.rb b/examples/rails8/app/controllers/snoop_controller.rb similarity index 100% rename from examples/rails7/app/controllers/snoop_controller.rb rename to examples/rails8/app/controllers/snoop_controller.rb diff --git a/examples/rails7/app/helpers/application_helper.rb b/examples/rails8/app/helpers/application_helper.rb similarity index 100% rename from examples/rails7/app/helpers/application_helper.rb rename to examples/rails8/app/helpers/application_helper.rb diff --git a/examples/rails7/app/helpers/assets_helper.rb b/examples/rails8/app/helpers/assets_helper.rb similarity index 100% rename from examples/rails7/app/helpers/assets_helper.rb rename to examples/rails8/app/helpers/assets_helper.rb diff --git a/examples/rails7/app/helpers/cache_headers_helper.rb b/examples/rails8/app/helpers/cache_headers_helper.rb similarity index 100% rename from examples/rails7/app/helpers/cache_headers_helper.rb rename to examples/rails8/app/helpers/cache_headers_helper.rb diff --git a/examples/rails7/app/helpers/queue_helper.rb b/examples/rails8/app/helpers/queue_helper.rb similarity index 100% rename from examples/rails7/app/helpers/queue_helper.rb rename to examples/rails8/app/helpers/queue_helper.rb diff --git a/examples/rails7/app/helpers/snoop_helper.rb b/examples/rails8/app/helpers/snoop_helper.rb similarity index 100% rename from examples/rails7/app/helpers/snoop_helper.rb rename to examples/rails8/app/helpers/snoop_helper.rb diff --git a/examples/rails7/app/views/body/index.html.erb b/examples/rails8/app/views/body/index.html.erb similarity index 100% rename from examples/rails7/app/views/body/index.html.erb rename to examples/rails8/app/views/body/index.html.erb diff --git a/examples/rails7/app/views/images/show.html.erb b/examples/rails8/app/views/images/show.html.erb similarity index 100% rename from examples/rails7/app/views/images/show.html.erb rename to examples/rails8/app/views/images/show.html.erb diff --git a/examples/rails7/app/views/jsp_include/index.html.erb b/examples/rails8/app/views/jsp_include/index.html.erb similarity index 100% rename from examples/rails7/app/views/jsp_include/index.html.erb rename to examples/rails8/app/views/jsp_include/index.html.erb diff --git a/examples/rails7/app/views/layouts/application.html.erb b/examples/rails8/app/views/layouts/application.html.erb similarity index 100% rename from examples/rails7/app/views/layouts/application.html.erb rename to examples/rails8/app/views/layouts/application.html.erb diff --git a/examples/rails7/app/views/simple_form/index.html.erb b/examples/rails8/app/views/simple_form/index.html.erb similarity index 100% rename from examples/rails7/app/views/simple_form/index.html.erb rename to examples/rails8/app/views/simple_form/index.html.erb diff --git a/examples/rails7/app/views/snoop/index.html.erb b/examples/rails8/app/views/snoop/index.html.erb similarity index 100% rename from examples/rails7/app/views/snoop/index.html.erb rename to examples/rails8/app/views/snoop/index.html.erb diff --git a/examples/rails7/app/views/snoop/session_form.html.erb b/examples/rails8/app/views/snoop/session_form.html.erb similarity index 100% rename from examples/rails7/app/views/snoop/session_form.html.erb rename to examples/rails8/app/views/snoop/session_form.html.erb diff --git a/examples/rails7/config.ru b/examples/rails8/config.ru similarity index 100% rename from examples/rails7/config.ru rename to examples/rails8/config.ru diff --git a/examples/rails7/config/application.rb b/examples/rails8/config/application.rb similarity index 96% rename from examples/rails7/config/application.rb rename to examples/rails8/config/application.rb index 5e4be1049..bba537d7f 100644 --- a/examples/rails7/config/application.rb +++ b/examples/rails8/config/application.rb @@ -18,10 +18,10 @@ # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) -module Rails7 +module Rails81 class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 7.2 + 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. diff --git a/examples/rails7/config/boot.rb b/examples/rails8/config/boot.rb similarity index 100% rename from examples/rails7/config/boot.rb rename to examples/rails8/config/boot.rb diff --git a/examples/rails7/config/credentials.yml.enc b/examples/rails8/config/credentials.yml.enc similarity index 100% rename from examples/rails7/config/credentials.yml.enc rename to examples/rails8/config/credentials.yml.enc diff --git a/examples/rails7/config/environment.rb b/examples/rails8/config/environment.rb similarity index 100% rename from examples/rails7/config/environment.rb rename to examples/rails8/config/environment.rb diff --git a/examples/rails7/config/environments/development.rb b/examples/rails8/config/environments/development.rb similarity index 58% rename from examples/rails7/config/environments/development.rb rename to examples/rails8/config/environments/development.rb index d6ec4a787..f68dd3575 100644 --- a/examples/rails7/config/environments/development.rb +++ b/examples/rails8/config/environments/development.rb @@ -3,9 +3,7 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # In the development environment your application's code is reloaded any time - # it changes. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. + # Make code changes take effect immediately without server restart. config.enable_reloading = true # Do not eager load code on boot. @@ -17,32 +15,22 @@ # Enable server timing. config.server_timing = true - # Enable/disable caching. By default caching is disabled. - # Run rails dev:cache to toggle caching. + # Enable/disable Action Controller caching. By default Action Controller caching is disabled. + # Run rails dev:cache to toggle Action Controller caching. if Rails.root.join("tmp/caching-dev.txt").exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true - - config.cache_store = :memory_store - config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } + config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false - - config.cache_store = :null_store end + # Change to :null_store to avoid any caching. + config.cache_store = :memory_store + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log - # Raise exceptions for disallowed deprecations. - config.active_support.disallowed_deprecation = :raise - - # Tell Active Support which deprecation messages to disallow. - config.active_support.disallowed_deprecation_warnings = [] - - # Suppress logger output for asset requests. - config.assets.quiet = true - # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/examples/rails8/config/environments/production.rb b/examples/rails8/config/environments/production.rb new file mode 100644 index 000000000..16d20d19a --- /dev/null +++ b/examples/rails8/config/environments/production.rb @@ -0,0 +1,73 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.enable_reloading = false + + # Eager load code on boot for better performance and memory savings (ignored by Rake tasks). + config.eager_load = true + + # Full error reports are disabled. + config.consider_all_requests_local = false + + # Turn on fragment caching in view templates. + config.action_controller.perform_caching = true + + # Cache digest stamped assets for far-future expiry. + # Short cache for others: robots.txt, sitemap.xml, 404.html, etc. + config.public_file_server.headers = { + "cache-control" => lambda do |path, _| + if path.start_with?("/assets/") + # Files in /assets/ are expected to be fully immutable. + # If the content change the URL too. + "public, immutable, max-age=#{1.year.to_i}" + else + # For anything else we cache for 1 minute. + "public, max-age=#{1.minute.to_i}, stale-while-revalidate=#{5.minutes.to_i}" + end + end + } + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.asset_host = "http://assets.example.com" + + # Assume all access to the app is happening through a SSL-terminating reverse proxy. + config.assume_ssl = false + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + config.force_ssl = false + + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } + + # Log to STDOUT with the current request id as a default log tag. + config.log_tags = [ :request_id ] + # config.logger = ActiveSupport::TaggedLogging.logger(STDOUT) + + # Change to "debug" to log everything (including potentially personally-identifiable information!). + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") + + # Prevent health checks from clogging up the logs. + config.silence_healthcheck_path = "/up" + + # Don't log any deprecations. + config.active_support.report_deprecations = false + + # Replace the default in-process memory cache store with a durable alternative. + # config.cache_store = :mem_cache_store + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Enable DNS rebinding protection and other `Host` header attacks. + # config.hosts = [ + # "example.com", # Allow requests from example.com + # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` + # ] + # + # Skip DNS rebinding protection for the default health check endpoint. + # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } +end diff --git a/examples/rails7/config/environments/test.rb b/examples/rails8/config/environments/test.rb similarity index 75% rename from examples/rails7/config/environments/test.rb rename to examples/rails8/config/environments/test.rb index 999db7091..14bc29e06 100644 --- a/examples/rails7/config/environments/test.rb +++ b/examples/rails8/config/environments/test.rb @@ -1,5 +1,3 @@ -require "active_support/core_ext/integer/time" - # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped @@ -17,12 +15,11 @@ # loading is working properly before deploying your code. config.eager_load = ENV["CI"].present? - # Configure public file server for tests with Cache-Control for performance. - config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" } + # Configure public file server for tests with cache-control for performance. + config.public_file_server.headers = { "cache-control" => "public, max-age=3600" } - # Show full error reports and disable caching. + # Show full error reports. config.consider_all_requests_local = true - config.action_controller.perform_caching = false config.cache_store = :null_store # Render exception templates for rescuable exceptions and raise for other exceptions. @@ -34,12 +31,6 @@ # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - # Raise exceptions for disallowed deprecations. - config.active_support.disallowed_deprecation = :raise - - # Tell Active Support which deprecation messages to disallow. - config.active_support.disallowed_deprecation_warnings = [] - # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/examples/rails7/config/initializers/assets.rb b/examples/rails8/config/initializers/assets.rb similarity index 100% rename from examples/rails7/config/initializers/assets.rb rename to examples/rails8/config/initializers/assets.rb diff --git a/examples/rails7/config/initializers/content_security_policy.rb b/examples/rails8/config/initializers/content_security_policy.rb similarity index 80% rename from examples/rails7/config/initializers/content_security_policy.rb rename to examples/rails8/config/initializers/content_security_policy.rb index b3076b38f..d51d71397 100644 --- a/examples/rails7/config/initializers/content_security_policy.rb +++ b/examples/rails8/config/initializers/content_security_policy.rb @@ -20,6 +20,10 @@ # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } # config.content_security_policy_nonce_directives = %w(script-src style-src) # +# # Automatically add `nonce` to `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag` +# # if the corresponding directives are specified in `content_security_policy_nonce_directives`. +# # config.content_security_policy_nonce_auto = true +# # # Report violations without enforcing the policy. # # config.content_security_policy_report_only = true # end diff --git a/examples/rails7/config/initializers/filter_parameter_logging.rb b/examples/rails8/config/initializers/filter_parameter_logging.rb similarity index 93% rename from examples/rails7/config/initializers/filter_parameter_logging.rb rename to examples/rails8/config/initializers/filter_parameter_logging.rb index c010b83dd..c0b717f7e 100644 --- a/examples/rails7/config/initializers/filter_parameter_logging.rb +++ b/examples/rails8/config/initializers/filter_parameter_logging.rb @@ -4,5 +4,5 @@ # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. Rails.application.config.filter_parameters += [ - :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc ] diff --git a/examples/rails7/config/initializers/inflections.rb b/examples/rails8/config/initializers/inflections.rb similarity index 100% rename from examples/rails7/config/initializers/inflections.rb rename to examples/rails8/config/initializers/inflections.rb diff --git a/examples/rails7/config/locales/en.yml b/examples/rails8/config/locales/en.yml similarity index 100% rename from examples/rails7/config/locales/en.yml rename to examples/rails8/config/locales/en.yml diff --git a/examples/rails7/config/routes.rb b/examples/rails8/config/routes.rb similarity index 67% rename from examples/rails7/config/routes.rb rename to examples/rails8/config/routes.rb index 277c82db6..0d8e1662f 100644 --- a/examples/rails7/config/routes.rb +++ b/examples/rails8/config/routes.rb @@ -5,8 +5,11 @@ # Can be used by load balancers and uptime monitors to verify that the app is live. get "up" => "rails/health#show", as: :rails_health_check - match ':controller(/:action(/:id))(.:format)', via: :all + # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb) + # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest + # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker + match ':controller(/:action(/:id))(.:format)', via: :all # Defines the root path route ("/") # root "posts#index" diff --git a/examples/rails7/config/warble.rb b/examples/rails8/config/warble.rb similarity index 100% rename from examples/rails7/config/warble.rb rename to examples/rails8/config/warble.rb diff --git a/examples/rails7/public/404.html b/examples/rails8/public/404.html similarity index 100% rename from examples/rails7/public/404.html rename to examples/rails8/public/404.html diff --git a/examples/rails7/public/406-unsupported-browser.html b/examples/rails8/public/406-unsupported-browser.html similarity index 100% rename from examples/rails7/public/406-unsupported-browser.html rename to examples/rails8/public/406-unsupported-browser.html diff --git a/examples/rails7/public/422.html b/examples/rails8/public/422.html similarity index 100% rename from examples/rails7/public/422.html rename to examples/rails8/public/422.html diff --git a/examples/rails7/public/500.html b/examples/rails8/public/500.html similarity index 100% rename from examples/rails7/public/500.html rename to examples/rails8/public/500.html diff --git a/examples/rails7/public/icon.png b/examples/rails8/public/icon.png similarity index 100% rename from examples/rails7/public/icon.png rename to examples/rails8/public/icon.png diff --git a/examples/rails7/public/icon.svg b/examples/rails8/public/icon.svg similarity index 100% rename from examples/rails7/public/icon.svg rename to examples/rails8/public/icon.svg diff --git a/examples/rails7/public/jsp/include.jsp b/examples/rails8/public/jsp/include.jsp similarity index 100% rename from examples/rails7/public/jsp/include.jsp rename to examples/rails8/public/jsp/include.jsp diff --git a/examples/rails7/public/jsp/index.jsp b/examples/rails8/public/jsp/index.jsp similarity index 100% rename from examples/rails7/public/jsp/index.jsp rename to examples/rails8/public/jsp/index.jsp diff --git a/examples/rails7/public/robots.txt b/examples/rails8/public/robots.txt similarity index 100% rename from examples/rails7/public/robots.txt rename to examples/rails8/public/robots.txt diff --git a/examples/sinatra/Gemfile b/examples/sinatra/Gemfile index 1e5be5398..6f663ad51 100644 --- a/examples/sinatra/Gemfile +++ b/examples/sinatra/Gemfile @@ -1,12 +1,12 @@ source 'https://rubygems.org' -ruby '~> 3.1.0' +ruby '~> 3.4.0' -gem 'sinatra', '< 4' -gem 'rack', '~> 2.2.0' +gem 'sinatra', '< 5' +gem 'rack', '~> 3.2.0' group :development do if !ENV['WARBLER_SRC']; gem 'warbler' else gem 'warbler', path: '../../../warbler' end if !ENV['JRUBY_RACK_SRC']; gem 'jruby-rack' else gem 'jruby-rack', path: '../../target' end - gem 'jruby-jars', '~> 9.4.0' + gem 'jruby-jars', '~> 10.0.0' end \ No newline at end of file diff --git a/examples/sinatra/lib/env.rb b/examples/sinatra/lib/env.rb index d5ac94e92..db1cbc59a 100644 --- a/examples/sinatra/lib/env.rb +++ b/examples/sinatra/lib/env.rb @@ -21,6 +21,7 @@ post '/body' do res = "Content-Type was: #{request.content_type.inspect}\n" + request.body.rewind # Need to rewind to re-read body with Rack 3 - and rewindable bodies are not mandatory... body = request.body.read if body.empty? status 400 diff --git a/examples/sinatra/views/info.erb b/examples/sinatra/views/info.erb index 089be1d91..32289c49b 100644 --- a/examples/sinatra/views/info.erb +++ b/examples/sinatra/views/info.erb @@ -1,4 +1,4 @@ -rack.version: <%= env["rack.version"].inspect %> +jruby.rack.version: <%= env["jruby.rack.version"].inspect %> CONTENT_TYPE: <%= env["CONTENT_TYPE"].inspect %> HTTP_HOST: <%= env["HTTP_HOST"].inspect %> HTTP_ACCEPT: <%= env["HTTP_ACCEPT"].inspect %> diff --git a/examples/sinatra/views/jsp_include.erb b/examples/sinatra/views/jsp_include.erb index 3e945476b..03a5e83d7 100644 --- a/examples/sinatra/views/jsp_include.erb +++ b/examples/sinatra/views/jsp_include.erb @@ -7,7 +7,7 @@
This line of text came from ERB.

-
And this is content from a JSP: <%= request.render "/jsp/include.jsp", {"message" => "I'm a message that came from Sinatra!"} %>
+
And this is content from a JSP:
<%= request.render "/jsp/include.jsp", {"message" => "I'm a message that came from Sinatra!"} %>
diff --git a/gemfiles/rails71_rack31.gemfile b/gemfiles/rails71_rack31.gemfile new file mode 100644 index 000000000..0176d8a8b --- /dev/null +++ b/gemfiles/rails71_rack31.gemfile @@ -0,0 +1,15 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "rake", "~> 13.3", group: :test, require: nil +gem "rspec", group: :test + +group :default do + gem "rack", "~> 3.1.0" + gem "rails", "~> 7.1.0" +end + +group :development do + gem "appraisal", require: nil +end diff --git a/gemfiles/rails72_rack31.gemfile b/gemfiles/rails72_rack31.gemfile new file mode 100644 index 000000000..d02e77452 --- /dev/null +++ b/gemfiles/rails72_rack31.gemfile @@ -0,0 +1,15 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "rake", "~> 13.3", group: :test, require: nil +gem "rspec", group: :test + +group :default do + gem "rack", "~> 3.1.0" + gem "rails", "~> 7.2.0" +end + +group :development do + gem "appraisal", require: nil +end diff --git a/gemfiles/rails80_rack31.gemfile b/gemfiles/rails80_rack31.gemfile new file mode 100644 index 000000000..733d84d0f --- /dev/null +++ b/gemfiles/rails80_rack31.gemfile @@ -0,0 +1,15 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "rake", "~> 13.3", group: :test, require: nil +gem "rspec", group: :test + +group :default do + gem "rack", "~> 3.1.0" + gem "rails", "~> 8.0.0" +end + +group :development do + gem "appraisal", require: nil +end diff --git a/gemfiles/rails80_rack32.gemfile b/gemfiles/rails80_rack32.gemfile new file mode 100644 index 000000000..3676caa60 --- /dev/null +++ b/gemfiles/rails80_rack32.gemfile @@ -0,0 +1,15 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "rake", "~> 13.3", group: :test, require: nil +gem "rspec", group: :test + +group :default do + gem "rack", "~> 3.2.0" + gem "rails", "~> 8.0.0" +end + +group :development do + gem "appraisal", require: nil +end diff --git a/gemfiles/rails81_rack31.gemfile b/gemfiles/rails81_rack31.gemfile new file mode 100644 index 000000000..6bb1ec456 --- /dev/null +++ b/gemfiles/rails81_rack31.gemfile @@ -0,0 +1,15 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "rake", "~> 13.3", group: :test, require: nil +gem "rspec", group: :test + +group :default do + gem "rack", "~> 3.1.0" + gem "rails", "~> 8.1.0" +end + +group :development do + gem "appraisal", require: nil +end diff --git a/gemfiles/rails81_rack32.gemfile b/gemfiles/rails81_rack32.gemfile new file mode 100644 index 000000000..ced295733 --- /dev/null +++ b/gemfiles/rails81_rack32.gemfile @@ -0,0 +1,15 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "rake", "~> 13.3", group: :test, require: nil +gem "rspec", group: :test + +group :default do + gem "rack", "~> 3.2.0" + gem "rails", "~> 8.1.0" +end + +group :development do + gem "appraisal", require: nil +end diff --git a/src/main/java/org/jruby/rack/RackEnvironment.java b/src/main/java/org/jruby/rack/RackEnvironment.java index 9d95fbb4c..ec6a87a72 100644 --- a/src/main/java/org/jruby/rack/RackEnvironment.java +++ b/src/main/java/org/jruby/rack/RackEnvironment.java @@ -157,4 +157,10 @@ public interface RackEnvironment { * @return the remote user */ String getRemoteUser(); + + /** + * @see javax.servlet.http.HttpServletRequest#getProtocol() + * @return a String containing the name of the scheme used to make this request + */ + String getProtocol(); } diff --git a/src/main/ruby/jruby/rack/chunked.rb b/src/main/ruby/jruby/rack/chunked.rb index 74c79b858..666905691 100644 --- a/src/main/ruby/jruby/rack/chunked.rb +++ b/src/main/ruby/jruby/rack/chunked.rb @@ -3,21 +3,23 @@ # See the file LICENSE.txt for details. #++ -require 'rack/chunked' # exists since Rack 1.1 +if Rack.release < '3' + require 'rack/chunked' # exists since Rack 1.1, removed in Rack 3.0 -# Disables the Rack response body chunking performed by `Rack::Chunked::Body`. -# It is "necessary" since Rails does instantiate the body directly instead of -# using `Rack::Chunked` as a middleware. -# -# @note This monkey-patch is not required to support chunking with servlets and -# won't be applied unless **jruby.rack.response.dechunk** is 'patch' (default). -# Set **jruby.rack.response.dechunk** to 'true' to simply "dechunk" the body and -# keep the `Rack::Chunked::Body` class as is (or 'false' to do no de-chunking at -# all). -Rack::Chunked::Body.class_eval do + # Disables the Rack response body chunking performed by `Rack::Chunked::Body`. + # It is "necessary" since Rails does instantiate the body directly instead of + # using `Rack::Chunked` as a middleware. + # + # @note This monkey-patch is not required to support chunking with servlets and + # won't be applied unless **jruby.rack.response.dechunk** is 'patch' (default). + # Set **jruby.rack.response.dechunk** to 'true' to simply "dechunk" the body and + # keep the `Rack::Chunked::Body` class as is (or 'false' to do no de-chunking at + # all). + Rack::Chunked::Body.class_eval do + + def each(&block) + @body.each(&block) # no-chunking on servlets + end - def each(&block) - @body.each(&block) # no-chunking on servlets end - end \ No newline at end of file diff --git a/src/main/ruby/jruby/rack/error_app/show_status.rb b/src/main/ruby/jruby/rack/error_app/show_status.rb index 605d61a24..43e8f6719 100644 --- a/src/main/ruby/jruby/rack/error_app/show_status.rb +++ b/src/main/ruby/jruby/rack/error_app/show_status.rb @@ -15,7 +15,6 @@ def initialize(app) def call(env) status, headers, body = @app.call(env) - headers = ::Rack::Utils::HeaderHash.new(headers) empty = headers['Content-Length'].to_i <= 0 detail = env['rack.showstatus.detail'] diff --git a/src/main/ruby/rack/handler/servlet/default_env.rb b/src/main/ruby/rack/handler/servlet/default_env.rb index 4c196a84a..65a93d2e0 100644 --- a/src/main/ruby/rack/handler/servlet/default_env.rb +++ b/src/main/ruby/rack/handler/servlet/default_env.rb @@ -19,15 +19,20 @@ class Servlet # ServletRequest input stream to be not read (e.g. for POSTs). class DefaultEnv < Hash # The environment must be an instance of Hash ! - BUILTINS = %w(rack.version rack.input rack.errors rack.url_scheme - rack.multithread rack.multiprocess rack.run_once rack.hijack? - java.servlet_request java.servlet_response java.servlet_context - jruby.rack.version). - map!(&:freeze) + BUILTINS = Rack.release < '3' ? + # rack 2.2.x + Set.new(%w(rack.version rack.multithread rack.multiprocess rack.run_once + rack.input rack.errors rack.url_scheme rack.hijack? + java.servlet_request java.servlet_response java.servlet_context + jruby.rack.context jruby.rack.version).map!(&:freeze)) : + # rack 3.0 and later + Set.new(%w(rack.input rack.errors rack.url_scheme rack.hijack? + java.servlet_request java.servlet_response java.servlet_context + jruby.rack.context jruby.rack.version).map!(&:freeze)) VARIABLES = %w(CONTENT_TYPE CONTENT_LENGTH PATH_INFO QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_USER REQUEST_METHOD REQUEST_URI - SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_SOFTWARE). + SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_SOFTWARE SERVER_PROTOCOL). map!(&:freeze) attr_reader :env @@ -216,6 +221,7 @@ def load_variable(env, key) when 'SCRIPT_NAME' then env[key] = @servlet_env.getScriptName when 'SERVER_NAME' then env[key] = @servlet_env.getServerName || '' when 'SERVER_PORT' then env[key] = @servlet_env.getServerPort.to_s + when 'SERVER_PROTOCOL' then env[key] = @servlet_env.getProtocol when 'SERVER_SOFTWARE' then env[key] = rack_context.getServerInfo else # NOTE: even though we allowed for overrides and loaded all attributes @@ -230,8 +236,10 @@ def load_variable(env, key) end def load_builtin(env, key) + return nil unless BUILTINS.include?(key) + case key - when 'rack.version' then env[key] = ::Rack::RELEASE + when 'rack.version' then env[key] = ::Rack.release when 'rack.multithread' then env[key] = true when 'rack.multiprocess' then env[key] = false when 'rack.run_once' then env[key] = false diff --git a/src/main/ruby/rack/handler/servlet/servlet_env.rb b/src/main/ruby/rack/handler/servlet/servlet_env.rb index 4fd358054..2f4fcc811 100644 --- a/src/main/ruby/rack/handler/servlet/servlet_env.rb +++ b/src/main/ruby/rack/handler/servlet/servlet_env.rb @@ -76,13 +76,13 @@ def load_parameters get_vals << v; true end end - store_parameter(key, get_vals, query_hash) - store_parameter(key, post_vals, form_hash) + store_parameter(query_hash, key, get_vals) + store_parameter(form_hash, key, post_vals) else - store_parameter(key, val, query_hash) + store_parameter(query_hash, key, val) end else # POST param : - store_parameter(key, val, form_hash) + store_parameter(form_hash, key, val) end end # Rack::Request#GET @@ -112,9 +112,10 @@ def [](key) # # @param key the param name # @param val the value(s) in a array-like structure - # @param hash the Hash to store the name, value pair - def store_parameter(key, val, hash) + # @param params the Hash to store the name, value pair + def store_parameter2(params, key, val, depth = 0) # emulating Rack::Utils.parse_nested_query behavior + raise ::Rack::Utils::ParamsTooDeepError if depth >= 32 if match = key.match(KEY_SEP) n_key = match[1]; sub = match[2] @@ -124,24 +125,110 @@ def store_parameter(key, val, hash) if sub if sub.empty? # e.g. foo[]=1&foo[]=2 - if arr = hash[ n_key ] + if arr = params[ n_key ] return mark_parameter_error "expected Array (got #{arr.class}) for param `#{n_key}'" unless arr.is_a?(Array) - hash[ n_key ] = arr + val.to_a; return + params[ n_key ] = arr + val.to_a; return end - hash[ n_key ] = val.to_a # String[] + params[ n_key ] = val.to_a # String[] else # foo[bar]=rrr&foo[baz]=zzz - if hsh = hash[ n_key ] + if hsh = params[ n_key ] return mark_parameter_error "expected Hash (got #{hsh.class}) for param `#{n_key}'" unless hsh.is_a?(Hash) - store_parameter(sub, val, hsh) + store_parameter2(hsh, sub, val, depth + 1) else - hash[ n_key ] = { sub => val[ val.length - 1 ] } + params[ n_key ] = { sub => val[ val.length - 1 ] } end end else # for 'foo=bad&foo=bar' does { 'foo' => 'bar' } - hash[ n_key ] = val[ val.length - 1 ] # last + params[ n_key ] = val[ val.length - 1 ] # last end end + private :store_parameter2 + + def store_parameter3(params, key, val, depth = 0) + raise ::Rack::Utils::ParamsTooDeepError if depth >= 32 + + if !key + # nil name, treat same as empty string (required by tests) + n_key = after = '' + elsif depth == 0 + # Start of parsing, don't treat [] or [ at start of string specially + if start = key.index('[', 1) + # Start of parameter nesting, use part before brackets as key + n_key = key[0, start] + after = key[start, key.length] + else + # Plain parameter with no nesting + n_key = key + after = '' + end + elsif key.start_with?('[]') + # Array nesting + n_key = '[]' + after = key[2, key.length] + elsif key.start_with?('[') && (start = key.index(']', 1)) + # Hash nesting, use the part inside brackets as the key + n_key = key[1, start-1] + after = key[start+1, key.length] + else + # Probably malformed input, nested but not starting with [ + # treat full name as key for backwards compatibility. + n_key = key + after = '' + end + + return if n_key.empty? + + if after == '' + if n_key == '[]' && depth != 0 + return val # was [val]: Different to original alg, as val is always an array + else + params[n_key] = val.last # was val: Different to original alg, as val is always an array + end + elsif after == "[" + params[key] = val.last # was val: Different to original alg, as val is always an array + elsif after == "[]" + params[n_key] ||= [] + return mark_parameter_error "expected Array (got #{params[n_key].class.name}) for param `#{n_key}'" unless params[n_key].is_a?(Array) + params[n_key] += val # was <<: Different to original alg, as val is always an array + elsif after.start_with?('[]') + # Recognize x[][y] (hash inside array) parameters + unless after[2] == '[' && after.end_with?(']') && (child_key = after[3, after.length-4]) && !child_key.empty? && !child_key.index('[') && !child_key.index(']') + # Handle other nested array parameters + child_key = after[2, after.length] + end + params[n_key] ||= [] + return mark_parameter_error "expected Array (got #{params[n_key].class.name}) for param `#{n_key}'" unless params[n_key].is_a?(Array) + + if params[n_key].last.is_a?(Hash) && !params_hash_has_key?(params[n_key].last, child_key) + store_parameter3(params[n_key].last, child_key, val, depth + 1) + else + params[n_key] += store_parameter3({ }, child_key, val, depth + 1) # was <<: Different to original alg, as val is always an array + end + else + params[n_key] ||= {} + return mark_parameter_error "expected Hash (got #{params[n_key].class.name}) for param `#{n_key}'" unless params[n_key].is_a?(Hash) + params[n_key] = store_parameter3(params[n_key], after, val, depth + 1) + end + + params + end + private :store_parameter3 + + def params_hash_has_key?(hash, key) + return false if /\[\]/.match?(key) + + key.split(/[\[\]]+/).inject(hash) do |h, part| + next h if part == '' + return false unless h.is_a?(Hash) && h.key?(part) + h[part] + end + + true + end + private :params_hash_has_key? + + alias_method :store_parameter, (Rack.release >= '3' ? :store_parameter3 : :store_parameter2) COOKIE_STRING = "rack.request.cookie_string".freeze COOKIE_HASH = "rack.request.cookie_hash".freeze diff --git a/src/spec/ruby/jruby/rack/booter_spec.rb b/src/spec/ruby/jruby/rack/booter_spec.rb index 4058aa826..0e87f1104 100644 --- a/src/spec/ruby/jruby/rack/booter_spec.rb +++ b/src/spec/ruby/jruby/rack/booter_spec.rb @@ -318,7 +318,7 @@ case name.to_sym when :getRealPath then case args.first - when '/WEB-INF' then File.expand_path('rails30/WEB-INF', STUB_DIR) + when '/WEB-INF' then File.expand_path('rails30/WEB-INF', STUB_DIR) # FIXME: rails 3.0 end when :getContextPath then '/' diff --git a/src/spec/ruby/jruby/rack/integration_spec.rb b/src/spec/ruby/jruby/rack/integration_spec.rb index 1ba98724f..8750d743f 100644 --- a/src/spec/ruby/jruby/rack/integration_spec.rb +++ b/src/spec/ruby/jruby/rack/integration_spec.rb @@ -153,10 +153,10 @@ end after(:all) { restore_rails } - it "loaded rack ~> 2.2.0" do + it "loaded default rack version" do @runtime = @rack_factory.getApplication.getRuntime should_eval_as_not_nil "defined?(Rack.release)" - should_eval_as_eql_to "Rack.release.to_s[0, 3]", '2.2' + should_eval_as_eql_to "%w(2.2 3.1 3.2).include? Rack.release.to_s[0, 3]", true end it "booted with a servlet logger" do @@ -197,6 +197,7 @@ end it "disables rack's chunked support (by default)" do + skip "Only runs on Rack < 3.0" unless Rack.release < '3' @runtime = @rack_factory.getApplication.getRuntime expect_to_have_monkey_patched_chunked end @@ -223,6 +224,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 = %{ diff --git a/src/spec/ruby/rack/handler/servlet_spec.rb b/src/spec/ruby/rack/handler/servlet_spec.rb index 186278d66..d7eb63344 100644 --- a/src/spec/ruby/rack/handler/servlet_spec.rb +++ b/src/spec/ruby/rack/handler/servlet_spec.rb @@ -44,10 +44,11 @@ def _env it "creates a hash with the Rack variables in it" do hash = servlet.create_env(@servlet_env) - expect(hash['rack.version']).to eq Rack::RELEASE - expect(hash['rack.multithread']).to eq true - expect(hash['rack.multiprocess']).to eq false - expect(hash['rack.run_once']).to eq false + expect(hash['rack.version']).to eq Rack.release < '3' ? Rack.release : nil + expect(hash['rack.multithread']).to eq Rack.release < '3' ? true : nil + expect(hash['rack.multiprocess']).to eq Rack.release < '3' ? false : nil + expect(hash['rack.run_once']).to eq Rack.release < '3' ? false : nil + expect(hash['rack.hijack?']).to eq false end it "adds all attributes from the servlet request" do @@ -68,6 +69,7 @@ def _env "SERVER_NAME" => "override", "SERVER_PORT" => 8080, "SERVER_SOFTWARE" => "servy", + "SERVER_PROTOCOL" => "HTTP/2.0", "REMOTE_HOST" => "override", "REMOTE_ADDR" => "192.168.0.1", "REMOTE_USER" => "override" @@ -83,6 +85,7 @@ def _env expect(env["SERVER_NAME"]).to eq "override" expect(env["SERVER_PORT"]).to eq "8080" expect(env["SERVER_SOFTWARE"]).to eq "servy" + expect(env["SERVER_PROTOCOL"]).to eq "HTTP/2.0" expect(env["REMOTE_HOST"]).to eq "override" expect(env["REMOTE_ADDR"]).to eq "192.168.0.1" expect(env["REMOTE_USER"]).to eq "override" @@ -163,6 +166,7 @@ def _env @servlet_request.setQueryString('hello=there') @servlet_request.setServerName('serverhost') @servlet_request.setServerPort(80) + @servlet_request.setProtocol('HTTP/1.1') @servlet_request.setRemoteAddr('127.0.0.1') @servlet_request.setRemoteHost('localhost') @servlet_request.setRemoteUser('admin') @@ -176,6 +180,7 @@ def _env expect(env["QUERY_STRING"]).to eq "hello=there" expect(env["SERVER_NAME"]).to eq "serverhost" expect(env["SERVER_PORT"]).to eq "80" + expect(env["SERVER_PROTOCOL"]).to eq "HTTP/1.1" expect(env["REMOTE_HOST"]).to eq "localhost" expect(env["REMOTE_ADDR"]).to eq "127.0.0.1" expect(env["REMOTE_USER"]).to eq "admin" @@ -193,6 +198,7 @@ def _env @servlet_request.setQueryString('hello=there') @servlet_request.setServerName('serverhost') @servlet_request.setServerPort(80) + @servlet_request.setProtocol('HTTP/1.1') @servlet_request.setRemoteAddr('127.0.0.1') @servlet_request.setRemoteHost('localhost') @servlet_request.setRemoteUser('admin') @@ -204,7 +210,7 @@ def _env end env = servlet.create_env @servlet_env - expect(env["rack.version"]).to eq Rack::RELEASE + expect(env["rack.version"]).to eq Rack.release < '3' ? Rack.release : nil expect(env["CONTENT_TYPE"]).to eq "text/html" expect(env["HTTP_HOST"]).to eq "serverhost" expect(env["HTTP_ACCEPT"]).to eq "text/*" @@ -215,6 +221,7 @@ def _env expect(env["QUERY_STRING"]).to eq "hello=there" expect(env["SERVER_NAME"]).to eq "serverhost" expect(env["SERVER_PORT"]).to eq "80" + expect(env["SERVER_PROTOCOL"]).to eq "HTTP/1.1" expect(env["REMOTE_HOST"]).to eq "localhost" expect(env["REMOTE_ADDR"]).to eq "127.0.0.1" expect(env["REMOTE_USER"]).to eq "admin" @@ -419,11 +426,12 @@ def getAttributeNames env = servlet.create_env(@servlet_env) rack_request = Rack::Request.new(env) - # { "foo" => "0", "bar[" => "1", "baz_" => "2", "meh" => "3" } + # Rack 2.2: { "foo" => "0", "bar" => "1", "baz_" => "2", "meh" => "3" } + # Rack 3.x: { "foo]" => "0", "bar[" => "1", "baz_" => "2", "[meh" => "3" } - expect(rack_request.GET['foo']).to eql('0') + expect(rack_request.GET[Rack.release >= '3' ? 'foo]' : 'foo']).to eql('0') expect(rack_request.GET['baz_']).to eql('2') - expect(rack_request.GET['meh']).to eql('3') + expect(rack_request.GET[Rack.release >= '3' ? '[meh' : 'meh']).to eql('3') expect(rack_request.query_string).to eql 'foo]=0&bar[=1&baz_=2&[meh=3' end @@ -447,12 +455,12 @@ def getAttributeNames env = servlet.create_env(@servlet_env) rack_request = Rack::Request.new(env) - # params = { "foo" => { "bar" => "2", "baz" => "1", "meh" => [ nil, nil ] }, "huh" => { "1" => "b", "0" => "a" } } - # expect(rack_request.GET).to eql(params) + # Rack 2.2 params = { "foo" => { "bar" => "2", "baz" => "1", "meh" => [ "x", "42" ] }, "huh" => { "1" => "b", "0" => "a" } } + # Rack 3.x params = { "foo" => { "bar" => "2", "baz" => "1", "meh[" => { "]" => "42" } }, "huh" => { "1" => "b", "0" => "a" } } expect(rack_request.GET['foo']['bar']).to eql('2') expect(rack_request.GET['foo']['baz']).to eql('1') - expect(rack_request.params['foo']['meh']).to be_a Array + expect(rack_request.params['foo'][Rack.release >= '3' ? 'meh[' : 'meh']).to be_a Rack.release >= '3' ? Hash : Array expect(rack_request.params['huh']).to eql({ "1" => "b", "0" => "a" }) expect(rack_request.POST).to eql Hash.new @@ -531,6 +539,7 @@ def getAttributeNames expect(env.keys).to include('QUERY_STRING') expect(env.keys).to include('SERVER_NAME') expect(env.keys).to include('SERVER_PORT') + expect(env.keys).to include('SERVER_PROTOCOL') expect(env.keys).to include('REMOTE_HOST') expect(env.keys).to include('REMOTE_ADDR') expect(env.keys).to include('REMOTE_USER') @@ -538,12 +547,16 @@ def getAttributeNames expect(env.keys).to include(key) end - expect(env.keys).to include('rack.version') + if Rack.release < '3' + expect(env.keys).to include('rack.version') + expect(env.keys).to include('rack.multithread') + expect(env.keys).to include('rack.multiprocess') + expect(env.keys).to include('rack.run_once') + end + expect(env.keys).to include('rack.input') expect(env.keys).to include('rack.errors') expect(env.keys).to include('rack.url_scheme') - expect(env.keys).to include('rack.multithread') - expect(env.keys).to include('rack.run_once') expect(env.keys).to include('java.servlet_context') expect(env.keys).to include('java.servlet_request') expect(env.keys).to include('java.servlet_response') @@ -570,11 +583,15 @@ def getAttributeNames expect { env['OTHER_METHOD'] }.to_not raise_error expect(env['OTHER_METHOD']).to be nil - expect { env['rack.version'] }.to_not raise_error + if Rack.release < '3' + expect { env['rack.version'] }.to_not raise_error + expect { env['rack.multithread'] }.to_not raise_error + expect { env['rack.multiprocess'] }.to_not raise_error + expect { env['rack.run_once'] }.to_not raise_error + end + expect { env['rack.input'] }.to_not raise_error expect { env['rack.errors'] }.to_not raise_error - expect { env['rack.run_once'] }.to_not raise_error - expect { env['rack.multithread'] }.to_not raise_error expect { env['java.servlet_context'] }.to_not raise_error expect { env['java.servlet_request'] }.to_not raise_error expect { env['java.servlet_response'] }.to_not raise_error @@ -700,17 +717,21 @@ def it_works(env) expect(env['SCRIPT_NAME']).to eql '/main' expect(env['SERVER_NAME']).to eql 'serverhost' expect(env['SERVER_PORT']).to eql '80' + expect(env['SERVER_PROTOCOL']).to eql 'HTTP/1.1' expect(env['OTHER_METHOD']).to be nil Rack::Handler::Servlet::DefaultEnv::VARIABLES.each do |key| expect(env[key]).to_not be(nil), "key: #{key.inspect} nil" end expect(env['rack.url_scheme']).to_not be nil - expect(env['rack.version']).to_not be nil expect(env['jruby.rack.version']).to_not be nil - expect(env['rack.run_once']).to be false - expect(env['rack.multithread']).to be true + if Rack.release < '3' + expect(env['rack.version']).to_not be nil + expect(env['rack.multithread']).to be true + expect(env['rack.multiprocess']).to be false + expect(env['rack.run_once']).to be false + end expect(env['rack.whatever']).to be nil @@ -841,6 +862,7 @@ def servlet.create_env(servlet_env) expect(env.keys).to include('QUERY_STRING') expect(env.keys).to include('SERVER_NAME') expect(env.keys).to include('SERVER_PORT') + expect(env.keys).to include('SERVER_PROTOCOL') expect(env.keys).to include('REMOTE_HOST') expect(env.keys).to include('REMOTE_ADDR') expect(env.keys).to include('REMOTE_USER') @@ -848,12 +870,16 @@ def servlet.create_env(servlet_env) expect(env.keys).to include(key) end - expect(env.keys).to include('rack.version') + if Rack.release < '3' + expect(env.keys).to include('rack.version') + expect(env.keys).to include('rack.multithread') + expect(env.keys).to include('rack.multiprocess') + expect(env.keys).to include('rack.run_once') + end + expect(env.keys).to include('rack.input') expect(env.keys).to include('rack.errors') expect(env.keys).to include('rack.url_scheme') - expect(env.keys).to include('rack.multithread') - expect(env.keys).to include('rack.run_once') expect(env.keys).to include('java.servlet_context') expect(env.keys).to include('java.servlet_request') expect(env.keys).to include('java.servlet_response') diff --git a/src/spec/stub/rails81/app/controllers/application_controller.rb b/src/spec/stub/rails81/app/controllers/application_controller.rb new file mode 100644 index 000000000..0d95db22b --- /dev/null +++ b/src/spec/stub/rails81/app/controllers/application_controller.rb @@ -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 diff --git a/src/spec/stub/rails81/app/helpers/application_helper.rb b/src/spec/stub/rails81/app/helpers/application_helper.rb new file mode 100644 index 000000000..de6be7945 --- /dev/null +++ b/src/spec/stub/rails81/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/src/spec/stub/rails81/config/application.rb b/src/spec/stub/rails81/config/application.rb new file mode 100644 index 000000000..bba537d7f --- /dev/null +++ b/src/spec/stub/rails81/config/application.rb @@ -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 diff --git a/src/spec/stub/rails81/config/boot.rb b/src/spec/stub/rails81/config/boot.rb new file mode 100644 index 000000000..282011619 --- /dev/null +++ b/src/spec/stub/rails81/config/boot.rb @@ -0,0 +1,3 @@ +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +require "bundler/setup" # Set up gems listed in the Gemfile. diff --git a/src/spec/stub/rails81/config/credentials.yml.enc b/src/spec/stub/rails81/config/credentials.yml.enc new file mode 100644 index 000000000..e67e3468f --- /dev/null +++ b/src/spec/stub/rails81/config/credentials.yml.enc @@ -0,0 +1 @@ +82NrCVblpkSw3wDVYHqQFkuTm4oS5bQt+rYDUmlei6JtoWvmkz/K1A81ysT3RXIojUAgbe2wo3zrl3dygSEmZCSV78wfDXJpBJi5fkVC84HQBJEBg0/8yFhZtDjvWG53X532RYnSVtAaPtPeqKS9F0uHNT7/G8Gkhfgu8JEg1oT1mlT4nT3VLExtH4QrXGBrvpWbFN6VzRIqFIO7Bk/tR92v6VCoAbl44j61pqEhW6SyDLb2PNGtW3o+Lq6RTTnsS9MOXXh/eNr4PZv97ghxbLSxcgzqXh6qBKsfZTTh30oxHRmv1yf1ur7hlopok1g4DcX5yK3Cul+ttkjYUoKdiSZ8cpZojQEqiMaRtvjVBGspHquqi9OuMtBlQaVxU1z3lGCdYnx1hqQDk6RnkBTE+Y7wbTGrjyQ1urKYXjw4bvu5WEh2n/UEHJsxrmg0v7yZZaOGESnfn+pbyWlbWfbA6fhH8ZguyqgzNiMh1TGU4Tqzvmgf/FAPQVNx--Jp0qmDUTnghki/m1--9Dp76qUD7YblOugS/mioww== \ No newline at end of file diff --git a/src/spec/stub/rails81/config/environment.rb b/src/spec/stub/rails81/config/environment.rb new file mode 100644 index 000000000..cac531577 --- /dev/null +++ b/src/spec/stub/rails81/config/environment.rb @@ -0,0 +1,5 @@ +# Load the Rails application. +require_relative "application" + +# Initialize the Rails application. +Rails.application.initialize! diff --git a/src/spec/stub/rails81/config/environments/development.rb b/src/spec/stub/rails81/config/environments/development.rb new file mode 100644 index 000000000..f68dd3575 --- /dev/null +++ b/src/spec/stub/rails81/config/environments/development.rb @@ -0,0 +1,42 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Make code changes take effect immediately without server restart. + config.enable_reloading = true + + # Do not eager load code on boot. + config.eager_load = false + + # Show full error reports. + config.consider_all_requests_local = true + + # Enable server timing. + config.server_timing = true + + # Enable/disable Action Controller caching. By default Action Controller caching is disabled. + # Run rails dev:cache to toggle Action Controller caching. + if Rails.root.join("tmp/caching-dev.txt").exist? + config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true + config.public_file_server.headers = { "cache-control" => "public, max-age=#{2.days.to_i}" } + else + config.action_controller.perform_caching = false + end + + # Change to :null_store to avoid any caching. + config.cache_store = :memory_store + + # Print deprecation notices to the Rails logger. + config.active_support.deprecation = :log + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + config.action_view.annotate_rendered_view_with_filenames = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true +end diff --git a/src/spec/stub/rails81/config/environments/production.rb b/src/spec/stub/rails81/config/environments/production.rb new file mode 100644 index 000000000..49ed6ab53 --- /dev/null +++ b/src/spec/stub/rails81/config/environments/production.rb @@ -0,0 +1,73 @@ +require "active_support/core_ext/integer/time" + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # Code is not reloaded between requests. + config.enable_reloading = false + + # Eager load code on boot for better performance and memory savings (ignored by Rake tasks). + config.eager_load = true + + # Full error reports are disabled. + config.consider_all_requests_local = false + + # Turn on fragment caching in view templates. + config.action_controller.perform_caching = true + + # Cache digest stamped assets for far-future expiry. + # Short cache for others: robots.txt, sitemap.xml, 404.html, etc. + config.public_file_server.headers = { + "cache-control" => lambda do |path, _| + if path.start_with?("/assets/") + # Files in /assets/ are expected to be fully immutable. + # If the content change the URL too. + "public, immutable, max-age=#{1.year.to_i}" + else + # For anything else we cache for 1 minute. + "public, max-age=#{1.minute.to_i}, stale-while-revalidate=#{5.minutes.to_i}" + end + end + } + + # Enable serving of images, stylesheets, and JavaScripts from an asset server. + # config.asset_host = "http://assets.example.com" + + # Assume all access to the app is happening through a SSL-terminating reverse proxy. + config.assume_ssl = true + + # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. + config.force_ssl = true + + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } + + # Log to STDOUT with the current request id as a default log tag. + config.log_tags = [ :request_id ] + # config.logger = ActiveSupport::TaggedLogging.logger(STDOUT) + + # Change to "debug" to log everything (including potentially personally-identifiable information!). + config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") + + # Prevent health checks from clogging up the logs. + config.silence_healthcheck_path = "/up" + + # Don't log any deprecations. + config.active_support.report_deprecations = false + + # Replace the default in-process memory cache store with a durable alternative. + # config.cache_store = :mem_cache_store + + # Enable locale fallbacks for I18n (makes lookups for any locale fall back to + # the I18n.default_locale when a translation cannot be found). + config.i18n.fallbacks = true + + # Enable DNS rebinding protection and other `Host` header attacks. + # config.hosts = [ + # "example.com", # Allow requests from example.com + # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` + # ] + # + # Skip DNS rebinding protection for the default health check endpoint. + # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } +end diff --git a/src/spec/stub/rails81/config/environments/test.rb b/src/spec/stub/rails81/config/environments/test.rb new file mode 100644 index 000000000..14bc29e06 --- /dev/null +++ b/src/spec/stub/rails81/config/environments/test.rb @@ -0,0 +1,42 @@ +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! + +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # While tests run files are not watched, reloading is not necessary. + config.enable_reloading = false + + # Eager loading loads your entire application. When running a single test locally, + # this is usually not necessary, and can slow down your test suite. However, it's + # recommended that you enable it in continuous integration systems to ensure eager + # loading is working properly before deploying your code. + config.eager_load = ENV["CI"].present? + + # Configure public file server for tests with cache-control for performance. + config.public_file_server.headers = { "cache-control" => "public, max-age=3600" } + + # Show full error reports. + config.consider_all_requests_local = true + config.cache_store = :null_store + + # Render exception templates for rescuable exceptions and raise for other exceptions. + config.action_dispatch.show_exceptions = :rescuable + + # Disable request forgery protection in test environment. + config.action_controller.allow_forgery_protection = false + + # Print deprecation notices to the stderr. + config.active_support.deprecation = :stderr + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true + + # Raise error when a before_action's only/except options reference missing actions. + config.action_controller.raise_on_missing_callback_actions = true +end diff --git a/src/spec/stub/rails81/config/initializers/content_security_policy.rb b/src/spec/stub/rails81/config/initializers/content_security_policy.rb new file mode 100644 index 000000000..d51d71397 --- /dev/null +++ b/src/spec/stub/rails81/config/initializers/content_security_policy.rb @@ -0,0 +1,29 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy. +# See the Securing Rails Applications Guide for more information: +# https://guides.rubyonrails.org/security.html#content-security-policy-header + +# Rails.application.configure do +# config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end +# +# # Generate session nonces for permitted importmap, inline scripts, and inline styles. +# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } +# config.content_security_policy_nonce_directives = %w(script-src style-src) +# +# # Automatically add `nonce` to `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag` +# # if the corresponding directives are specified in `content_security_policy_nonce_directives`. +# # config.content_security_policy_nonce_auto = true +# +# # Report violations without enforcing the policy. +# # config.content_security_policy_report_only = true +# end diff --git a/src/spec/stub/rails81/config/initializers/filter_parameter_logging.rb b/src/spec/stub/rails81/config/initializers/filter_parameter_logging.rb new file mode 100644 index 000000000..c0b717f7e --- /dev/null +++ b/src/spec/stub/rails81/config/initializers/filter_parameter_logging.rb @@ -0,0 +1,8 @@ +# Be sure to restart your server when you modify this file. + +# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. +# Use this to limit dissemination of sensitive information. +# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. +Rails.application.config.filter_parameters += [ + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc +] diff --git a/src/spec/stub/rails81/config/initializers/inflections.rb b/src/spec/stub/rails81/config/initializers/inflections.rb new file mode 100644 index 000000000..3860f659e --- /dev/null +++ b/src/spec/stub/rails81/config/initializers/inflections.rb @@ -0,0 +1,16 @@ +# Be sure to restart your server when you modify this file. + +# Add new inflection rules using the following format. Inflections +# are locale specific, and you may define rules for as many different +# locales as you wish. All of these examples are active by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.plural /^(ox)$/i, "\\1en" +# inflect.singular /^(ox)en/i, "\\1" +# inflect.irregular "person", "people" +# inflect.uncountable %w( fish sheep ) +# end + +# These inflection rules are supported but not enabled by default: +# ActiveSupport::Inflector.inflections(:en) do |inflect| +# inflect.acronym "RESTful" +# end diff --git a/src/spec/stub/rails81/config/locales/en.yml b/src/spec/stub/rails81/config/locales/en.yml new file mode 100644 index 000000000..6c349ae5e --- /dev/null +++ b/src/spec/stub/rails81/config/locales/en.yml @@ -0,0 +1,31 @@ +# Files in the config/locales directory are used for internationalization and +# are automatically loaded by Rails. If you want to use locales other than +# English, add the necessary files in this directory. +# +# To use the locales, use `I18n.t`: +# +# I18n.t "hello" +# +# In views, this is aliased to just `t`: +# +# <%= t("hello") %> +# +# To use a different locale, set it with `I18n.locale`: +# +# I18n.locale = :es +# +# This would use the information in config/locales/es.yml. +# +# To learn more about the API, please read the Rails Internationalization guide +# at https://guides.rubyonrails.org/i18n.html. +# +# Be aware that YAML interprets the following case-insensitive strings as +# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings +# must be quoted to be interpreted as strings. For example: +# +# en: +# "yes": yup +# enabled: "ON" + +en: + hello: "Hello world" diff --git a/src/spec/stub/rails81/config/master.key b/src/spec/stub/rails81/config/master.key new file mode 100644 index 000000000..e7883e7b5 --- /dev/null +++ b/src/spec/stub/rails81/config/master.key @@ -0,0 +1 @@ +c8efa0d2b5305bf19054185dc011f0f7 \ No newline at end of file diff --git a/src/spec/stub/rails81/config/routes.rb b/src/spec/stub/rails81/config/routes.rb new file mode 100644 index 000000000..48254e88e --- /dev/null +++ b/src/spec/stub/rails81/config/routes.rb @@ -0,0 +1,14 @@ +Rails.application.routes.draw do + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html + + # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500. + # Can be used by load balancers and uptime monitors to verify that the app is live. + get "up" => "rails/health#show", as: :rails_health_check + + # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb) + # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest + # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker + + # Defines the root path route ("/") + # root "posts#index" +end diff --git a/src/spec/stub/rails81/public/robots.txt b/src/spec/stub/rails81/public/robots.txt new file mode 100644 index 000000000..c19f78ab6 --- /dev/null +++ b/src/spec/stub/rails81/public/robots.txt @@ -0,0 +1 @@ +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file