rack 1.0.1 → 2.2.21

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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +829 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +355 -0
  6. data/Rakefile +64 -98
  7. data/SPEC.rdoc +292 -0
  8. data/bin/rackup +3 -174
  9. data/contrib/rack.png +0 -0
  10. data/contrib/rack.svg +150 -0
  11. data/contrib/rack_logo.svg +164 -111
  12. data/contrib/rdoc.css +412 -0
  13. data/example/lobster.ru +2 -0
  14. data/example/protectedlobster.rb +4 -2
  15. data/example/protectedlobster.ru +3 -1
  16. data/lib/rack/auth/abstract/handler.rb +7 -5
  17. data/lib/rack/auth/abstract/request.rb +12 -2
  18. data/lib/rack/auth/basic.rb +6 -4
  19. data/lib/rack/auth/digest/md5.rb +20 -13
  20. data/lib/rack/auth/digest/nonce.rb +6 -4
  21. data/lib/rack/auth/digest/params.rb +11 -12
  22. data/lib/rack/auth/digest/request.rb +15 -12
  23. data/lib/rack/body_proxy.rb +45 -0
  24. data/lib/rack/builder.rb +213 -19
  25. data/lib/rack/cascade.rb +50 -18
  26. data/lib/rack/chunked.rb +95 -27
  27. data/lib/rack/common_logger.rb +84 -0
  28. data/lib/rack/conditional_get.rb +83 -0
  29. data/lib/rack/config.rb +22 -0
  30. data/lib/rack/content_length.rb +19 -10
  31. data/lib/rack/content_type.rb +12 -5
  32. data/lib/rack/core_ext/regexp.rb +14 -0
  33. data/lib/rack/deflater.rb +90 -42
  34. data/lib/rack/directory.rb +118 -72
  35. data/lib/rack/etag.rb +77 -0
  36. data/lib/rack/events.rb +153 -0
  37. data/lib/rack/file.rb +4 -85
  38. data/lib/rack/files.rb +218 -0
  39. data/lib/rack/handler/cgi.rb +22 -24
  40. data/lib/rack/handler/fastcgi.rb +34 -24
  41. data/lib/rack/handler/lsws.rb +16 -15
  42. data/lib/rack/handler/scgi.rb +38 -29
  43. data/lib/rack/handler/thin.rb +23 -7
  44. data/lib/rack/handler/webrick.rb +95 -37
  45. data/lib/rack/handler.rb +49 -14
  46. data/lib/rack/head.rb +19 -13
  47. data/lib/rack/lint.rb +466 -217
  48. data/lib/rack/lobster.rb +19 -14
  49. data/lib/rack/lock.rb +24 -8
  50. data/lib/rack/logger.rb +20 -0
  51. data/lib/rack/media_type.rb +53 -0
  52. data/lib/rack/method_override.rb +52 -0
  53. data/lib/rack/mime.rb +652 -171
  54. data/lib/rack/mock.rb +203 -64
  55. data/lib/rack/multipart/generator.rb +97 -0
  56. data/lib/rack/multipart/parser.rb +440 -0
  57. data/lib/rack/multipart/uploaded_file.rb +41 -0
  58. data/lib/rack/multipart.rb +64 -0
  59. data/lib/rack/null_logger.rb +39 -0
  60. data/lib/rack/query_parser.rb +266 -0
  61. data/lib/rack/recursive.rb +18 -11
  62. data/lib/rack/reloader.rb +13 -5
  63. data/lib/rack/request.rb +607 -191
  64. data/lib/rack/response.rb +245 -110
  65. data/lib/rack/rewindable_input.rb +15 -21
  66. data/lib/rack/runtime.rb +34 -0
  67. data/lib/rack/sendfile.rb +192 -0
  68. data/lib/rack/server.rb +466 -0
  69. data/lib/rack/session/abstract/id.rb +449 -68
  70. data/lib/rack/session/cookie.rb +159 -47
  71. data/lib/rack/session/memcache.rb +4 -103
  72. data/lib/rack/session/pool.rb +39 -49
  73. data/lib/rack/show_exceptions.rb +390 -0
  74. data/lib/rack/{showstatus.rb → show_status.rb} +21 -14
  75. data/lib/rack/static.rb +164 -14
  76. data/lib/rack/tempfile_reaper.rb +22 -0
  77. data/lib/rack/urlmap.rb +60 -18
  78. data/lib/rack/utils.rb +508 -277
  79. data/lib/rack/version.rb +29 -0
  80. data/lib/rack.rb +85 -34
  81. data/rack.gemspec +43 -51
  82. metadata +119 -207
  83. data/KNOWN-ISSUES +0 -18
  84. data/RDOX +0 -428
  85. data/README +0 -364
  86. data/SPEC +0 -164
  87. data/lib/rack/adapter/camping.rb +0 -22
  88. data/lib/rack/auth/openid.rb +0 -480
  89. data/lib/rack/commonlogger.rb +0 -61
  90. data/lib/rack/conditionalget.rb +0 -47
  91. data/lib/rack/handler/evented_mongrel.rb +0 -8
  92. data/lib/rack/handler/mongrel.rb +0 -87
  93. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  94. data/lib/rack/methodoverride.rb +0 -27
  95. data/lib/rack/showexceptions.rb +0 -349
  96. data/test/cgi/lighttpd.conf +0 -20
  97. data/test/cgi/test +0 -9
  98. data/test/cgi/test.fcgi +0 -8
  99. data/test/cgi/test.ru +0 -7
  100. data/test/multipart/binary +0 -0
  101. data/test/multipart/empty +0 -10
  102. data/test/multipart/ie +0 -6
  103. data/test/multipart/nested +0 -10
  104. data/test/multipart/none +0 -9
  105. data/test/multipart/semicolon +0 -6
  106. data/test/multipart/text +0 -10
  107. data/test/spec_rack_auth_basic.rb +0 -73
  108. data/test/spec_rack_auth_digest.rb +0 -226
  109. data/test/spec_rack_auth_openid.rb +0 -84
  110. data/test/spec_rack_builder.rb +0 -84
  111. data/test/spec_rack_camping.rb +0 -51
  112. data/test/spec_rack_cascade.rb +0 -50
  113. data/test/spec_rack_cgi.rb +0 -89
  114. data/test/spec_rack_chunked.rb +0 -62
  115. data/test/spec_rack_commonlogger.rb +0 -32
  116. data/test/spec_rack_conditionalget.rb +0 -41
  117. data/test/spec_rack_content_length.rb +0 -43
  118. data/test/spec_rack_content_type.rb +0 -30
  119. data/test/spec_rack_deflater.rb +0 -127
  120. data/test/spec_rack_directory.rb +0 -61
  121. data/test/spec_rack_fastcgi.rb +0 -89
  122. data/test/spec_rack_file.rb +0 -75
  123. data/test/spec_rack_handler.rb +0 -43
  124. data/test/spec_rack_head.rb +0 -30
  125. data/test/spec_rack_lint.rb +0 -521
  126. data/test/spec_rack_lobster.rb +0 -45
  127. data/test/spec_rack_lock.rb +0 -38
  128. data/test/spec_rack_methodoverride.rb +0 -60
  129. data/test/spec_rack_mock.rb +0 -157
  130. data/test/spec_rack_mongrel.rb +0 -189
  131. data/test/spec_rack_recursive.rb +0 -77
  132. data/test/spec_rack_request.rb +0 -504
  133. data/test/spec_rack_response.rb +0 -218
  134. data/test/spec_rack_rewindable_input.rb +0 -118
  135. data/test/spec_rack_session_cookie.rb +0 -82
  136. data/test/spec_rack_session_memcache.rb +0 -240
  137. data/test/spec_rack_session_pool.rb +0 -172
  138. data/test/spec_rack_showexceptions.rb +0 -21
  139. data/test/spec_rack_showstatus.rb +0 -72
  140. data/test/spec_rack_static.rb +0 -37
  141. data/test/spec_rack_thin.rb +0 -91
  142. data/test/spec_rack_urlmap.rb +0 -185
  143. data/test/spec_rack_utils.rb +0 -387
  144. data/test/spec_rack_webrick.rb +0 -130
  145. data/test/testrequest.rb +0 -57
  146. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  147. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/files.rb ADDED
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ module Rack
6
+ # Rack::Files serves files below the +root+ directory given, according to the
7
+ # path info of the Rack request.
8
+ # e.g. when Rack::Files.new("/etc") is used, you can access 'passwd' file
9
+ # as http://localhost:9292/passwd
10
+ #
11
+ # Handlers can detect if bodies are a Rack::Files, and use mechanisms
12
+ # like sendfile on the +path+.
13
+
14
+ class Files
15
+ ALLOWED_VERBS = %w[GET HEAD OPTIONS]
16
+ ALLOW_HEADER = ALLOWED_VERBS.join(', ')
17
+ MULTIPART_BOUNDARY = 'AaB03x'
18
+
19
+ # @todo remove in 3.0
20
+ def self.method_added(name)
21
+ if name == :response_body
22
+ raise "#{self.class}\#response_body is no longer supported."
23
+ end
24
+ super
25
+ end
26
+
27
+ attr_reader :root
28
+
29
+ def initialize(root, headers = {}, default_mime = 'text/plain')
30
+ @root = (::File.expand_path(root) if root)
31
+ @headers = headers
32
+ @default_mime = default_mime
33
+ @head = Rack::Head.new(lambda { |env| get env })
34
+ end
35
+
36
+ def call(env)
37
+ # HEAD requests drop the response body, including 4xx error messages.
38
+ @head.call env
39
+ end
40
+
41
+ def get(env)
42
+ request = Rack::Request.new env
43
+ unless ALLOWED_VERBS.include? request.request_method
44
+ return fail(405, "Method Not Allowed", { 'Allow' => ALLOW_HEADER })
45
+ end
46
+
47
+ path_info = Utils.unescape_path request.path_info
48
+ return fail(400, "Bad Request") unless Utils.valid_path?(path_info)
49
+
50
+ clean_path_info = Utils.clean_path_info(path_info)
51
+ path = ::File.join(@root, clean_path_info)
52
+
53
+ available = begin
54
+ ::File.file?(path) && ::File.readable?(path)
55
+ rescue SystemCallError
56
+ # Not sure in what conditions this exception can occur, but this
57
+ # is a safe way to handle such an error.
58
+ # :nocov:
59
+ false
60
+ # :nocov:
61
+ end
62
+
63
+ if available
64
+ serving(request, path)
65
+ else
66
+ fail(404, "File not found: #{path_info}")
67
+ end
68
+ end
69
+
70
+ def serving(request, path)
71
+ if request.options?
72
+ return [200, { 'Allow' => ALLOW_HEADER, CONTENT_LENGTH => '0' }, []]
73
+ end
74
+ last_modified = ::File.mtime(path).httpdate
75
+ return [304, {}, []] if request.get_header('HTTP_IF_MODIFIED_SINCE') == last_modified
76
+
77
+ headers = { "Last-Modified" => last_modified }
78
+ mime_type = mime_type path, @default_mime
79
+ headers[CONTENT_TYPE] = mime_type if mime_type
80
+
81
+ # Set custom headers
82
+ headers.merge!(@headers) if @headers
83
+
84
+ status = 200
85
+ size = filesize path
86
+
87
+ ranges = Rack::Utils.get_byte_ranges(request.get_header('HTTP_RANGE'), size)
88
+ if ranges.nil?
89
+ # No ranges:
90
+ ranges = [0..size - 1]
91
+ elsif ranges.empty?
92
+ # Unsatisfiable. Return error, and file size:
93
+ response = fail(416, "Byte range unsatisfiable")
94
+ response[1]["Content-Range"] = "bytes */#{size}"
95
+ return response
96
+ elsif ranges.size >= 1
97
+ # Partial content
98
+ partial_content = true
99
+
100
+ if ranges.size == 1
101
+ range = ranges[0]
102
+ headers["Content-Range"] = "bytes #{range.begin}-#{range.end}/#{size}"
103
+ else
104
+ headers[CONTENT_TYPE] = "multipart/byteranges; boundary=#{MULTIPART_BOUNDARY}"
105
+ end
106
+
107
+ status = 206
108
+ body = BaseIterator.new(path, ranges, mime_type: mime_type, size: size)
109
+ size = body.bytesize
110
+ end
111
+
112
+ headers[CONTENT_LENGTH] = size.to_s
113
+
114
+ if request.head?
115
+ body = []
116
+ elsif !partial_content
117
+ body = Iterator.new(path, ranges, mime_type: mime_type, size: size)
118
+ end
119
+
120
+ [status, headers, body]
121
+ end
122
+
123
+ class BaseIterator
124
+ attr_reader :path, :ranges, :options
125
+
126
+ def initialize(path, ranges, options)
127
+ @path = path
128
+ @ranges = ranges
129
+ @options = options
130
+ end
131
+
132
+ def each
133
+ ::File.open(path, "rb") do |file|
134
+ ranges.each do |range|
135
+ yield multipart_heading(range) if multipart?
136
+
137
+ each_range_part(file, range) do |part|
138
+ yield part
139
+ end
140
+ end
141
+
142
+ yield "\r\n--#{MULTIPART_BOUNDARY}--\r\n" if multipart?
143
+ end
144
+ end
145
+
146
+ def bytesize
147
+ size = ranges.inject(0) do |sum, range|
148
+ sum += multipart_heading(range).bytesize if multipart?
149
+ sum += range.size
150
+ end
151
+ size += "\r\n--#{MULTIPART_BOUNDARY}--\r\n".bytesize if multipart?
152
+ size
153
+ end
154
+
155
+ def close; end
156
+
157
+ private
158
+
159
+ def multipart?
160
+ ranges.size > 1
161
+ end
162
+
163
+ def multipart_heading(range)
164
+ <<-EOF
165
+ \r
166
+ --#{MULTIPART_BOUNDARY}\r
167
+ Content-Type: #{options[:mime_type]}\r
168
+ Content-Range: bytes #{range.begin}-#{range.end}/#{options[:size]}\r
169
+ \r
170
+ EOF
171
+ end
172
+
173
+ def each_range_part(file, range)
174
+ file.seek(range.begin)
175
+ remaining_len = range.end - range.begin + 1
176
+ while remaining_len > 0
177
+ part = file.read([8192, remaining_len].min)
178
+ break unless part
179
+ remaining_len -= part.length
180
+
181
+ yield part
182
+ end
183
+ end
184
+ end
185
+
186
+ class Iterator < BaseIterator
187
+ alias :to_path :path
188
+ end
189
+
190
+ private
191
+
192
+ def fail(status, body, headers = {})
193
+ body += "\n"
194
+
195
+ [
196
+ status,
197
+ {
198
+ CONTENT_TYPE => "text/plain",
199
+ CONTENT_LENGTH => body.size.to_s,
200
+ "X-Cascade" => "pass"
201
+ }.merge!(headers),
202
+ [body]
203
+ ]
204
+ end
205
+
206
+ # The MIME type for the contents of the file located at @path
207
+ def mime_type(path, default_mime)
208
+ Mime.mime_type(::File.extname(path), default_mime)
209
+ end
210
+
211
+ def filesize(path)
212
+ # We check via File::size? whether this file provides size info
213
+ # via stat (e.g. /proc files often don't), otherwise we have to
214
+ # figure it out by reading the whole file into memory.
215
+ ::File.size?(path) || ::File.read(path).bytesize
216
+ end
217
+ end
218
+ end
@@ -1,34 +1,32 @@
1
- require 'rack/content_length'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Rack
4
4
  module Handler
