pax_global_header 0000666 0000000 0000000 00000000064 15114527355 0014522 g ustar 00root root 0000000 0000000 52 comment=f3fa1d6ada90e9e7aa1f712488ddde87ea2a2075 rackup-2.3.1/ 0000775 0000000 0000000 00000000000 15114527355 0013012 5 ustar 00root root 0000000 0000000 rackup-2.3.1/.contributors.yaml 0000664 0000000 0000000 00000031073 15114527355 0016515 0 ustar 00root root 0000000 0000000 - time: 2022-08-04T16:08:33+12:00 author: name: Samuel Williams email: samuel.williams@oriontransfer.co.nz - time: 2022-08-03T15:46:01+12:00 author: name: Samuel Williams email: samuel.williams@oriontransfer.co.nz - time: 2022-07-25T22:23:00-05:00 author: name: Andrew Hoglund email: ahoglund@github.com - time: 2022-05-11T05:25:32+09:00 author: name: Akira Matsuda email: ronnie@dio.jp - time: 2022-05-06T11:45:58-07:00 author: name: Jeremy Evans email: code@jeremyevans.net - time: 2022-04-13T12:32:58-07:00 author: name: Jeremy Evans email: code@jeremyevans.net - time: 2022-04-12T03:33:50+12:00 author: name: Samuel Williams email: samuel.williams@oriontransfer.co.nz - time: 2022-02-23T16:20:48-08:00 author: name: Jeremy Evans email: code@jeremyevans.net - time: 2022-02-03T13:20:12-08:00 author: name: Jeremy Evans email: code@jeremyevans.net - time: 2022-01-24T12:02:03-08:00 author: name: Jeremy Evans email: code@jeremyevans.net - time: 2021-11-05T15:32:51-05:00 author: name: Stephen Paul Weber email: singpolyma@singpolyma.net - time: 2021-10-20T11:02:13+08:00 author: name: Kang Sheng email: Magi-KS@users.noreply.github.com - time: 2021-04-29T10:47:16+09:00 author: name: Katsuhiko Yoshida email: claddvd@gmail.com - time: 2021-04-28T08:51:24+09:00 author: name: Katsuhiko Yoshida email: claddvd@gmail.com - time: 2020-05-25T02:14:31+12:00 author: name: Samuel Williams email: samuel.williams@oriontransfer.co.nz - time: 2020-05-24T22:48:46+12:00 author: name: Samuel Williams email: samuel.williams@oriontransfer.co.nz - time: 2020-01-28T13:27:46-08:00 author: name: Jeremy Evans email: code@jeremyevans.net - time: 2020-01-27T14:30:11-08:00 author: name: Jeremy Evans email: code@jeremyevans.net - time: 2020-01-27T12:43:53-08:00 author: name: Jeremy Evans email: code@jeremyevans.net - time: 2020-01-27T08:22:45-08:00 author: name: Jeremy Evans email: code@jeremyevans.net - time: 2016-10-20T02:21:25+09:00 author: name: Kazuya Hotta email: khotta116@gmail.com - time: 2020-01-23T15:55:47+13:00 author: name: Samuel Williams email: samuel.williams@oriontransfer.co.nz - time: 2020-01-12T09:52:28+13:00 author: name: Samuel Williams email: samuel.williams@oriontransfer.co.nz - time: 2019-10-28T22:26:38-04:00 author: name: Rafael França email: rafael@franca.dev - time: 2019-10-16T14:14:13-04:00 author: name: Rafael França email: rafael@franca.dev - time: 2019-08-13T20:13:39+09:00 author: name: Misaki Shioi email: shioi.mm@gmail.com - time: 2019-05-24T13:20:43+02:00 author: name: Krzysztof Rybka email: krzysztof.rybka@gmail.com - time: 2019-04-17T18:52:13+02:00 author: name: Krzysztof Rybka email: krzysztof.rybka@gmail.com - time: 2019-01-04T16:09:14-05:00 author: name: Rafael França email: rafaelmfranca@gmail.com - time: 2018-12-05T18:40:20-06:00 author: name: Nick LaMuro email: nicklamuro@gmail.com - time: 2018-12-05T18:37:36-06:00 author: name: Nick LaMuro email: nicklamuro@gmail.com - time: 2018-04-17T17:50:18+09:00 author: name: Yoshiyuki Hirano email: yhirano@me.com - time: 2018-04-17T12:46:09+09:00 author: name: Yoshiyuki Hirano email: yhirano@me.com - time: 2018-04-17T02:41:39+09:00 author: name: Yoshiyuki Hirano email: yhirano@me.com - time: 2018-04-13T21:48:52-07:00 author: name: Dillon Welch email: daw0328@gmail.com - time: 2018-04-11T13:16:59-07:00 author: name: Aaron Patterson email: aaron.patterson@gmail.com - time: 2017-06-21T21:15:17+12:00 author: name: Samuel Williams email: samuel.williams@oriontransfer.co.nz - time: 2017-04-17T00:32:39+09:00 author: name: Ryunosuke Sato email: tricknotes.rs@gmail.com - time: 2016-06-14T17:33:09-04:00 author: name: Sophie Deziel email: courrier@sophiedeziel.com - time: 2016-06-14T13:52:52-04:00 author: name: Sophie Deziel email: courrier@sophiedeziel.com - time: 2016-06-09T13:55:31-04:00 author: name: Sophie Deziel email: courrier@sophiedeziel.com - time: 2016-03-02T15:52:16-08:00 author: name: James Tucker email: jftucker@gmail.com - time: 2015-08-30T03:36:29+02:00 author: name: Martin Hrdlicka email: deepjungle.maca@gmail.com - time: 2015-03-27T19:44:18+09:00 author: name: Tadashi Saito email: tadashi_saito@dwango.co.jp - time: 2015-05-25T04:18:16+02:00 author: name: Martin Hrdlicka email: deepjungle.maca@gmail.com - time: 2015-05-26T14:18:00-07:00 author: name: Zachary Scott email: e@zzak.io - time: 2015-02-15T11:14:21+00:00 author: name: Sean McGivern email: sean@mcgivern.me.uk - time: 2015-01-06T10:45:23+00:00 author: name: Peter Wilmott email: p@p8952.info - time: 2014-10-01T18:09:37-05:00 author: name: Richard Schneeman email: richard.schneeman@gmail.com - time: 2014-09-01T07:52:39-07:00 author: name: Jeremy Kemper email: jeremykemper@gmail.com - time: 2014-04-09T10:12:07+04:00 author: name: Igor Bochkariov email: ujifgc@gmail.com - time: 2014-07-18T15:27:36-03:00 author: name: Rafael França email: rafael.franca@plataformatec.com.br - time: 2014-03-27T17:24:21-04:00 author: name: Lenny Marks email: lenny@aps.org - time: 2014-07-13T16:19:51-07:00 author: name: James Tucker email: jftucker@gmail.com - time: 2014-06-27T15:56:50-04:00 author: name: Max Cantor email: max@maxcantor.net - time: 2014-06-27T15:44:16-04:00 author: name: Max Cantor email: max@maxcantor.net - time: 2014-06-27T15:16:58-04:00 author: name: Max Cantor email: max@maxcantor.net - time: 2014-06-30T11:24:58-04:00 author: name: David Celis email: me@davidcel.is - time: 2014-02-27T23:42:01+08:00 author: name: Wyatt Pan email: wppurking@gmail.com - time: 2013-08-05T16:42:34-04:00 author: name: Joe Fiorini email: joe@joefiorini.com - time: 2013-04-22T08:43:11-07:00 author: name: James Tucker email: jftucker@gmail.com - time: 2013-04-21T13:16:20-07:00 author: name: James Tucker email: jftucker@gmail.com - time: 2013-04-12T10:32:13+08:00 author: name: Bas Vodde email: basv@odd-e.com - time: 2013-02-09T21:28:55-08:00 author: name: Hal Brodigan email: postmodern.mod3@gmail.com - time: 2013-01-30T13:45:32+11:00 author: name: Tim Moore email: tmoore@incrementalism.net - time: 2013-01-21T13:24:24-08:00 author: name: James Tucker email: jftucker@gmail.com - time: 2013-01-03T12:00:27+09:00 author: name: Uchio Kondo email: udzura@udzura.jp - time: 2012-12-28T22:50:55+00:00 author: name: Anurag Priyam email: anurag08priyam@gmail.com - time: 2012-12-28T22:46:41+00:00 author: name: Anurag Priyam email: anurag08priyam@gmail.com - time: 2012-05-22T17:00:02+02:00 author: name: Hrvoje Šimić email: shime.ferovac@gmail.com - time: 2012-05-13T10:51:45-07:00 author: name: James Tucker email: jftucker@gmail.com - time: 2012-05-05T12:05:32-04:00 author: name: Jean Boussier email: jean.boussier@gmail.com - time: 2012-03-21T11:31:02+01:00 author: name: Jean Boussier email: jean.boussier@gmail.com - time: 2012-04-26T14:59:59+05:30 author: name: Anurag Priyam email: anurag08priyam@gmail.com - time: 2012-04-10T11:21:30-05:00 author: name: Trevor Wennblom email: trevor@well.com - time: 2011-12-27T14:17:44+09:00 author: name: Tsutomu Kuroda email: t-kuroda@oiax.jp - time: 2011-12-21T19:53:35-04:00 author: name: James Tucker email: jftucker@gmail.com - time: 2011-05-23T19:30:53-07:00 author: name: Blake Mizerany email: blake.mizerany@gmail.com - time: 2011-05-03T01:54:33-07:00 author: name: James Tucker email: jftucker@gmail.com - time: 2011-05-02T22:10:25-07:00 author: name: James Tucker email: jftucker@gmail.com - time: 2011-05-02T21:41:43-07:00 author: name: James Tucker email: jftucker@gmail.com - time: 2011-03-22T00:27:18+01:00 author: name: Konstantin Haase email: konstantin.mailinglists@googlemail.com - time: 2011-03-21T22:00:56+01:00 author: name: Konstantin Haase email: konstantin.mailinglists@googlemail.com - time: 2011-01-14T06:10:08+08:00 author: name: Aaron Patterson email: aaron.patterson@gmail.com - time: 2010-12-01T18:58:36-07:00 author: name: Megan Batty email: megan@stormbrew.ca - time: 2010-09-08T04:40:48+08:00 author: name: Andrew Bortz email: abortz@cs.stanford.edu - time: 2010-09-22T23:55:24+08:00 author: name: John Barnette email: jbarnette@gmail.com - time: 2010-11-05T14:19:12-06:00 author: name: John Sumsion email: sumsionjg@familysearch.org - time: 2010-10-04T13:28:32-03:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-10-04T13:00:25-03:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-06-19T19:03:48-04:00 author: name: Loren Segal email: lsegal@soen.ca - time: 2010-07-08T14:23:39+01:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-07-08T14:23:12+01:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-06-16T11:13:54-03:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-06-16T11:11:12-03:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-06-16T10:57:45-03:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-03-31T19:29:47+08:00 author: name: Timur Batyrshin email: erthad@altlinux.org - time: 2010-06-09T00:29:10+09:00 author: name: Michael Fellinger email: m.fellinger@gmail.com - time: 2010-03-23T19:29:44+00:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-03-23T19:23:43+00:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-03-23T19:22:54+00:00 author: name: James Tucker email: jftucker@gmail.com - time: 2010-02-01T12:21:47+01:00 author: name: Julik Tarkhanov email: me@julik.nl - time: 2009-12-26T18:10:36-06:00 author: name: Joshua Peek email: josh@joshpeek.com - time: 2009-12-26T17:50:26-06:00 author: name: Joshua Peek email: josh@joshpeek.com - time: 2009-12-26T17:25:20-06:00 author: name: Joshua Peek email: josh@joshpeek.com - time: 2009-12-26T16:42:00-06:00 author: name: Joshua Peek email: josh@joshpeek.com - time: 2009-11-23T15:06:30-08:00 author: name: Carl Lerche email: carllerche@mac.com - time: 2009-11-21T10:52:33-08:00 author: name: Yehuda Katz email: ykatz+clerche@engineyard.com - time: 2009-09-05T13:27:50-05:00 author: name: Joshua Peek email: josh@joshpeek.com - time: 2009-07-19T10:23:02+09:00 author: name: Genki Takiuchi email: genki@s21g.com - time: 2009-03-31T12:13:49+09:00 author: name: Michael Fellinger email: m.fellinger@gmail.com - time: 2009-03-18T02:01:29+09:00 author: name: Michael Fellinger email: m.fellinger@gmail.com - time: 2009-02-26T16:51:56-07:00 author: name: Megan Batty email: megan@stormbrew.ca - time: 2009-02-26T16:23:56-07:00 author: name: Megan Batty email: megan@stormbrew.ca - time: 2009-02-26T15:36:57-07:00 author: name: Megan Batty email: megan@stormbrew.ca - time: 2009-03-12T01:35:06+01:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2009-01-24T19:26:43-05:00 author: name: Aaron Pfeifer email: aaron.pfeifer@gmail.com - time: 2008-12-17T10:02:15-05:00 author: name: Marc-André Cournoyer email: macournoyer@gmail.com - time: 2008-09-07T20:20:30+02:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2008-05-24T15:54:00+00:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2008-05-18T15:05:00+00:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2008-05-10T15:10:00+00:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2008-03-20T16:06:00+00:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2007-11-18T19:51:00+00:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2007-03-06T21:12:00+00:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2007-02-26T18:45:00+00:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2007-02-25T15:49:00+00:00 author: name: Leah Neukirchen email: leah@vuxu.org - time: 2007-02-24T18:03:00+00:00 author: name: Leah Neukirchen email: leah@vuxu.org rackup-2.3.1/.github/ 0000775 0000000 0000000 00000000000 15114527355 0014352 5 ustar 00root root 0000000 0000000 rackup-2.3.1/.github/workflows/ 0000775 0000000 0000000 00000000000 15114527355 0016407 5 ustar 00root root 0000000 0000000 rackup-2.3.1/.github/workflows/test-external.yaml 0000664 0000000 0000000 00000001274 15114527355 0022076 0 ustar 00root root 0000000 0000000 name: Test External on: [push, pull_request] permissions: contents: read env: CONSOLE_OUTPUT: XTerm jobs: test: name: ${{matrix.ruby}} on ${{matrix.os}} runs-on: ${{matrix.os}}-latest strategy: matrix: os: - ubuntu ruby: - "3.2" - "3.3" - "3.4" steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{matrix.ruby}} bundler-cache: true - name: Install dependencies timeout-minutes: 5 run: sudo apt-get install -y ragel - name: Run tests timeout-minutes: 10 run: bundle exec bake test:external rackup-2.3.1/.github/workflows/test.yaml 0000664 0000000 0000000 00000002352 15114527355 0020254 0 ustar 00root root 0000000 0000000 name: Test on: [push, pull_request] permissions: contents: read env: CONSOLE_OUTPUT: XTerm jobs: test: name: ${{matrix.ruby}} on ${{matrix.os}} runs-on: ${{matrix.os}}-latest continue-on-error: ${{matrix.experimental}} strategy: matrix: os: - ubuntu - macos ruby: - "2.5" - "2.6" - "2.7" - "3.0" - "3.1" - "3.2" - "3.3" - "3.4" rubygems: - latest experimental: [false] include: - os: ubuntu ruby: truffleruby experimental: true - os: ubuntu ruby: jruby rubygems: default experimental: true - os: ubuntu ruby: head experimental: true exclude: # Not supported on macos-arm64: - os: macos ruby: "2.5" steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: rubygems: ${{matrix.rubygems}} ruby-version: ${{matrix.ruby}} bundler-cache: true - name: Run tests timeout-minutes: 10 run: bundle exec rake test rackup-2.3.1/.gitignore 0000664 0000000 0000000 00000000064 15114527355 0015002 0 ustar 00root root 0000000 0000000 /.bundle/ /pkg/ /gems.locked /.covered.db /external rackup-2.3.1/Rakefile 0000664 0000000 0000000 00000000421 15114527355 0014454 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true require "bundler/gem_tasks" require "rake/testtask" desc "Run all the tests" task default: :test Rake::TestTask.new("test") do |t| t.libs << "test" t.test_files = FileList["test/**/spec_*.rb"] t.warning = false t.verbose = true end rackup-2.3.1/bake.rb 0000664 0000000 0000000 00000000501 15114527355 0014235 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2024, by Samuel Williams. # Update the project documentation with the new version number. # # @parameter version [String] The new version number. def after_gem_release_version_increment(version) context["releases:update"].call(version) end rackup-2.3.1/bin/ 0000775 0000000 0000000 00000000000 15114527355 0013562 5 ustar 00root root 0000000 0000000 rackup-2.3.1/bin/rackup 0000775 0000000 0000000 00000000151 15114527355 0014772 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby # frozen_string_literal: true require_relative "../lib/rackup" Rackup::Server.start rackup-2.3.1/config/ 0000775 0000000 0000000 00000000000 15114527355 0014257 5 ustar 00root root 0000000 0000000 rackup-2.3.1/config/external.yaml 0000664 0000000 0000000 00000000365 15114527355 0016771 0 ustar 00root root 0000000 0000000 rack: url: https://github.com/rack/rack.git command: bundle exec rake test falcon: url: https://github.com/socketry/falcon.git command: bundle exec bake test puma: url: https://github.com/puma/puma.git command: bundle exec rake test rackup-2.3.1/gems.rb 0000664 0000000 0000000 00000001020 15114527355 0014263 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2024, by Samuel Williams. source 'https://rubygems.org' gemspec group :maintenance, optional: true do gem "bake" gem "bake-gem" gem "bake-modernize" if RUBY_VERSION >= "3.1" gem "bake-releases" end end group :doc do gem 'rdoc' end group :test do gem "bake-test" gem "bake-test-external" gem "webrick", "~> 1.8" gem "minitest", "~> 5.0" gem "minitest-global_expectations" gem "minitest-sprint" gem "rake" end rackup-2.3.1/lib/ 0000775 0000000 0000000 00000000000 15114527355 0013560 5 ustar 00root root 0000000 0000000 rackup-2.3.1/lib/rackup.rb 0000664 0000000 0000000 00000001164 15114527355 0015374 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2024, by Samuel Williams. require_relative 'rackup/handler' require_relative 'rackup/server' require_relative 'rackup/version' begin # Although webrick is gone from Ruby since 3.0, it still warns all the way # through to 3.3. Only on 3.4 will requiring it not produce a warning anymore. verbose, $VERBOSE = $VERBOSE, nil require 'webrick' # If the user happens to have webrick in their bundle, make the handler available. require_relative 'rackup/handler/webrick' rescue LoadError # ¯\_(ツ)_/¯ ensure $VERBOSE = verbose end rackup-2.3.1/lib/rackup/ 0000775 0000000 0000000 00000000000 15114527355 0015045 5 ustar 00root root 0000000 0000000 rackup-2.3.1/lib/rackup/handler.rb 0000664 0000000 0000000 00000005473 15114527355 0017020 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. module Rackup # *Handlers* connect web servers with Rack. # # Rackup includes Handlers for WEBrick and CGI. # # Handlers usually are activated by calling MyHandler.run(myapp). # A second optional hash can be passed to include server-specific # configuration. module Handler @handlers = {} # Register a named handler class. def self.register(name, klass) if klass.is_a?(String) warn "Calling Rackup::Handler.register with a string is deprecated, use the class/module itself.", uplevel: 1 klass = self.const_get(klass, false) end name = name.to_sym @handlers[name] = klass end def self.[](name) name = name.to_sym begin @handlers[name] || self.const_get(name, false) rescue NameError # Ignore. end end def self.get(name) return nil unless name name = name.to_sym if server = self[name] return server end begin require_handler("rackup/handler", name) rescue LoadError require_handler("rack/handler", name) end return self[name] end RACK_HANDLER = 'RACK_HANDLER' RACKUP_HANDLER = 'RACKUP_HANDLER' SERVER_NAMES = %i(puma falcon webrick).freeze private_constant :SERVER_NAMES # Select first available Rack handler given an `Array` of server names. # Raises `LoadError` if no handler was found. # # > pick ['puma', 'webrick'] # => Rackup::Handler::WEBrick def self.pick(server_names) server_names = Array(server_names) server_names.each do |server_name| begin server = self.get(server_name) return server if server rescue LoadError # Ignore. end end raise LoadError, "Couldn't find handler for: #{server_names.join(', ')}." end def self.default if rack_handler = ENV[RACKUP_HANDLER] self.get(rack_handler) elsif rack_handler = ENV[RACK_HANDLER] warn "RACK_HANDLER is deprecated, use RACKUP_HANDLER." self.get(rack_handler) else pick SERVER_NAMES end end # Transforms server-name constants to their canonical form as filenames, # then tries to require them but silences the LoadError if not found # # Naming convention: # # Foo # => 'foo' # FooBar # => 'foo_bar.rb' # FooBAR # => 'foobar.rb' # FOObar # => 'foobar.rb' # FOOBAR # => 'foobar.rb' # FooBarBaz # => 'foo_bar_baz.rb' def self.require_handler(prefix, const_name) file = const_name.to_s.gsub(/^[A-Z]+/) { |pre| pre.downcase }. gsub(/[A-Z]+[^A-Z]/, '_\&').downcase require(::File.join(prefix, file)) end end end rackup-2.3.1/lib/rackup/handler/ 0000775 0000000 0000000 00000000000 15114527355 0016462 5 ustar 00root root 0000000 0000000 rackup-2.3.1/lib/rackup/handler/cgi.rb 0000664 0000000 0000000 00000002510 15114527355 0017547 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. module Rackup module Handler class CGI include Rack def self.run(app, **options) $stdin.binmode serve app end def self.serve(app) env = ENV.to_hash env.delete "HTTP_CONTENT_LENGTH" env[SCRIPT_NAME] = "" if env[SCRIPT_NAME] == "/" env.update( RACK_INPUT => $stdin, RACK_ERRORS => $stderr, RACK_URL_SCHEME => ["yes", "on", "1"].include?(ENV[HTTPS]) ? "https" : "http" ) env[QUERY_STRING] ||= "" env[REQUEST_PATH] ||= "/" status, headers, body = app.call(env) begin send_headers status, headers send_body body ensure body.close if body.respond_to? :close end end def self.send_headers(status, headers) $stdout.print "Status: #{status}\r\n" headers.each { |k, vs| vs.split("\n").each { |v| $stdout.print "#{k}: #{v}\r\n" } } $stdout.print "\r\n" $stdout.flush end def self.send_body(body) body.each { |part| $stdout.print part $stdout.flush } end end register :cgi, CGI end end rackup-2.3.1/lib/rackup/handler/webrick.rb 0000664 0000000 0000000 00000013037 15114527355 0020441 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. # Copyright, 2022, by Jeremy Evans. require 'webrick' require 'stringio' require 'rack/constants' require_relative '../handler' require_relative '../version' require_relative '../stream' module Rackup module Handler class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet # A WEBrick HTTPServer subclass that invokes the Rack app directly, # bypassing the mount table and default OPTIONS * handling. class Server < ::WEBrick::HTTPServer def initialize(app, config) super(config) @handler = Handler::WEBrick.new(self, app) end def service(req, res) @handler.service(req, res) end end def self.run(app, **options) environment = ENV['RACK_ENV'] || 'development' default_host = environment == 'development' ? 'localhost' : nil if !options[:BindAddress] || options[:Host] options[:BindAddress] = options.delete(:Host) || default_host end options[:Port] ||= 8080 if options[:SSLEnable] require 'webrick/https' end @server = Server.new(app, options) yield @server if block_given? @server.start end def self.valid_options environment = ENV['RACK_ENV'] || 'development' default_host = environment == 'development' ? 'localhost' : '0.0.0.0' { "Host=HOST" => "Hostname to listen on (default: #{default_host})", "Port=PORT" => "Port to listen on (default: 8080)", } end def self.shutdown if @server @server.shutdown @server = nil end end def initialize(server, app) super server @app = app end # This handles mapping the WEBrick request to a Rack input stream. class Input include Stream::Reader def initialize(request) @request = request @reader = Fiber.new do @request.body do |chunk| Fiber.yield(chunk) end Fiber.yield(nil) # End of stream: @reader = nil end end def close @request = nil @reader = nil end private # Read one chunk from the request body. def read_next @reader&.resume end end def service(req, res) env = req.meta_vars env.delete_if { |k, v| v.nil? } input = Input.new(req) env.update( ::Rack::RACK_INPUT => input, ::Rack::RACK_ERRORS => $stderr, ::Rack::RACK_URL_SCHEME => ["yes", "on", "1"].include?(env[::Rack::HTTPS]) ? "https" : "http", ::Rack::RACK_IS_HIJACK => true, ) env[::Rack::QUERY_STRING] ||= "" # Handle OPTIONS * requests which have no path if req.unparsed_uri == "*" env[::Rack::PATH_INFO] = "*" env[::Rack::REQUEST_PATH] = "*" # Ensure SERVER_NAME and SERVER_PORT are set from server config. # (WEBrick allows these to be nil for OPTIONS * requests) # See https://github.com/ruby/webrick/pull/182 for a proper fix. server_name = env[::Rack::SERVER_NAME] if server_name.nil? || server_name == "" env[::Rack::SERVER_NAME] = @server[:ServerName] || @server[:BindAddress] || "localhost" end # Legacy versions of WEBrick can set server_port to "" in some cases: server_port = env[::Rack::SERVER_PORT] if server_port.nil? || server_port == "" env[::Rack::SERVER_PORT] = (@server[:Port] || 80).to_s end else unless env[::Rack::PATH_INFO] == "" # Strip the script name prefix from the path to get path info script_name_length = env[::Rack::SCRIPT_NAME].length env[::Rack::PATH_INFO] = req.request_uri.path[script_name_length..-1] || "" end env[::Rack::REQUEST_PATH] ||= env[::Rack::SCRIPT_NAME] + env[::Rack::PATH_INFO] end status, headers, body = @app.call(env) begin res.status = status if value = headers[::Rack::RACK_HIJACK] io_lambda = value body = nil elsif !body.respond_to?(:to_path) && !body.respond_to?(:each) io_lambda = body body = nil end if value = headers.delete('set-cookie') res.cookies.concat(Array(value)) end headers.each do |key, value| # Skip keys starting with rack., per Rack SPEC next if key.start_with?('rack.') # Since WEBrick won't accept repeated headers, # merge the values per RFC 1945 section 4.2. value = value.join(", ") if Array === value res[key] = value end if io_lambda protocol = headers['rack.protocol'] || headers['upgrade'] if protocol # Set all the headers correctly for an upgrade response: res.upgrade!(protocol) end res.body = io_lambda elsif body.respond_to?(:to_path) res.body = ::File.open(body.to_path, 'rb') else buffer = String.new body.each do |part| buffer << part end res.body = buffer end ensure body.close if body.respond_to?(:close) end end end register :webrick, WEBrick end end rackup-2.3.1/lib/rackup/lobster.rb 0000664 0000000 0000000 00000004305 15114527355 0017046 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. require 'zlib' require 'rack/constants' require 'rack/request' require 'rack/response' module Rackup # Paste has a Pony, Rack has a Lobster! class Lobster include Rack LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2 P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0 t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0]) LambdaLobster = lambda { |env| if env[QUERY_STRING].include?("flip") lobster = LobsterString.split("\n"). map { |line| line.ljust(42).reverse }. join("\n") href = "?" else lobster = LobsterString href = "?flip" end content = ["
", lobster, "", "flip!"] length = content.inject(0) { |a, e| a + e.size }.to_s [200, { CONTENT_TYPE => "text/html", CONTENT_LENGTH => length }, content] } def call(env) req = Request.new(env) if req.GET["flip"] == "left" lobster = LobsterString.split("\n").map do |line| line.ljust(42).reverse. gsub('\\', 'TEMP'). gsub('/', '\\'). gsub('TEMP', '/'). gsub('{', '}'). gsub('(', ')') end.join("\n") href = "?flip=right" elsif req.GET["flip"] == "crash" raise "Lobster crashed" else lobster = LobsterString href = "?flip=left" end res = Response.new res.write "
"
res.write lobster
res.write ""
res.write ""
res.write ""
res.finish
end
end
end
if $0 == __FILE__
# :nocov:
require_relative 'server'
require_relative 'show_exceptions'
require_relative 'lint'
Rackup::Server.start(
app: Rack::ShowExceptions.new(Rack::Lint.new(Rackup::Lobster.new)), Port: 9292
)
# :nocov:
end
rackup-2.3.1/lib/rackup/server.rb 0000664 0000000 0000000 00000032333 15114527355 0016704 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
require 'optparse'
require 'fileutils'
require 'rack/builder'
require 'rack/common_logger'
require 'rack/content_length'
require 'rack/show_exceptions'
require 'rack/lint'
require 'rack/tempfile_reaper'
require 'rack/version'
require_relative 'version'
require_relative 'handler'
module Rackup
class Server
class Options
def parse!(args)
options = {}
opt_parser = OptionParser.new("", 24, ' ') do |opts|
opts.banner = "Usage: rackup [ruby options] [rack options] [rackup config]"
opts.separator ""
opts.separator "Ruby options:"
lineno = 1
opts.on("-e", "--eval LINE", "evaluate a LINE of code") { |line|
eval line, TOPLEVEL_BINDING, "-e", lineno
lineno += 1
}
opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
options[:debug] = true
}
opts.on("-w", "--warn", "turn warnings on for your script") {
options[:warn] = true
}
opts.on("-q", "--quiet", "turn off logging") {
options[:quiet] = true
}
opts.on("-I", "--include PATH",
"specify $LOAD_PATH (may be used more than once)") { |path|
(options[:include] ||= []).concat(path.split(":"))
}
opts.on("-r", "--require LIBRARY",
"require the library, before executing your script") { |library|
(options[:require] ||= []) << library
}
opts.separator ""
opts.separator "Rack options:"
opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
options[:builder] = line
}
opts.on("-s", "--server SERVER", "serve using SERVER (thin/puma/webrick)") { |s|
options[:server] = s
}
opts.on("-o", "--host HOST", "listen on HOST (default: localhost)") { |host|
options[:Host] = host
}
opts.on("-p", "--port PORT", "use PORT (default: 9292)") { |port|
options[:Port] = port
}
opts.on("-O", "--option NAME[=VALUE]", "pass VALUE to the server as option NAME. If no VALUE, sets it to true. Run '#{$0} -s SERVER -h' to get a list of options for SERVER") { |name|
name, value = name.split('=', 2)
value = true if value.nil?
options[name.to_sym] = value
}
opts.on("-E", "--env ENVIRONMENT", "use ENVIRONMENT for defaults (default: development)") { |e|
options[:environment] = e
}
opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
options[:daemonize] ||= true
}
opts.on("--daemonize-noclose", "run daemonized in the background without closing stdout/stderr") {
options[:daemonize] = :noclose
}
opts.on("-P", "--pid FILE", "file to store PID") { |f|
options[:pid] = ::File.expand_path(f)
}
opts.separator ""
opts.separator "Profiling options:"
opts.on("--heap HEAPFILE", "Build the application, then dump the heap to HEAPFILE") do |e|
options[:heapfile] = e
end
opts.on("--profile PROFILE", "Dump CPU or Memory profile to PROFILE (defaults to a tempfile)") do |e|
options[:profile_file] = e
end
opts.on("--profile-mode MODE", "Profile mode (cpu|wall|object)") do |e|
unless %w[cpu wall object].include?(e)
raise OptionParser::InvalidOption, "unknown profile mode: #{e}"
end
options[:profile_mode] = e.to_sym
end
opts.separator ""
opts.separator "Common options:"
opts.on_tail("-h", "-?", "--help", "Show this message") do
puts opts
puts handler_opts(options)
exit
end
opts.on_tail("--version", "Show version") do
puts "Rack #{Rack::RELEASE}"
exit
end
end
begin
opt_parser.parse! args
rescue OptionParser::InvalidOption => e
warn e.message
abort opt_parser.to_s
end
options[:config] = args.last if args.last && !args.last.empty?
options
end
def handler_opts(options)
info = []
server = Rackup::Handler.get(options[:server]) || Rackup::Handler.default
if server && server.respond_to?(:valid_options)
info << ""
info << "Server-specific options for #{server.name}:"
has_options = false
server.valid_options.each do |name, description|
next if /^(Host|Port)[^a-zA-Z]/.match?(name.to_s) # ignore handler's host and port options, we do our own.
info << sprintf(" -O %-21s %s", name, description)
has_options = true
end
return "" if !has_options
end
info.join("\n")
rescue NameError, LoadError
return "Warning: Could not find handler specified (#{options[:server] || 'default'}) to determine handler-specific options"
end
end
# Start a new rack server (like running rackup). This will parse ARGV and
# provide standard ARGV rackup options, defaulting to load 'config.ru'.
#
# Providing an options hash will prevent ARGV parsing and will not include
# any default options.
#
# This method can be used to very easily launch a CGI application, for
# example:
#
# Rackup::Server.start(
# :app => lambda do |e|
# [200, {'content-type' => 'text/html'}, ['hello world']]
# end,
# :server => 'cgi'
# )
#
# Further options available here are documented on Rackup::Server#initialize
def self.start(options = nil)
new(options).start
end
attr_writer :options
# Options may include:
# * :app
# a rack application to run (overrides :config and :builder)
# * :builder
# a string to evaluate a Rack::Builder from
# * :config
# a rackup configuration file path to load (.ru)
# * :environment
# this selects the middleware that will be wrapped around
# your application. Default options available are:
# - development: CommonLogger, ShowExceptions, and Lint
# - deployment: CommonLogger
# - none: no extra middleware
# note: when the server is a cgi server, CommonLogger is not included.
# * :server
# choose a specific Rackup::Handler, e.g. cgi, fcgi, webrick
# * :daemonize
# if truthy, the server will daemonize itself (fork, detach, etc)
# if :noclose, the server will not close STDOUT/STDERR
# * :pid
# path to write a pid file after daemonize
# * :Host
# the host address to bind to (used by supporting Rackup::Handler)
# * :Port
# the port to bind to (used by supporting Rackup::Handler)
# * :AccessLog
# webrick access log options (or supporting Rackup::Handler)
# * :debug
# turn on debug output ($DEBUG = true)
# * :warn
# turn on warnings ($-w = true)
# * :include
# add given paths to $LOAD_PATH
# * :require
# require the given libraries
#
# Additional options for profiling app initialization include:
# * :heapfile
# location for ObjectSpace.dump_all to write the output to
# * :profile_file
# location for CPU/Memory (StackProf) profile output (defaults to a tempfile)
# * :profile_mode
# StackProf profile mode (cpu|wall|object)
def initialize(options = nil)
@ignore_options = []
if options
@use_default_options = false
@options = options
@app = options[:app] if options[:app]
else
@use_default_options = true
@options = parse_options(ARGV)
end
end
def options
merged_options = @use_default_options ? default_options.merge(@options) : @options
merged_options.reject { |k, v| @ignore_options.include?(k) }
end
def default_options
environment = ENV['RACK_ENV'] || 'development'
default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
{
environment: environment,
pid: nil,
Port: 9292,
Host: default_host,
AccessLog: [],
config: "config.ru"
}
end
def app
@app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
end
class << self
def logging_middleware
lambda { |server|
/CGI/.match?(server.server.name) || server.options[:quiet] ? nil : [Rack::CommonLogger, $stderr]
}
end
def default_middleware_by_environment
m = Hash.new {|h, k| h[k] = []}
m["deployment"] = [
[Rack::ContentLength],
logging_middleware,
[Rack::TempfileReaper]
]
m["development"] = [
[Rack::ContentLength],
logging_middleware,
[Rack::ShowExceptions],
[Rack::Lint],
[Rack::TempfileReaper]
]
m
end
def middleware
default_middleware_by_environment
end
end
def middleware
self.class.middleware
end
def start(&block)
if options[:warn]
$-w = true
end
if includes = options[:include]
$LOAD_PATH.unshift(*includes)
end
Array(options[:require]).each do |library|
require library
end
if options[:debug]
$DEBUG = true
require 'pp'
p options[:server]
pp wrapped_app
pp app
end
check_pid! if options[:pid]
# Touch the wrapped app, so that the config.ru is loaded before
# daemonization (i.e. before chdir, etc).
handle_profiling(options[:heapfile], options[:profile_mode], options[:profile_file]) do
wrapped_app
end
daemonize_app if options[:daemonize]
write_pid if options[:pid]
trap(:INT) do
if server.respond_to?(:shutdown)
server.shutdown
else
exit
end
end
server.run(wrapped_app, **options, &block)
end
def server
@_server ||= Handler.get(options[:server]) || Handler.default
end
private
def build_app_and_options_from_config
if !::File.exist? options[:config]
abort "configuration #{options[:config]} not found"
end
return Rack::Builder.parse_file(self.options[:config])
end
def handle_profiling(heapfile, profile_mode, filename)
if heapfile
require "objspace"
ObjectSpace.trace_object_allocations_start
yield
GC.start
::File.open(heapfile, "w") { |f| ObjectSpace.dump_all(output: f) }
exit
end
if profile_mode
require "stackprof"
require "tempfile"
make_profile_name(filename) do |filename|
::File.open(filename, "w") do |f|
StackProf.run(mode: profile_mode, out: f) do
yield
end
puts "Profile written to: #{filename}"
end
end
exit
end
yield
end
def make_profile_name(filename)
if filename
yield filename
else
::Dir::Tmpname.create("profile.dump") do |tmpname, _, _|
yield tmpname
end
end
end
def build_app_from_string
Rack::Builder.new_from_string(self.options[:builder])
end
def parse_options(args)
# Don't evaluate CGI ISINDEX parameters.
args.clear if ENV.include?(Rack::REQUEST_METHOD)
@options = opt_parser.parse!(args)
@options[:config] = ::File.expand_path(options[:config])
ENV["RACK_ENV"] = options[:environment]
@options
end
def opt_parser
Options.new
end
def build_app(app)
middleware[options[:environment]].reverse_each do |middleware|
middleware = middleware.call(self) if middleware.respond_to?(:call)
next unless middleware
klass, *args = middleware
app = klass.new(app, *args)
end
app
end
def wrapped_app
@wrapped_app ||= build_app app
end
def daemonize_app
# Cannot be covered as it forks
# :nocov:
Process.daemon(true, options[:daemonize] == :noclose)
# :nocov:
end
def write_pid
::File.open(options[:pid], ::File::CREAT | ::File::EXCL | ::File::WRONLY ){ |f| f.write("#{Process.pid}") }
at_exit { ::FileUtils.rm_f(options[:pid]) }
rescue Errno::EEXIST
check_pid!
retry
end
def check_pid!
return unless ::File.exist?(options[:pid])
pid = ::File.read(options[:pid]).to_i
raise Errno::ESRCH if pid == 0
Process.kill(0, pid)
exit_with_pid(pid)
rescue Errno::ESRCH
::File.delete(options[:pid])
rescue Errno::EPERM
exit_with_pid(pid)
end
def exit_with_pid(pid)
$stderr.puts "A server is already running (pid: #{pid}, file: #{options[:pid]})."
exit(1)
end
end
end
rackup-2.3.1/lib/rackup/stream.rb 0000664 0000000 0000000 00000012212 15114527355 0016663 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2023-2024, by Samuel Williams.
module Rackup
# The input stream is an IO-like object which contains the raw HTTP POST data. When applicable, its external encoding must be “ASCII-8BIT” and it must be opened in binary mode, for Ruby 1.9 compatibility. The input stream must respond to gets, each, read and rewind.
class Stream
def initialize(input = nil, output = Buffered.new)
@input = input
@output = output
raise ArgumentError, "Non-writable output!" unless output.respond_to?(:write)
# Will hold remaining data in `#read`.
@buffer = nil
@closed = false
end
attr :input
attr :output
# This provides a read-only interface for data, which is surprisingly tricky to implement correctly.
module Reader
# rack.hijack_io must respond to:
# read, write, read_nonblock, write_nonblock, flush, close, close_read, close_write, closed?
# read behaves like IO#read. Its signature is read([length, [buffer]]). If given, length must be a non-negative Integer (>= 0) or nil, and buffer must be a String and may not be nil. If length is given and not nil, then this method reads at most length bytes from the input stream. If length is not given or nil, then this method reads all data until EOF. When EOF is reached, this method returns nil if length is given and not nil, or “” if length is not given or is nil. If buffer is given, then the read data will be placed into buffer instead of a newly created String object.
# @param length [Integer] the amount of data to read
# @param buffer [String] the buffer which will receive the data
# @return a buffer containing the data
def read(length = nil, buffer = nil)
return '' if length == 0
buffer ||= String.new.force_encoding(Encoding::BINARY)
# Take any previously buffered data and replace it into the given buffer.
if @buffer
buffer.replace(@buffer)
@buffer = nil
else
buffer.clear
end
if length
while buffer.bytesize < length and chunk = read_next
buffer << chunk
end
# This ensures the subsequent `slice!` works correctly.
buffer.force_encoding(Encoding::BINARY)
# This will be at least one copy:
@buffer = buffer.byteslice(length, buffer.bytesize)
# This should be zero-copy:
buffer.slice!(length, buffer.bytesize)
if buffer.empty?
return nil
else
return buffer
end
else
while chunk = read_next
buffer << chunk
end
return buffer
end
end
# Read at most `length` bytes from the stream. Will avoid reading from the underlying stream if possible.
def read_partial(length = nil)
if @buffer
buffer = @buffer
@buffer = nil
else
buffer = read_next
end
if buffer and length
if buffer.bytesize > length
# This ensures the subsequent `slice!` works correctly.
buffer.force_encoding(Encoding::BINARY)
@buffer = buffer.byteslice(length, buffer.bytesize)
buffer.slice!(length, buffer.bytesize)
end
end
return buffer
end
def gets
read_partial
end
def each
while chunk = read_partial
yield chunk
end
end
def read_nonblock(length, buffer = nil)
@buffer ||= read_next
chunk = nil
unless @buffer
buffer&.clear
return
end
if @buffer.bytesize > length
chunk = @buffer.byteslice(0, length)
@buffer = @buffer.byteslice(length, @buffer.bytesize)
else
chunk = @buffer
@buffer = nil
end
if buffer
buffer.replace(chunk)
else
buffer = chunk
end
return buffer
end
end
include Reader
def write(buffer)
if @output
@output.write(buffer)
return buffer.bytesize
else
raise IOError, "Stream is not writable, output has been closed!"
end
end
def write_nonblock(buffer)
write(buffer)
end
def <<(buffer)
write(buffer)
end
def flush
end
def close_read
@input&.close
@input = nil
end
# close must never be called on the input stream. huh?
def close_write
if @output.respond_to?(:close)
@output&.close
end
@output = nil
end
# Close the input and output bodies.
def close(error = nil)
self.close_read
self.close_write
return nil
ensure
@closed = true
end
# Whether the stream has been closed.
def closed?
@closed
end
# Whether there are any output chunks remaining?
def empty?
@output.empty?
end
private
def read_next
if @input
return @input.read
else
@input = nil
raise IOError, "Stream is not readable, input has been closed!"
end
end
end
end
rackup-2.3.1/lib/rackup/version.rb 0000664 0000000 0000000 00000000224 15114527355 0017055 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
module Rackup
VERSION = "2.3.1"
end
rackup-2.3.1/license.md 0000664 0000000 0000000 00000006364 15114527355 0014767 0 ustar 00root root 0000000 0000000 # MIT License
Copyright, 2007-2009, by Leah Neukirchen.
Copyright, 2008, by Marc-André Cournoyer.
Copyright, 2009, by Aaron Pfeifer.
Copyright, 2009-2010, by Megan Batty.
Copyright, 2009-2010, by Michael Fellinger.
Copyright, 2009, by Genki Takiuchi.
Copyright, 2009, by Joshua Peek.
Copyright, 2009, by Yehuda Katz.
Copyright, 2009, by Carl Lerche.
Copyright, 2010, by Julik Tarkhanov.
Copyright, 2010-2016, by James Tucker.
Copyright, 2010, by Timur Batyrshin.
Copyright, 2010, by Loren Segal.
Copyright, 2010, by Andrew Bortz.
Copyright, 2010, by John Barnette.
Copyright, 2010, by John Sumsion.
Copyright, 2011-2018, by Aaron Patterson.
Copyright, 2011, by Konstantin Haase.
Copyright, 2011, by Blake Mizerany.
Copyright, 2011, by Tsutomu Kuroda.
Copyright, 2012, by Jean Boussier.
Copyright, 2012, by Trevor Wennblom.
Copyright, 2012, by Anurag Priyam.
Copyright, 2012, by Hrvoje Šimić.
Copyright, 2013, by Uchio Kondo.
Copyright, 2013, by Tim Moore.
Copyright, 2013, by Hal Brodigan.
Copyright, 2013, by Bas Vodde.
Copyright, 2013, by Joe Fiorini.
Copyright, 2014, by Wyatt Pan.
Copyright, 2014, by Lenny Marks.
Copyright, 2014, by Igor Bochkariov.
Copyright, 2014, by Max Cantor.
Copyright, 2014, by David Celis.
Copyright, 2014-2019, by Rafael França.
Copyright, 2014, by Jeremy Kemper.
Copyright, 2014, by Richard Schneeman.
Copyright, 2015, by Peter Wilmott.
Copyright, 2015, by Sean McGivern.
Copyright, 2015, by Tadashi Saito.
Copyright, 2015, by Martin Hrdlicka.
Copyright, 2015, by Zachary Scott.
Copyright, 2016, by Sophie Deziel.
Copyright, 2016, by Kazuya Hotta.
Copyright, 2017, by Ryunosuke Sato.
Copyright, 2017-2024, by Samuel Williams.
Copyright, 2018, by Dillon Welch.
Copyright, 2018, by Yoshiyuki Hirano.
Copyright, 2018, by Nick LaMuro.
Copyright, 2019, by Krzysztof Rybka.
Copyright, 2019, by Misaki Shioi.
Copyright, 2020-2022, by Jeremy Evans.
Copyright, 2021, by Katsuhiko Yoshida.
Copyright, 2021, by Kang Sheng.
Copyright, 2021, by Stephen Paul Weber.
Copyright, 2022, by Akira Matsuda.
Copyright, 2022, by Andrew Hoglund.
Copyright, 2024, by Olle Jonsson.
Copyright, 2024, by Geremia Taglialatela.
Copyright, 2024, by Petrik de Heus.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
rackup-2.3.1/rackup.gemspec 0000664 0000000 0000000 00000003261 15114527355 0015646 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
require_relative "lib/rackup/version"
Gem::Specification.new do |spec|
spec.name = "rackup"
spec.version = Rackup::VERSION
spec.summary = "A general server command for Rack applications."
spec.authors = ["Samuel Williams", "James Tucker", "Leah Neukirchen", "Jeremy Evans", "Joshua Peek", "Megan Batty", "Rafael França", "Anurag Priyam", "Max Cantor", "Michael Fellinger", "Sophie Deziel", "Yoshiyuki Hirano", "Aaron Patterson", "Jean Boussier", "Katsuhiko Yoshida", "Konstantin Haase", "Krzysztof Rybka", "Martin Hrdlicka", "Nick LaMuro", "Aaron Pfeifer", "Akira Matsuda", "Andrew Bortz", "Andrew Hoglund", "Bas Vodde", "Blake Mizerany", "Carl Lerche", "David Celis", "Dillon Welch", "Genki Takiuchi", "Geremia Taglialatela", "Hal Brodigan", "Hrvoje Šimić", "Igor Bochkariov", "Jeremy Kemper", "Joe Fiorini", "John Barnette", "John Sumsion", "Julik Tarkhanov", "Kang Sheng", "Kazuya Hotta", "Lenny Marks", "Loren Segal", "Marc-André Cournoyer", "Misaki Shioi", "Olle Jonsson", "Peter Wilmott", "Petrik de Heus", "Richard Schneeman", "Ryunosuke Sato", "Sean McGivern", "Stephen Paul Weber", "Tadashi Saito", "Tim Moore", "Timur Batyrshin", "Trevor Wennblom", "Tsutomu Kuroda", "Uchio Kondo", "Wyatt Pan", "Yehuda Katz", "Zachary Scott"]
spec.license = "MIT"
spec.homepage = "https://github.com/rack/rackup"
spec.metadata = {
"changelog_uri" => spec.homepage + "/blob/main/releases.md",
"rubygems_mfa_required" => "true",
"source_code_uri" => "https://github.com/rack/rackup.git",
}
spec.files = Dir["{bin,lib}/**/*", "*.md"]
spec.executables = ["rackup"]
spec.required_ruby_version = ">= 2.5"
spec.add_dependency "rack", ">= 3"
end
rackup-2.3.1/readme.md 0000664 0000000 0000000 00000006200 15114527355 0014567 0 ustar 00root root 0000000 0000000 # Rackup
`rackup` provides a command line interface for running a Rack-compatible application. It also provides a generic interface for starting a `rack`-compatible server: `Rackup::Handler`. It is not designed for production use.
[](https://github.com/rack/rackup/actions?workflow=Test)
## Installation
``` bash
-- For Puma
$ gem install rackup puma
-- For Falcon
$ gem install rackup falcon
```
## Usage
In a directory with your `config.ru` simply run the command:
``` bash
$ rackup
```
Your application should now be available locally, typically `http://localhost:9292`.
### Server Handler
You can also use `Rackup::Handler` to start a server programmatically:
``` ruby
require 'rackup'
# Use the default server:
handler = Rackup::Handler.default
handler.run(app, **options)
# Use a specific server:
handler = Rackup::Handler.get('puma')
handler.run(app, **options)
```
Do not require specific handlers or assume they will exist/work. Instead, use the `default` method to get the best available handler.
## (Soft) Deprecation
For a long time, `rackup` (the executable and implementation) was part of `rack`, and `webrick` was the default server, included with Ruby. It made it easy to run a Rack application without having to worry about the details of the server - great for documentation and demos.
When `webrick` was removed from the Ruby standard library, `rack` started depending on `webrick` as a default server. Every web application and server would pull in `webrick` as a dependency, even if it was not used. To avoid this, the `rackup` component of `rack` was moved to this gem, which depended on `webrick`.
However, many libraries (e.g. `rails`) still depend on `rackup` and end up pulling in `webrick` as a dependency. To avoid this, the decision was made to cut `webrick` as a dependency of `rackup`. This means that `rackup` no longer depends on `webrick`, and you need to install it separately if you want to use it.
As a consequence of this, the value of the `rackup` gem is further diminished. In other words, why would you do this:
``` bash
$ gem install rackup puma
$ rackup ...
```
when you can do this:
``` bash
$ gem install puma
$ puma ...
```
In summary, the maintainers of `rack` recommend the following:
- Libraries should not depend on `rackup` if possible. `rackup` as an executable made sense when webrick shipped with Ruby, so there was always a fallback. But that hasn't been true since Ruby 3.0.
- Frameworks and applications should focus on providing `config.ru` files, so that users can use the webserver program of their choice directly (e.g. puma, falcon).
- There is still some value in the generic `rackup` and `Rackup::Handler` interfaces, but we encourage users to invoke the server command directly if possible.
- Webrick should be avoided if possible.
## Contributing
We welcome contributions to this project.
1. Fork it.
2. Create your feature branch (`git checkout -b my-new-feature`).
3. Commit your changes (`git commit -am 'Add some feature'`).
4. Push to the branch (`git push origin my-new-feature`).
5. Create new Pull Request.
rackup-2.3.1/releases.md 0000664 0000000 0000000 00000001247 15114527355 0015143 0 ustar 00root root 0000000 0000000 # Releases
All notable changes to this project will be documented in this file. For info on how to format all future additions to this file please reference [Keep A Changelog](https://keepachangelog.com/en/1.0.0/).
## v2.2.1
- Try to require `webrick` and `rackup/handler/webrick` by default, for compatibility with code that expects them to be available.
## v2.2.0
- Remove old rack shims.
- Remove `webrick` dependency.
## v2.1.0
- Correctly support streaming responses with `webrick`.
## v2.0.0
- Initial release and migration of code from `rack`.
## v1.0.1
- Fix `rackup.rb` invalid requires.
## v1.0.0
- Initial release of empty shim for Rack v2.
rackup-2.3.1/security.md 0000664 0000000 0000000 00000000145 15114527355 0015203 0 ustar 00root root 0000000 0000000 # Security Policy
Please see our main security policy: https://github.com/rack/rack/security/policy
rackup-2.3.1/test/ 0000775 0000000 0000000 00000000000 15114527355 0013771 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/builder/ 0000775 0000000 0000000 00000000000 15114527355 0015417 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/builder/line.ru 0000664 0000000 0000000 00000000156 15114527355 0016720 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
run lambda{ |env| [200, { 'content-type' => 'text/plain' }, [__LINE__.to_s]] }
rackup-2.3.1/test/cgi/ 0000775 0000000 0000000 00000000000 15114527355 0014533 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/cgi/assets/ 0000775 0000000 0000000 00000000000 15114527355 0016035 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/cgi/assets/folder/ 0000775 0000000 0000000 00000000000 15114527355 0017310 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/cgi/assets/folder/test.js 0000664 0000000 0000000 00000000021 15114527355 0020616 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.3.1/test/cgi/assets/fonts/ 0000775 0000000 0000000 00000000000 15114527355 0017166 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/cgi/assets/fonts/font.eot 0000664 0000000 0000000 00000000021 15114527355 0020636 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.3.1/test/cgi/assets/images/ 0000775 0000000 0000000 00000000000 15114527355 0017302 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/cgi/assets/images/image.png 0000664 0000000 0000000 00000000021 15114527355 0021063 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.3.1/test/cgi/assets/index.html 0000664 0000000 0000000 00000000021 15114527355 0020023 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.3.1/test/cgi/assets/javascripts/ 0000775 0000000 0000000 00000000000 15114527355 0020366 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/cgi/assets/javascripts/app.js 0000664 0000000 0000000 00000000021 15114527355 0021475 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.3.1/test/cgi/assets/stylesheets/ 0000775 0000000 0000000 00000000000 15114527355 0020411 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/cgi/assets/stylesheets/app.css 0000664 0000000 0000000 00000000021 15114527355 0021674 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.3.1/test/cgi/rackup_stub.rb 0000775 0000000 0000000 00000000273 15114527355 0017407 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby
# frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
$:.unshift '../../lib'
require 'rack'
Rack::Server.start
rackup-2.3.1/test/cgi/sample_rackup.ru 0000775 0000000 0000000 00000000136 15114527355 0017734 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
require '../test_request'
run Rack::Lint.new(TestRequest.new)
rackup-2.3.1/test/cgi/test 0000775 0000000 0000000 00000000321 15114527355 0015434 0 ustar 00root root 0000000 0000000 ***** DO NOT MODIFY THIS FILE! *****
If you modify this file, tests will break!!!
The quick brown fox jumps over the ruby dog.
The quick brown fox jumps over the lazy dog.
***** DO NOT MODIFY THIS FILE! *****
rackup-2.3.1/test/cgi/test+directory/ 0000775 0000000 0000000 00000000000 15114527355 0017512 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/cgi/test+directory/test+file 0000664 0000000 0000000 00000000027 15114527355 0021326 0 ustar 00root root 0000000 0000000 this file has plusses!
rackup-2.3.1/test/cgi/test.gz 0000664 0000000 0000000 00000000273 15114527355 0016056 0 ustar 00root root 0000000 0000000 dZ X~=@l9q5 EA0ut84EV_ou${tQ KSNN֡4,eyDʿ׀LrkXV2:M}`dwKG.r\mhpƬ@,7H^<}4sJ7tH rackup-2.3.1/test/cgi/test.ru 0000775 0000000 0000000 00000000160 15114527355 0016062 0 ustar 00root root 0000000 0000000 #!../../bin/rackup
# frozen_string_literal: true
require '../test_request'
run Rack::Lint.new(TestRequest.new)
rackup-2.3.1/test/helper.rb 0000664 0000000 0000000 00000001247 15114527355 0015601 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
if ENV.delete('COVERAGE')
require 'coverage'
require 'simplecov'
def SimpleCov.rack_coverage(**opts)
start do
add_filter "/test/"
add_group('Missing'){|src| src.covered_percent < 100}
add_group('Covered'){|src| src.covered_percent == 100}
end
end
SimpleCov.rack_coverage
end
$:.unshift(File.expand_path('../lib', __dir__))
if ENV['SEPARATE']
def self.separate_testing
yield
end
else
require_relative '../lib/rackup'
def self.separate_testing
end
end
require 'minitest/global_expectations/autorun'
require 'stringio'
rackup-2.3.1/test/load/ 0000775 0000000 0000000 00000000000 15114527355 0014710 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/load/rack-test-a.rb 0000664 0000000 0000000 00000000156 15114527355 0017352 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
rackup-2.3.1/test/load/rack-test-b.rb 0000664 0000000 0000000 00000000156 15114527355 0017353 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
rackup-2.3.1/test/psych_fix.rb 0000664 0000000 0000000 00000000312 15114527355 0016306 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
unless YAML.respond_to?(:unsafe_load)
def YAML.unsafe_load(body)
load(body)
end
end
rackup-2.3.1/test/registering_handler/ 0000775 0000000 0000000 00000000000 15114527355 0020010 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/registering_handler/rack/ 0000775 0000000 0000000 00000000000 15114527355 0020730 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/registering_handler/rack/handler/ 0000775 0000000 0000000 00000000000 15114527355 0022345 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/registering_handler/rack/handler/registering_myself.rb 0000664 0000000 0000000 00000000360 15114527355 0026572 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
module Rackup
module Handler
class RegisteringMyself
end
register :registering_myself, RegisteringMyself
end
end
rackup-2.3.1/test/spec_handler.rb 0000664 0000000 0000000 00000003504 15114527355 0016747 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
require_relative 'helper'
separate_testing do
require_relative '../lib/rackup/handler'
end
class Rackup::Handler::Lobster; end
class RockLobster; end
describe Rackup::Handler do
it "has registered default handlers" do
Rackup::Handler.get('cgi').must_equal Rackup::Handler::CGI
Rackup::Handler.get('webrick').must_equal Rackup::Handler::WEBrick
end
it "raise LoadError if handler doesn't exist" do
lambda {
Rackup::Handler.get('boom')
}.must_raise(LoadError)
lambda {
Rackup::Handler.get('Object')
}.must_raise(LoadError)
end
it "get unregistered, but already required, handler by name" do
Rackup::Handler.get('Lobster').must_equal Rackup::Handler::Lobster
end
it "register custom handler" do
Rackup::Handler.register('rock_lobster', RockLobster)
Rackup::Handler.get('rock_lobster').must_equal RockLobster
end
it "not need registration for properly coded handlers even if not already required" do
begin
$LOAD_PATH.push File.expand_path('../unregistered_handler', __FILE__)
Rackup::Handler.get('Unregistered').must_equal Rackup::Handler::Unregistered
lambda { Rackup::Handler.get('UnRegistered') }.must_raise LoadError
Rackup::Handler.get('UnregisteredLongOne').must_equal Rackup::Handler::UnregisteredLongOne
ensure
$LOAD_PATH.delete File.expand_path('../unregistered_handler', __FILE__)
end
end
it "allow autoloaded handlers to be registered properly while being loaded" do
path = File.expand_path('../registering_handler', __FILE__)
begin
$LOAD_PATH.push path
Rackup::Handler.get('registering_myself').must_equal Rackup::Handler::RegisteringMyself
ensure
$LOAD_PATH.delete path
end
end
end
rackup-2.3.1/test/spec_lobster.rb 0000664 0000000 0000000 00000002575 15114527355 0017013 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
require_relative 'helper'
require_relative '../lib/rackup/lobster'
require 'rack/lint'
require 'rack/mock_request'
module LobsterHelpers
def lobster
Rack::MockRequest.new Rack::Lint.new(Rackup::Lobster.new)
end
def lambda_lobster
Rack::MockRequest.new Rack::Lint.new(Rackup::Lobster::LambdaLobster)
end
end
describe Rackup::Lobster::LambdaLobster do
include LobsterHelpers
it "be a single lambda" do
Rackup::Lobster::LambdaLobster.must_be_kind_of Proc
end
it "look like a lobster" do
res = lambda_lobster.get("/")
res.must_be :ok?
res.body.must_include "(,(,,(,,,("
res.body.must_include "?flip"
end
it "be flippable" do
res = lambda_lobster.get("/?flip")
res.must_be :ok?
res.body.must_include "(,,,(,,(,("
end
end
describe Rackup::Lobster do
include LobsterHelpers
it "look like a lobster" do
res = lobster.get("/")
res.must_be :ok?
res.body.must_include "(,(,,(,,,("
res.body.must_include "?flip"
res.body.must_include "crash"
end
it "be flippable" do
res = lobster.get("/?flip=left")
res.must_be :ok?
res.body.must_include "),,,),,),)"
end
it "provide crashing for testing purposes" do
lambda {
lobster.get("/?flip=crash")
}.must_raise RuntimeError
end
end
rackup-2.3.1/test/spec_server.rb 0000664 0000000 0000000 00000047400 15114527355 0016643 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
require_relative 'helper'
require 'tempfile'
require 'socket'
require 'webrick'
require 'open-uri'
require 'net/http'
require 'net/https'
begin
require 'stackprof'
require 'tmpdir'
rescue LoadError
else
test_profile = true
end
separate_testing do
require_relative '../lib/rack/server'
require_relative '../lib/rack/lint'
require_relative '../lib/rack/mock_request'
require_relative '../lib/rack/show_exceptions'
require_relative '../lib/rack/tempfile_reaper'
require_relative '../lib/rack/handler'
require_relative '../lib/rack/handler/cgi'
end
describe Rackup::Server do
argv = Rackup::Server::ARGV = []
define_method(:argv) { argv }
before { argv.clear }
def app
lambda { |env| [200, { 'content-type' => 'text/plain' }, ['success']] }
end
def with_stderr
old, $stderr = $stderr, StringIO.new
yield $stderr
ensure
$stderr = old
end
it "overrides :config if :app is passed in" do
server = Rackup::Server.new(app: "FOO")
server.app.must_equal "FOO"
end
it "Options#parse parses -p and --port options into :Port" do
Rackup::Server::Options.new.parse!(%w[-p 1234]).must_equal :Port => '1234'
Rackup::Server::Options.new.parse!(%w[--port 1234]).must_equal :Port => '1234'
end
it "Options#parse parses -D and --daemonize option into :daemonize" do
Rackup::Server::Options.new.parse!(%w[-D]).must_equal :daemonize => true
Rackup::Server::Options.new.parse!(%w[--daemonize]).must_equal :daemonize => true
end
it "Options#parse parses --daemonize-noclose option into :daemonize => :noclose" do
Rackup::Server::Options.new.parse!(%w[--daemonize-noclose]).must_equal :daemonize => :noclose
Rackup::Server::Options.new.parse!(%w[-D --daemonize-noclose]).must_equal :daemonize => :noclose
Rackup::Server::Options.new.parse!(%w[--daemonize-noclose -D]).must_equal :daemonize => :noclose
end
it "Options#parse parses --profile option into :profile" do
Rackup::Server::Options.new.parse!(%w[--profile foo]).must_equal :profile_file => 'foo'
end
it "Options#parse parses --profile-mode option into :profile_mode" do
Rackup::Server::Options.new.parse!(%w[--profile-mode cpu]).must_equal :profile_mode => :cpu
end
it "Options#parse parses argument into :config" do
Rackup::Server::Options.new.parse!(%w[foo]).must_equal :config => 'foo'
end
it "Options#handler_opts doesn't include Host/Port options" do
tester = Object.new
def tester.valid_options
{'Host: ' => 'anything', 'Port: ' => 'anything'}
end
def tester.to_s
'HPOT'
end
def tester.name
'HPOT'
end
Rackup::Handler.const_set(:HPOT, tester)
Rackup::Handler.register(:host_port_option_tester, tester)
Rackup::Server::Options.new.handler_opts(server: :host_port_option_tester).must_equal ""
end
it "logging_middleware will include common logger except for CGI" do
c = Class.new(Rackup::Server)
def c.middleware
Hash.new{[logging_middleware]}
end
argv.replace(['-swebrick', '-b', 'run ->(env){[200, {}, []]}'])
c.new.send(:wrapped_app).must_be_kind_of Rack::CommonLogger
argv.replace(['-scgi', '-b', 'run ->(env){[200, {}, []]}'])
c.new.send(:wrapped_app).must_be_kind_of Proc
end
it "#app aborts when config.ru file does not exist" do
argv.replace(['-swebrick', 'non-existant.ru'])
c = Class.new(Rackup::Server) do
alias abort raise
end
proc{c.new.app}.must_raise(RuntimeError).message.must_match(/\Aconfiguration .* not found\z/)
end
it "#app returns app when config.ru file exists" do
argv.replace(['-swebrick', 'test/builder/line.ru'])
Rackup::Server.new.app.must_be_kind_of Proc
end
it "#start daemonizes if daemonize option is given" do
server = Rackup::Server.new(daemonize: true, app: proc{}, server: :cgi)
def server.daemonize_app
throw :foo, :bar
end
catch(:foo){server.start}.must_equal :bar
end
if test_profile
it "#profiles to temp file if :profile_mode option is given and :profile_file option is not given" do
server = Rackup::Server.new(app: proc{[200, {}, []]}, server: :cgi, profile_mode: :cpu)
output = String.new
server.define_singleton_method(:puts){|str| output << str}
def server.exit
throw :foo, :bar
end
catch(:foo){server.start}.must_equal :bar
filename = output.split.last
File.file?(filename).must_equal true
File.size(filename).must_be :>, 0
File.delete(filename)
end
it "#profiles to given file if :profile_mode and :profile_file options are given" do
Dir.mktmpdir('test-rack-') do |dir|
filename = File.join(dir, 'profile')
server = Rackup::Server.new(app: proc{[200, {}, []]}, server: :cgi, profile_mode: :cpu, profile_file: filename)
output = String.new
server.define_singleton_method(:puts){|str| output << str}
def server.exit
throw :foo, :bar
end
catch(:foo){server.start}.must_equal :bar
output.split.last.must_include 'profile'
File.file?(filename).must_equal true
File.size(filename).must_be :>, 0
File.delete(filename)
end
end
end
it "clears arguments if ENV['REQUEST_METHOD'] is set" do
begin
ENV['REQUEST_METHOD'] = 'GET'
argv.replace(%w[-scgi config.ru])
Rackup::Server.new
argv.must_be_empty
ensure
ENV.delete('REQUEST_METHOD')
end
end
it "prefer to use :builder when it is passed in" do
server = Rackup::Server.new(builder: "run lambda { |env| [200, {'content-type' => 'text/plain'}, ['success']] }")
Rack::MockRequest.new(server.app).get("/").body.to_s.must_equal 'success'
end
it "allow subclasses to override middleware" do
server = Class.new(Rackup::Server).class_eval { def middleware; Hash.new [] end; self }
server.middleware['deployment'].wont_equal []
server.new(app: 'foo').middleware['deployment'].must_equal []
end
it "allow subclasses to override default middleware" do
server = Class.new(Rackup::Server).instance_eval { def default_middleware_by_environment; Hash.new [] end; self }
server.middleware['deployment'].must_equal []
server.new(app: 'foo').middleware['deployment'].must_equal []
end
it "only provide default middleware for development and deployment environments" do
Rackup::Server.default_middleware_by_environment.keys.sort.must_equal %w(deployment development)
end
it "always return an empty array for unknown environments" do
server = Rackup::Server.new(app: 'foo')
server.middleware['production'].must_equal []
end
it "not include Rack::Lint in deployment environment" do
server = Rackup::Server.new(app: 'foo')
server.middleware['deployment'].flatten.wont_include Rack::Lint
end
it "not include Rack::ShowExceptions in deployment environment" do
server = Rackup::Server.new(app: 'foo')
server.middleware['deployment'].flatten.wont_include Rack::ShowExceptions
end
it "include Rack::TempfileReaper in deployment environment" do
server = Rackup::Server.new(app: 'foo')
server.middleware['deployment'].flatten.must_include Rack::TempfileReaper
end
it "be quiet if said so" do
server = Rackup::Server.new(app: "FOO", quiet: true)
Rackup::Server.logging_middleware.call(server).must_be_nil
end
it "use a full path to the pidfile" do
# avoids issues with daemonize chdir
opts = Rackup::Server.new.send(:parse_options, %w[--pid testing.pid])
opts[:pid].must_equal ::File.expand_path('testing.pid')
end
it "get options from ARGV" do
argv.replace(['--debug', '-sthin', '--env', 'production', '-w', '-q', '-o', 'localhost', '-O', 'NAME=VALUE', '-ONAME2', '-D'])
server = Rackup::Server.new
server.options[:debug].must_equal true
server.options[:server].must_equal 'thin'
server.options[:environment].must_equal 'production'
server.options[:warn].must_equal true
server.options[:quiet].must_equal true
server.options[:Host].must_equal 'localhost'
server.options[:NAME].must_equal 'VALUE'
server.options[:NAME2].must_equal true
server.options[:daemonize].must_equal true
end
def test_options_server(*args)
argv.replace(args)
output = String.new
Class.new(Rackup::Server) do
define_method(:opt_parser) do
Class.new(Rackup::Server::Options) do
define_method(:puts) do |*args|
output << args.join("\n") << "\n"
end
alias warn puts
alias abort puts
define_method(:exit) do
output << "exited"
end
end.new
end
end.new
output
end
it "support -h option to get help" do
test_options_server('-scgi', '-h').must_match(/\AUsage: rackup.*Ruby options:.*Rack options.*Profiling options.*Common options.*exited\z/m)
end
it "support -h option to get handler-specific help" do
cgi = Rackup::Handler.get('cgi')
begin
def cgi.valid_options; { "FOO=BAR" => "BAZ" } end
test_options_server('-scgi', '-h').must_match(/Server-specific options for Rackup::Handler::CGI.*-O +FOO=BAR +BAZ/m)
ensure
cgi.singleton_class.send(:remove_method, :valid_options)
end
end
it "support -h option to display warning for invalid handler" do
test_options_server('-sbanana', '-h').must_match(/\AUsage: rackup.*Ruby options:.*Rack options.*Profiling options.*Common options.*Warning: Could not find handler specified \(banana\) to determine handler-specific options.*exited\z/m)
end
it "support -v option to get version" do
test_options_server('-v').must_match(/\ARack \d+\.\d+.\d+(.*?)\nexited\z/)
end
it "warn for invalid --profile-mode option" do
test_options_server('--profile-mode', 'foo').must_match(/\Ainvalid option: --profile-mode unknown profile mode: foo.*Usage: rackup/m)
end
it "warn for invalid options" do
test_options_server('--banana').must_match(/\Ainvalid option: --banana.*Usage: rackup/m)
end
it "support -b option to specify inline rackup config" do
argv.replace(['-scgi', '-E', 'development', '-b', 'use Rack::ContentLength; run ->(env){[200, {}, []]}'])
server = Rackup::Server.new
server.server.singleton_class.send(:remove_method, :run)
def (server.server).run(app, **) app end
s, h, b = server.start.call('rack.errors' => StringIO.new)
s.must_equal 500
h['content-type'].must_equal 'text/plain'
b.join.must_include 'Rack::Lint::LintError'
end
it "support -e option to evaluate ruby code" do
argv.replace(['-scgi', '-e', 'Object::XYZ = 2'])
begin
Rackup::Server.new
Object::XYZ.must_equal 2
ensure
Object.send(:remove_const, :XYZ)
end
end
it "abort if config file does not exist" do
argv.replace(['-scgi'])
server = Rackup::Server.new
def server.abort(s) throw :abort, s end
message = catch(:abort) do
server.start
end
message.must_match(/\Aconfiguration .*config\.ru not found/)
end
it "support -I option to change the load path and -r to require" do
argv.replace(['-scgi', '-Ifoo/bar', '-Itest/load', '-rrack-test-a', '-rrack-test-b'])
begin
server = Rackup::Server.new
server.server.singleton_class.send(:remove_method, :run)
def (server.server).run(*) end
def server.handle_profiling(*) end
def server.app(*) end
server.start
$LOAD_PATH.must_include('foo/bar')
$LOAD_PATH.must_include('test/load')
$LOADED_FEATURES.must_include(File.join(Dir.pwd, "test/load/rack-test-a.rb"))
$LOADED_FEATURES.must_include(File.join(Dir.pwd, "test/load/rack-test-b.rb"))
ensure
$LOAD_PATH.delete('foo/bar')
$LOAD_PATH.delete('test/load')
$LOADED_FEATURES.delete(File.join(Dir.pwd, "test/load/rack-test-a.rb"))
$LOADED_FEATURES.delete(File.join(Dir.pwd, "test/load/rack-test-b.rb"))
end
end
it "support -w option to warn and -d option to debug" do
argv.replace(['-scgi', '-d', '-w'])
warn = $-w
debug = $DEBUG
begin
server = Rackup::Server.new
server.server.singleton_class.send(:remove_method, :run)
def (server.server).run(*) end
def server.handle_profiling(*) end
def server.app(*) end
def server.p(*) end
def server.pp(*) end
def server.require(*) end
server.start
$-w.must_equal true
$DEBUG.must_equal true
ensure
$-w = warn
$DEBUG = debug
end
end
if RUBY_ENGINE == "ruby"
it "support --heap option for heap profiling" do
begin
require 'objspace'
rescue LoadError
else
t = Tempfile.new
begin
argv.replace(['-scgi', '--heap', t.path, '-E', 'production', '-b', 'run ->(env){[200, {}, []]}'])
server = Rackup::Server.new
server.server.singleton_class.send(:remove_method, :run)
def (server.server).run(*) end
def server.exit; throw :exit end
catch :exit do
server.start
end
File.file?(t.path).must_equal true
ensure
File.delete t.path
end
end
end
it "support --profile-mode option for stackprof profiling" do
begin
require 'stackprof'
rescue LoadError
else
t = Tempfile.new
begin
argv.replace(['-scgi', '--profile', t.path, '--profile-mode', 'cpu', '-E', 'production', '-b', 'run ->(env){[200, {}, []]}'])
server = Rackup::Server.new
def (server.server).run(*) end
def server.puts(*) end
def server.exit; throw :exit end
catch :exit do
server.start
end
File.file?(t.path).must_equal true
ensure
File.delete t.path
end
end
end
it "support --profile-mode option for stackprof profiling without --profile option" do
begin
require 'stackprof'
rescue LoadError
else
begin
argv.replace(['-scgi', '--profile-mode', 'cpu', '-E', 'production', '-b', 'run ->(env){[200, {}, []]}'])
server = Rackup::Server.new
def (server.server).run(*) end
filename = nil
server.define_singleton_method(:make_profile_name) do |fname, &block|
super(fname) do |fn|
filename = fn
block.call(filename)
end
end
def server.puts(*) end
def server.exit; throw :exit end
catch :exit do
server.start
end
File.file?(filename).must_equal true
ensure
File.delete filename
end
end
end
end
it "support exit for INT signal when server does not respond to shutdown" do
argv.replace(['-scgi'])
server = Rackup::Server.new
server.server.singleton_class.send(:remove_method, :run)
def (server.server).run(*) end
def server.handle_profiling(*) end
def server.app(*) end
exited = false
server.define_singleton_method(:exit) do
exited = true
end
server.start
exited.must_equal false
Process.kill(:INT, $$)
sleep 1 unless RUBY_ENGINE == 'ruby'
exited.must_equal true
end
it "support support Server.start for starting" do
argv.replace(['-scgi'])
c = Class.new(Rackup::Server) do
def start(*) [self.class, :started] end
end
c.start.must_equal [c, :started]
end
it "run a server" do
pidfile = Tempfile.open('pidfile') { |f| break f }
FileUtils.rm pidfile.path
server = Rackup::Server.new(
app: app,
environment: 'none',
pid: pidfile.path,
Port: TCPServer.open('localhost', 0){|s| s.addr[1] },
Host: 'localhost',
Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
AccessLog: [],
daemonize: false,
server: 'webrick'
)
t = Thread.new { server.start { |s| Thread.current[:server] = s } }
t.join(0.01) until t[:server] && t[:server].status != :Stop
body = if URI.respond_to?(:open)
URI.open("http://localhost:#{server.options[:Port]}/") { |f| f.read }
else
open("http://localhost:#{server.options[:Port]}/") { |f| f.read }
end
body.must_equal 'success'
Process.kill(:INT, $$)
t.join
open(pidfile.path) { |f| f.read.must_equal $$.to_s }
end
it "run a secure server" do
pidfile = Tempfile.open('pidfile') { |f| break f }
FileUtils.rm pidfile.path
server = Rackup::Server.new(
app: app,
environment: 'none',
pid: pidfile.path,
Port: TCPServer.open('localhost', 0){|s| s.addr[1] },
Host: 'localhost',
Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
AccessLog: [],
daemonize: false,
server: 'webrick',
SSLEnable: true,
SSLCertName: [['CN', 'nobody'], ['DC', 'example']]
)
t = Thread.new { server.start { |s| Thread.current[:server] = s } }
t.join(0.01) until t[:server] && t[:server].status != :Stop
uri = URI.parse("https://localhost:#{server.options[:Port]}/")
Net::HTTP.start("localhost", uri.port, use_ssl: true,
verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
request = Net::HTTP::Get.new uri
body = http.request(request).body
body.must_equal 'success'
end
Process.kill(:INT, $$)
t.join
File.open(pidfile.path) { |f| f.read.must_equal $$.to_s }
end if RUBY_VERSION >= "2.6" && RUBY_ENGINE == "ruby"
it "check pid file presence and running process" do
pidfile = Tempfile.open('pidfile') { |f| f.write($$); break f }.path
server = Rackup::Server.new(pid: pidfile)
with_stderr do |err|
lambda { server.send(:check_pid!) }.must_raise SystemExit
err.rewind
output = err.read
output.must_match(/already running \(pid: #{$$}, file: #{pidfile}\)/)
end
end
it "check pid file presence and dead process" do
dead_pid = `echo $$`.to_i
pidfile = Tempfile.open('pidfile') { |f| f.write(dead_pid); break f }.path
server = Rackup::Server.new(pid: pidfile)
server.send(:check_pid!)
::File.exist?(pidfile).must_equal false
end
it "check pid file presence and exited process" do
pidfile = Tempfile.open('pidfile') { |f| break f }.path
::File.delete(pidfile)
server = Rackup::Server.new(pid: pidfile)
server.send(:check_pid!)
end
it "check pid file presence and not owned process" do
owns_pid_1 = (Process.kill(0, 1) rescue nil) == 1
skip "cannot test if pid 1 owner matches current process (eg. docker/lxc)" if owns_pid_1
pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
server = Rackup::Server.new(pid: pidfile)
with_stderr do |err|
lambda { server.send(:check_pid!) }.must_raise SystemExit
err.rewind
output = err.read
output.must_match(/already running \(pid: 1, file: #{pidfile}\)/)
end
end
it "rewrite pid file when it does not reference a running process" do
pidfile = Tempfile.open('pidfile') { |f| break f }.path
server = Rackup::Server.new(pid: pidfile)
::File.open(pidfile, 'w') { }
server.send(:write_pid)
::File.read(pidfile).to_i.must_equal $$
end
it "not write pid file when it references a running process" do
pidfile = Tempfile.open('pidfile') { |f| break f }.path
::File.delete(pidfile)
server = Rackup::Server.new(pid: pidfile)
::File.open(pidfile, 'w') { |f| f.write(1) }
with_stderr do |err|
lambda { server.send(:write_pid) }.must_raise SystemExit
err.rewind
output = err.read
output.must_match(/already running \(pid: 1, file: #{pidfile}\)/)
end
end
it "inform the user about existing pidfiles with running processes" do
pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
server = Rackup::Server.new(pid: pidfile)
with_stderr do |err|
lambda { server.start }.must_raise SystemExit
err.rewind
output = err.read
output.must_match(/already running \(pid: 1, file: #{pidfile}\)/)
end
end
end
rackup-2.3.1/test/spec_webrick.rb 0000664 0000000 0000000 00000016543 15114527355 0016767 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
require_relative 'helper'
require 'thread'
require 'webrick'
require 'rack/lint'
require 'rack/response'
require_relative 'test_request'
separate_testing do
require_relative '../lib/rackup/handler'
end
require_relative '../lib/rackup/handler/webrick'
Thread.abort_on_exception = true
describe Rackup::Handler::WEBrick do
include TestRequest::Helpers
before do
@server = WEBrick::HTTPServer.new(Host: @host = 'localhost',
Port: @port = 9202,
Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
AccessLog: [])
@server.mount "/test", Rackup::Handler::WEBrick,
Rack::Lint.new(TestRequest.new)
@thread = Thread.new { @server.start }
trap(:INT) { @server.shutdown }
@status_thread = Thread.new do
seconds = 10
wait_time = 0.1
until is_running? || seconds <= 0
seconds -= wait_time
sleep wait_time
end
raise "Server never reached status 'Running'" unless is_running?
end
end
def is_running?
@server.status == :Running
end
it "respond" do
GET("/test")
status.must_equal 200
end
it "be a WEBrick" do
GET("/test")
status.must_equal 200
response["SERVER_SOFTWARE"].must_match(/WEBrick/)
response["SERVER_PROTOCOL"].must_equal "HTTP/1.1"
response["SERVER_PORT"].must_equal "9202"
response["SERVER_NAME"].must_equal "localhost"
end
it "have CGI headers on GET" do
GET("/test")
response["REQUEST_METHOD"].must_equal "GET"
response["SCRIPT_NAME"].must_equal "/test"
response["REQUEST_PATH"].must_equal "/test"
response["PATH_INFO"].must_equal ""
response["QUERY_STRING"].must_equal ""
response["test.postdata"].must_equal ""
GET("/test/foo?quux=1")
response["REQUEST_METHOD"].must_equal "GET"
response["SCRIPT_NAME"].must_equal "/test"
response["REQUEST_PATH"].must_equal "/test/foo"
response["PATH_INFO"].must_equal "/foo"
response["QUERY_STRING"].must_equal "quux=1"
GET("/test/foo%25encoding?quux=1")
response["REQUEST_METHOD"].must_equal "GET"
response["SCRIPT_NAME"].must_equal "/test"
response["REQUEST_PATH"].must_equal "/test/foo%25encoding"
response["PATH_INFO"].must_equal "/foo%25encoding"
response["QUERY_STRING"].must_equal "quux=1"
end
it "have CGI headers on POST" do
POST("/test", { "rack-form-data" => "23" }, { 'X-test-header' => '42' })
status.must_equal 200
response["REQUEST_METHOD"].must_equal "POST"
response["SCRIPT_NAME"].must_equal "/test"
response["REQUEST_PATH"].must_equal "/test"
response["PATH_INFO"].must_equal ""
response["QUERY_STRING"].must_equal ""
response["HTTP_X_TEST_HEADER"].must_equal "42"
response["test.postdata"].must_equal "rack-form-data=23"
end
it "support HTTP auth" do
GET("/test", { user: "ruth", passwd: "secret" })
response["HTTP_AUTHORIZATION"].must_equal "Basic cnV0aDpzZWNyZXQ="
end
it "set status" do
GET("/test?secret")
status.must_equal 403
response["rack.url_scheme"].must_equal "http"
end
it "correctly set cookies" do
@server.mount "/cookie-test", Rackup::Handler::WEBrick,
Rack::Lint.new(lambda { |req|
res = Rack::Response.new
res.set_cookie "one", "1"
res.set_cookie "two", "2"
res.finish
})
Net::HTTP.start(@host, @port) { |http|
res = http.get("/cookie-test")
res.code.to_i.must_equal 200
res.get_fields("set-cookie").must_equal ["one=1", "two=2"]
}
end
it "provide a .run" do
queue = Queue.new
t = Thread.new do
Rackup::Handler::WEBrick.run(lambda {},
Host: 'localhost',
Port: 9210,
Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
AccessLog: []) { |server|
assert_kind_of WEBrick::HTTPServer, server
queue.push(server)
}
end
server = queue.pop
# The server may not yet have started: wait for it
seconds = 10
wait_time = 0.1
until server.status == :Running || seconds <= 0
seconds -= wait_time
sleep wait_time
end
raise "Server never reached status 'Running'" unless server.status == :Running
server.shutdown
t.join
end
it "return repeated headers" do
@server.mount "/headers", Rackup::Handler::WEBrick,
Rack::Lint.new(lambda { |req|
[
401,
{ "content-type" => "text/plain",
"www-authenticate" => ["Bar realm=X", "Baz realm=Y"] },
[""]
]
})
Net::HTTP.start(@host, @port) { |http|
res = http.get("/headers")
res.code.to_i.must_equal 401
res["www-authenticate"].must_equal "Bar realm=X, Baz realm=Y"
}
end
it "support Rack partial hijack" do
io_lambda = lambda{ |io|
5.times do
io.write "David\r\n"
end
io.close
}
@server.mount "/partial", Rackup::Handler::WEBrick,
Rack::Lint.new(lambda{ |req|
[
200,
{ "rack.hijack" => io_lambda },
[""]
]
})
Net::HTTP.start(@host, @port){ |http|
res = http.get("/partial")
res.body.must_equal "David\r\nDavid\r\nDavid\r\nDavid\r\nDavid\r\n"
}
end
it "produce correct HTTP semantics with upgrade response" do
app = proc do |env|
body = proc do |io|
io.write "hello"
io.close
end
[101, {"connection" => "upgrade", "upgrade" => "text"}, body]
end
@server.mount "/app", Rackup::Handler::WEBrick, Rack::Lint.new(app)
TCPSocket.open(@host, @port) do |socket|
socket.write "GET /app HTTP/1.1\r\n"
socket.write "Host: #{@host}\r\n\r\n"
response = socket.read
response.must_match(/HTTP\/1.1 101 Switching Protocols/)
response.must_match(/Connection: upgrade/)
response.must_match(/Upgrade: text/)
response.must_match(/hello/)
end
end
it "handle OPTIONS * requests through the Rack app" do
app = proc do |env|
if env["REQUEST_METHOD"] == "OPTIONS" && env["PATH_INFO"] == "*"
[200, {"allow" => "GET,HEAD,POST,PUT,DELETE,OPTIONS"}, [""]]
else
[404, {"content-type" => "text/plain"}, ["Not Found"]]
end
end
server = Rackup::Handler::WEBrick::Server.new(
Rack::Lint.new(app),
Host: @host,
Port: 9203,
Logger: WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
AccessLog: []
)
thread = Thread.new { server.start }
# Wait for server to start
seconds = 10
wait_time = 0.1
until server.status == :Running || seconds <= 0
seconds -= wait_time
sleep wait_time
end
begin
TCPSocket.open(@host, 9203) do |socket|
socket.write "OPTIONS * HTTP/1.1\r\n"
socket.write "Host: #{@host}\r\n"
socket.write "Connection: close\r\n\r\n"
response = socket.read
response.must_match(/HTTP\/1.1 200/)
# The Rack app should set the Allow header, not WEBrick's default
response.must_match(/Allow: GET,HEAD,POST,PUT,DELETE,OPTIONS/i)
end
ensure
server.shutdown
thread.join
end
end
after do
@status_thread.join
@server.shutdown
@thread.join
end
end
rackup-2.3.1/test/test_request.rb 0000664 0000000 0000000 00000004137 15114527355 0017052 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
require 'yaml'
require_relative 'psych_fix'
require 'net/http'
require 'rack/lint'
class TestRequest
NOSERIALIZE = [Method, Proc, Rack::Lint::Wrapper::InputWrapper]
def call(env)
status = env["QUERY_STRING"] =~ /secret/ ? 403 : 200
env["test.postdata"] = env["rack.input"].read
minienv = env.dup
# This may in the future want to replace with a dummy value instead.
minienv.delete_if { |k, v| NOSERIALIZE.any? { |c| v.kind_of?(c) } }
body = minienv.to_yaml
size = body.bytesize
[status, { "content-type" => "text/yaml", "content-length" => size.to_s }, [body]]
end
module Helpers
attr_reader :status, :response
ROOT = File.expand_path(File.dirname(__FILE__) + "/..")
ENV["RUBYOPT"] = "-I#{ROOT}/lib -rubygems"
def root
ROOT
end
def rackup
"#{ROOT}/bin/rackup"
end
def GET(path, header = {})
Net::HTTP.start(@host, @port) { |http|
user = header.delete(:user)
passwd = header.delete(:passwd)
get = Net::HTTP::Get.new(path, header)
get.basic_auth user, passwd if user && passwd
http.request(get) { |response|
@status = response.code.to_i
begin
@response = YAML.unsafe_load(response.body)
rescue TypeError, ArgumentError
@response = nil
end
}
}
end
def POST(path, formdata = {}, header = {})
Net::HTTP.start(@host, @port) { |http|
user = header.delete(:user)
passwd = header.delete(:passwd)
post = Net::HTTP::Post.new(path, header)
post.form_data = formdata
post.basic_auth user, passwd if user && passwd
http.request(post) { |response|
@status = response.code.to_i
@response = YAML.unsafe_load(response.body)
}
}
end
end
end
class StreamingRequest
def self.call(env)
[200, { "content-type" => "text/plain" }, new]
end
def each
yield "hello there!\n"
sleep 5
yield "that is all.\n"
end
end
rackup-2.3.1/test/unregistered_handler/ 0000775 0000000 0000000 00000000000 15114527355 0020166 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/unregistered_handler/rack/ 0000775 0000000 0000000 00000000000 15114527355 0021106 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/unregistered_handler/rack/handler/ 0000775 0000000 0000000 00000000000 15114527355 0022523 5 ustar 00root root 0000000 0000000 rackup-2.3.1/test/unregistered_handler/rack/handler/unregistered.rb 0000664 0000000 0000000 00000000374 15114527355 0025554 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
module Rackup
module Handler
# this class doesn't do anything, we're just seeing if we get it.
class Unregistered
end
end
end
rackup-2.3.1/test/unregistered_handler/rack/handler/unregistered_long_one.rb 0000664 0000000 0000000 00000000403 15114527355 0027425 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
module Rackup
module Handler
# this class doesn't do anything, we're just seeing if we get it.
class UnregisteredLongOne
end
end
end