pax_global_header00006660000000000000000000000064143646376050014530gustar00rootroot0000000000000052 comment=eaea24a3d64a1b117df943a9d06779e659bb61af rackup-2.1.0/000077500000000000000000000000001436463760500130155ustar00rootroot00000000000000rackup-2.1.0/.contributors.yaml000066400000000000000000000310651436463760500165210ustar00rootroot00000000000000- 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/000077500000000000000000000000001436463760500143555ustar00rootroot00000000000000rackup-2.1.0/.github/workflows/000077500000000000000000000000001436463760500164125ustar00rootroot00000000000000rackup-2.1.0/.github/workflows/test-external.yaml000066400000000000000000000013161436463760500220760ustar00rootroot00000000000000name: 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.yaml000066400000000000000000000017061436463760500202610ustar00rootroot00000000000000name: 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/.gitignore000066400000000000000000000000641436463760500150050ustar00rootroot00000000000000/.bundle/ /pkg/ /gems.locked /.covered.db /external rackup-2.1.0/Rakefile000066400000000000000000000004211436463760500144570ustar00rootroot00000000000000# 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/000077500000000000000000000000001436463760500135655ustar00rootroot00000000000000rackup-2.1.0/bin/rackup000077500000000000000000000001511436463760500147750ustar00rootroot00000000000000#!/usr/bin/env ruby # frozen_string_literal: true require_relative "../lib/rackup" Rackup::Server.start rackup-2.1.0/config/000077500000000000000000000000001436463760500142625ustar00rootroot00000000000000rackup-2.1.0/config/external.yaml000066400000000000000000000003651436463760500167740ustar00rootroot00000000000000rack: 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.rb000066400000000000000000000006411436463760500142760ustar00rootroot00000000000000# 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/000077500000000000000000000000001436463760500135635ustar00rootroot00000000000000rackup-2.1.0/lib/rack/000077500000000000000000000000001436463760500145035ustar00rootroot00000000000000rackup-2.1.0/lib/rack/handler.rb000066400000000000000000000004031436463760500164420ustar00rootroot00000000000000# 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.rb000066400000000000000000000003761436463760500163440ustar00rootroot00000000000000# 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.rb000066400000000000000000000004441436463760500153770ustar00rootroot00000000000000# 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/000077500000000000000000000000001436463760500150505ustar00rootroot00000000000000rackup-2.1.0/lib/rackup/handler.rb000066400000000000000000000054731436463760500170230ustar00rootroot00000000000000# 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/000077500000000000000000000000001436463760500164655ustar00rootroot00000000000000rackup-2.1.0/lib/rackup/handler/cgi.rb000066400000000000000000000025101436463760500175520ustar00rootroot00000000000000# 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.rb000066400000000000000000000103721436463760500204430ustar00rootroot00000000000000# 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.rb000066400000000000000000000043051436463760500170510ustar00rootroot00000000000000# 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 = ["Lobstericious!", "
", 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 "Lobstericious!" res.write "
"
      res.write lobster
      res.write "
" res.write "

flip!

" res.write "

crash!

