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 +4 -4
- data/bin/rackup +5 -0
- data/lib/rackup/handler/cgi.rb +61 -0
- data/lib/rackup/handler/webrick.rb +162 -0
- data/lib/rackup/handler.rb +113 -0
- data/lib/rackup/lobster.rb +81 -0
- data/lib/rackup/server.rb +462 -0
- data/lib/rackup/stream.rb +199 -0
- data/lib/rackup/version.rb +2 -2
- data/lib/rackup.rb +13 -3
- data/license.md +60 -2
- data/readme.md +35 -2
- data/releases.md +24 -0
- metadata +77 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f990d6a3b6103f23a015bacb051fd97d92b81abe5ecf57dc327c7b260d1514e
|
4
|
+
data.tar.gz: 92844c4a1bd05547a93a0b4742c20cf6c41b05a67f43d90fc38a17bb147d8fe0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ba5437ff800937391e10b6018de1097eb6022329820e6588d34324c35d78a606dc0bdfb17299ee2e9f251a474f765fc87b954bf58ea5ea1754b7848cc8db174
|
7
|
+
data.tar.gz: 68c499a29127a5989a4d893cdd64706850d03a30b257ad1b3f3745507652d5678a83eecb18aa398e8c4bfcfd18a2891e1f718e7dc969033d068584d03cfe7128
|
data/bin/rackup
ADDED
@@ -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
|