rack 2.2.10 → 3.1.10

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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +346 -90
  3. data/CONTRIBUTING.md +63 -55
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +328 -0
  6. data/SPEC.rdoc +204 -131
  7. data/lib/rack/auth/abstract/handler.rb +3 -1
  8. data/lib/rack/auth/abstract/request.rb +3 -1
  9. data/lib/rack/auth/basic.rb +1 -3
  10. data/lib/rack/bad_request.rb +8 -0
  11. data/lib/rack/body_proxy.rb +21 -3
  12. data/lib/rack/builder.rb +102 -69
  13. data/lib/rack/cascade.rb +2 -3
  14. data/lib/rack/common_logger.rb +25 -19
  15. data/lib/rack/conditional_get.rb +18 -15
  16. data/lib/rack/constants.rb +67 -0
  17. data/lib/rack/content_length.rb +12 -16
  18. data/lib/rack/content_type.rb +8 -5
  19. data/lib/rack/deflater.rb +40 -26
  20. data/lib/rack/directory.rb +9 -3
  21. data/lib/rack/etag.rb +14 -23
  22. data/lib/rack/events.rb +4 -0
  23. data/lib/rack/files.rb +15 -17
  24. data/lib/rack/head.rb +9 -8
  25. data/lib/rack/headers.rb +238 -0
  26. data/lib/rack/lint.rb +840 -644
  27. data/lib/rack/lock.rb +2 -5
  28. data/lib/rack/logger.rb +3 -0
  29. data/lib/rack/media_type.rb +8 -3
  30. data/lib/rack/method_override.rb +5 -1
  31. data/lib/rack/mime.rb +14 -5
  32. data/lib/rack/mock.rb +1 -271
  33. data/lib/rack/mock_request.rb +161 -0
  34. data/lib/rack/mock_response.rb +124 -0
  35. data/lib/rack/multipart/generator.rb +7 -5
  36. data/lib/rack/multipart/parser.rb +213 -95
  37. data/lib/rack/multipart/uploaded_file.rb +4 -0
  38. data/lib/rack/multipart.rb +53 -40
  39. data/lib/rack/null_logger.rb +9 -0
  40. data/lib/rack/query_parser.rb +81 -102
  41. data/lib/rack/recursive.rb +2 -0
  42. data/lib/rack/reloader.rb +0 -2
  43. data/lib/rack/request.rb +260 -123
  44. data/lib/rack/response.rb +151 -66
  45. data/lib/rack/rewindable_input.rb +24 -5
  46. data/lib/rack/runtime.rb +7 -6
  47. data/lib/rack/sendfile.rb +30 -25
  48. data/lib/rack/show_exceptions.rb +21 -4
  49. data/lib/rack/show_status.rb +17 -7
  50. data/lib/rack/static.rb +8 -8
  51. data/lib/rack/tempfile_reaper.rb +15 -4
  52. data/lib/rack/urlmap.rb +3 -1
  53. data/lib/rack/utils.rb +236 -237
  54. data/lib/rack/version.rb +1 -9
  55. data/lib/rack.rb +13 -89
  56. metadata +15 -44
  57. data/README.rdoc +0 -320
  58. data/Rakefile +0 -130
  59. data/bin/rackup +0 -5
  60. data/contrib/rack.png +0 -0
  61. data/contrib/rack.svg +0 -150
  62. data/contrib/rack_logo.svg +0 -164
  63. data/contrib/rdoc.css +0 -412
  64. data/example/lobster.ru +0 -6
  65. data/example/protectedlobster.rb +0 -16
  66. data/example/protectedlobster.ru +0 -10
  67. data/lib/rack/auth/digest/md5.rb +0 -131
  68. data/lib/rack/auth/digest/nonce.rb +0 -53
  69. data/lib/rack/auth/digest/params.rb +0 -54
  70. data/lib/rack/auth/digest/request.rb +0 -43
  71. data/lib/rack/chunked.rb +0 -117
  72. data/lib/rack/core_ext/regexp.rb +0 -14
  73. data/lib/rack/file.rb +0 -7
  74. data/lib/rack/handler/cgi.rb +0 -59
  75. data/lib/rack/handler/fastcgi.rb +0 -100
  76. data/lib/rack/handler/lsws.rb +0 -61
  77. data/lib/rack/handler/scgi.rb +0 -71
  78. data/lib/rack/handler/thin.rb +0 -36
  79. data/lib/rack/handler/webrick.rb +0 -129
  80. data/lib/rack/handler.rb +0 -104
  81. data/lib/rack/lobster.rb +0 -70
  82. data/lib/rack/server.rb +0 -466
  83. data/lib/rack/session/abstract/id.rb +0 -523
  84. data/lib/rack/session/cookie.rb +0 -203
  85. data/lib/rack/session/memcache.rb +0 -10
  86. data/lib/rack/session/pool.rb +0 -85
  87. data/rack.gemspec +0 -46
