rack 1.6.11 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +694 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +157 -163
  6. data/Rakefile +38 -32
  7. data/{SPEC → SPEC.rdoc} +41 -13
  8. data/bin/rackup +1 -0
  9. data/contrib/rack_logo.svg +164 -111
  10. data/example/lobster.ru +2 -0
  11. data/example/protectedlobster.rb +4 -2
  12. data/example/protectedlobster.ru +3 -1
  13. data/lib/rack/auth/abstract/handler.rb +3 -1
  14. data/lib/rack/auth/abstract/request.rb +6 -2
  15. data/lib/rack/auth/basic.rb +7 -4
  16. data/lib/rack/auth/digest/md5.rb +13 -11
  17. data/lib/rack/auth/digest/nonce.rb +6 -3
  18. data/lib/rack/auth/digest/params.rb +5 -4
  19. data/lib/rack/auth/digest/request.rb +6 -4
  20. data/lib/rack/body_proxy.rb +21 -15
  21. data/lib/rack/builder.rb +119 -26
  22. data/lib/rack/cascade.rb +28 -12
  23. data/lib/rack/chunked.rb +70 -22
  24. data/lib/rack/common_logger.rb +80 -0
  25. data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -16
  26. data/lib/rack/config.rb +2 -0
  27. data/lib/rack/content_length.rb +9 -8
  28. data/lib/rack/content_type.rb +5 -4
  29. data/lib/rack/core_ext/regexp.rb +14 -0
  30. data/lib/rack/deflater.rb +60 -70
  31. data/lib/rack/directory.rb +117 -85
  32. data/lib/rack/etag.rb +9 -7
  33. data/lib/rack/events.rb +153 -0
  34. data/lib/rack/file.rb +4 -149
  35. data/lib/rack/files.rb +218 -0
  36. data/lib/rack/handler/cgi.rb +17 -19
  37. data/lib/rack/handler/fastcgi.rb +17 -18
  38. data/lib/rack/handler/lsws.rb +14 -14
  39. data/lib/rack/handler/scgi.rb +22 -21
  40. data/lib/rack/handler/thin.rb +6 -3
  41. data/lib/rack/handler/webrick.rb +39 -32
  42. data/lib/rack/handler.rb +9 -26
  43. data/lib/rack/head.rb +16 -18
  44. data/lib/rack/lint.rb +110 -64
  45. data/lib/rack/lobster.rb +10 -10
  46. data/lib/rack/lock.rb +17 -11
  47. data/lib/rack/logger.rb +4 -2
  48. data/lib/rack/media_type.rb +43 -0
  49. data/lib/rack/{methodoverride.rb → method_override.rb} +10 -8
  50. data/lib/rack/mime.rb +27 -6
  51. data/lib/rack/mock.rb +124 -65
  52. data/lib/rack/multipart/generator.rb +20 -16
  53. data/lib/rack/multipart/parser.rb +273 -162
  54. data/lib/rack/multipart/uploaded_file.rb +15 -8
  55. data/lib/rack/multipart.rb +39 -8
  56. data/lib/rack/{nulllogger.rb → null_logger.rb} +3 -1
  57. data/lib/rack/query_parser.rb +217 -0
  58. data/lib/rack/recursive.rb +11 -9
  59. data/lib/rack/reloader.rb +8 -4
  60. data/lib/rack/request.rb +553 -305
  61. data/lib/rack/response.rb +244 -88
  62. data/lib/rack/rewindable_input.rb +5 -15
  63. data/lib/rack/runtime.rb +12 -18
  64. data/lib/rack/sendfile.rb +17 -15
  65. data/lib/rack/server.rb +125 -47
  66. data/lib/rack/session/abstract/id.rb +217 -93
  67. data/lib/rack/session/cookie.rb +46 -31
  68. data/lib/rack/session/memcache.rb +4 -87
  69. data/lib/rack/session/pool.rb +26 -17
  70. data/lib/rack/show_exceptions.rb +390 -0
  71. data/lib/rack/{showstatus.rb → show_status.rb} +12 -12
  72. data/lib/rack/static.rb +48 -11
  73. data/lib/rack/tempfile_reaper.rb +3 -3
  74. data/lib/rack/urlmap.rb +26 -19
  75. data/lib/rack/utils.rb +212 -294
  76. data/lib/rack/version.rb +29 -0
  77. data/lib/rack.rb +76 -33
  78. data/rack.gemspec +43 -30
  79. metadata +65 -186
  80. data/HISTORY.md +0 -375
  81. data/KNOWN-ISSUES +0 -44
  82. data/lib/rack/backports/uri/common_18.rb +0 -56
  83. data/lib/rack/backports/uri/common_192.rb +0 -52
  84. data/lib/rack/backports/uri/common_193.rb +0 -29
  85. data/lib/rack/commonlogger.rb +0 -72
  86. data/lib/rack/handler/evented_mongrel.rb +0 -8
  87. data/lib/rack/handler/mongrel.rb +0 -106
  88. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  89. data/lib/rack/showexceptions.rb +0 -387
  90. data/lib/rack/utils/okjson.rb +0 -600
  91. data/test/builder/anything.rb +0 -5
  92. data/test/builder/comment.ru +0 -4
  93. data/test/builder/end.ru +0 -5
  94. data/test/builder/line.ru +0 -1
  95. data/test/builder/options.ru +0 -2
  96. data/test/cgi/assets/folder/test.js +0 -1
  97. data/test/cgi/assets/fonts/font.eot +0 -1
  98. data/test/cgi/assets/images/image.png +0 -1
  99. data/test/cgi/assets/index.html +0 -1
  100. data/test/cgi/assets/javascripts/app.js +0 -1
  101. data/test/cgi/assets/stylesheets/app.css +0 -1
  102. data/test/cgi/lighttpd.conf +0 -26
  103. data/test/cgi/rackup_stub.rb +0 -6
  104. data/test/cgi/sample_rackup.ru +0 -5
  105. data/test/cgi/test +0 -9
  106. data/test/cgi/test+directory/test+file +0 -1
  107. data/test/cgi/test.fcgi +0 -8
  108. data/test/cgi/test.ru +0 -5
  109. data/test/gemloader.rb +0 -10
  110. data/test/multipart/bad_robots +0 -259
  111. data/test/multipart/binary +0 -0
  112. data/test/multipart/content_type_and_no_filename +0 -6
  113. data/test/multipart/empty +0 -10
  114. data/test/multipart/fail_16384_nofile +0 -814
  115. data/test/multipart/file1.txt +0 -1
  116. data/test/multipart/filename_and_modification_param +0 -7
  117. data/test/multipart/filename_and_no_name +0 -6
  118. data/test/multipart/filename_with_escaped_quotes +0 -6
  119. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  120. data/test/multipart/filename_with_null_byte +0 -7
  121. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  122. data/test/multipart/filename_with_unescaped_percentages +0 -6
  123. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  124. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  125. data/test/multipart/filename_with_unescaped_quotes +0 -6
  126. data/test/multipart/ie +0 -6
  127. data/test/multipart/invalid_character +0 -6
  128. data/test/multipart/mixed_files +0 -21
  129. data/test/multipart/nested +0 -10
  130. data/test/multipart/none +0 -9
  131. data/test/multipart/semicolon +0 -6
  132. data/test/multipart/text +0 -15
  133. data/test/multipart/three_files_three_fields +0 -31
  134. data/test/multipart/webkit +0 -32
  135. data/test/rackup/config.ru +0 -31
  136. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  137. data/test/spec_auth_basic.rb +0 -81
  138. data/test/spec_auth_digest.rb +0 -259
  139. data/test/spec_body_proxy.rb +0 -85
  140. data/test/spec_builder.rb +0 -223
  141. data/test/spec_cascade.rb +0 -61
  142. data/test/spec_cgi.rb +0 -102
  143. data/test/spec_chunked.rb +0 -101
  144. data/test/spec_commonlogger.rb +0 -93
  145. data/test/spec_conditionalget.rb +0 -102
  146. data/test/spec_config.rb +0 -22
  147. data/test/spec_content_length.rb +0 -85
  148. data/test/spec_content_type.rb +0 -45
  149. data/test/spec_deflater.rb +0 -339
  150. data/test/spec_directory.rb +0 -88
  151. data/test/spec_etag.rb +0 -107
  152. data/test/spec_fastcgi.rb +0 -107
  153. data/test/spec_file.rb +0 -221
  154. data/test/spec_handler.rb +0 -72
  155. data/test/spec_head.rb +0 -45
  156. data/test/spec_lint.rb +0 -550
  157. data/test/spec_lobster.rb +0 -58
  158. data/test/spec_lock.rb +0 -164
  159. data/test/spec_logger.rb +0 -23
  160. data/test/spec_methodoverride.rb +0 -111
  161. data/test/spec_mime.rb +0 -51
  162. data/test/spec_mock.rb +0 -297
  163. data/test/spec_mongrel.rb +0 -182
  164. data/test/spec_multipart.rb +0 -600
  165. data/test/spec_nulllogger.rb +0 -20
  166. data/test/spec_recursive.rb +0 -72
  167. data/test/spec_request.rb +0 -1232
  168. data/test/spec_response.rb +0 -407
  169. data/test/spec_rewindable_input.rb +0 -118
  170. data/test/spec_runtime.rb +0 -49
  171. data/test/spec_sendfile.rb +0 -130
  172. data/test/spec_server.rb +0 -167
  173. data/test/spec_session_abstract_id.rb +0 -53
  174. data/test/spec_session_cookie.rb +0 -410
  175. data/test/spec_session_memcache.rb +0 -321
  176. data/test/spec_session_pool.rb +0 -209
  177. data/test/spec_showexceptions.rb +0 -98
  178. data/test/spec_showstatus.rb +0 -103
  179. data/test/spec_static.rb +0 -145
  180. data/test/spec_tempfile_reaper.rb +0 -63
  181. data/test/spec_thin.rb +0 -91
  182. data/test/spec_urlmap.rb +0 -236
  183. data/test/spec_utils.rb +0 -647
  184. data/test/spec_version.rb +0 -17
  185. data/test/spec_webrick.rb +0 -184
  186. data/test/static/another/index.html +0 -1
  187. data/test/static/index.html +0 -1
  188. data/test/testrequest.rb +0 -78
  189. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  190. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,93 +1,10 @@
