rack 1.1.6 → 1.6.9

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 (212) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +1 -1
  3. data/HISTORY.md +375 -0
  4. data/KNOWN-ISSUES +23 -0
  5. data/README.rdoc +312 -0
  6. data/Rakefile +124 -0
  7. data/SPEC +125 -32
  8. data/contrib/rack.png +0 -0
  9. data/contrib/rack.svg +150 -0
  10. data/contrib/rack_logo.svg +1 -1
  11. data/contrib/rdoc.css +412 -0
  12. data/example/protectedlobster.rb +1 -1
  13. data/lib/rack/auth/abstract/handler.rb +4 -4
  14. data/lib/rack/auth/abstract/request.rb +7 -5
  15. data/lib/rack/auth/basic.rb +1 -1
  16. data/lib/rack/auth/digest/md5.rb +7 -3
  17. data/lib/rack/auth/digest/nonce.rb +1 -1
  18. data/lib/rack/auth/digest/params.rb +7 -9
  19. data/lib/rack/auth/digest/request.rb +10 -9
  20. data/lib/rack/backports/uri/common_18.rb +56 -0
  21. data/lib/rack/backports/uri/common_192.rb +52 -0
  22. data/lib/rack/backports/uri/common_193.rb +29 -0
  23. data/lib/rack/body_proxy.rb +39 -0
  24. data/lib/rack/builder.rb +106 -22
  25. data/lib/rack/cascade.rb +17 -6
  26. data/lib/rack/chunked.rb +44 -24
  27. data/lib/rack/commonlogger.rb +36 -13
  28. data/lib/rack/conditionalget.rb +49 -17
  29. data/lib/rack/config.rb +5 -0
  30. data/lib/rack/content_length.rb +14 -6
  31. data/lib/rack/content_type.rb +7 -1
  32. data/lib/rack/deflater.rb +73 -15
  33. data/lib/rack/directory.rb +18 -8
  34. data/lib/rack/etag.rb +59 -9
  35. data/lib/rack/file.rb +106 -44
  36. data/lib/rack/handler/cgi.rb +11 -11
  37. data/lib/rack/handler/fastcgi.rb +18 -6
  38. data/lib/rack/handler/lsws.rb +2 -4
  39. data/lib/rack/handler/mongrel.rb +22 -6
  40. data/lib/rack/handler/scgi.rb +16 -8
  41. data/lib/rack/handler/thin.rb +19 -4
  42. data/lib/rack/handler/webrick.rb +72 -19
  43. data/lib/rack/handler.rb +47 -14
  44. data/lib/rack/head.rb +10 -2
  45. data/lib/rack/lint.rb +260 -75
  46. data/lib/rack/lobster.rb +13 -8
  47. data/lib/rack/lock.rb +13 -3
  48. data/lib/rack/logger.rb +0 -2
  49. data/lib/rack/methodoverride.rb +27 -8
  50. data/lib/rack/mime.rb +625 -167
  51. data/lib/rack/mock.rb +78 -53
  52. data/lib/rack/multipart/generator.rb +93 -0
  53. data/lib/rack/multipart/parser.rb +253 -0
  54. data/lib/rack/multipart/uploaded_file.rb +34 -0
  55. data/lib/rack/multipart.rb +34 -0
  56. data/lib/rack/nulllogger.rb +21 -2
  57. data/lib/rack/recursive.rb +10 -5
  58. data/lib/rack/reloader.rb +3 -2
  59. data/lib/rack/request.rb +201 -74
  60. data/lib/rack/response.rb +41 -28
  61. data/lib/rack/rewindable_input.rb +15 -11
  62. data/lib/rack/runtime.rb +16 -3
  63. data/lib/rack/sendfile.rb +47 -29
  64. data/lib/rack/server.rb +223 -47
  65. data/lib/rack/session/abstract/id.rb +289 -30
  66. data/lib/rack/session/cookie.rb +133 -44
  67. data/lib/rack/session/memcache.rb +30 -56
  68. data/lib/rack/session/pool.rb +19 -43
  69. data/lib/rack/showexceptions.rb +53 -15
  70. data/lib/rack/showstatus.rb +14 -7
  71. data/lib/rack/static.rb +124 -12
  72. data/lib/rack/tempfile_reaper.rb +22 -0
  73. data/lib/rack/urlmap.rb +49 -15
  74. data/lib/rack/utils/okjson.rb +600 -0
  75. data/lib/rack/utils.rb +363 -361
  76. data/lib/rack.rb +17 -23
  77. data/rack.gemspec +11 -20
  78. data/test/builder/anything.rb +5 -0
  79. data/test/builder/comment.ru +4 -0
  80. data/test/builder/end.ru +5 -0
  81. data/test/builder/line.ru +1 -0
  82. data/test/builder/options.ru +2 -0
  83. data/test/cgi/assets/folder/test.js +1 -0
  84. data/test/cgi/assets/fonts/font.eot +1 -0
  85. data/test/cgi/assets/images/image.png +1 -0
  86. data/test/cgi/assets/index.html +1 -0
  87. data/test/cgi/assets/javascripts/app.js +1 -0
  88. data/test/cgi/assets/stylesheets/app.css +1 -0
  89. data/test/cgi/lighttpd.conf +26 -0
  90. data/test/cgi/rackup_stub.rb +6 -0
  91. data/test/cgi/sample_rackup.ru +5 -0
  92. data/test/cgi/test +9 -0
  93. data/test/cgi/test+directory/test+file +1 -0
  94. data/test/cgi/test.fcgi +8 -0
  95. data/test/cgi/test.ru +5 -0
  96. data/test/gemloader.rb +10 -0
  97. data/test/multipart/bad_robots +259 -0
  98. data/test/multipart/binary +0 -0
  99. data/test/multipart/content_type_and_no_filename +6 -0
  100. data/test/multipart/empty +10 -0
  101. data/test/multipart/fail_16384_nofile +814 -0
  102. data/test/multipart/file1.txt +1 -0
  103. data/test/multipart/filename_and_modification_param +7 -0
  104. data/test/multipart/filename_and_no_name +6 -0
  105. data/test/multipart/filename_with_escaped_quotes +6 -0
  106. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  107. data/test/multipart/filename_with_null_byte +7 -0
  108. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  109. data/test/multipart/filename_with_unescaped_percentages +6 -0
  110. data/test/multipart/filename_with_unescaped_percentages2 +6 -0
  111. data/test/multipart/filename_with_unescaped_percentages3 +6 -0
  112. data/test/multipart/filename_with_unescaped_quotes +6 -0
  113. data/test/multipart/ie +6 -0
  114. data/test/multipart/invalid_character +6 -0
  115. data/test/multipart/mixed_files +21 -0
  116. data/test/multipart/nested +10 -0
  117. data/test/multipart/none +9 -0
  118. data/test/multipart/semicolon +6 -0
  119. data/test/multipart/text +15 -0
  120. data/test/multipart/three_files_three_fields +31 -0
  121. data/test/multipart/webkit +32 -0
  122. data/test/rackup/config.ru +31 -0
  123. data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
  124. data/test/{spec_rack_auth_basic.rb → spec_auth_basic.rb} +23 -15
  125. data/test/{spec_rack_auth_digest.rb → spec_auth_digest.rb} +56 -29
  126. data/test/spec_body_proxy.rb +85 -0
  127. data/test/spec_builder.rb +223 -0
  128. data/test/{spec_rack_cascade.rb → spec_cascade.rb} +28 -15
  129. data/test/{spec_rack_cgi.rb → spec_cgi.rb} +44 -31
  130. data/test/spec_chunked.rb +101 -0
  131. data/test/spec_commonlogger.rb +93 -0
  132. data/test/spec_conditionalget.rb +102 -0
  133. data/test/{spec_rack_config.rb → spec_config.rb} +6 -8
  134. data/test/spec_content_length.rb +85 -0
  135. data/test/spec_content_type.rb +45 -0
  136. data/test/spec_deflater.rb +339 -0
  137. data/test/{spec_rack_directory.rb → spec_directory.rb} +37 -10
  138. data/test/spec_etag.rb +107 -0
  139. data/test/{spec_rack_fastcgi.rb → spec_fastcgi.rb} +47 -29
  140. data/test/spec_file.rb +221 -0
  141. data/test/spec_handler.rb +72 -0
  142. data/test/spec_head.rb +45 -0
  143. data/test/{spec_rack_lint.rb → spec_lint.rb} +82 -60
  144. data/test/spec_lobster.rb +58 -0
  145. data/test/spec_lock.rb +164 -0
  146. data/test/spec_logger.rb +23 -0
  147. data/test/spec_methodoverride.rb +95 -0
  148. data/test/spec_mime.rb +51 -0
  149. data/test/{spec_rack_mock.rb → spec_mock.rb} +92 -38
  150. data/test/{spec_rack_mongrel.rb → spec_mongrel.rb} +46 -53
  151. data/test/spec_multipart.rb +600 -0
  152. data/test/spec_nulllogger.rb +20 -0
  153. data/test/spec_recursive.rb +72 -0
  154. data/test/spec_request.rb +1227 -0
  155. data/test/spec_response.rb +407 -0
  156. data/test/spec_rewindable_input.rb +118 -0
  157. data/test/spec_runtime.rb +49 -0
  158. data/test/spec_sendfile.rb +130 -0
  159. data/test/spec_server.rb +167 -0
  160. data/test/spec_session_abstract_id.rb +53 -0
  161. data/test/spec_session_cookie.rb +410 -0
  162. data/test/{spec_rack_session_memcache.rb → spec_session_memcache.rb} +119 -71
  163. data/test/{spec_rack_session_pool.rb → spec_session_pool.rb} +106 -69
  164. data/test/spec_showexceptions.rb +85 -0
  165. data/test/spec_showstatus.rb +103 -0
  166. data/test/spec_static.rb +145 -0
  167. data/test/spec_tempfile_reaper.rb +63 -0
  168. data/test/{spec_rack_thin.rb → spec_thin.rb} +35 -35
  169. data/test/{spec_rack_urlmap.rb → spec_urlmap.rb} +40 -19
  170. data/test/spec_utils.rb +647 -0
  171. data/test/spec_version.rb +17 -0
  172. data/test/spec_webrick.rb +184 -0
  173. data/test/static/another/index.html +1 -0
  174. data/test/static/index.html +1 -0
  175. data/test/testrequest.rb +78 -0
  176. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  177. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  178. metadata +220 -239
  179. data/RDOX +0 -0
  180. data/README +0 -592
  181. data/lib/rack/adapter/camping.rb +0 -22
  182. data/test/spec_auth.rb +0 -57
  183. data/test/spec_rack_builder.rb +0 -84
  184. data/test/spec_rack_camping.rb +0 -55
  185. data/test/spec_rack_chunked.rb +0 -62
  186. data/test/spec_rack_commonlogger.rb +0 -61
  187. data/test/spec_rack_conditionalget.rb +0 -41
  188. data/test/spec_rack_content_length.rb +0 -43
  189. data/test/spec_rack_content_type.rb +0 -30
  190. data/test/spec_rack_deflater.rb +0 -127
  191. data/test/spec_rack_etag.rb +0 -17
  192. data/test/spec_rack_file.rb +0 -75
  193. data/test/spec_rack_handler.rb +0 -43
  194. data/test/spec_rack_head.rb +0 -30
  195. data/test/spec_rack_lobster.rb +0 -45
  196. data/test/spec_rack_lock.rb +0 -38
  197. data/test/spec_rack_logger.rb +0 -21
  198. data/test/spec_rack_methodoverride.rb +0 -60
  199. data/test/spec_rack_nulllogger.rb +0 -13
  200. data/test/spec_rack_recursive.rb +0 -77
  201. data/test/spec_rack_request.rb +0 -594
  202. data/test/spec_rack_response.rb +0 -221
  203. data/test/spec_rack_rewindable_input.rb +0 -118
  204. data/test/spec_rack_runtime.rb +0 -35
  205. data/test/spec_rack_sendfile.rb +0 -86
  206. data/test/spec_rack_session_cookie.rb +0 -92
  207. data/test/spec_rack_showexceptions.rb +0 -21
  208. data/test/spec_rack_showstatus.rb +0 -72
  209. data/test/spec_rack_static.rb +0 -37
  210. data/test/spec_rack_utils.rb +0 -557
  211. data/test/spec_rack_webrick.rb +0 -130
  212. data/test/spec_rackup.rb +0 -164
