rackup 1.0.0 → 2.2.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: 92bf342afdd0c82df66b17a4d0a18102b82ff4f6fe0d6c0a9c6c448c1833b020
4
- data.tar.gz: '0803347f74697ae828f519c5ec3bb3c9d52aa67605c1cc86c918e460ef3b2f22'
3
+ metadata.gz: 5f990d6a3b6103f23a015bacb051fd97d92b81abe5ecf57dc327c7b260d1514e
4
+ data.tar.gz: 92844c4a1bd05547a93a0b4742c20cf6c41b05a67f43d90fc38a17bb147d8fe0
5
5
  SHA512:
6
- metadata.gz: ccc75df436ba2fba6a7971d0eb0db09714cf90364eeca96d380f3cc86b57845e5d4c38fb1bb9d91cce47207656926de42ebec03d2688dad292fc00c48f76dcf0
7
- data.tar.gz: 074af927d0ed8d981ad74d02139e7493269d6406b3426675aab136d051e4d93e7355f98ca5ae4081458e35572efa8360db47fe657e6dce6ab57a7cf711bae626
6
+ metadata.gz: 6ba5437ff800937391e10b6018de1097eb6022329820e6588d34324c35d78a606dc0bdfb17299ee2e9f251a474f765fc87b954bf58ea5ea1754b7848cc8db174
7
+ data.tar.gz: 68c499a29127a5989a4d893cdd64706850d03a30b257ad1b3f3745507652d5678a83eecb18aa398e8c4bfcfd18a2891e1f718e7dc969033d068584d03cfe7128
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,162 @@
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
+ def self.run(app, **options)
20
+ environment = ENV['RACK_ENV'] || 'development'
21
+ default_host = environment == 'development' ? 'localhost' : nil
22
+
23
+ if !options[:BindAddress] || options[:Host]
24
+ options[:BindAddress] = options.delete(:Host) || default_host
25
+ end
26
+ options[:Port] ||= 8080
27
+ if options[:SSLEnable]
28
+ require 'webrick/https'
29
+ end
30
+
31
+ @server = ::WEBrick::HTTPServer.new(options)
32
+ @server.mount "/", Rackup::Handler::WEBrick, app
33
+ yield @server if block_given?
34
+ @server.start
35
+ end
36
+
37
+ def self.valid_options
38
+ environment = ENV['RACK_ENV'] || 'development'
39
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
40
+
41
+ {
42
+ "Host=HOST" => "Hostname to listen on (default: #{default_host})",
43
+ "Port=PORT" => "Port to listen on (default: 8080)",
44
+ }
45
+ end
46
+
47
+ def self.shutdown
48
+ if @server
49
+ @server.shutdown
50
+ @server = nil
51
+ end
52
+ end
53
+
54
+ def initialize(server, app)
55
+ super server
56
+ @app = app
57
+ end
58
+
59
+ # This handles mapping the WEBrick request to a Rack input stream.
60
+ class Input
61
+ include Stream::Reader
62
+
63
+ def initialize(request)
64
+ @request = request
65
+
66
+ @reader = Fiber.new do
67
+ @request.body do |chunk|
68
+ Fiber.yield(chunk)
69
+ end
70
+
71
+ Fiber.yield(nil)
72
+
73
+ # End of stream:
74
+ @reader = nil
75
+ end
76
+ end
77
+
78
+ def close
79
+ @request = nil
80
+ @reader = nil
81
+ end
82
+
83
+ private
84
+
85
+ # Read one chunk from the request body.
86
+ def read_next
87
+ @reader&.resume
88
+ end
89
+ end
90
+
91
+ def service(req, res)
92
+ env = req.meta_vars
93
+ env.delete_if { |k, v| v.nil? }
94
+
95
+ input = Input.new(req)
96
+
97
+ env.update(
98
+ ::Rack::RACK_INPUT => input,
99
+ ::Rack::RACK_ERRORS => $stderr,
100
+ ::Rack::RACK_URL_SCHEME => ["yes", "on", "1"].include?(env[::Rack::HTTPS]) ? "https" : "http",
101
+ ::Rack::RACK_IS_HIJACK => true,
102
+ )
103
+
104
+ env[::Rack::QUERY_STRING] ||= ""
105
+ unless env[::Rack::PATH_INFO] == ""
106
+ path, n = req.request_uri.path, env[::Rack::SCRIPT_NAME].length
107
+ env[::Rack::PATH_INFO] = path[n, path.length - n]
108
+ end
109
+ env[::Rack::REQUEST_PATH] ||= [env[::Rack::SCRIPT_NAME], env[::Rack::PATH_INFO]].join
110
+
111
+ status, headers, body = @app.call(env)
112
+ begin
113
+ res.status = status
114
+
115
+ if value = headers[::Rack::RACK_HIJACK]
116
+ io_lambda = value
117
+ body = nil
118
+ elsif !body.respond_to?(:to_path) && !body.respond_to?(:each)
119
+ io_lambda = body
120
+ body = nil
121
+ end
122
+
123
+ if value = headers.delete('set-cookie')
124
+ res.cookies.concat(Array(value))
125
+ end
126
+
127
+ headers.each do |key, value|
128
+ # Skip keys starting with rack., per Rack SPEC
129
+ next if key.start_with?('rack.')
130
+
131
+ # Since WEBrick won't accept repeated headers,
132
+ # merge the values per RFC 1945 section 4.2.
133
+ value = value.join(", ") if Array === value
134
+ res[key] = value
135
+ end
136
+
137
+ if io_lambda
138
+ protocol = headers['rack.protocol'] || headers['upgrade']
139
+
140
+ if protocol
141
+ # Set all the headers correctly for an upgrade response:
142
+ res.upgrade!(protocol)
143
+ end
144
+ res.body = io_lambda
145
+ elsif body.respond_to?(:to_path)
146
+ res.body = ::File.open(body.to_path, 'rb')
147
+ else
148
+ buffer = String.new
149
+ body.each do |part|
150
+ buffer << part
151
+ end
152
+ res.body = buffer
153
+ end
154
+ ensure
155
+ body.close if body.respond_to?(:close)
156
+ end
157
+ end
158
+ end
159
+
160
+ register :webrick, WEBrick
161
+ end
162
+ 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