1
- # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
1
+ # frozen_string_literal: true
2
2
 
3
- require 'rack/session/abstract/id'
4
- require 'memcache'
3
+ require 'rack/session/dalli'
5
4
 
6
5
  module Rack
7
6
  module Session
8
- # Rack::Session::Memcache provides simple cookie based session management.
9
- # Session data is stored in memcached. The corresponding session key is
10
- # maintained in the cookie.
11
- # You may treat Session::Memcache as you would Session::Pool with the
12
- # following caveats.
13
- #
14
- # * Setting :expire_after to 0 would note to the Memcache server to hang
15
- # onto the session data until it would drop it according to it's own
16
- # specifications. However, the cookie sent to the client would expire
17
- # immediately.
18
- #
19
- # Note that memcache does drop data before it may be listed to expire. For
20
- # a full description of behaviour, please see memcache's documentation.
21
-
22
- class Memcache < Abstract::ID
23
- attr_reader :mutex, :pool
24
-
25
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
26
- :namespace => 'rack:session',
27
- :memcache_server => 'localhost:11211'
28
-
29
- def initialize(app, options={})
30
- super
31
-
32
- @mutex = Mutex.new
33
- mserv = @default_options[:memcache_server]
34
- mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
35
-
36
- @pool = options[:cache] || MemCache.new(mserv, mopts)
37
- unless @pool.active? and @pool.servers.any?{|c| c.alive? }
38
- raise 'No memcache servers'
39
- end
40
- end
41
-
42
- def generate_sid
43
- loop do
44
- sid = super
45
- break sid unless @pool.get(sid, true)
46
- end
47
- end
48
-
49
- def get_session(env, sid)
50
- with_lock(env) do
51
- unless sid and session = @pool.get(sid)
52
- sid, session = generate_sid, {}
53
- unless /^STORED/ =~ @pool.add(sid, session)
54
- raise "Session collision on '#{sid.inspect}'"
55
- end
56
- end
57
- [sid, session]
58
- end
59
- end
60
-
61
- def set_session(env, session_id, new_session, options)
62
- expiry = options[:expire_after]
63
- expiry = expiry.nil? ? 0 : expiry + 1
64
-
65
- with_lock(env) do
66
- @pool.set session_id, new_session, expiry
67
- session_id
68
- end
69
- end
70
-
71
- def destroy_session(env, session_id, options)
72
- with_lock(env) do
73
- @pool.delete(session_id)
74
- generate_sid unless options[:drop]
75
- end
76
- end
77
-
78
- def with_lock(env)
79
- @mutex.lock if env['rack.multithread']
80
- yield
81
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED
82
- if $VERBOSE
83
- warn "#{self} is unable to find memcached server."
84
- warn $!.inspect
85
- end
86
- raise
87
- ensure
88
- @mutex.unlock if @mutex.locked?
89
- end
90
-
91
- end
7
+ warn "Rack::Session::Memcache is deprecated, please use Rack::Session::Dalli from 'dalli' gem instead."
8
+ Memcache = Dalli
92
9
  end
