rackup 1.0.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8cc45621b46eb19ef735b5e68dd3738292345ccf3bb43554c48f2517c323cbd
4
- data.tar.gz: 49d22e5f97022cc38312ba0afce490ac81f005a6240ee4f3bab521f23f928e8a
3
+ metadata.gz: 500a1eebb3f2dc73a7f4e0b50753603f19f4b2c841a55c01c047188ed6d2214f
4
+ data.tar.gz: 7e51b56d52e396e9c208b06c7a25be3389d0df9c55c3c3333f0379e5aec73719
5
5
  SHA512:
6
- metadata.gz: 84de21b2a8b4bb24a7d4dc58cbe6b74e63bfbf90c7a45ac3dce5cdf900369f7f8664b3e56e4a78cb5d4c68f8228021e09cb2ad9424da208896a7ae5adab0ebe3
7
- data.tar.gz: d9c659a4b6aba77ddefa35947bade7c6ee89e7a6ab3502851fce839c35cc75597649b76c7eff089d84f8e304a70758f0f887e9c0f26ac8e2df28d1d1a93478c9
6
+ metadata.gz: 9aa96ee4990bf4d93f23c4d3d2b16b28c793e9f4fabc2a24a007f241067262168608bcf0e2091368131563dfe9bb4b7fc2845330a75e96c7886b956a12e5f933
7
+ data.tar.gz: 62821749a10a8f337c3f0b4dde6f83fab067c68a1d3864f74a8cd1b28fb62d8be8430a571112ea5f5b60b753ad278140d130ec3f73c3a67f739345b288f73a49
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