puma-simon 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/.github/issue_template.md +20 -0
  3. data/.gitignore +18 -0
  4. data/.hoeignore +12 -0
  5. data/.travis.yml +29 -0
  6. data/DEPLOYMENT.md +91 -0
  7. data/Gemfile +12 -0
  8. data/History.md +1254 -0
  9. data/LICENSE +26 -0
  10. data/Manifest.txt +78 -0
  11. data/README.md +353 -0
  12. data/Rakefile +158 -0
  13. data/Release.md +9 -0
  14. data/bin/puma +10 -0
  15. data/bin/puma-wild +31 -0
  16. data/bin/pumactl +12 -0
  17. data/docs/nginx.md +80 -0
  18. data/docs/signals.md +43 -0
  19. data/docs/systemd.md +197 -0
  20. data/examples/CA/cacert.pem +23 -0
  21. data/examples/CA/newcerts/cert_1.pem +19 -0
  22. data/examples/CA/newcerts/cert_2.pem +19 -0
  23. data/examples/CA/private/cakeypair.pem +30 -0
  24. data/examples/CA/serial +1 -0
  25. data/examples/config.rb +200 -0
  26. data/examples/plugins/redis_stop_puma.rb +46 -0
  27. data/examples/puma/cert_puma.pem +19 -0
  28. data/examples/puma/client-certs/ca.crt +19 -0
  29. data/examples/puma/client-certs/ca.key +27 -0
  30. data/examples/puma/client-certs/client.crt +19 -0
  31. data/examples/puma/client-certs/client.key +27 -0
  32. data/examples/puma/client-certs/client_expired.crt +19 -0
  33. data/examples/puma/client-certs/client_expired.key +27 -0
  34. data/examples/puma/client-certs/client_unknown.crt +19 -0
  35. data/examples/puma/client-certs/client_unknown.key +27 -0
  36. data/examples/puma/client-certs/generate.rb +78 -0
  37. data/examples/puma/client-certs/keystore.jks +0 -0
  38. data/examples/puma/client-certs/server.crt +19 -0
  39. data/examples/puma/client-certs/server.key +27 -0
  40. data/examples/puma/client-certs/server.p12 +0 -0
  41. data/examples/puma/client-certs/unknown_ca.crt +19 -0
  42. data/examples/puma/client-certs/unknown_ca.key +27 -0
  43. data/examples/puma/csr_puma.pem +11 -0
  44. data/examples/puma/keystore.jks +0 -0
  45. data/examples/puma/puma_keypair.pem +15 -0
  46. data/examples/qc_config.rb +13 -0
  47. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  48. data/ext/puma_http11/ext_help.h +15 -0
  49. data/ext/puma_http11/extconf.rb +15 -0
  50. data/ext/puma_http11/http11_parser.c +1069 -0
  51. data/ext/puma_http11/http11_parser.h +65 -0
  52. data/ext/puma_http11/http11_parser.java.rl +161 -0
  53. data/ext/puma_http11/http11_parser.rl +147 -0
  54. data/ext/puma_http11/http11_parser_common.rl +54 -0
  55. data/ext/puma_http11/io_buffer.c +155 -0
  56. data/ext/puma_http11/mini_ssl.c +457 -0
  57. data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
  58. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +473 -0
  59. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +339 -0
  60. data/ext/puma_http11/puma_http11.c +500 -0
  61. data/gemfiles/2.1-Gemfile +12 -0
  62. data/lib/puma.rb +15 -0
  63. data/lib/puma/accept_nonblock.rb +23 -0
  64. data/lib/puma/app/status.rb +66 -0
  65. data/lib/puma/binder.rb +402 -0
  66. data/lib/puma/cli.rb +220 -0
  67. data/lib/puma/client.rb +434 -0
  68. data/lib/puma/cluster.rb +510 -0
  69. data/lib/puma/commonlogger.rb +106 -0
  70. data/lib/puma/compat.rb +14 -0
  71. data/lib/puma/configuration.rb +364 -0
  72. data/lib/puma/const.rb +224 -0
  73. data/lib/puma/control_cli.rb +259 -0
  74. data/lib/puma/convenient.rb +23 -0
  75. data/lib/puma/daemon_ext.rb +31 -0
  76. data/lib/puma/delegation.rb +11 -0
  77. data/lib/puma/detect.rb +13 -0
  78. data/lib/puma/dsl.rb +486 -0
  79. data/lib/puma/events.rb +152 -0
  80. data/lib/puma/io_buffer.rb +7 -0
  81. data/lib/puma/java_io_buffer.rb +45 -0
  82. data/lib/puma/jruby_restart.rb +83 -0
  83. data/lib/puma/launcher.rb +410 -0
  84. data/lib/puma/minissl.rb +221 -0
  85. data/lib/puma/null_io.rb +42 -0
  86. data/lib/puma/plugin.rb +115 -0
  87. data/lib/puma/plugin/tmp_restart.rb +35 -0
  88. data/lib/puma/rack/backports/uri/common_193.rb +33 -0
  89. data/lib/puma/rack/builder.rb +298 -0
  90. data/lib/puma/rack/urlmap.rb +91 -0
  91. data/lib/puma/rack_default.rb +7 -0
  92. data/lib/puma/reactor.rb +210 -0
  93. data/lib/puma/runner.rb +171 -0
  94. data/lib/puma/server.rb +949 -0
  95. data/lib/puma/single.rb +112 -0
  96. data/lib/puma/state_file.rb +29 -0
  97. data/lib/puma/tcp_logger.rb +39 -0
  98. data/lib/puma/thread_pool.rb +297 -0
  99. data/lib/puma/util.rb +128 -0
  100. data/lib/rack/handler/puma.rb +78 -0
  101. data/puma.gemspec +52 -0
  102. data/test/ab_rs.rb +22 -0
  103. data/test/config.rb +2 -0
  104. data/test/config/app.rb +9 -0
  105. data/test/config/plugin.rb +1 -0
  106. data/test/config/settings.rb +2 -0
  107. data/test/config/state_file_testing_config.rb +14 -0
  108. data/test/hello-bind.ru +2 -0
  109. data/test/hello-delay.ru +3 -0
  110. data/test/hello-map.ru +3 -0
  111. data/test/hello-post.ru +4 -0
  112. data/test/hello-stuck.ru +1 -0
  113. data/test/hello-tcp.ru +5 -0
  114. data/test/hello.ru +1 -0
  115. data/test/hijack.ru +6 -0
  116. data/test/hijack2.ru +5 -0
  117. data/test/lobster.ru +4 -0
  118. data/test/shell/run.sh +24 -0
  119. data/test/shell/t1.rb +19 -0
  120. data/test/shell/t1_conf.rb +3 -0
  121. data/test/shell/t2.rb +17 -0
  122. data/test/shell/t2_conf.rb +6 -0
  123. data/test/shell/t3.rb +25 -0
  124. data/test/shell/t3_conf.rb +5 -0
  125. data/test/slow.ru +4 -0
  126. data/test/ssl_config.rb +4 -0
  127. data/test/test_app_status.rb +93 -0
  128. data/test/test_binder.rb +31 -0
  129. data/test/test_cli.rb +209 -0
  130. data/test/test_config.rb +95 -0
  131. data/test/test_events.rb +161 -0
  132. data/test/test_helper.rb +50 -0
  133. data/test/test_http10.rb +27 -0
  134. data/test/test_http11.rb +186 -0
  135. data/test/test_integration.rb +247 -0
  136. data/test/test_iobuffer.rb +39 -0
  137. data/test/test_minissl.rb +29 -0
  138. data/test/test_null_io.rb +49 -0
  139. data/test/test_persistent.rb +245 -0
  140. data/test/test_puma_server.rb +626 -0
  141. data/test/test_puma_server_ssl.rb +222 -0
  142. data/test/test_rack_handler.rb +57 -0
  143. data/test/test_rack_server.rb +138 -0
  144. data/test/test_tcp_logger.rb +39 -0
  145. data/test/test_tcp_rack.rb +36 -0
  146. data/test/test_thread_pool.rb +250 -0
  147. data/test/test_unix_socket.rb +35 -0
  148. data/test/test_web_server.rb +88 -0
  149. data/tools/jungle/README.md +9 -0
  150. data/tools/jungle/init.d/README.md +59 -0
  151. data/tools/jungle/init.d/puma +421 -0
  152. data/tools/jungle/init.d/run-puma +18 -0
  153. data/tools/jungle/upstart/README.md +61 -0
  154. data/tools/jungle/upstart/puma-manager.conf +31 -0
  155. data/tools/jungle/upstart/puma.conf +69 -0
  156. data/tools/trickletest.rb +45 -0
  157. metadata +297 -0