93
10
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
4
  # THANKS:
3
5
  # apeiros, for session id generation, expiry setup, and threadiness
4
6
  # sergio, threadiness and bugreps
5
7
 
6
- require 'rack/session/abstract/id'
8
+ require_relative 'abstract/id'
7
9
  require 'thread'
8
10
 
9
11
  module Rack
@@ -24,11 +26,11 @@ module Rack
24
26
  # )
25
27
  # Rack::Handler::WEBrick.run sessioned
26
28
 
27
- class Pool < Abstract::ID
29
+ class Pool < Abstract::PersistedSecure
28
30
  attr_reader :mutex, :pool
29
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
31
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge drop: false
30
32
 
31
- def initialize(app, options={})
33
+ def initialize(app, options = {})
32
34
  super
33
35
  @pool = Hash.new
34
36
  @mutex = Mutex.new
@@ -37,40 +39,47 @@ module Rack
37
39
  def generate_sid
38
40
  loop do
39
41
  sid = super
40
- break sid unless @pool.key? sid
42
+ break sid unless @pool.key? sid.private_id
41
43
  end
42
44
  end
43
45
 
44
- def get_session(env, sid)
45
- with_lock(env) do
46
- unless sid and session = @pool[sid]
46
+ def find_session(req, sid)
47
+ with_lock(req) do
48
+ unless sid and session = get_session_with_fallback(sid)
47
49
  sid, session = generate_sid, {}
