rackup 1.0.1 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8cc45621b46eb19ef735b5e68dd3738292345ccf3bb43554c48f2517c323cbd
4
- data.tar.gz: 49d22e5f97022cc38312ba0afce490ac81f005a6240ee4f3bab521f23f928e8a
3
+ metadata.gz: 1e1f9c41ea38c6bde36ea2c4e61d66ffe4a61d2a92fe73bbbe4090d0adc3cad6
4
+ data.tar.gz: e08ff923a6568b6b30f5810cbfcfc046e536d944086e97960cf3869daaff0ae5
5
5
  SHA512:
6
- metadata.gz: 84de21b2a8b4bb24a7d4dc58cbe6b74e63bfbf90c7a45ac3dce5cdf900369f7f8664b3e56e4a78cb5d4c68f8228021e09cb2ad9424da208896a7ae5adab0ebe3
7
- data.tar.gz: d9c659a4b6aba77ddefa35947bade7c6ee89e7a6ab3502851fce839c35cc75597649b76c7eff089d84f8e304a70758f0f887e9c0f26ac8e2df28d1d1a93478c9
6
+ metadata.gz: 0fb28d087940a39f79a4bfa6749455c41bae3c48ec652b054c1fc99acf4b6176f892a7b6ffb300304ceb2bd5378f129c3793da08e507628f1acb02c31cf3d10c
7
+ data.tar.gz: 5ab015f40bd2f87d3ae8ae71afc10a567ce1d6cb4ed7be74b029f4f0e8ccaf7292a96df731e1b5b13f6ab45865b5a2bbe11ad32b47dc65bed7e058567e5a51b0
data/bin/rackup ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/rackup"
5
+ Rackup::Server.start
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2023, by Samuel Williams.
5
+
6
+ module Rackup
7
+ module Handler
8
+ class CGI
9
+ include Rack
10
+
11
+ def self.run(app, **options)
12
+ $stdin.binmode
13
+ serve app
14
+ end
15
+
16
+ def self.serve(app)
17
+ env = ENV.to_hash
18
+ env.delete "HTTP_CONTENT_LENGTH"
19
+
20
+ env[SCRIPT_NAME] = "" if env[SCRIPT_NAME] == "/"
21
+
22
+ env.update(
23
+ RACK_INPUT => $stdin,
24
+ RACK_ERRORS => $stderr,
25
+ RACK_URL_SCHEME => ["yes", "on", "1"].include?(ENV[HTTPS]) ? "https" : "http"
26
+ )
27
+
28
+ env[QUERY_STRING] ||= ""
29
+ env[REQUEST_PATH] ||= "/"
30
+
31
+ status, headers, body = app.call(env)
32
+ begin
33
+ send_headers status, headers
34
+ send_body body
35
+ ensure
36
+ body.close if body.respond_to? :close
37
+ end
38
+ end
39
+
40
+ def self.send_headers(status, headers)
41
+ $stdout.print "Status: #{status}\r\n"
42
+ headers.each { |k, vs|
43
+ vs.split("\n").each { |v|
44
+ $stdout.print "#{k}: #{v}\r\n"
45
+ }
46
+ }
47
+ $stdout.print "\r\n"
48
+ $stdout.flush
49
+ end
50
+
51
+ def self.send_body(body)
52
+ body.each { |part|
53
+ $stdout.print part
54
+ $stdout.flush
55
+ }
56
+ end
57
+ end
58
+
59
+ register :cgi, CGI
60
+ end
61
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2023, by Samuel Williams.
5
+ # Copyright, 2022, by Jeremy Evans.
6
+
7
+ require 'webrick'
8
+ require 'stringio'
9
+
10
+ require 'rack/constants'
11
+ require_relative '../handler'
12
+ require_relative '../version'
13
+
14
+ require_relative '../stream'
15
+
16
+ module Rackup
17
+ module Handler
18
+ class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
19
+ # A WEBrick HTTPServer subclass that invokes the Rack app directly,
20
+ # bypassing the mount table and default OPTIONS * handling.
21
+ class Server < ::WEBrick::HTTPServer
22
+ def initialize(app, config)
23
+ super(config)
24
+ @handler = Handler::WEBrick.new(self, app)
25
+ end
26
+
27
+ def service(req, res)
28
+ @handler.service(req, res)
29
+ end
30
+ end
31
+
32
+ def self.run(app, **options)
33
+ environment = ENV['RACK_ENV'] || 'development'
34
+ default_host = environment == 'development' ? 'localhost' : nil
35
+
36
+ if !options[:BindAddress] || options[:Host]
37
+ options[:BindAddress] = options.delete(:Host) || default_host
38
+ end
39
+ options[:Port] ||= 8080
40
+ if options[:SSLEnable]
41
+ require 'webrick/https'
42
+ end
43
+
44
+ @server = Server.new(app, options)
45
+ yield @server if block_given?
46
+ @server.start
47
+ end
48
+
49
+ def self.valid_options
50
+ environment = ENV['RACK_ENV'] || 'development'
51
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
52
+
53
+ {
54
+ "Host=HOST" => "Hostname to listen on (default: #{default_host})",
55
+ "Port=PORT" => "Port to listen on (default: 8080)",
56
+ }
57
+ end
58
+
59
+ def self.shutdown
60
+ if @server
61
+ @server.shutdown
62
+ @server = nil
63
+ end
64
+ end
65
+
66
+ def initialize(server, app)
67
+ super server
68
+ @app = app
69
+ end
70
+
71
+ # This handles mapping the WEBrick request to a Rack input stream.
72
+ class Input
73
+ include Stream::Reader
74
+
75
+ def initialize(request)
76
+ @request = request
77
+
78
+ @reader = Fiber.new do
79
+ @request.body do |chunk|
80
+ Fiber.yield(chunk)
81
+ end
82
+
83
+ Fiber.yield(nil)
84
+
85
+ # End of stream:
86
+ @reader = nil
87
+ end
88
+ end
89
+
90
+ def close
91
+ @request = nil
92
+ @reader = nil
93
+ end
94
+
95
+ private
96
+
97
+ # Read one chunk from the request body.
98
+ def read_next
99
+ @reader&.resume
100
+ end
101
+ end
102
+
103
+ def service(req, res)
104
+ env = req.meta_vars
105
+ env.delete_if { |k, v| v.nil? }
106
+
107
+ input = Input.new(req)
108
+
109
+ env.update(
110
+ ::Rack::RACK_INPUT => input,
111
+ ::Rack::RACK_ERRORS => $stderr,
112
+ ::Rack::RACK_URL_SCHEME => ["yes", "on", "1"].include?(env[::Rack::HTTPS]) ? "https" : "http",
113
+ ::Rack::RACK_IS_HIJACK => true,
114
+ )
115
+
116
+ env[::Rack::QUERY_STRING] ||= ""
117
+
118
+ # Handle OPTIONS * requests which have no path
119
+ if req.unparsed_uri == "*"
120
+ env[::Rack::PATH_INFO] = "*"
121
+ env[::Rack::REQUEST_PATH] = "*"
122
+
123
+ # Ensure SERVER_NAME and SERVER_PORT are set from server config.
124
+ # (WEBrick allows these to be nil for OPTIONS * requests)
125
+ # See https://github.com/ruby/webrick/pull/182 for a proper fix.
126
+ server_name = env[::Rack::SERVER_NAME]
127
+ if server_name.nil? || server_name == ""
128
+ env[::Rack::SERVER_NAME] = @server[:ServerName] || @server[:BindAddress] || "localhost"
129
+ end
130
+
131
+ # Legacy versions of WEBrick can set server_port to "" in some cases:
132
+ server_port = env[::Rack::SERVER_PORT]
133
+ if server_port.nil? || server_port == ""
134
+ env[::Rack::SERVER_PORT] = (@server[:Port] || 80).to_s
135
+ end
136
+ else
137
+ unless env[::Rack::PATH_INFO] == ""
138
+ # Strip the script name prefix from the path to get path info
139
+ script_name_length = env[::Rack::SCRIPT_NAME].length
140
+ env[::Rack::PATH_INFO] = req.request_uri.path[script_name_length..-1] || ""
141
+ end
142
+ env[::Rack::REQUEST_PATH] ||= env[::Rack::SCRIPT_NAME] + env[::Rack::PATH_INFO]
143
+ end
144
+
145
+ status, headers, body = @app.call(env)
146
+ begin
147
+ res.status = status
148
+
149
+ if value = headers[::Rack::RACK_HIJACK]
150
+ io_lambda = value
151
+ body = nil
152
+ elsif !body.respond_to?(:to_path) && !body.respond_to?(:each)
153
+ io_lambda = body
154
+ body = nil
155
+ end
156
+
157
+ if value = headers.delete('set-cookie')
158
+ res.cookies.concat(Array(value))
159
+ end
160
+
161
+ headers.each do |key, value|
162
+ # Skip keys starting with rack., per Rack SPEC
163
+ next if key.start_with?('rack.')
164
+
165
+ # Since WEBrick won't accept repeated headers,
166
+ # merge the values per RFC 1945 section 4.2.
167
+ value = value.join(", ") if Array === value
168
+ res[key] = value
169
+ end
170
+
171
+ if io_lambda
172
+ protocol = headers['rack.protocol'] || headers['upgrade']
173
+
174
+ if protocol
175
+ # Set all the headers correctly for an upgrade response:
176
+ res.upgrade!(protocol)
177
+ end
178
+ res.body = io_lambda
179
+ elsif body.respond_to?(:to_path)
180
+ res.body = ::File.open(body.to_path, 'rb')
181
+ else
182
+ buffer = String.new
183
+ body.each do |part|
184
+ buffer << part
185
+ end
186
+ res.body = buffer
187
+ end
188
+ ensure
189
+ body.close if body.respond_to?(:close)
190
+ end
191
+ end
192
+ end
193
+
194
+ register :webrick, WEBrick
195
+ end
196
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2023, by Samuel Williams.
5
+
6
+ module Rackup
7
+ # *Handlers* connect web servers with Rack.
8
+ #
9
+ # Rackup includes Handlers for WEBrick and CGI.
10
+ #
11
+ # Handlers usually are activated by calling <tt>MyHandler.run(myapp)</tt>.
12
+ # A second optional hash can be passed to include server-specific
13
+ # configuration.
14
+ module Handler
15
+ @handlers = {}
16
+
17
+ # Register a named handler class.
18
+ def self.register(name, klass)
19
+ if klass.is_a?(String)
20
+ warn "Calling Rackup::Handler.register with a string is deprecated, use the class/module itself.", uplevel: 1
21
+
22
+ klass = self.const_get(klass, false)
23
+ end
24
+
25
+ name = name.to_sym
26
+
27
+ @handlers[name] = klass
28
+ end
29
+
30
+ def self.[](name)
31
+ name = name.to_sym
32
+
33
+ begin
34
+ @handlers[name] || self.const_get(name, false)
35
+ rescue NameError
36
+ # Ignore.
37
+ end
38
+ end
39
+
40
+ def self.get(name)
41
+ return nil unless name
42
+
43
+ name = name.to_sym
44
+
45
+ if server = self[name]
46
+ return server
47
+ end
48
+
49
+ begin
50
+ require_handler("rackup/handler", name)
51
+ rescue LoadError
52
+ require_handler("rack/handler", name)
53
+ end
54
+
55
+ return self[name]
56
+ end
57
+
58
+ RACK_HANDLER = 'RACK_HANDLER'
59
+ RACKUP_HANDLER = 'RACKUP_HANDLER'
60
+
61
+ SERVER_NAMES = %i(puma falcon webrick).freeze
62
+ private_constant :SERVER_NAMES
63
+
64
+ # Select first available Rack handler given an `Array` of server names.
65
+ # Raises `LoadError` if no handler was found.
66
+ #
67
+ # > pick ['puma', 'webrick']
68
+ # => Rackup::Handler::WEBrick
69
+ def self.pick(server_names)
70
+ server_names = Array(server_names)
71
+
72
+ server_names.each do |server_name|
73
+ begin
74
+ server = self.get(server_name)
75
+ return server if server
76
+ rescue LoadError
77
+ # Ignore.
78
+ end
79
+ end
80
+
81
+ raise LoadError, "Couldn't find handler for: #{server_names.join(', ')}."
82
+ end
83
+
84
+ def self.default
85
+ if rack_handler = ENV[RACKUP_HANDLER]
86
+ self.get(rack_handler)
87
+ elsif rack_handler = ENV[RACK_HANDLER]
88
+ warn "RACK_HANDLER is deprecated, use RACKUP_HANDLER."
89
+ self.get(rack_handler)
90
+ else
91
+ pick SERVER_NAMES
92
+ end
93
+ end
94
+
95
+ # Transforms server-name constants to their canonical form as filenames,
96
+ # then tries to require them but silences the LoadError if not found
97
+ #
98
+ # Naming convention:
99
+ #
100
+ # Foo # => 'foo'
101
+ # FooBar # => 'foo_bar.rb'
102
+ # FooBAR # => 'foobar.rb'
103
+ # FOObar # => 'foobar.rb'
104
+ # FOOBAR # => 'foobar.rb'
105
+ # FooBarBaz # => 'foo_bar_baz.rb'
106
+ def self.require_handler(prefix, const_name)
107
+ file = const_name.to_s.gsub(/^[A-Z]+/) { |pre| pre.downcase }.
108
+ gsub(/[A-Z]+[^A-Z]/, '_\&').downcase
109
+
110
+ require(::File.join(prefix, file))
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022-2023, by Samuel Williams.
5
+
6
+ require 'zlib'
7
+
8
+ require 'rack/constants'
9
+ require 'rack/request'
10
+ require 'rack/response'
11
+
12
+ module Rackup
13
+ # Paste has a Pony, Rack has a Lobster!
14
+ class Lobster
15
+ include Rack
16
+
17
+ LobsterString = Zlib::Inflate.inflate("eJx9kEEOwyAMBO99xd7MAcytUhPlJyj2
18
+ P6jy9i4k9EQyGAnBarEXeCBqSkntNXsi/ZCvC48zGQoZKikGrFMZvgS5ZHd+aGWVuWwhVF0
19
+ t1drVmiR42HcWNz5w3QanT+2gIvTVCiE1lm1Y0eU4JGmIIbaKwextKn8rvW+p5PIwFl8ZWJ
20
+ I8jyiTlhTcYXkekJAzTyYN6E08A+dk8voBkAVTJQ==".delete("\n ").unpack("m*")[0])
21
+
22
+ LambdaLobster = lambda { |env|
23
+ if env[QUERY_STRING].include?("flip")
24
+ lobster = LobsterString.split("\n").
25
+ map { |line| line.ljust(42).reverse }.
26
+ join("\n")
27
+ href = "?"
28
+ else
29
+ lobster = LobsterString
30
+ href = "?flip"
31
+ end
32
+
33
+ content = ["<title>Lobstericious!</title>",
34
+ "<pre>", lobster, "</pre>",
35
+ "<a href='#{href}'>flip!</a>"]
36
+ length = content.inject(0) { |a, e| a + e.size }.to_s
37
+ [200, { CONTENT_TYPE => "text/html", CONTENT_LENGTH => length }, content]
38
+ }
39
+
40
+ def call(env)
41
+ req = Request.new(env)
42
+ if req.GET["flip"] == "left"
43
+ lobster = LobsterString.split("\n").map do |line|
44
+ line.ljust(42).reverse.
45
+ gsub('\\', 'TEMP').
46
+ gsub('/', '\\').
47
+ gsub('TEMP', '/').
48
+ gsub('{', '}').
49
+ gsub('(', ')')
50
+ end.join("\n")
51
+ href = "?flip=right"
52
+ elsif req.GET["flip"] == "crash"
53
+ raise "Lobster crashed"
54
+ else
55
+ lobster = LobsterString
56
+ href = "?flip=left"
57
+ end
58
+
59
+ res = Response.new
60
+ res.write "<title>Lobstericious!</title>"
61
+ res.write "<pre>"
62
+ res.write lobster
63
+ res.write "</pre>"
64
+ res.write "<p><a href='#{href}'>flip!</a></p>"
65
+ res.write "<p><a href='?flip=crash'>crash!</a></p>"
66
+ res.finish
67
+ end
68
+
69
+ end
70
+ end
71
+
72
+ if $0 == __FILE__
73
+ # :nocov:
74
+ require_relative 'server'
75
+ require_relative 'show_exceptions'
76
+ require_relative 'lint'
77
+ Rackup::Server.start(
78
+ app: Rack::ShowExceptions.new(Rack::Lint.new(Rackup::Lobster.new)), Port: 9292
79
+ )
80
+ # :nocov:
81
+ end