5
5
  class CGI
6
- def self.run(app, options=nil)
6
+ def self.run(app, **options)
7
+ $stdin.binmode
7
8
  serve app
8
9
  end
9
10
 
10
11
  def self.serve(app)
11
- app = ContentLength.new(app)
12
-
13
12
  env = ENV.to_hash
14
13
  env.delete "HTTP_CONTENT_LENGTH"
15
14
 
16
- env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
17
-
18
- env.update({"rack.version" => [1,0],
19
- "rack.input" => $stdin,
20
- "rack.errors" => $stderr,
21
-
22
- "rack.multithread" => false,
23
- "rack.multiprocess" => true,
24
- "rack.run_once" => true,
15
+ env[SCRIPT_NAME] = "" if env[SCRIPT_NAME] == "/"
25
16
 
26
- "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
27
- })
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
+ )
28
26
 
29
- env["QUERY_STRING"] ||= ""
30
- env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
31
- env["REQUEST_PATH"] ||= "/"
27
+ env[QUERY_STRING] ||= ""
28
+ env[HTTP_VERSION] ||= env[SERVER_PROTOCOL]
29
+ env[REQUEST_PATH] ||= "/"
32
30
 
33
31
  status, headers, body = app.call(env)
34
32
  begin
@@ -40,20 +38,20 @@ module Rack
40
38
  end
