Skip to content

Commit ac038be

Browse files
committed
[fix] Remove the undocumented and unsafe jruby.rack.env.gem_path = false init parameter option
Changes the `false` value to behave the same way as empty, and not touch ENV['GEM_PATH'] or Gem.path at all. Previously this allowed you to alter Gem.path without touching the ENV['GEM_PATH']'. This was unsafe with Bundler 1.6+ (so for a long time), because many things may cause Gem.path to be recalculated from env, including bundler initialisations, restarts and later changes within an application's boot hooks. Furthermore the behaviour varied wildly depending on whether Rubygems was initialised prior to the Rack booter, or afterwards, even without bundler causing indeterminism and issues similar to jruby/warbler#575 I cannot find any reference to use of this value on GitHub, or discussion, and it would not work at all with modern JRuby/bundler so removing it, because it helps avoid further issues. Note that it was added in 2fd826b and then the default changed back in d3ee8f0 shortly after when issues were discovered, so given it's undocumented/unsupported, it is unlikely to be relied upon by anyone.
1 parent 8bbcbdf commit ac038be

File tree

4 files changed

+46
-45
lines changed

4 files changed

+46
-45
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- Add missing block-only signature for debug logging
44
- update (bundled) rack to 2.2.20
55
- Ensure rack boot process leaves ENV['GEM_PATH'] and Gem.paths in a consistent state
6+
- Remove undocumented and unsafe jruby.rack.env.gem_path = false option (unusable on Bundler 1.6+)
67

78
## 1.2.5
89

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ as context init parameters in web.xml or as VM-wide system properties.
233233
- `jruby.runtime.env.rubyopt`: This option is used for compatibility with the
234234
(deprecated) `jruby.rack.ignore.env` option since it cleared out the ENV after
235235
RUBYOPT has been processed, by setting it to true ENV['RUBYOPT'] will be kept.
236+
- `jruby.rack.env.gem_path`: If set to `true` (the default) jruby-rack will
237+
ensure ENV['GEM_PATH'] is altered to include the `gem.path` above. If you set it to a
238+
value, this value will be used as GEM_PATH, overriding the environment and
239+
ignoring `gem.path` etc. By setting this option to en empty string the ENV['GEM_PATH'] will
240+
not be modified by jruby-rack at all and will retain its original values implied by
241+
the process environment and `jruby.runtime.env` setting.
236242
- `jruby.rack.logging`: Specify the logging device to use. Defaults to
237243
`servlet_context`. See below.
238244
- `jruby.rack.request.size.initial.bytes`: Initial size for request body memory

src/main/ruby/jruby/rack/booter.rb

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -104,55 +104,45 @@ def boot!
104104
protected
105105

106106
def adjust_gem_path
107-
gem_path = self.gem_path
107+
desired_gem_path = self.gem_path
108+
108109
case set_gem_path = env_gem_path
109-
when true then
110-
if env_path = ENV['GEM_PATH']
111-
if gem_path.nil? || gem_path.empty?
112-
return # keep ENV['GEM_PATH'] as is
113-
elsif env_path != gem_path
114-
separator = File::PATH_SEPARATOR
115-
unless env_path.split(separator).include?(gem_path)
116-
ENV['GEM_PATH'] = "#{gem_path}#{separator}#{env_path}"
117-
end
118-
end
119-
else
120-
ENV['GEM_PATH'] = gem_path
121-
end
122-
when false then
123-
begin
124-
require 'rubygems' unless defined? Gem.path
125-
rescue LoadError
110+
when true then # default behaviour
111+
if (current_env_gem_path = ENV['GEM_PATH'])
112+
# keep ENV['GEM_PATH'] as is if we have nothing to do
113+
return if desired_gem_path.nil? || desired_gem_path.empty?
114+
return if current_env_gem_path == desired_gem_path
115+
return if current_env_gem_path.split(File::PATH_SEPARATOR).include?(desired_gem_path)
116+
117+
# need to prepend it
118+
ENV['GEM_PATH'] = "#{desired_gem_path}#{File::PATH_SEPARATOR}#{current_env_gem_path}"
126119
else
127-
return if gem_path.nil? || gem_path.empty?
128-
Gem.path.unshift(gem_path) unless Gem.path.include?(gem_path)
120+
ENV['GEM_PATH'] = desired_gem_path
129121
end
130-
return false
131-
when nil then # org.jruby.rack.RackLogger::DEBUG
132-
if gem_path && ! gem_path.empty? &&
133-
( ! defined?(Gem.path) || ! Gem.path.include?(gem_path) )
134-
@rack_context.log("Gem.path won't be updated although seems configured: #{gem_path}")
122+
when nil then
123+
if desired_gem_path && !desired_gem_path.empty? && (!defined?(Gem.path) || !Gem.path.include?(desired_gem_path) )
124+
@rack_context.log("Gem.path won't be updated although seems configured: #{desired_gem_path}")
135125
end
136-
return nil
137-
else # 'jruby.rack.env.gem_path' "forced" to an explicit value
126+
return nil # do nothing to ENV['GEM_PATH']
127+
else # "forced" to an explicit value
138128
ENV['GEM_PATH'] = set_gem_path
139129
end
140130
# Whenever we touch ENV['GEM_PATH`], ensure we clear any cached paths. All other cases should exit early.
141131
Gem.clear_paths if defined?(Gem.clear_paths)
142132
end
143133