" 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.rb000066400000000000000000000323271436463760500167120ustar00rootroot00000000000000# 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.rb000066400000000000000000000122121436463760500166660ustar00rootroot00000000000000# 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.rb000066400000000000000000000002241436463760500170600ustar00rootroot00000000000000# 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.md000066400000000000000000000062431436463760500147660ustar00rootroot00000000000000# 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.gemspec000066400000000000000000000014631436463760500156530ustar00rootroot00000000000000# 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.md000066400000000000000000000013651436463760500146010ustar00rootroot00000000000000# Rackup `rackup` provides a command line interface for running a Rack-compatible application. [![Development Status](https://github.com/rack/rackup/workflows/Test/badge.svg)](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.md000066400000000000000000000001451436463760500152060ustar00rootroot00000000000000# Security Policy Please see our main security policy: https://github.com/rack/rack/security/policy rackup-2.1.0/test/000077500000000000000000000000001436463760500137745ustar00rootroot00000000000000rackup-2.1.0/test/builder/000077500000000000000000000000001436463760500154225ustar00rootroot00000000000000rackup-2.1.0/test/builder/line.ru000066400000000000000000000001561436463760500167230ustar00rootroot00000000000000# frozen_string_literal: true run lambda{ |env| [200, { 'content-type' => 'text/plain' }, [__LINE__.to_s]] } rackup-2.1.0/test/cgi/000077500000000000000000000000001436463760500145365ustar00rootroot00000000000000rackup-2.1.0/test/cgi/assets/000077500000000000000000000000001436463760500160405ustar00rootroot00000000000000rackup-2.1.0/test/cgi/assets/folder/000077500000000000000000000000001436463760500173135ustar00rootroot00000000000000rackup-2.1.0/test/cgi/assets/folder/test.js000066400000000000000000000000211436463760500206210ustar00rootroot00000000000000### TestFile ### rackup-2.1.0/test/cgi/assets/fonts/000077500000000000000000000000001436463760500171715ustar00rootroot00000000000000rackup-2.1.0/test/cgi/assets/fonts/font.eot000066400000000000000000000000211436463760500206410ustar00rootroot00000000000000### TestFile ### rackup-2.1.0/test/cgi/assets/images/000077500000000000000000000000001436463760500173055ustar00rootroot00000000000000rackup-2.1.0/test/cgi/assets/images/image.png000066400000000000000000000000211436463760500210660ustar00rootroot00000000000000### TestFile ### rackup-2.1.0/test/cgi/assets/index.html000066400000000000000000000000211436463760500200260ustar00rootroot00000000000000### TestFile ### rackup-2.1.0/test/cgi/assets/javascripts/000077500000000000000000000000001436463760500203715ustar00rootroot00000000000000rackup-2.1.0/test/cgi/assets/javascripts/app.js000066400000000000000000000000211436463760500215000ustar00rootroot00000000000000### TestFile ### rackup-2.1.0/test/cgi/assets/stylesheets/000077500000000000000000000000001436463760500204145ustar00rootroot00000000000000rackup-2.1.0/test/cgi/assets/stylesheets/app.css000066400000000000000000000000211436463760500216770ustar00rootroot00000000000000### TestFile ### rackup-2.1.0/test/cgi/rackup_stub.rb000077500000000000000000000002731436463760500174120ustar00rootroot00000000000000#!/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.ru000077500000000000000000000001361436463760500177370ustar00rootroot00000000000000# frozen_string_literal: true require '../test_request' run Rack::Lint.new(TestRequest.new) rackup-2.1.0/test/cgi/test000077500000000000000000000003211436463760500154370ustar00rootroot00000000000000***** 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/000077500000000000000000000000001436463760500175155ustar00rootroot00000000000000rackup-2.1.0/test/cgi/test+directory/test+file000066400000000000000000000000271436463760500213310ustar00rootroot00000000000000this file has plusses! rackup-2.1.0/test/cgi/test.gz000066400000000000000000000002731436463760500160610ustar00rootroot00000000000000dZX~=@l9q5EA 0 ut84E V_ou${tQKS NN֡4,eyDʿ׀LrkXV2:M}`dwKG .r\mhpƬ@,7H^<}4sJ7tHrackup-2.1.0/test/cgi/test.ru000077500000000000000000000001601436463760500160650ustar00rootroot00000000000000#!../../bin/rackup # frozen_string_literal: true require '../test_request' run Rack::Lint.new(TestRequest.new) rackup-2.1.0/test/helper.rb000066400000000000000000000012471436463760500156040ustar00rootroot00000000000000# 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/000077500000000000000000000000001436463760500147135ustar00rootroot00000000000000rackup-2.1.0/test/load/rack-test-a.rb000066400000000000000000000001561436463760500173550ustar00rootroot00000000000000# frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. rackup-2.1.0/test/load/rack-test-b.rb000066400000000000000000000001561436463760500173560ustar00rootroot00000000000000# frozen_string_literal: true # Released under the MIT License. # Copyright, 2022-2023, by Samuel Williams. rackup-2.1.0/test/psych_fix.rb000066400000000000000000000003121436463760500163110ustar00rootroot00000000000000# 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/000077500000000000000000000000001436463760500200135ustar00rootroot00000000000000rackup-2.1.0/test/registering_handler/rack/000077500000000000000000000000001436463760500207335ustar00rootroot00000000000000rackup-2.1.0/test/registering_handler/rack/handler/000077500000000000000000000000001436463760500223505ustar00rootroot00000000000000rackup-2.1.0/test/registering_handler/rack/handler/registering_myself.rb000066400000000000000000000003601436463760500265750ustar00rootroot00000000000000# 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.rb000066400000000000000000000035041436463760500167520ustar00rootroot00000000000000# 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.rb000066400000000000000000000025751436463760500170160ustar00rootroot00000000000000# 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.rb000066400000000000000000000473731436463760500166570ustar00rootroot00000000000000# 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.rb000066400000000000000000000142251436463760500167650ustar00rootroot00000000000000# 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.rb000066400000000000000000000041371436463760500170550ustar00rootroot00000000000000# 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/000077500000000000000000000000001436463760500201715ustar00rootroot00000000000000rackup-2.1.0/test/unregistered_handler/rack/000077500000000000000000000000001436463760500211115ustar00rootroot00000000000000rackup-2.1.0/test/unregistered_handler/rack/handler/000077500000000000000000000000001436463760500225265ustar00rootroot00000000000000rackup-2.1.0/test/unregistered_handler/rack/handler/unregistered.rb000066400000000000000000000003741436463760500255570ustar00rootroot00000000000000# 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.rb000066400000000000000000000004031436463760500274300ustar00rootroot00000000000000# 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