41
39
 
42
40
  def self.send_headers(status, headers)
43
- STDOUT.print "Status: #{status}\r\n"
41
+ $stdout.print "Status: #{status}\r\n"
44
42
  headers.each { |k, vs|
45
43
  vs.split("\n").each { |v|
46
- STDOUT.print "#{k}: #{v}\r\n"
44
+ $stdout.print "#{k}: #{v}\r\n"
47
45
  }
48
46
  }
49
- STDOUT.print "\r\n"
50
- STDOUT.flush
47
+ $stdout.print "\r\n"
48
+ $stdout.flush
51
49
  end
52
50
 
53
51
  def self.send_body(body)
54
52
  body.each { |part|
55
- STDOUT.print part
56
- STDOUT.flush
53
+ $stdout.print part
54
+ $stdout.flush
57
55
  }
58
56
  end
59
57
  end
@@ -1,13 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fcgi'
2
4
  require 'socket'
3
- require 'rack/content_length'
4
- require 'rack/rewindable_input'
5
5
 
6
6
  if defined? FCGI::Stream
7
7
  class FCGI::Stream
8
8
  alias _rack_read_without_buffer read
9
9
 
10
- def read(n, buffer=nil)
10
+ def read(n, buffer = nil)
11
11
  buf = _rack_read_without_buffer n