144-
# @return whether to update Gem.path and/or the environment GEM_PATH
145-
# - true (default) forces ENV['GEM_PATH'] to be updated due compatibility
146-
# Bundler 1.6 fails to revolve gems correctly when Gem.path is updated
147-
# instead of the ENV['GEM_PATH'] environment variable
148-
# - false disables ENV['GEM_PATH'] mangling for good (updates Gem.path)
134+
# @return whether to update the environment GEM_PATH
135+
# - true (default) forces ENV['GEM_PATH'] to be updated to include the `gem.path` above.
136+
# If you set it to a non-empty value, GEM_PATH will be forced to an explicit value,
137+
# overriding the environment and ignoring `gem.path`, `gem.home` etc.
149138
#
150-
# - if not specified Gem.path will be updated based on setting
139+
# - By setting this option to an empty string the ENV['GEM_PATH'] should not be modified
140+
# at all and will retain its original values implied by the process environment and
141+
# `jruby.runtime.env` setting.
151142
def env_gem_path
152143
gem_path = @rack_context.getInitParameter('jruby.rack.env.gem_path')
153144
return true if gem_path.nil? || gem_path.to_s == 'true'
154-
return false if gem_path.to_s == 'false'
155-
return nil if gem_path.empty? # set to an empty disables mangling
145+
return nil if gem_path.empty? || gem_path.to_s == 'false' # treat false as "don't touch either ENV or Gem.path"
156146
gem_path
157147
end
158148
private :env_gem_path

src/spec/ruby/jruby/rack/booter_spec.rb

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,6 @@
9898
expect(booter.rack_env).to eq 'production'
9999
end
100100

101-
it "prepends gem_path to Gem.path (when configured to not mangle with ENV)" do
102-
Gem.path.replace ['/opt/gems']
103-
expect(@rack_context).to receive(:getInitParameter).with("jruby.rack.env.gem_path").and_return 'false'
104-
105-
booter.gem_path = "wsjar:file:/opt/deploy/sample.war!/WEB-INF/gems"
106-
booter.boot!
107-
108-
expect(Gem.path).to eql ['wsjar:file:/opt/deploy/sample.war!/WEB-INF/gems', '/opt/gems']
109-
end
110-
111101
it "prepends gem_path to Gem.path if not already present" do
112102
ENV['GEM_PATH'] = "file:/home/gems#{File::PATH_SEPARATOR}/usr/local/gems"
113103
Gem.clear_paths
@@ -156,6 +146,20 @@
156146
expect(Gem.path).to start_with ["/opt/gems"]
157147
end
158148

149+
it "does not prepend gem_path to ENV['GEM_PATH'] if jruby.rack.gem_path set to false" do
150+
ENV['GEM_PATH'] = '/opt/gems'
151+
Gem.clear_paths
152+
153+
expect(@rack_context).to receive(:getInitParameter).with("jruby.rack.env.gem_path").and_return 'false'
154+
expect(@rack_context).to receive(:getRealPath).with("/WEB-INF").and_return "/opt/deploy/sample.war!/WEB-INF"
155+
expect(@rack_context).to receive(:getRealPath).with("/WEB-INF/gems").and_return "/opt/deploy/sample.war!/WEB-INF/gems"
156+
157+
booter.boot!
158+
159+
expect(ENV['GEM_PATH']).to eq "/opt/gems"
160+
expect(Gem.path).to start_with ["/opt/gems"]
161+
end
162+
159163
it "prepends gem_path to ENV['GEM_PATH'] if not already present" do
160164
ENV['GEM_PATH'] = "/home/gems#{File::PATH_SEPARATOR}/usr/local/gems"
161165
Gem.clear_paths

0 commit comments

Comments
 (0)