48
- @pool.store sid, session
50
+ @pool.store sid.private_id, session
49
51
  end
50
52
  [sid, session]
51
53
  end
52
54
  end
53
55
 
54
- def set_session(env, session_id, new_session, options)
55
- with_lock(env) do
56
- @pool.store session_id, new_session
56
+ def write_session(req, session_id, new_session, options)
57
+ with_lock(req) do
58
+ @pool.store session_id.private_id, new_session
57
59
  session_id
58
60
  end
59
61
  end
60
62
 
61
- def destroy_session(env, session_id, options)
62
- with_lock(env) do
63
- @pool.delete(session_id)
63
+ def delete_session(req, session_id, options)
64
+ with_lock(req) do
65
+ @pool.delete(session_id.public_id)
66
+ @pool.delete(session_id.private_id)
64
67
  generate_sid unless options[:drop]
65
68
  end
66
69
  end
67
70
 
68
- def with_lock(env)
69
- @mutex.lock if env['rack.multithread']
71
+ def with_lock(req)
72
+ @mutex.lock if req.multithread?
70
73
  yield
71
74
  ensure
72
75
  @mutex.unlock if @mutex.locked?
73
76
  end
77
+
78
+ private
79
+
80
+ def get_session_with_fallback(sid)
81
+ @pool[sid.private_id] || @pool[sid.public_id]
82
+ end
74
83
  end
75
84
  end
76
85
  end