@@ -1,22 +1,22 @@
1
1
  require 'rack/content_length'
2
+ require 'rack/rewindable_input'
2
3
 
3
4
  module Rack
4
5
  module Handler
5
6
  class CGI
6
7
  def self.run(app, options=nil)
8
+ $stdin.binmode
7
9
  serve app
8
10
  end
9
11
 
10
12
  def self.serve(app)
11
- app = ContentLength.new(app)
12
-
13
13
  env = ENV.to_hash
14
14
  env.delete "HTTP_CONTENT_LENGTH"
15
15
 
16
16
  env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
17
17
 
18
- env.update({"rack.version" => [1,1],
19
- "rack.input" => $stdin,
18
+ env.update({"rack.version" => Rack::VERSION,
19
+ "rack.input" => Rack::RewindableInput.new($stdin),
20
20
  "rack.errors" => $stderr,
21
21
 
22
22
  "rack.multithread" => false,
@@ -26,7 +26,7 @@ module Rack
26
26
  "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
27
27
  })
28
28
 
29
- env["QUERY_STRING"] ||= ""
29
+ env[QUERY_STRING] ||= ""
30
30
  env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
31
31
  env["REQUEST_PATH"] ||= "/"
32
32
 
@@ -40,20 +40,20 @@ module Rack
40
40
  end
41
41
 
42
42
  def self.send_headers(status, headers)
43
- STDOUT.print "Status: #{status}\r\n"
43
+ $stdout.print "Status: #{status}\r\n"
44
44
  headers.each { |k, vs|
45
45
  vs.split("\n").each { |v|
46
- STDOUT.print "#{k}: #{v}\r\n"
46
+ $stdout.print "#{k}: #{v}\r\n"
47
47
  }
48
48
  }
49
- STDOUT.print "\r\n"
50
- STDOUT.flush
49
+ $stdout.print "\r\n"
50
+ $stdout.flush
51
51
  end
52
52
 
53
53
  def self.send_body(body)
54
54
  body.each { |part|
55
- STDOUT.print part
56
- STDOUT.flush
55
+ $stdout.print part
56
+ $stdout.flush
57
57
  }
58
58
  end
59
59
  end
@@ -19,16 +19,28 @@ module Rack
19
19
  module Handler
20
20
  class FastCGI
21
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))
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
 