12
12
  buffer.replace(buf.to_s) if buffer
13
13
  buf
@@ -18,39 +18,49 @@ end
18
18
  module Rack
19
19
  module Handler
20
20
  class FastCGI
21
- def self.run(app, options={})
22
- file = options[:File] and STDIN.reopen(UNIXServer.new(file))
23
- port = options[:Port] and STDIN.reopen(TCPServer.new(port))
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
24
27
  FCGI.each { |request|
25
28
  serve request, app
26
29
  }
27
30
  end
28
31
 
29
- def self.serve(request, app)
30
- app = Rack::ContentLength.new(app)
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
31
42
 
43
+ def self.serve(request, app)
32
44
  env = request.env
33
45
  env.delete "HTTP_CONTENT_LENGTH"
34
46
 
35
- env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
36
-
37
- rack_input = RewindableInput.new(request.in)
47
+ env[SCRIPT_NAME] = "" if env[SCRIPT_NAME] == "/"
38
48
 
39
- env.update({"rack.version" => [1,0],
40
- "rack.input" => rack_input,
41
- "rack.errors" => request.err,
42
-
43
- "rack.multithread" => false,
44
- "rack.multiprocess" => true,
45
- "rack.run_once" => false,
49
+ rack_input = RewindableInput.new(request.in)
46
50
 
47
- "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
48
- })
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
+ )
49
60
 