@@ -0,0 +1,390 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+ require 'erb'
5
+
6
+ module Rack
7
+ # Rack::ShowExceptions catches all exceptions raised from the app it
8
+ # wraps. It shows a useful backtrace with the sourcefile and
9
+ # clickable context, the whole Rack environment and the request
10
+ # data.
11
+ #
12
+ # Be careful when you use this on public-facing sites as it could
13
+ # reveal information helpful to attackers.
14
+
15
+ class ShowExceptions
16
+ CONTEXT = 7
17
+
18
+ def initialize(app)
19
+ @app = app
20
+ end
21
+
22
+ def call(env)
23
+ @app.call(env)
24
+ rescue StandardError, LoadError, SyntaxError => e
25
+ exception_string = dump_exception(e)
26
+
27
+ env[RACK_ERRORS].puts(exception_string)
28
+ env[RACK_ERRORS].flush
29
+
30
+ if accepts_html?(env)
31
+ content_type = "text/html"
32
+ body = pretty(env, e)
33
+ else
34
+ content_type = "text/plain"
35
+ body = exception_string
36
+ end
37
+
38
+ [
39
+ 500,
40
+ {
41
+ CONTENT_TYPE => content_type,
42
+ CONTENT_LENGTH => body.bytesize.to_s,
43
+ },
44
+ [body],
45
+ ]
46
+ end
47
+
48
+ def prefers_plaintext?(env)
49
+ !accepts_html?(env)
50
+ end
51
+
52
+ def accepts_html?(env)
53
+ Rack::Utils.best_q_match(env["HTTP_ACCEPT"], %w[text/html])
54
+ end
55
+ private :accepts_html?
56
+
57
+ def dump_exception(exception)
58
+ string = "#{exception.class}: #{exception.message}\n".dup
59
+ string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
60
+ string
61
+ end
62
+
63
+ def pretty(env, exception)
64
+ req = Rack::Request.new(env)
65
+
66
+ # This double assignment is to prevent an "unused variable" warning.
67
+ # Yes, it is dumb, but I don't like Ruby yelling at me.
68
+ path = path = (req.script_name + req.path_info).squeeze("/")
69
+
70
+ # This double assignment is to prevent an "unused variable" warning.
71
+ # Yes, it is dumb, but I don't like Ruby yelling at me.
72
+ frames = frames = exception.backtrace.map { |line|
73
+ frame = OpenStruct.new
74
+ if line =~ /(.*?):(\d+)(:in `(.*)')?/
75
+ frame.filename = $1
76
+ frame.lineno = $2.to_i
77
+ frame.function = $4
78
+
79
+ begin
80
+ lineno = frame.lineno - 1
81
+ lines = ::File.readlines(frame.filename)
82
+ frame.pre_context_lineno = [lineno - CONTEXT, 0].max
83
+ frame.pre_context = lines[frame.pre_context_lineno...lineno]
84
+ frame.context_line = lines[lineno].chomp
85
+ frame.post_context_lineno = [lineno + CONTEXT, lines.size].min
86
+ frame.post_context = lines[lineno + 1..frame.post_context_lineno]
87
+ rescue
88
+ end
89
+
90
+ frame
91
+ else
92
+ nil
93
+ end
94
+ }.compact
95
+
96
+ template.result(binding)
97
+ end
98
+
99
+ def template
100
+ TEMPLATE
101
+ end
102
+
103
+ def h(obj) # :nodoc:
104
+ case obj
105
+ when String
106
+ Utils.escape_html(obj)
107
+ else
108
+ Utils.escape_html(obj.inspect)
109
+ end
110
+ end
111
+
112
+ # :stopdoc:
113
+
114
+ # adapted from Django <www.djangoproject.com>
115
+ # Copyright (c) Django Software Foundation and individual contributors.
116
+ # Used under the modified BSD license:
117
+ # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
118
+ TEMPLATE = ERB.new(<<-'HTML'.gsub(/^ /, ''))
119
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
120
+ <html lang="en">
121
+ <head>
122
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
123
+ <meta name="robots" content="NONE,NOARCHIVE" />
124
+ <title><%=h exception.class %> at <%=h path %></title>
125
+ <style type="text/css">
126
+ html * { padding:0; margin:0; }
127
+ body * { padding:10px 20px; }
128
+ body * * { padding:0; }
129
+ body { font:small sans-serif; }
130
+ body>div { border-bottom:1px solid #ddd; }
131
+ h1 { font-weight:normal; }
132
+ h2 { margin-bottom:.8em; }
133
+ h2 span { font-size:80%; color:#666; font-weight:normal; }
134
+ h3 { margin:1em 0 .5em 0; }
135
+ h4 { margin:0 0 .5em 0; font-weight: normal; }
136
+ table {
137
+ border:1px solid #ccc; border-collapse: collapse; background:white; }
138
+ tbody td, tbody th { vertical-align:top; padding:2px 3px; }
139
+ thead th {
140
+ padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
141
+ font-weight:normal; font-size:11px; border:1px solid #ddd; }
142
+ tbody th { text-align:right; color:#666; padding-right:.5em; }
143
+ table.vars { margin:5px 0 2px 40px; }
144
+ table.vars td, table.req td { font-family:monospace; }
145
+ table td.code { width:100%;}
146
+ table td.code div { overflow:hidden; }
147
+ table.source th { color:#666; }
148
+ table.source td {
149
+ font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
150
+ ul.traceback { list-style-type:none; }
151
+ ul.traceback li.frame { margin-bottom:1em; }
152
+ div.context { margin: 10px 0; }
153
+ div.context ol {
154
+ padding-left:30px; margin:0 10px; list-style-position: inside; }
155
+ div.context ol li {
156
+ font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
157
+ div.context ol.context-line li { color:black; background-color:#ccc; }
158
+ div.context ol.context-line li span { float: right; }
159
+ div.commands { margin-left: 40px; }
160
+ div.commands a { color:black; text-decoration:none; }
161
+ #summary { background: #ffc; }
162
+ #summary h2 { font-weight: normal; color: #666; }
163
+ #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
164
+ #summary ul#quicklinks li { float: left; padding: 0 1em; }
165
+ #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
166
+ #explanation { background:#eee; }
167
+ #template, #template-not-exist { background:#f6f6f6; }
168
+ #template-not-exist ul { margin: 0 0 0 20px; }
169
+ #traceback { background:#eee; }
170
+ #requestinfo { background:#f6f6f6; padding-left:120px; }
171
+ #summary table { border:none; background:transparent; }
172
+ #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
173
+ #requestinfo h3 { margin-bottom:-1em; }
174
+ .error { background: #ffc; }
175
+ .specific { color:#cc3300; font-weight:bold; }
176
+ </style>
177
+ <script type="text/javascript">
178
+ //<!--
179
+ function getElementsByClassName(oElm, strTagName, strClassName){
180
+ // Written by Jonathan Snook, http://www.snook.ca/jon;
181
+ // Add-ons by Robert Nyman, http://www.robertnyman.com
182
+ var arrElements = (strTagName == "*" && document.all)? document.all :
183
+ oElm.getElementsByTagName(strTagName);
184
+ var arrReturnElements = new Array();
185
+ strClassName = strClassName.replace(/\-/g, "\\-");
186
+ var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
187
+ var oElement;
188
+ for(var i=0; i<arrElements.length; i++){
189
+ oElement = arrElements[i];
190
+ if(oRegExp.test(oElement.className)){
191
+ arrReturnElements.push(oElement);
192
+ }
193
+ }
194
+ return (arrReturnElements)
195
+ }
196
+ function hideAll(elems) {
197
+ for (var e = 0; e < elems.length; e++) {
198
+ elems[e].style.display = 'none';
199
+ }
200
+ }
201
+ window.onload = function() {
202
+ hideAll(getElementsByClassName(document, 'table', 'vars'));
203
+ hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
204
+ hideAll(getElementsByClassName(document, 'ol', 'post-context'));
205
+ }
206
+ function toggle() {
207
+ for (var i = 0; i < arguments.length; i++) {
208
+ var e = document.getElementById(arguments[i]);
209
+ if (e) {
210
+ e.style.display = e.style.display == 'none' ? 'block' : 'none';
211
+ }
212
+ }
213
+ return false;
214
+ }
215
+ function varToggle(link, id) {
216
+ toggle('v' + id);
217
+ var s = link.getElementsByTagName('span')[0];
218
+ var uarr = String.fromCharCode(0x25b6);
219
+ var darr = String.fromCharCode(0x25bc);
220
+ s.innerHTML = s.innerHTML == uarr ? darr : uarr;
221
+ return false;
222
+ }
223
+ //-->
224
+ </script>
225
+ </head>
226
+ <body>
227
+
228
+ <div id="summary">
229
+ <h1><%=h exception.class %> at <%=h path %></h1>
230
+ <h2><%=h exception.message %></h2>
231
+ <table><tr>
232
+ <th>Ruby</th>
233
+ <td>
234
+ <% if first = frames.first %>
235
+ <code><%=h first.filename %></code>: in <code><%=h first.function %></code>, line <%=h frames.first.lineno %>
236
+ <% else %>
237
+ unknown location
238
+ <% end %>
239
+ </td>
240
+ </tr><tr>
241
+ <th>Web</th>
242
+ <td><code><%=h req.request_method %> <%=h(req.host + path)%></code></td>
243
+ </tr></table>
244
+
245
+ <h3>Jump to:</h3>
246
+ <ul id="quicklinks">
247
+ <li><a href="#get-info">GET</a></li>
248
+ <li><a href="#post-info">POST</a></li>
249
+ <li><a href="#cookie-info">Cookies</a></li>
250
+ <li><a href="#env-info">ENV</a></li>
251
+ </ul>
252
+ </div>
253
+
254
+ <div id="traceback">
255
+ <h2>Traceback <span>(innermost first)</span></h2>
256
+ <ul class="traceback">
257
+ <% frames.each { |frame| %>
258
+ <li class="frame">
259
+ <code><%=h frame.filename %></code>: in <code><%=h frame.function %></code>
260
+
261
+ <% if frame.context_line %>
262
+ <div class="context" id="c<%=h frame.object_id %>">
263
+ <% if frame.pre_context %>
264
+ <ol start="<%=h frame.pre_context_lineno+1 %>" class="pre-context" id="pre<%=h frame.object_id %>">
265
+ <% frame.pre_context.each { |line| %>
266
+ <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
267
+ <% } %>
268
+ </ol>
269
+ <% end %>
270
+
271
+ <ol start="<%=h frame.lineno %>" class="context-line">
272
+ <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h frame.context_line %><span>...</span></li></ol>
273
+
274
+ <% if frame.post_context %>
275
+ <ol start='<%=h frame.lineno+1 %>' class="post-context" id="post<%=h frame.object_id %>">
276
+ <% frame.post_context.each { |line| %>
277
+ <li onclick="toggle('pre<%=h frame.object_id %>', 'post<%=h frame.object_id %>')"><%=h line %></li>
278
+ <% } %>
279
+ </ol>
280
+ <% end %>
281
+ </div>
282
+ <% end %>
283
+ </li>
284
+ <% } %>
285
+ </ul>
286
+ </div>
287
+
288
+ <div id="requestinfo">
289
+ <h2>Request information</h2>
290
+
291
+ <h3 id="get-info">GET</h3>
292
+ <% if req.GET and not req.GET.empty? %>
293
+ <table class="req">
294
+ <thead>
295
+ <tr>
296
+ <th>Variable</th>
297
+ <th>Value</th>
298
+ </tr>
299
+ </thead>
300
+ <tbody>
301
+ <% req.GET.sort_by { |k, v| k.to_s }.each { |key, val| %>
302
+ <tr>
303
+ <td><%=h key %></td>
304
+ <td class="code"><div><%=h val.inspect %></div></td>
305
+ </tr>
306
+ <% } %>
307
+ </tbody>
308
+ </table>
309
+ <% else %>
310
+ <p>No GET data.</p>
311
+ <% end %>
312
+
313
+ <h3 id="post-info">POST</h3>
314
+ <% if ((req.POST and not req.POST.empty?) rescue (no_post_data = "Invalid POST data"; nil)) %>
315
+ <table class="req">
316
+ <thead>
317
+ <tr>
318
+ <th>Variable</th>
319
+ <th>Value</th>
320
+ </tr>
321
+ </thead>
322
+ <tbody>
323
+ <% req.POST.sort_by { |k, v| k.to_s }.each { |key, val| %>
324
+ <tr>
325
+ <td><%=h key %></td>
326
+ <td class="code"><div><%=h val.inspect %></div></td>
327
+ </tr>
328
+ <% } %>
329
+ </tbody>
330
+ </table>
331
+ <% else %>
332
+ <p><%= no_post_data || "No POST data" %>.</p>
333
+ <% end %>
334
+
335
+
336
+ <h3 id="cookie-info">COOKIES</h3>
337
+ <% unless req.cookies.empty? %>
338
+ <table class="req">
339
+ <thead>
340
+ <tr>
341
+ <th>Variable</th>
342
+ <th>Value</th>
343
+ </tr>
344
+ </thead>
345
+ <tbody>
346
+ <% req.cookies.each { |key, val| %>
347
+ <tr>
348
+ <td><%=h key %></td>
349
+ <td class="code"><div><%=h val.inspect %></div></td>
350
+ </tr>
351
+ <% } %>
352
+ </tbody>
353
+ </table>
354
+ <% else %>
355
+ <p>No cookie data.</p>
356
+ <% end %>
357
+
358
+ <h3 id="env-info">Rack ENV</h3>
359
+ <table class="req">
360
+ <thead>
361
+ <tr>
362
+ <th>Variable</th>
363
+ <th>Value</th>
364
+ </tr>
365
+ </thead>
366
+ <tbody>
367
+ <% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
368
+ <tr>
369
+ <td><%=h key %></td>
370
+ <td class="code"><div><%=h val.inspect %></div></td>
371
+ </tr>
372
+ <% } %>
373
+ </tbody>
374
+ </table>
375
+
376
+ </div>
377
+
378
+ <div id="explanation">
379
+ <p>
380
+ You're seeing this error because you use <code>Rack::ShowExceptions</code>.
381
+ </p>
382
+ </div>
383
+
384
+ </body>
385
+ </html>
386
+ HTML
387
+
388
+ # :startdoc:
389
+ end
390
+ end
@@ -1,6 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
- require 'rack/request'
3
- require 'rack/utils'
4
4
 
5
5
  module Rack
6
6
  # Rack::ShowStatus catches all empty responses and replaces them
@@ -18,23 +18,23 @@ module Rack
18
18
 
19
19
  def call(env)
20
20
  status, headers, body = @app.call(env)
21
- headers = Utils::HeaderHash.new(headers)
21
+ headers = Utils::HeaderHash[headers]
22
22
  empty = headers[CONTENT_LENGTH].to_i <= 0
23
23
 
24
24
  # client or server error, or explicit message
25
- if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"]
26
- # This double assignment is to prevent an "unused variable" warning on
27
- # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
25
+ if (status.to_i >= 400 && empty) || env[RACK_SHOWSTATUS_DETAIL]
26
+ # This double assignment is to prevent an "unused variable" warning.
27
+ # Yes, it is dumb, but I don't like Ruby yelling at me.
28
28
  req = req = Rack::Request.new(env)
29
29
 
30
30
  message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s
31
31
 
32
- # This double assignment is to prevent an "unused variable" warning on
33
- # Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
34
- detail = detail = env["rack.showstatus.detail"] || message
32
+ # This double assignment is to prevent an "unused variable" warning.
33
+ # Yes, it is dumb, but I don't like Ruby yelling at me.
34
+ detail = detail = env[RACK_SHOWSTATUS_DETAIL] || message
35
35
 
36
36
  body = @template.result(binding)
37
- size = Rack::Utils.bytesize(body)
37
+ size = body.bytesize
38
38
  [status, headers.merge(CONTENT_TYPE => "text/html", CONTENT_LENGTH => size.to_s), [body]]
39
39
  else
40
40
  [status, headers, body]
@@ -52,8 +52,8 @@ module Rack
52
52
 
53
53
  # :stopdoc:
54
54
 
55
- # adapted from Django <djangoproject.com>
56
- # Copyright (c) 2005, the Lawrence Journal-World
55
+ # adapted from Django <www.djangoproject.com>
56
+ # Copyright (c) Django Software Foundation and individual contributors.
57
57
  # Used under the modified BSD license:
58
58
  # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
59
59
  TEMPLATE = <<'HTML'