data/lib/rack/chunked.rb DELETED
@@ -1,117 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Rack
4
-
5
- # Middleware that applies chunked transfer encoding to response bodies
6
- # when the response does not include a Content-Length header.
7
- #
8
- # This supports the Trailer response header to allow the use of trailing
9
- # headers in the chunked encoding. However, using this requires you manually
10
- # specify a response body that supports a +trailers+ method. Example:
11
- #
12
- # [200, { 'Trailer' => 'Expires'}, ["Hello", "World"]]
13
- # # error raised
14
- #
15
- # body = ["Hello", "World"]
16
- # def body.trailers
17
- # { 'Expires' => Time.now.to_s }
18
- # end
19
- # [200, { 'Trailer' => 'Expires'}, body]
20
- # # No exception raised
21
- class Chunked
22
- include Rack::Utils
23
-
24
- # A body wrapper that emits chunked responses.
25
- class Body
26
- TERM = "\r\n"
27
- TAIL = "0#{TERM}"
28
-
29
- # Store the response body to be chunked.
30
- def initialize(body)
31
- @body = body
32
- end
33
-
34
- # For each element yielded by the response body, yield
35
- # the element in chunked encoding.
36
- def each(&block)
37
- term = TERM
38
- @body.each do |chunk|
39
- size = chunk.bytesize
40
- next if size == 0
41
-
42
- yield [size.to_s(16), term, chunk.b, term].join
43
- end
44
- yield TAIL
45
- yield_trailers(&block)
46
- yield term
47
- end
48
-
49
- # Close the response body if the response body supports it.
50
- def close
51
- @body.close if @body.respond_to?(:close)
52
- end
53
-
54
- private
55
-
56
- # Do nothing as this class does not support trailer headers.
57
- def yield_trailers
58
- end
59
- end
60
-
61
- # A body wrapper that emits chunked responses and also supports
62
- # sending Trailer headers. Note that the response body provided to
63
- # initialize must have a +trailers+ method that returns a hash
64
- # of trailer headers, and the rack response itself should have a
65
- # Trailer header listing the headers that the +trailers+ method
66
- # will return.
67
- class TrailerBody < Body
68
- private
69
-
70
- # Yield strings for each trailer header.
71
- def yield_trailers
72
- @body.trailers.each_pair do |k, v|
73
- yield "#{k}: #{v}\r\n"
74
- end
75
- end
76
- end
77
-
78
- def initialize(app)
79
- @app = app
80
- end
81
-
82
- # Whether the HTTP version supports chunked encoding (HTTP 1.1 does).
83
- def chunkable_version?(ver)
84
- case ver
85
- # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
86
- # a version (nor response headers)
87
- when 'HTTP/1.0', nil, 'HTTP/0.9'
88
- false
89
- else
90
- true
91
- end
92
- end
93
-
94
- # If the rack app returns a response that should have a body,
95
- # but does not have Content-Length or Transfer-Encoding headers,
96
- # modify the response to use chunked Transfer-Encoding.
97
- def call(env)
98
- status, headers, body = @app.call(env)
99
- headers = HeaderHash[headers]
100
-
101
- if chunkable_version?(env[SERVER_PROTOCOL]) &&
102
- !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
103
- !headers[CONTENT_LENGTH] &&
104
- !headers[TRANSFER_ENCODING]
105
-
106
- headers[TRANSFER_ENCODING] = 'chunked'
107
- if headers['Trailer']
108
- body = TrailerBody.new(body)
109
- else
110
- body = Body.new(body)
111
- end
112
- end
113
-
114
- [status, headers, body]
115
- end
116
- end
117
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Regexp has `match?` since Ruby 2.4
4
- # so to support Ruby < 2.4 we need to define this method
5
-
6
- module Rack
7
- module RegexpExtensions
8
- refine Regexp do
9
- def match?(string, pos = 0)
10
- !!match(string, pos)
11
- end
12
- end unless //.respond_to?(:match?)
13
- end
14
- end
data/lib/rack/file.rb DELETED
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'files'
4
-
5
- module Rack
6
- File = Files
7
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Rack
4
- module Handler
5
- class CGI
6
- def self.run(app, **options)
7
- $stdin.binmode
8
- serve app
9
- end
10
-
11
- def self.serve(app)
12
- env = ENV.to_hash
13
- env.delete "HTTP_CONTENT_LENGTH"
14
-
15
- env[SCRIPT_NAME] = "" if env[SCRIPT_NAME] == "/"
16
-
17
- env.update(
18
- RACK_VERSION => Rack::VERSION,
19
- RACK_INPUT => Rack::RewindableInput.new($stdin),
20
- RACK_ERRORS => $stderr,
21
- RACK_MULTITHREAD => false,
22
- RACK_MULTIPROCESS => true,
23
- RACK_RUNONCE => true,
24
- RACK_URL_SCHEME => ["yes", "on", "1"].include?(ENV[HTTPS]) ? "https" : "http"
25
- )
26
-
27
- env[QUERY_STRING] ||= ""
28
- env[HTTP_VERSION] ||= env[SERVER_PROTOCOL]
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
- end
59
- end
@@ -1,100 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'fcgi'
4
- require 'socket'
5
-
6
- if defined? FCGI::Stream
7
- class FCGI::Stream
8
- alias _rack_read_without_buffer read
9
-
10
- def read(n, buffer = nil)
11
- buf = _rack_read_without_buffer n
12
- buffer.replace(buf.to_s) if buffer
13
- buf
14
- end
15
- end
16
- end
17
-
18
- module Rack
19
- module Handler
20
- class FastCGI
21
- def self.run(app, **options)
22
- if options[:File]
23
- STDIN.reopen(UNIXServer.new(options[:File]))
24
- elsif options[:Port]
25
- STDIN.reopen(TCPServer.new(options[:Host], options[:Port]))
26
- end
27
- FCGI.each { |request|
28
- serve request, app
29
- }
30
- end
31
-
32
- def self.valid_options
33
- environment = ENV['RACK_ENV'] || 'development'
34
- default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
35
-
36
- {
37
- "Host=HOST" => "Hostname to listen on (default: #{default_host})",
38
- "Port=PORT" => "Port to listen on (default: 8080)",
39
- "File=PATH" => "Creates a Domain socket at PATH instead of a TCP socket. Ignores Host and Port if set.",
40
- }
41
- end
42
-
43
- def self.serve(request, app)
44
- env = request.env
45
- env.delete "HTTP_CONTENT_LENGTH"
46
-
47
- env[SCRIPT_NAME] = "" if env[SCRIPT_NAME] == "/"
48
-
49
- rack_input = RewindableInput.new(request.in)
50
-
51
- env.update(
52
- RACK_VERSION => Rack::VERSION,
53
- RACK_INPUT => rack_input,
54
- RACK_ERRORS => request.err,
55
- RACK_MULTITHREAD => false,
56
- RACK_MULTIPROCESS => true,
57
- RACK_RUNONCE => false,
58
- RACK_URL_SCHEME => ["yes", "on", "1"].include?(env[HTTPS]) ? "https" : "http"
59
- )
60
-
61
- env[QUERY_STRING] ||= ""
62
- env[HTTP_VERSION] ||= env[SERVER_PROTOCOL]
63
- env[REQUEST_PATH] ||= "/"
64
- env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == ""
65
- env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == ""
66
-
67
- begin
68
- status, headers, body = app.call(env)
69
- begin
70
- send_headers request.out, status, headers
71
- send_body request.out, body
72
- ensure
73
- body.close if body.respond_to? :close
74
- end
75
- ensure
76
- rack_input.close
77
- request.finish
78
- end
79
- end
80
-
81
- def self.send_headers(out, status, headers)
82
- out.print "Status: #{status}\r\n"
83
- headers.each { |k, vs|
84
- vs.split("\n").each { |v|
85
- out.print "#{k}: #{v}\r\n"
86
- }
87
- }
88
- out.print "\r\n"
89
- out.flush
90
- end
91
-
92
- def self.send_body(out, body)
93
- body.each { |part|
94
- out.print part
95
- out.flush
96
- }
97
- end
98
- end
99
- end
100
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'lsapi'
4
-
5
- module Rack
6
- module Handler
7
- class LSWS
8
- def self.run(app, **options)
9
- while LSAPI.accept != nil
10
- serve app
11
- end
12
- end
13
- def self.serve(app)
14
- env = ENV.to_hash
15
- env.delete "HTTP_CONTENT_LENGTH"
16
- env[SCRIPT_NAME] = "" if env[SCRIPT_NAME] == "/"
17
-
18
- rack_input = RewindableInput.new($stdin.read.to_s)
19
-
20
- env.update(
21
- RACK_VERSION => Rack::VERSION,
22
- RACK_INPUT => rack_input,
23
- RACK_ERRORS => $stderr,
24
- RACK_MULTITHREAD => false,
25
- RACK_MULTIPROCESS => true,
26
- RACK_RUNONCE => false,
27
- RACK_URL_SCHEME => ["yes", "on", "1"].include?(ENV[HTTPS]) ? "https" : "http"
28
- )
29
-
30
- env[QUERY_STRING] ||= ""
31
- env[HTTP_VERSION] ||= env[SERVER_PROTOCOL]
32
- env[REQUEST_PATH] ||= "/"
33
- status, headers, body = app.call(env)
34
- begin
35
- send_headers status, headers
36
- send_body body
37
- ensure
38
- body.close if body.respond_to? :close
39
- end
40
- ensure
41
- rack_input.close
42
- end
43
- def self.send_headers(status, headers)
44
- print "Status: #{status}\r\n"
45
- headers.each { |k, vs|
46
- vs.split("\n").each { |v|
47
- print "#{k}: #{v}\r\n"
48
- }
49
- }
50
- print "\r\n"
51
- STDOUT.flush
52
- end
53
- def self.send_body(body)
54
- body.each { |part|
55
- print part
56
- STDOUT.flush
57
- }
58
- end
59
- end
60
- end
61
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'scgi'
4
- require 'stringio'
5
-
6
- module Rack
7
- module Handler
8
- class SCGI < ::SCGI::Processor
9
- attr_accessor :app
10
-
11
- def self.run(app, **options)
12
- options[:Socket] = UNIXServer.new(options[:File]) if options[:File]
13
- new(options.merge(app: app,
14
- host: options[:Host],
15
- port: options[:Port],
16
- socket: options[:Socket])).listen
17
- end
18
-
19
- def self.valid_options
20
- environment = ENV['RACK_ENV'] || 'development'
21
- default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
22
-
23
- {
24
- "Host=HOST" => "Hostname to listen on (default: #{default_host})",
25
- "Port=PORT" => "Port to listen on (default: 8080)",
26
- }
27
- end
28
-
29
- def initialize(settings = {})
30
- @app = settings[:app]
31
- super(settings)
32
- end
33
-
34
- def process_request(request, input_body, socket)
35
- env = Hash[request]
36
- env.delete "HTTP_CONTENT_TYPE"
37
- env.delete "HTTP_CONTENT_LENGTH"
38
- env[REQUEST_PATH], env[QUERY_STRING] = env["REQUEST_URI"].split('?', 2)
39
- env[HTTP_VERSION] ||= env[SERVER_PROTOCOL]
40
- env[PATH_INFO] = env[REQUEST_PATH]
41
- env[QUERY_STRING] ||= ""
42
- env[SCRIPT_NAME] = ""
43
-
44
- rack_input = StringIO.new(input_body)
45
- rack_input.set_encoding(Encoding::BINARY)
46
-
47
- env.update(
48
- RACK_VERSION => Rack::VERSION,
49
- RACK_INPUT => rack_input,
50
- RACK_ERRORS => $stderr,
51
- RACK_MULTITHREAD => true,
52
- RACK_MULTIPROCESS => true,
53
- RACK_RUNONCE => false,
54
- RACK_URL_SCHEME => ["yes", "on", "1"].include?(env[HTTPS]) ? "https" : "http"
55
- )
56
-
57
- status, headers, body = app.call(env)
58
- begin
59
- socket.write("Status: #{status}\r\n")
60
- headers.each do |k, vs|
61
- vs.split("\n").each { |v| socket.write("#{k}: #{v}\r\n")}
62
- end
63
- socket.write("\r\n")
64
- body.each {|s| socket.write(s)}
65
- ensure
66
- body.close if body.respond_to? :close
67
- end
68
- end
69
- end
70
- end
71
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "thin"
4
- require "thin/server"
5
- require "thin/logging"
6
- require "thin/backends/tcp_server"
7
-
8
- module Rack
9
- module Handler
10
- class Thin
11
- def self.run(app, **options)
12
- environment = ENV['RACK_ENV'] || 'development'
13
- default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
14
-
15
- host = options.delete(:Host) || default_host
16
- port = options.delete(:Port) || 8080
17
- args = [host, port, app, options]
18
- # Thin versions below 0.8.0 do not support additional options
19
- args.pop if ::Thin::VERSION::MAJOR < 1 && ::Thin::VERSION::MINOR < 8
20
- server = ::Thin::Server.new(*args)
21
- yield server if block_given?
22
- server.start
23
- end
24
-
25
- def self.valid_options
26
- environment = ENV['RACK_ENV'] || 'development'
27
- default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
28
-
29
- {
30
- "Host=HOST" => "Hostname to listen on (default: #{default_host})",
31
- "Port=PORT" => "Port to listen on (default: 8080)",
32
- }
33
- end
34
- end
35
- end
36
- end
@@ -1,129 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'webrick'
4
- require 'stringio'
5
-
6
- # This monkey patch allows for applications to perform their own chunking
7
- # through WEBrick::HTTPResponse if rack is set to true.
8
- class WEBrick::HTTPResponse
9
- attr_accessor :rack
10
-
11
- alias _rack_setup_header setup_header
12
- def setup_header
13
- app_chunking = rack && @header['transfer-encoding'] == 'chunked'
14
-
15
- @chunked = app_chunking if app_chunking
16
-
17
- _rack_setup_header
18
-
19
- @chunked = false if app_chunking
20
- end
21
- end
22
-
23
- module Rack
24
- module Handler
25
- class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
26
- def self.run(app, **options)
27
- environment = ENV['RACK_ENV'] || 'development'
28
- default_host = environment == 'development' ? 'localhost' : nil
29
-
30
- if !options[:BindAddress] || options[:Host]
31
- options[:BindAddress] = options.delete(:Host) || default_host
32
- end
33
- options[:Port] ||= 8080
34
- if options[:SSLEnable]
35
- require 'webrick/https'
36
- end
37
-
38
- @server = ::WEBrick::HTTPServer.new(options)
39
- @server.mount "/", Rack::Handler::WEBrick, app
40
- yield @server if block_given?
41
- @server.start
42
- end
43
-
44
- def self.valid_options
45
- environment = ENV['RACK_ENV'] || 'development'
46
- default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
47
-
48
- {
49
- "Host=HOST" => "Hostname to listen on (default: #{default_host})",
50
- "Port=PORT" => "Port to listen on (default: 8080)",
51
- }
52
- end
53
-
54
- def self.shutdown
55
- if @server
56
- @server.shutdown
57
- @server = nil
58
- end
59
- end
60
-
61
- def initialize(server, app)
62
- super server
63
- @app = app
64
- end
65
-
66
- def service(req, res)
67
- res.rack = true
68
- env = req.meta_vars
69
- env.delete_if { |k, v| v.nil? }
70
-
71
- rack_input = StringIO.new(req.body.to_s)
72
- rack_input.set_encoding(Encoding::BINARY)
73
-
74
- env.update(
75
- RACK_VERSION => Rack::VERSION,
76
- RACK_INPUT => rack_input,
77
- RACK_ERRORS => $stderr,
78
- RACK_MULTITHREAD => true,
79
- RACK_MULTIPROCESS => false,
80
- RACK_RUNONCE => false,
81
- RACK_URL_SCHEME => ["yes", "on", "1"].include?(env[HTTPS]) ? "https" : "http",
82
- RACK_IS_HIJACK => true,
83
- RACK_HIJACK => lambda { raise NotImplementedError, "only partial hijack is supported."},
84
- RACK_HIJACK_IO => nil
85
- )
86
-
87
- env[HTTP_VERSION] ||= env[SERVER_PROTOCOL]
88
- env[QUERY_STRING] ||= ""
89
- unless env[PATH_INFO] == ""
90
- path, n = req.request_uri.path, env[SCRIPT_NAME].length
91
- env[PATH_INFO] = path[n, path.length - n]
92
- end
93
- env[REQUEST_PATH] ||= [env[SCRIPT_NAME], env[PATH_INFO]].join
94
-
95
- status, headers, body = @app.call(env)
96
- begin
97
- res.status = status.to_i
98
- io_lambda = nil
99
- headers.each { |k, vs|
100
- if k == RACK_HIJACK
101
- io_lambda = vs
102
- elsif k.downcase == "set-cookie"
103
- res.cookies.concat vs.split("\n")
104
- else
105
- # Since WEBrick won't accept repeated headers,
106
- # merge the values per RFC 1945 section 4.2.
107
- res[k] = vs.split("\n").join(", ")
108
- end
109
- }
110
-
111
- if io_lambda
112
- rd, wr = IO.pipe
113
- res.body = rd
114
- res.chunked = true
115
- io_lambda.call wr
116
- elsif body.respond_to?(:to_path)
117
- res.body = ::File.open(body.to_path, 'rb')
118
- else
119
- body.each { |part|
120
- res.body << part
121
- }
122
- end
123
- ensure
124
- body.close if body.respond_to? :close
125
- end
126
- end
127
- end
128
- end
129
- end
data/lib/rack/handler.rb DELETED
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Rack
4
- # *Handlers* connect web servers with Rack.
5
- #
6
- # Rack includes Handlers for Thin, WEBrick, FastCGI, CGI, SCGI
7
- # and LiteSpeed.
8
- #
9
- # Handlers usually are activated by calling <tt>MyHandler.run(myapp)</tt>.
10
- # A second optional hash can be passed to include server-specific
11
- # configuration.
12
- module Handler
13
- def self.get(server)
14
- return unless server
15
- server = server.to_s
16
-
17
- unless @handlers.include? server
18
- load_error = try_require('rack/handler', server)
19
- end
20
-
21
- if klass = @handlers[server]
22
- const_get(klass)
23
- else
24
- const_get(server, false)
25
- end
26
-
27
- rescue NameError => name_error
28
- raise load_error || name_error
29
- end
30
-
31
- # Select first available Rack handler given an `Array` of server names.
32
- # Raises `LoadError` if no handler was found.
33
- #
34
- # > pick ['thin', 'webrick']
35
- # => Rack::Handler::WEBrick
36
- def self.pick(server_names)
37
- server_names = Array(server_names)
38
- server_names.each do |server_name|
39
- begin
40
- return get(server_name.to_s)
41
- rescue LoadError, NameError
42
- end
43
- end
44
-
45
- raise LoadError, "Couldn't find handler for: #{server_names.join(', ')}."
46
- end
47
-
48
- SERVER_NAMES = %w(puma thin falcon webrick).freeze
49
- private_constant :SERVER_NAMES
50
-
51
- def self.default
52
- # Guess.
53
- if ENV.include?("PHP_FCGI_CHILDREN")
54
- Rack::Handler::FastCGI
55
- elsif ENV.include?(REQUEST_METHOD)
56
- Rack::Handler::CGI
57
- elsif ENV.include?("RACK_HANDLER")
58
- self.get(ENV["RACK_HANDLER"])
59
- else
60
- pick SERVER_NAMES
61
- end
62
- end
63
-
64
- # Transforms server-name constants to their canonical form as filenames,
65
- # then tries to require them but silences the LoadError if not found
66
- #
67
- # Naming convention:
68
- #
69
- # Foo # => 'foo'
70
- # FooBar # => 'foo_bar.rb'
71
- # FooBAR # => 'foobar.rb'
72
- # FOObar # => 'foobar.rb'
73
- # FOOBAR # => 'foobar.rb'
74
- # FooBarBaz # => 'foo_bar_baz.rb'
75
- def self.try_require(prefix, const_name)
76
- file = const_name.gsub(/^[A-Z]+/) { |pre| pre.downcase }.
77
- gsub(/[A-Z]+[^A-Z]/, '_\&').downcase
78
-
79
- require(::File.join(prefix, file))
80
- nil
81
- rescue LoadError => error
82
- error
83
- end
84
-
85
- def self.register(server, klass)
86
- @handlers ||= {}
87
- @handlers[server.to_s] = klass.to_s
88
- end
89
-
90
- autoload :CGI, "rack/handler/cgi"
91
- autoload :FastCGI, "rack/handler/fastcgi"
92
- autoload :WEBrick, "rack/handler/webrick"
93
- autoload :LSWS, "rack/handler/lsws"
94
- autoload :SCGI, "rack/handler/scgi"
95
- autoload :Thin, "rack/handler/thin"
96
-
97
- register 'cgi', 'Rack::Handler::CGI'
98
- register 'fastcgi', 'Rack::Handler::FastCGI'
99
- register 'webrick', 'Rack::Handler::WEBrick'
100
- register 'lsws', 'Rack::Handler::LSWS'
101
- register 'scgi', 'Rack::Handler::SCGI'
102
- register 'thin', 'Rack::Handler::Thin'
103
- end
104
- end