pax_global_header 0000666 0000000 0000000 00000000064 14364637605 0014530 g ustar 00root root 0000000 0000000 52 comment=eaea24a3d64a1b117df943a9d06779e659bb61af rackup-2.1.0/ 0000775 0000000 0000000 00000000000 14364637605 0013015 5 ustar 00root root 0000000 0000000 rackup-2.1.0/.contributors.yaml 0000664 0000000 0000000 00000031065 14364637605 0016521 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: KS 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: deepj 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: deepj 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 Mendonça 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: Postmodern 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 + Carl Lerche 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.1.0/.github/ 0000775 0000000 0000000 00000000000 14364637605 0014355 5 ustar 00root root 0000000 0000000 rackup-2.1.0/.github/workflows/ 0000775 0000000 0000000 00000000000 14364637605 0016412 5 ustar 00root root 0000000 0000000 rackup-2.1.0/.github/workflows/test-external.yaml 0000664 0000000 0000000 00000001316 14364637605 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: - "2.7" - "3.0" - "3.1" - "3.2" steps: - uses: actions/checkout@v3 - 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.1.0/.github/workflows/test.yaml 0000664 0000000 0000000 00000001706 14364637605 0020261 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.7" - "3.0" - "3.1" - "3.2" experimental: [false] include: - os: ubuntu ruby: truffleruby experimental: true - os: ubuntu ruby: jruby experimental: true - os: ubuntu ruby: head experimental: true steps: - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{matrix.ruby}} bundler-cache: true - name: Run tests timeout-minutes: 10 run: bundle exec rake test rackup-2.1.0/.gitignore 0000664 0000000 0000000 00000000064 14364637605 0015005 0 ustar 00root root 0000000 0000000 /.bundle/ /pkg/ /gems.locked /.covered.db /external rackup-2.1.0/Rakefile 0000664 0000000 0000000 00000000421 14364637605 0014457 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.1.0/bin/ 0000775 0000000 0000000 00000000000 14364637605 0013565 5 ustar 00root root 0000000 0000000 rackup-2.1.0/bin/rackup 0000775 0000000 0000000 00000000151 14364637605 0014775 0 ustar 00root root 0000000 0000000 #!/usr/bin/env ruby # frozen_string_literal: true require_relative "../lib/rackup" Rackup::Server.start rackup-2.1.0/config/ 0000775 0000000 0000000 00000000000 14364637605 0014262 5 ustar 00root root 0000000 0000000 rackup-2.1.0/config/external.yaml 0000664 0000000 0000000 00000000365 14364637605 0016774 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.1.0/gems.rb 0000664 0000000 0000000 00000000641 14364637605 0014276 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. source 'https://rubygems.org' gemspec group :maintenance, optional: true do gem "bake" gem "bake-gem" gem "bake-modernize" gem "rubocop", require: false gem "rubocop-packaging", require: false end group :doc do gem 'rdoc' end group :test do gem "bake-test" gem "bake-test-external" end rackup-2.1.0/lib/ 0000775 0000000 0000000 00000000000 14364637605 0013563 5 ustar 00root root 0000000 0000000 rackup-2.1.0/lib/rack/ 0000775 0000000 0000000 00000000000 14364637605 0014503 5 ustar 00root root 0000000 0000000 rackup-2.1.0/lib/rack/handler.rb 0000664 0000000 0000000 00000000403 14364637605 0016442 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. warn "Rack::Handler is deprecated and replaced by Rackup::Handler" require_relative '../rackup/handler' module Rack Handler = ::Rackup::Handler end rackup-2.1.0/lib/rack/server.rb 0000664 0000000 0000000 00000000376 14364637605 0016344 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. warn "Rack::Server is deprecated and replaced by Rackup::Server" require_relative '../rackup/server' module Rack Server = ::Rackup::Server end rackup-2.1.0/lib/rackup.rb 0000664 0000000 0000000 00000000444 14364637605 0015377 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. require_relative 'rackup/handler' require_relative 'rackup/server' require_relative 'rackup/version' require_relative 'rackup/handler/webrick' require_relative 'rackup/handler/cgi' rackup-2.1.0/lib/rackup/ 0000775 0000000 0000000 00000000000 14364637605 0015050 5 ustar 00root root 0000000 0000000 rackup-2.1.0/lib/rackup/handler.rb 0000664 0000000 0000000 00000005473 14364637605 0017023 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.1.0/lib/rackup/handler/ 0000775 0000000 0000000 00000000000 14364637605 0016465 5 ustar 00root root 0000000 0000000 rackup-2.1.0/lib/rackup/handler/cgi.rb 0000664 0000000 0000000 00000002510 14364637605 0017552 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.1.0/lib/rackup/handler/webrick.rb 0000664 0000000 0000000 00000010372 14364637605 0020443 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 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 = ::WEBrick::HTTPServer.new(options) @server.mount "/", Rackup::Handler::WEBrick, app 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] ||= "" unless env[::Rack::PATH_INFO] == "" path, n = req.request_uri.path, env[::Rack::SCRIPT_NAME].length env[::Rack::PATH_INFO] = path[n, path.length - n] end env[::Rack::REQUEST_PATH] ||= [env[::Rack::SCRIPT_NAME], env[::Rack::PATH_INFO]].join 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.1.0/lib/rackup/lobster.rb 0000664 0000000 0000000 00000004305 14364637605 0017051 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.1.0/lib/rackup/server.rb 0000664 0000000 0000000 00000032327 14364637605 0016712 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:
#
# Rack::Server.start(
# :app => lambda do |e|
# [200, {'content-type' => 'text/html'}, ['hello world']]
# end,
# :server => 'cgi'
# )
#
# Further options available here are documented on Rack::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.1.0/lib/rackup/stream.rb 0000664 0000000 0000000 00000012212 14364637605 0016666 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2019-2022, 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.1.0/lib/rackup/version.rb 0000664 0000000 0000000 00000000224 14364637605 0017060 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.1.0"
end
rackup-2.1.0/license.md 0000664 0000000 0000000 00000006243 14364637605 0014766 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 + Carl Lerche.
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 Postmodern.
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, by Rafael Mendonça 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 deepj.
Copyright, 2015, by Zachary Scott.
Copyright, 2016, by Sophie Deziel.
Copyright, 2016, by Kazuya Hotta.
Copyright, 2017, by Ryunosuke Sato.
Copyright, 2017-2023, by Samuel Williams.
Copyright, 2018, by Dillon Welch.
Copyright, 2018, by Yoshiyuki Hirano.
Copyright, 2018, by Nick LaMuro.
Copyright, 2019, by Rafael França.
Copyright, 2019, by Krzysztof Rybka.
Copyright, 2019, by Misaki Shioi.
Copyright, 2020-2022, by Jeremy Evans.
Copyright, 2021, by Katsuhiko YOSHIDA.
Copyright, 2021, by KS.
Copyright, 2021, by Stephen Paul Weber.
Copyright, 2022, by Akira Matsuda.
Copyright, 2022, by Andrew Hoglund.
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.1.0/rackup.gemspec 0000664 0000000 0000000 00000001463 14364637605 0015653 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", "Jeremy Evans"]
spec.license = "MIT"
spec.homepage = "https://github.com/rack/rackup"
spec.files = Dir['{bin,lib}/**/*', '*.md']
spec.executables = ["rackup"]
spec.required_ruby_version = ">= 2.4.0"
spec.add_dependency "rack", ">= 3"
spec.add_dependency "webrick", "~> 1.8"
spec.add_development_dependency "bundler"
spec.add_development_dependency "minitest", "~> 5.0"
spec.add_development_dependency "minitest-global_expectations"
spec.add_development_dependency "minitest-sprint"
spec.add_development_dependency "rake"
end
rackup-2.1.0/readme.md 0000664 0000000 0000000 00000001365 14364637605 0014601 0 ustar 00root root 0000000 0000000 # Rackup
`rackup` provides a command line interface for running a Rack-compatible application.
[](https://github.com/rack/rackup/actions?workflow=Test)
## Installation
``` bash
$ gem install rackup
```
## 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`.
## 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.1.0/security.md 0000664 0000000 0000000 00000000145 14364637605 0015206 0 ustar 00root root 0000000 0000000 # Security Policy
Please see our main security policy: https://github.com/rack/rack/security/policy
rackup-2.1.0/test/ 0000775 0000000 0000000 00000000000 14364637605 0013774 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/builder/ 0000775 0000000 0000000 00000000000 14364637605 0015422 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/builder/line.ru 0000664 0000000 0000000 00000000156 14364637605 0016723 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
run lambda{ |env| [200, { 'content-type' => 'text/plain' }, [__LINE__.to_s]] }
rackup-2.1.0/test/cgi/ 0000775 0000000 0000000 00000000000 14364637605 0014536 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/cgi/assets/ 0000775 0000000 0000000 00000000000 14364637605 0016040 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/cgi/assets/folder/ 0000775 0000000 0000000 00000000000 14364637605 0017313 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/cgi/assets/folder/test.js 0000664 0000000 0000000 00000000021 14364637605 0020621 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.1.0/test/cgi/assets/fonts/ 0000775 0000000 0000000 00000000000 14364637605 0017171 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/cgi/assets/fonts/font.eot 0000664 0000000 0000000 00000000021 14364637605 0020641 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.1.0/test/cgi/assets/images/ 0000775 0000000 0000000 00000000000 14364637605 0017305 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/cgi/assets/images/image.png 0000664 0000000 0000000 00000000021 14364637605 0021066 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.1.0/test/cgi/assets/index.html 0000664 0000000 0000000 00000000021 14364637605 0020026 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.1.0/test/cgi/assets/javascripts/ 0000775 0000000 0000000 00000000000 14364637605 0020371 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/cgi/assets/javascripts/app.js 0000664 0000000 0000000 00000000021 14364637605 0021500 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.1.0/test/cgi/assets/stylesheets/ 0000775 0000000 0000000 00000000000 14364637605 0020414 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/cgi/assets/stylesheets/app.css 0000664 0000000 0000000 00000000021 14364637605 0021677 0 ustar 00root root 0000000 0000000 ### TestFile ###
rackup-2.1.0/test/cgi/rackup_stub.rb 0000775 0000000 0000000 00000000273 14364637605 0017412 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.1.0/test/cgi/sample_rackup.ru 0000775 0000000 0000000 00000000136 14364637605 0017737 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
require '../test_request'
run Rack::Lint.new(TestRequest.new)
rackup-2.1.0/test/cgi/test 0000775 0000000 0000000 00000000321 14364637605 0015437 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.1.0/test/cgi/test+directory/ 0000775 0000000 0000000 00000000000 14364637605 0017515 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/cgi/test+directory/test+file 0000664 0000000 0000000 00000000027 14364637605 0021331 0 ustar 00root root 0000000 0000000 this file has plusses!
rackup-2.1.0/test/cgi/test.gz 0000664 0000000 0000000 00000000273 14364637605 0016061 0 ustar 00root root 0000000 0000000 dZ X~=@l9q5 EA0ut84EV_ou${tQ KSNN֡4,eyDʿ׀LrkXV2:M}`dwKG.r\mhpƬ@,7H^<}4sJ7tH rackup-2.1.0/test/cgi/test.ru 0000775 0000000 0000000 00000000160 14364637605 0016065 0 ustar 00root root 0000000 0000000 #!../../bin/rackup
# frozen_string_literal: true
require '../test_request'
run Rack::Lint.new(TestRequest.new)
rackup-2.1.0/test/helper.rb 0000664 0000000 0000000 00000001247 14364637605 0015604 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.1.0/test/load/ 0000775 0000000 0000000 00000000000 14364637605 0014713 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/load/rack-test-a.rb 0000664 0000000 0000000 00000000156 14364637605 0017355 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
rackup-2.1.0/test/load/rack-test-b.rb 0000664 0000000 0000000 00000000156 14364637605 0017356 0 ustar 00root root 0000000 0000000 # frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
rackup-2.1.0/test/psych_fix.rb 0000664 0000000 0000000 00000000312 14364637605 0016311 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.1.0/test/registering_handler/ 0000775 0000000 0000000 00000000000 14364637605 0020013 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/registering_handler/rack/ 0000775 0000000 0000000 00000000000 14364637605 0020733 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/registering_handler/rack/handler/ 0000775 0000000 0000000 00000000000 14364637605 0022350 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/registering_handler/rack/handler/registering_myself.rb 0000664 0000000 0000000 00000000360 14364637605 0026575 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.1.0/test/spec_handler.rb 0000664 0000000 0000000 00000003504 14364637605 0016752 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.1.0/test/spec_lobster.rb 0000664 0000000 0000000 00000002575 14364637605 0017016 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.1.0/test/spec_server.rb 0000664 0000000 0000000 00000047373 14364637605 0016657 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
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.1.0/test/spec_webrick.rb 0000664 0000000 0000000 00000014225 14364637605 0016765 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
after do
@status_thread.join
@server.shutdown
@thread.join
end
end
rackup-2.1.0/test/test_request.rb 0000664 0000000 0000000 00000004137 14364637605 0017055 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.1.0/test/unregistered_handler/ 0000775 0000000 0000000 00000000000 14364637605 0020171 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/unregistered_handler/rack/ 0000775 0000000 0000000 00000000000 14364637605 0021111 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/unregistered_handler/rack/handler/ 0000775 0000000 0000000 00000000000 14364637605 0022526 5 ustar 00root root 0000000 0000000 rackup-2.1.0/test/unregistered_handler/rack/handler/unregistered.rb 0000664 0000000 0000000 00000000374 14364637605 0025557 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.1.0/test/unregistered_handler/rack/handler/unregistered_long_one.rb 0000664 0000000 0000000 00000000403 14364637605 0027430 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