@@ -36,7 +48,7 @@ module Rack
36
48
 
37
49
  rack_input = RewindableInput.new(request.in)
38
50
 
39
- env.update({"rack.version" => [1,1],
51
+ env.update({"rack.version" => Rack::VERSION,
40
52
  "rack.input" => rack_input,
41
53
  "rack.errors" => request.err,
42
54
 
@@ -47,7 +59,7 @@ module Rack
47
59
  "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
48
60
  })
49
61
 
50
- env["QUERY_STRING"] ||= ""
62
+ env[QUERY_STRING] ||= ""
51
63
  env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
52
64
  env["REQUEST_PATH"] ||= "/"
53
65
  env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == ""
@@ -11,8 +11,6 @@ module Rack
11
11
  end
12
12
  end
13
13
  def self.serve(app)
14
- app = Rack::ContentLength.new(app)
15
-
16
14
  env = ENV.to_hash
17
15
  env.delete "HTTP_CONTENT_LENGTH"
18
16
  env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
@@ -20,7 +18,7 @@ module Rack
20
18
  rack_input = RewindableInput.new($stdin.read.to_s)
21
19
 
22
20
  env.update(
23
- "rack.version" => [1,1],
21
+ "rack.version" => Rack::VERSION,
24
22
  "rack.input" => rack_input,
25
23
  "rack.errors" => $stderr,
26
24
  "rack.multithread" => false,
@@ -29,7 +27,7 @@ module Rack
29
27
  "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
30
28
  )
31
29
 
32
- env["QUERY_STRING"] ||= ""
30
+ env[QUERY_STRING] ||= ""
33
31
  env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
34
32
  env["REQUEST_PATH"] ||= "/"
35
33
  status, headers, body = app.call(env)
@@ -7,8 +7,11 @@ module Rack
7
7
  module Handler
8
8
  class Mongrel < ::Mongrel::HttpHandler
9
9
  def self.run(app, options={})
10
+ environment = ENV['RACK_ENV'] || 'development'
11
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
12
+
10
13
  server = ::Mongrel::HttpServer.new(
11
- options[:Host] || '0.0.0.0',
14
+ options[:Host] || default_host,
12
15
  options[:Port] || 8080,
13
16
  options[:num_processors] || 950,
14
17
  options[:throttle] || 0,
@@ -38,12 +41,25 @@ module Rack
38
41
  server.run.join
39
42
  end
40
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
+ "Processors=N" => "Number of concurrent processors to accept (default: 950)",
52
+ "Timeout=N" => "Time before a request is dropped for inactivity (default: 60)",
53
+ "Throttle=N" => "Throttle time between socket.accept calls in hundredths of a second (default: 0)",
54
+ }
55
+ end
56
+
41
57
  def initialize(app)
42
- @app = Rack::Chunked.new(Rack::ContentLength.new(app))
58
+ @app = app
43
59
  end
44
60
 
45
61
  def process(request, response)
46
- env = {}.replace(request.params)
62
+ env = Hash[request.params]
47
63
  env.delete "HTTP_CONTENT_TYPE"
48
64
  env.delete "HTTP_CONTENT_LENGTH"
49
65
 
@@ -52,7 +68,7 @@ module Rack
52
68
  rack_input = request.body || StringIO.new('')
53
69
  rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
54
70
 
55
- env.update({"rack.version" => [1,1],
71
+ env.update({"rack.version" => Rack::VERSION,
56
72
  "rack.input" => rack_input,
57
73
  "rack.errors" => $stderr,
58
74
 
@@ -60,9 +76,9 @@ module Rack
60
76
  "rack.multiprocess" => false, # ???
61
77
  "rack.run_once" => false,
62
78
 
63
- "rack.url_scheme" => "http",
79
+ "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
64
80
  })
65
- env["QUERY_STRING"] ||= ""
81
+ env[QUERY_STRING] ||= ""
66
82
 
67
83
  status, headers, body = @app.call(env)
68
84
 
@@ -9,34 +9,42 @@ module Rack
9
9
  attr_accessor :app
10
10
 
11
11
  def self.run(app, options=nil)
12
+ options[:Socket] = UNIXServer.new(options[:File]) if options[:File]
12
13
  new(options.merge(:app=>app,
13
14
  :host=>options[:Host],
14
15
  :port=>options[:Port],
15
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)
38
+ env["REQUEST_PATH"], env[QUERY_STRING] = env["REQUEST_URI"].split('?', 2)
31
39
  env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
32
- env["PATH_INFO"] = env["REQUEST_PATH"]
40
+ env[PATH_INFO] = env["REQUEST_PATH"]
33
41
  env["QUERY_STRING"] ||= ""
34
42
  env["SCRIPT_NAME"] = ""
35
43
 
36
44
  rack_input = StringIO.new(input_body)
37
45
  rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
38
46
 
39
- env.update({"rack.version" => [1,1],
47
+ env.update({"rack.version" => Rack::VERSION,
40
48
  "rack.input" => rack_input,
41
49
  "rack.errors" => $stderr,
42
50
  "rack.multithread" => true,
@@ -6,13 +6,28 @@ module Rack
6
6
  module Handler
7
7
  class Thin
8
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)
9
+ environment = ENV['RACK_ENV'] || 'development'
10
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
11
+
12
+ host = options.delete(:Host) || default_host
13
+ port = options.delete(:Port) || 8080
14
+ args = [host, port, app, options]
15
+ # Thin versions below 0.8.0 do not support additional options
16
+ args.pop if ::Thin::VERSION::MAJOR < 1 && ::Thin::VERSION::MINOR < 8
17
+ server = ::Thin::Server.new(*args)
13
18
  yield server if block_given?
14
19
  server.start
15
20
  end
21
+
22
+ def self.valid_options
23
+ environment = ENV['RACK_ENV'] || 'development'
24
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
25
+
26
+ {
27
+ "Host=HOST" => "Hostname to listen on (default: #{default_host})",
28
+ "Port=PORT" => "Port to listen on (default: 8080)",
29
+ }
30
+ end
16
31
  end
17
32
  end
18
33
  end
@@ -2,31 +2,67 @@ require 'webrick'
2
2
  require 'stringio'
3
3
  require 'rack/content_length'
4
4
 
5
+ # This monkey patch allows for applications to perform their own chunking
6
+ # through WEBrick::HTTPResponse iff rack is set to true.
7
+ class WEBrick::HTTPResponse
8
+ attr_accessor :rack
9
+
10
+ alias _rack_setup_header setup_header
11
+ def setup_header
12
+ app_chunking = rack && @header['transfer-encoding'] == 'chunked'
13
+
14
+ @chunked = app_chunking if app_chunking
15
+
16
+ _rack_setup_header
17
+
18
+ @chunked = false if app_chunking
19
+ end
20
+ end
21
+
5
22
  module Rack
6
23
  module Handler
7
24
  class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
8
25
  def self.run(app, options={})
9
- options[:BindAddress] = options.delete(:Host) if options[:Host]
10
- server = ::WEBrick::HTTPServer.new(options)
11
- server.mount "/", Rack::Handler::WEBrick, app
12
- trap(:INT) { server.shutdown }
13
- yield server if block_given?
14
- server.start
26
+ environment = ENV['RACK_ENV'] || 'development'
27
+ default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
28
+
29
+ options[:BindAddress] = options.delete(:Host) || default_host
30
+ options[:Port] ||= 8080
31
+ @server = ::WEBrick::HTTPServer.new(options)
32
+ @server.mount "/", Rack::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
+ @server.shutdown
49
+ @server = nil
15
50
  end
16
51
 
17
52
  def initialize(server, app)
18
53
  super server
19
- @app = Rack::ContentLength.new(app)
54
+ @app = app
20
55
  end
21
56
 
22
57
  def service(req, res)
58
+ res.rack = true
23
59
  env = req.meta_vars
24
60
  env.delete_if { |k, v| v.nil? }
25
61
 
26
62
  rack_input = StringIO.new(req.body.to_s)
27
63
  rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
28
64
 
29
- env.update({"rack.version" => [1,1],
65
+ env.update({"rack.version" => Rack::VERSION,
30
66
  "rack.input" => rack_input,
31
67
  "rack.errors" => $stderr,
32
68
 
@@ -34,32 +70,49 @@ module Rack
34
70
  "rack.multiprocess" => false,
35
71
  "rack.run_once" => false,
36
72
 
37
- "rack.url_scheme" => ["yes", "on", "1"].include?(ENV["HTTPS"]) ? "https" : "http"
73
+ "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http",
74
+
75
+ "rack.hijack?" => true,
76
+ "rack.hijack" => lambda { raise NotImplementedError, "only partial hijack is supported."},
77
+ "rack.hijack_io" => nil,
38
78
  })
39
79
 
40
80
  env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
41
- env["QUERY_STRING"] ||= ""
42
- env["REQUEST_PATH"] ||= "/"
43
- unless env["PATH_INFO"] == ""
81
+ env[QUERY_STRING] ||= ""
82
+ unless env[PATH_INFO] == ""
44
83
  path, n = req.request_uri.path, env["SCRIPT_NAME"].length
45
- env["PATH_INFO"] = path[n, path.length-n]
84
+ env[PATH_INFO] = path[n, path.length-n]
46
85
  end
86
+ env["REQUEST_PATH"] ||= [env["SCRIPT_NAME"], env[PATH_INFO]].join
47
87
 
48
88
  status, headers, body = @app.call(env)
49
89
  begin
50
90
  res.status = status.to_i
51
91
  headers.each { |k, vs|
92
+ next if k.downcase == "rack.hijack"
93
+
52
94
  if k.downcase == "set-cookie"
53
95
  res.cookies.concat vs.split("\n")
54
96
  else
55
- vs.split("\n").each { |v|
56
- res[k] = v
57
- }
97
+ # Since WEBrick won't accept repeated headers,
98
+ # merge the values per RFC 1945 section 4.2.
99
+ res[k] = vs.split("\n").join(", ")
58
100
  end
59
101
  }
60
- body.each { |part|
61
- res.body << part
62
- }
102
+
103
+ io_lambda = headers["rack.hijack"]
104
+ if io_lambda
105
+ rd, wr = IO.pipe
106
+ res.body = rd
107
+ res.chunked = true
108
+ io_lambda.call wr
109
+ elsif body.respond_to?(:to_path)
110
+ res.body = ::File.open(body.to_path, 'rb')
111
+ else
112
+ body.each { |part|
113
+ res.body << part
114
+ }
115
+ end
63
116
  ensure
64
117
  body.close if body.respond_to? :close
65
118
  end
data/lib/rack/handler.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Rack
2
2
  # *Handlers* connect web servers with Rack.
3
3
  #
4
- # Rack includes Handlers for Mongrel, WEBrick, FastCGI, CGI, SCGI
4
+ # Rack includes Handlers for Thin, WEBrick, FastCGI, CGI, SCGI
5
5
  # and LiteSpeed.
6
6
  #
7
7
  # Handlers usually are activated by calling <tt>MyHandler.run(myapp)</tt>.
@@ -12,14 +12,47 @@ module Rack
12
12
  return unless server
13
13
  server = server.to_s
14
14
 
15
+ unless @handlers.include? server
16
+ load_error = try_require('rack/handler', server)
17
+ end
18
+
15
19
  if klass = @handlers[server]
16
- obj = Object
17
- klass.split("::").each { |x| obj = obj.const_get(x) }
18
- obj
20
+ klass.split("::").inject(Object) { |o, x| o.const_get(x) }
19
21
  else
20
- try_require('rack/handler', server)
21
- const_get(server)
22
+ _const_get(server, false)
22
23
  end
24
+
25
+ rescue NameError => name_error
26
+ raise load_error || name_error
27
+ end
28
+
29
+ begin
30
+ ::Object.const_get("Object", false)
31
+ def self._const_get(str, inherit = true)
32
+ const_get(str, inherit)
33
+ end
34
+ rescue
35
+ def self._const_get(str, inherit = true)
36
+ const_get(str)
37
+ end
38
+ end
39
+
40
+
41
+ # Select first available Rack handler given an `Array` of server names.
42
+ # Raises `LoadError` if no handler was found.
43
+ #
44
+ # > pick ['thin', 'webrick']
45
+ # => Rack::Handler::WEBrick
46
+ def self.pick(server_names)
47
+ server_names = Array(server_names)
48
+ server_names.each do |server_name|
49
+ begin
50
+ return get(server_name.to_s)
51
+ rescue LoadError, NameError
52
+ end
53
+ end
54
+
55
+ raise LoadError, "Couldn't find handler for: #{server_names.join(', ')}."
23
56
  end
24
57
 
25
58
  def self.default(options = {})
@@ -30,14 +63,12 @@ module Rack
30
63
  options.delete :Port
31
64
 
32
65
  Rack::Handler::FastCGI
33
- elsif ENV.include?("REQUEST_METHOD")
66
+ elsif ENV.include?(REQUEST_METHOD)
34
67
  Rack::Handler::CGI
68
+ elsif ENV.include?("RACK_HANDLER")
69
+ self.get(ENV["RACK_HANDLER"])
35
70
  else
36
- begin
37
- Rack::Handler::Mongrel
38
- rescue LoadError => e
39
- Rack::Handler::WEBrick
40
- end
71
+ pick ['thin', 'puma', 'webrick']
41
72
  end
42
73
  end
43
74
 
@@ -57,12 +88,14 @@ module Rack
57
88
  gsub(/[A-Z]+[^A-Z]/, '_\&').downcase
58
89
 
59
90
  require(::File.join(prefix, file))
60
- rescue LoadError
91
+ nil
92
+ rescue LoadError => error
93
+ error
61
94
  end
62
95
 
63
96
  def self.register(server, klass)
64
97
  @handlers ||= {}
65
- @handlers[server] = klass
98
+ @handlers[server.to_s] = klass.to_s
66
99
  end
67
100
 
68
101
  autoload :CGI, "rack/handler/cgi"
data/lib/rack/head.rb CHANGED
@@ -1,6 +1,10 @@
1
+ require 'rack/body_proxy'
2
+
1
3
  module Rack
2
4
 
3
5
  class Head
6
+ # Rack::Head returns an empty body for all HEAD requests. It leaves
7
+ # all other requests unchanged.
4
8
  def initialize(app)
5
9
  @app = app
6
10
  end
@@ -8,8 +12,12 @@ class Head
8
12
  def call(env)
9
13
  status, headers, body = @app.call(env)
10
14
 
11
- if env["REQUEST_METHOD"] == "HEAD"
12
- [status, headers, []]
15
+ if env[REQUEST_METHOD] == HEAD
16
+ [
17
+ status, headers, Rack::BodyProxy.new([]) do
18
+ body.close if body.respond_to? :close
19
+ end
20
+ ]
13
21
  else
14
22
  [status, headers, body]
15
23
  end