50
- env["QUERY_STRING"] ||= ""
51
- env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
52
- env["REQUEST_PATH"] ||= "/"
53
- env.delete "PATH_INFO" if env["PATH_INFO"] == ""
61
+ env[QUERY_STRING] ||= ""
62
+ env[HTTP_VERSION] ||= env[SERVER_PROTOCOL]
63
+ env[REQUEST_PATH] ||= "/"
54
64
  env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == ""
55
65
  env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == ""
56
66
 
@@ -1,36 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'lsapi'
2
- require 'rack/content_length'
3
4
 
4
5
  module Rack
5
6
  module Handler
6
7
  class LSWS
7
- def self.run(app, options=nil)
8
+ def self.run(app, **options)
8
9
  while LSAPI.accept != nil
9
10
  serve app
10
11
  end
11
12
  end
12
13
  def self.serve(app)
13
- app = Rack::ContentLength.new(app)
14
-
15
14
  env = ENV.to_hash
16
15
  env.delete "HTTP_CONTENT_LENGTH"
17
- env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
16
+ env[SCRIPT_NAME] = "" if env[SCRIPT_NAME] == "/"
18
17
 
19
18
  rack_input = RewindableInput.new($stdin.read.to_s)
20
19
 
21
20
  env.update(
22
- "rack.version" => [1,0],
23
- "rack.input" => rack_input,
24
- "rack.errors" => $stderr,
25
- "rack.multithread" => false,
26
- "rack.multiprocess" => true,
27
- "rack.run_once" => false,
28
- "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
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"
29
28
  )