@@ -0,0 +1,25 @@
1
+ system "ruby -rubygems -I../../lib ../../bin/puma -p 10102 -C t3_conf.rb ../hello.ru &"
2
+ sleep 5
3
+
4
+ worker_pid_was_present = File.file? "t3-worker-2-pid"
5
+
6
+ system "kill `cat t3-worker-2-pid`" # kill off a worker
7
+
8
+ sleep 2
9
+
10
+ worker_index_within_number_of_workers = !File.file?("t3-worker-3-pid")
11
+
12
+ system "kill `cat t3-pid`"
13
+
14
+ File.unlink "t3-pid" if File.file? "t3-pid"
15
+ File.unlink "t3-worker-0-pid" if File.file? "t3-worker-0-pid"
16
+ File.unlink "t3-worker-1-pid" if File.file? "t3-worker-1-pid"
17
+ File.unlink "t3-worker-2-pid" if File.file? "t3-worker-2-pid"
18
+ File.unlink "t3-worker-3-pid" if File.file? "t3-worker-3-pid"
19
+
20
+ if worker_pid_was_present and worker_index_within_number_of_workers
21
+ exit 0
22
+ else
23
+ exit 1
24
+ end
25
+
@@ -0,0 +1,5 @@
1
+ pidfile "t3-pid"
2
+ workers 3
3
+ on_worker_boot do |index|
4
+ File.open("t3-worker-#{index}-pid", "w") { |f| f.puts Process.pid }
5
+ end
@@ -0,0 +1,4 @@
1
+ run lambda { |env|
2
+ 30000000.times { }
3
+ [200, {}, ["Hello World"]]
4
+ }
@@ -0,0 +1,4 @@
1
+ key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
2
+ cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
3
+
4
+ ssl_bind "0.0.0.0", 9292, :cert => cert, :key => key
@@ -0,0 +1,93 @@
1
+ require "test_helper"
2
+
3
+ require "puma/app/status"
4
+ require "rack"
5
+
6
+ class TestAppStatus < Minitest::Test
7
+ class FakeServer
8
+ def initialize
9
+ @status = :running
10
+ @backlog = 0
11
+ @running = 0
12
+ end
13
+
14
+ attr_reader :status
15
+ attr_accessor :backlog, :running
16
+
17
+ def stop
18
+ @status = :stop
19
+ end
20
+
21
+ def halt
22
+ @status = :halt
23
+ end
24
+
25
+ def stats
26
+ "{}"
27
+ end
28
+ end
29
+
30
+ def setup
31
+ @server = FakeServer.new
32
+ @app = Puma::App::Status.new(@server)
33
+ @app.auth_token = nil
34
+ end
35
+
36
+ def lint(uri)
37
+ app = Rack::Lint.new @app
38
+ mock_env = Rack::MockRequest.env_for uri
39
+ app.call mock_env
40
+ end
41
+
42
+ def test_bad_token
43
+ @app.auth_token = "abcdef"
44
+
45
+ status, _, _ = lint('/whatever')
46
+
47
+ assert_equal 403, status
48
+ end
49
+
50
+ def test_good_token
51
+ @app.auth_token = "abcdef"
52
+
53
+ status, _, _ = lint('/whatever?token=abcdef')
54
+
55
+ assert_equal 404, status
56
+ end
57
+
58
+ def test_unsupported
59
+ status, _, _ = lint('/not-real')
60
+
61
+ assert_equal 404, status
62
+ end
63
+
64
+ def test_stop
65
+ status, _ , app = lint('/stop')
66
+
67
+ assert_equal :stop, @server.status
68
+ assert_equal 200, status
69
+ assert_equal ['{ "status": "ok" }'], app.enum_for.to_a
70
+ end
71
+
72
+ def test_halt
73
+ status, _ , app = lint('/halt')
74
+
75
+ assert_equal :halt, @server.status
76
+ assert_equal 200, status
77
+ assert_equal ['{ "status": "ok" }'], app.enum_for.to_a
78
+ end
79
+
80
+ def test_stats
81
+ @server.backlog = 1
82
+ @server.running = 9
83
+ status, _ , app = lint('/stats')
84
+
85
+ assert_equal 200, status
86
+ assert_equal ['{}'], app.enum_for.to_a
87
+ end
88
+
89
+ def test_alternate_location
90
+ status, _ , _ = lint('__alternatE_location_/stats')
91
+ assert_equal 200, status
92
+ end
93
+ end
@@ -0,0 +1,31 @@
1
+ require "test_helper"
2
+
3
+ require "puma/binder"
4
+ require "puma/events"
5
+ require "puma/puma_http11"
6
+
7
+ class TestBinder < Minitest::Test
8
+ def setup
9
+ @events = Puma::Events.null
10
+ @binder = Puma::Binder.new(@events)
11
+ end
12
+
13
+ def test_localhost_addresses_dont_alter_listeners_for_tcp_addresses
14
+ skip_on_jruby
15
+
16
+ @binder.parse(["tcp://localhost:10001"], @events)
17
+
18
+ assert_equal [], @binder.listeners
19
+ end
20
+
21
+ def test_localhost_addresses_dont_alter_listeners_for_ssl_addresses
22
+ skip_on_jruby
23
+
24
+ key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__
25
+ cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__
26
+
27
+ @binder.parse(["ssl://localhost:10002?key=#{key}&cert=#{cert}"], @events)
28
+
29
+ assert_equal [], @binder.listeners
30
+ end
31
+ end
@@ -0,0 +1,209 @@
1
+ require "test_helper"
2
+
3
+ require "puma/cli"
4
+
5
+ class TestCLI < Minitest::Test
6
+ def setup
7
+ @environment = 'production'
8
+ @tmp_file = Tempfile.new("puma-test")
9
+ @tmp_path = @tmp_file.path
10
+ @tmp_file.close!
11
+
12
+ @tmp_path2 = "#{@tmp_path}2"
13
+
14
+ File.unlink @tmp_path if File.exist? @tmp_path
15
+ File.unlink @tmp_path2 if File.exist? @tmp_path2
16
+
17
+ @wait, @ready = IO.pipe
18
+
19
+ @events = Puma::Events.strings
20
+ @events.on_booted { @ready << "!" }
21
+ end
22
+
23
+ def wait_booted
24
+ @wait.sysread 1
25
+ end
26
+
27
+ def teardown
28
+ File.unlink @tmp_path if File.exist? @tmp_path
29
+ File.unlink @tmp_path2 if File.exist? @tmp_path2
30
+
31
+ @wait.close
32
+ @ready.close
33
+ end
34
+
35
+ def test_pid_file
36
+ cli = Puma::CLI.new ["--pidfile", @tmp_path]
37
+ cli.launcher.write_pid
38
+
39
+ assert_equal File.read(@tmp_path).strip.to_i, Process.pid
40
+ end
41
+
42
+ def test_control_for_tcp
43
+ url = "tcp://127.0.0.1:9877/"
44
+ cli = Puma::CLI.new ["-b", "tcp://127.0.0.1:9876",
45
+ "--control", url,
46
+ "--control-token", "",
47
+ "test/lobster.ru"], @events
48
+
49
+ t = Thread.new do
50
+ Thread.current.abort_on_exception = true
51
+ cli.run
52
+ end
53
+
54
+ wait_booted
55
+
56
+ s = TCPSocket.new "127.0.0.1", 9877
57
+ s << "GET /stats HTTP/1.0\r\n\r\n"
58
+ body = s.read
59
+ assert_equal '{ "backlog": 0, "running": 0 }', body.split("\r\n").last
60
+
61
+ cli.launcher.stop
62
+ t.join
63
+ end
64
+
65
+ unless Puma.jruby? || Puma.windows?
66
+ def test_control_clustered
67
+ url = "unix://#{@tmp_path}"
68
+
69
+ cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
70
+ "-t", "2:2",
71
+ "-w", "2",
72
+ "--control", url,
73
+ "--control-token", "",
74
+ "test/lobster.ru"], @events
75
+
76
+ t = Thread.new { cli.run }
77
+ t.abort_on_exception = true
78
+
79
+ wait_booted
80
+
81
+ sleep 2
82
+
83
+ s = UNIXSocket.new @tmp_path
84
+ s << "GET /stats HTTP/1.0\r\n\r\n"
85
+ body = s.read
86
+
87
+ require 'json'
88
+ status = JSON.parse(body.split("\n").last)
89
+
90
+ assert_equal 2, status["workers"]
91
+
92
+ # wait until the first status ping has come through
93
+ sleep 6
94
+ s = UNIXSocket.new @tmp_path
95
+ s << "GET /stats HTTP/1.0\r\n\r\n"
96
+ body = s.read
97
+ assert_match(/\{ "workers": 2, "phase": 0, "booted_workers": 2, "old_workers": 0, "worker_status": \[\{ "pid": \d+, "index": 0, "phase": 0, "booted": true, "last_checkin": "[^"]+", "last_status": \{ "backlog":0, "running":2 \} \},\{ "pid": \d+, "index": 1, "phase": 0, "booted": true, "last_checkin": "[^"]+", "last_status": \{ "backlog":0, "running":2 \} \}\] \}/, body.split("\r\n").last)
98
+
99
+ cli.launcher.stop
100
+ t.join
101
+ end
102
+
103
+ def test_control
104
+ url = "unix://#{@tmp_path}"
105
+
106
+ cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
107
+ "--control", url,
108
+ "--control-token", "",
109
+ "test/lobster.ru"], @events
110
+
111
+ t = Thread.new { cli.run }
112
+ t.abort_on_exception = true
113
+
114
+ wait_booted
115
+
116
+ s = UNIXSocket.new @tmp_path
117
+ s << "GET /stats HTTP/1.0\r\n\r\n"
118
+ body = s.read
119
+
120
+ assert_equal '{ "backlog": 0, "running": 0 }', body.split("\r\n").last
121
+
122
+ cli.launcher.stop
123
+ t.join
124
+ end
125
+
126
+ def test_control_stop
127
+ url = "unix://#{@tmp_path}"
128
+
129
+ cli = Puma::CLI.new ["-b", "unix://#{@tmp_path2}",
130
+ "--control", url,
131
+ "--control-token", "",
132
+ "test/lobster.ru"], @events
133
+
134
+ t = Thread.new { cli.run }
135
+ t.abort_on_exception = true
136
+
137
+ wait_booted
138
+
139
+ s = UNIXSocket.new @tmp_path
140
+ s << "GET /stop HTTP/1.0\r\n\r\n"
141
+ body = s.read
142
+
143
+ assert_equal '{ "status": "ok" }', body.split("\r\n").last
144
+
145
+ t.join
146
+ end
147
+
148
+ def test_tmp_control
149
+ url = "tcp://127.0.0.1:8232"
150
+ cli = Puma::CLI.new ["--state", @tmp_path, "--control", "auto"]
151
+ cli.launcher.write_state
152
+
153
+ data = YAML.load File.read(@tmp_path)
154
+
155
+ assert_equal Process.pid, data["pid"]
156
+
157
+ url = data["control_url"]
158
+
159
+ m = %r!unix://(.*)!.match(url)
160
+
161
+ assert m, "'#{url}' is not a URL"
162
+ end
163
+
164
+ def test_state_file_callback_filtering
165
+ cli = Puma::CLI.new [ "--config", "test/config/state_file_testing_config.rb",
166
+ "--state", @tmp_path ]
167
+ cli.launcher.write_state
168
+
169
+ data = YAML.load_file(@tmp_path)
170
+
171
+ keys_not_stripped = data.keys & Puma::CLI::KEYS_NOT_TO_PERSIST_IN_STATE
172
+ assert_empty keys_not_stripped
173
+ end
174
+
175
+ end # JRUBY or Windows
176
+
177
+ def test_state
178
+ url = "tcp://127.0.0.1:8232"
179
+ cli = Puma::CLI.new ["--state", @tmp_path, "--control", url]
180
+ cli.launcher.write_state
181
+
182
+ data = YAML.load File.read(@tmp_path)
183
+
184
+ assert_equal Process.pid, data["pid"]
185
+ assert_equal url, data["control_url"]
186
+ end
187
+
188
+ def test_load_path
189
+ cli = Puma::CLI.new ["--include", 'foo/bar']
190
+
191
+ assert_equal 'foo/bar', $LOAD_PATH[0]
192
+ $LOAD_PATH.shift
193
+
194
+ cli = Puma::CLI.new ["--include", 'foo/bar:baz/qux']
195
+
196
+ assert_equal 'foo/bar', $LOAD_PATH[0]
197
+ $LOAD_PATH.shift
198
+ assert_equal 'baz/qux', $LOAD_PATH[0]
199
+ $LOAD_PATH.shift
200
+ end
201
+
202
+ def test_environment
203
+ ENV.delete 'RACK_ENV'
204
+
205
+ Puma::CLI.new ["--environment", @environment]
206
+
207
+ assert_equal ENV['RACK_ENV'], @environment
208
+ end
209
+ end
@@ -0,0 +1,95 @@
1
+ require "test_helper"
2
+
3
+ require "puma/configuration"
4
+
5
+ class TestConfigFile < Minitest::Test
6
+ def test_app_from_rackup
7
+ conf = Puma::Configuration.new do |c|
8
+ c.rackup "test/hello-bind.ru"
9
+ end
10
+ conf.load
11
+
12
+ conf.app
13
+
14
+ assert_equal ["tcp://127.0.0.1:9292"], conf.options[:binds]
15
+ end
16
+
17
+ def test_app_from_app_DSL
18
+ conf = Puma::Configuration.new do |c|
19
+ c.load "test/config/app.rb"
20
+ end
21
+ conf.load
22
+
23
+ app = conf.app
24
+
25
+ assert_equal [200, {}, ["embedded app"]], app.call({})
26
+ end
27
+
28
+ def test_double_bind_port
29
+ port = (rand(10_000) + 30_000).to_s
30
+ with_env("PORT" => port) do
31
+ conf = Puma::Configuration.new do |c|
32
+ c.bind "tcp://#{Puma::Configuration::DefaultTCPHost}:#{port}"
33
+ c.load "test/config/app.rb"
34
+ end
35
+
36
+ conf.load
37
+
38
+ assert_equal ["tcp://0.0.0.0:#{port}"], conf.options[:binds]
39
+ end
40
+ end
41
+
42
+ def test_lowleve_error_handler_DSL
43
+ conf = Puma::Configuration.new do |c|
44
+ c.load "test/config/app.rb"
45
+ end
46
+ conf.load
47
+
48
+ app = conf.options[:lowlevel_error_handler]
49
+
50
+ assert_equal [200, {}, ["error page"]], app.call({})
51
+ end
52
+
53
+ def test_allow_users_to_override_default_options
54
+ conf = Puma::Configuration.new(restart_cmd: 'bin/rails server')
55
+
56
+ assert_equal 'bin/rails server', conf.options[:restart_cmd]
57
+ end
58
+
59
+ def test_overwrite_options
60
+ conf = Puma::Configuration.new do |c|
61
+ c.workers 3
62
+ end
63
+ conf.load
64
+
65
+ assert_equal conf.options[:workers], 3
66
+ conf.options[:workers] += 1
67
+ assert_equal conf.options[:workers], 4
68
+ end
69
+
70
+ def test_parameters_overwrite_files
71
+ conf = Puma::Configuration.new(config_files: ['test/config/settings.rb']) do |c|
72
+ c.port 3030
73
+ end
74
+ conf.load
75
+
76
+ assert_match(/:3030$/, conf.options[:binds].first)
77
+ assert_equal 3, conf.options[:min_threads]
78
+ assert_equal 5, conf.options[:max_threads]
79
+ end
80
+
81
+ private
82
+
83
+ def with_env(env = {})
84
+ original_env = {}
85
+ env.each do |k, v|
86
+ original_env[k] = ENV[k]
87
+ ENV[k] = v
88
+ end
89
+ yield
90
+ ensure
91
+ original_env.each do |k, v|
92
+ ENV[k] = v
93
+ end
94
+ end
95
+ end