30
29
 
31
- env["QUERY_STRING"] ||= ""
32
- env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
33
- env["REQUEST_PATH"] ||= "/"
30
+ env[QUERY_STRING] ||= ""
31
+ env[HTTP_VERSION] ||= env[SERVER_PROTOCOL]
32
+ env[REQUEST_PATH] ||= "/"
34
33
  status, headers, body = app.call(env)
35
34
  begin
36
35
  send_headers status, headers
@@ -38,6 +37,8 @@ module Rack
38
37
  ensure
39
38
  body.close if body.respond_to? :close
40
39
  end
40
+ ensure
41
+ rack_input.close
41
42
  end
42
43
  def self.send_headers(status, headers)
43
44
  print "Status: #{status}\r\n"
@@ -1,50 +1,59 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'scgi'
2
4
  require 'stringio'
3
- require 'rack/content_length'
4
- require 'rack/chunked'
5
5
 
6
6
  module Rack
7
7
  module Handler
8
8
  class SCGI < ::SCGI::Processor
9
9
  attr_accessor :app
10
-
11
- def self.run(app, options=nil)
12
- new(options.merge(:app=>app,
13
- :host=>options[:Host],
14
- :port=>options[:Port],
15
- :socket=>options[:Socket])).listen
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
16
17
  end
17
-
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
+
18
29
  def initialize(settings = {})
19
- @app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app]))
20
- @log = Object.new
21
- def @log.info(*args); end
22
- def @log.error(*args); end
30
+ @app = settings[:app]
23
31
  super(settings)
24
32
  end
25
-
33
+
26
34
  def process_request(request, input_body, socket)
27
- env = {}.replace(request)
35
+ env = Hash[request]
28
36
  env.delete "HTTP_CONTENT_TYPE"
29
37
  env.delete "HTTP_CONTENT_LENGTH"
30
- env["REQUEST_PATH"], env["QUERY_STRING"] = env["REQUEST_URI"].split('?', 2)
31
- env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
32
- env["PATH_INFO"] = env["REQUEST_PATH"]
33
- env["QUERY_STRING"] ||= ""
34
- env["SCRIPT_NAME"] = ""
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] = ""
35
43
 
36
44
  rack_input = StringIO.new(input_body)
37
- rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
45
+ rack_input.set_encoding(Encoding::BINARY)
38
46
 
39
- env.update({"rack.version" => [1,0],
40
- "rack.input" => rack_input,
41
- "rack.errors" => $stderr,
42
- "rack.multithread" => true,
43
- "rack.multiprocess" => true,
44
- "rack.run_once" => false,
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
+ )
45
56
 
46
- "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
47
- })
48
57
  status, headers, body = app.call(env)
49
58
  begin
50
59
  socket.write("Status: #{status}\r\n")
@@ -1,18 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "thin"
2
- require "rack/content_length"
3
- require "rack/chunked"
4
+ require "thin/server"
5
+ require "thin/logging"
6
+ require "thin/backends/tcp_server"
4
7
 
5
8
  module Rack
6
9
  module Handler
7
10
  class Thin
8
- def self.run(app, options={})
9
- app = Rack::Chunked.new(Rack::ContentLength.new(app))
10
- server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
11
- options[:Port] || 8080,
12
- app)
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
+ server = ::Thin::Server.new(*args)
13
19
  yield server if block_given?
14
20
  server.start
15
21
  end
22
+
23
+ def self.valid_options
24
+ environment = ENV['RACK_ENV'] || 'development'
25
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
26
+
27
+ {
28
+ "Host=HOST" => "Hostname to listen on (default: #{default_host})",
29
+ "Port=PORT" => "Port to listen on (default: 8080)",
30
+ }
31
+ end
16
32
  end
17
33
  end
18
34
  end