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.
- checksums.yaml +7 -0
- data/COPYING +1 -1
- data/HISTORY.md +375 -0
- data/KNOWN-ISSUES +23 -0
- data/README.rdoc +312 -0
- data/Rakefile +124 -0
- data/SPEC +125 -32
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +150 -0
- data/contrib/rack_logo.svg +1 -1
- data/contrib/rdoc.css +412 -0
- data/example/protectedlobster.rb +1 -1
- data/lib/rack/auth/abstract/handler.rb +4 -4
- data/lib/rack/auth/abstract/request.rb +7 -5
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/md5.rb +7 -3
- data/lib/rack/auth/digest/nonce.rb +1 -1
- data/lib/rack/auth/digest/params.rb +7 -9
- data/lib/rack/auth/digest/request.rb +10 -9
- data/lib/rack/backports/uri/common_18.rb +56 -0
- data/lib/rack/backports/uri/common_192.rb +52 -0
- data/lib/rack/backports/uri/common_193.rb +29 -0
- data/lib/rack/body_proxy.rb +39 -0
- data/lib/rack/builder.rb +106 -22
- data/lib/rack/cascade.rb +17 -6
- data/lib/rack/chunked.rb +44 -24
- data/lib/rack/commonlogger.rb +36 -13
- data/lib/rack/conditionalget.rb +49 -17
- data/lib/rack/config.rb +5 -0
- data/lib/rack/content_length.rb +14 -6
- data/lib/rack/content_type.rb +7 -1
- data/lib/rack/deflater.rb +73 -15
- data/lib/rack/directory.rb +18 -8
- data/lib/rack/etag.rb +59 -9
- data/lib/rack/file.rb +106 -44
- data/lib/rack/handler/cgi.rb +11 -11
- data/lib/rack/handler/fastcgi.rb +18 -6
- data/lib/rack/handler/lsws.rb +2 -4
- data/lib/rack/handler/mongrel.rb +22 -6
- data/lib/rack/handler/scgi.rb +16 -8
- data/lib/rack/handler/thin.rb +19 -4
- data/lib/rack/handler/webrick.rb +72 -19
- data/lib/rack/handler.rb +47 -14
- data/lib/rack/head.rb +10 -2
- data/lib/rack/lint.rb +260 -75
- data/lib/rack/lobster.rb +13 -8
- data/lib/rack/lock.rb +13 -3
- data/lib/rack/logger.rb +0 -2
- data/lib/rack/methodoverride.rb +27 -8
- data/lib/rack/mime.rb +625 -167
- data/lib/rack/mock.rb +78 -53
- data/lib/rack/multipart/generator.rb +93 -0
- data/lib/rack/multipart/parser.rb +253 -0
- data/lib/rack/multipart/uploaded_file.rb +34 -0
- data/lib/rack/multipart.rb +34 -0
- data/lib/rack/nulllogger.rb +21 -2
- data/lib/rack/recursive.rb +10 -5
- data/lib/rack/reloader.rb +3 -2
- data/lib/rack/request.rb +201 -74
- data/lib/rack/response.rb +41 -28
- data/lib/rack/rewindable_input.rb +15 -11
- data/lib/rack/runtime.rb +16 -3
- data/lib/rack/sendfile.rb +47 -29
- data/lib/rack/server.rb +223 -47
- data/lib/rack/session/abstract/id.rb +289 -30
- data/lib/rack/session/cookie.rb +133 -44
- data/lib/rack/session/memcache.rb +30 -56
- data/lib/rack/session/pool.rb +19 -43
- data/lib/rack/showexceptions.rb +53 -15
- data/lib/rack/showstatus.rb +14 -7
- data/lib/rack/static.rb +124 -12
- data/lib/rack/tempfile_reaper.rb +22 -0
- data/lib/rack/urlmap.rb +49 -15
- data/lib/rack/utils/okjson.rb +600 -0
- data/lib/rack/utils.rb +363 -361
- data/lib/rack.rb +17 -23
- data/rack.gemspec +11 -20
- data/test/builder/anything.rb +5 -0
- data/test/builder/comment.ru +4 -0
- data/test/builder/end.ru +5 -0
- data/test/builder/line.ru +1 -0
- data/test/builder/options.ru +2 -0
- data/test/cgi/assets/folder/test.js +1 -0
- data/test/cgi/assets/fonts/font.eot +1 -0
- data/test/cgi/assets/images/image.png +1 -0
- data/test/cgi/assets/index.html +1 -0
- data/test/cgi/assets/javascripts/app.js +1 -0
- data/test/cgi/assets/stylesheets/app.css +1 -0
- data/test/cgi/lighttpd.conf +26 -0
- data/test/cgi/rackup_stub.rb +6 -0
- data/test/cgi/sample_rackup.ru +5 -0
- data/test/cgi/test +9 -0
- data/test/cgi/test+directory/test+file +1 -0
- data/test/cgi/test.fcgi +8 -0
- data/test/cgi/test.ru +5 -0
- data/test/gemloader.rb +10 -0
- data/test/multipart/bad_robots +259 -0
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +6 -0
- data/test/multipart/empty +10 -0
- data/test/multipart/fail_16384_nofile +814 -0
- data/test/multipart/file1.txt +1 -0
- data/test/multipart/filename_and_modification_param +7 -0
- data/test/multipart/filename_and_no_name +6 -0
- data/test/multipart/filename_with_escaped_quotes +6 -0
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
- data/test/multipart/filename_with_null_byte +7 -0
- data/test/multipart/filename_with_percent_escaped_quotes +6 -0
- data/test/multipart/filename_with_unescaped_percentages +6 -0
- data/test/multipart/filename_with_unescaped_percentages2 +6 -0
- data/test/multipart/filename_with_unescaped_percentages3 +6 -0
- data/test/multipart/filename_with_unescaped_quotes +6 -0
- data/test/multipart/ie +6 -0
- data/test/multipart/invalid_character +6 -0
- data/test/multipart/mixed_files +21 -0
- data/test/multipart/nested +10 -0
- data/test/multipart/none +9 -0
- data/test/multipart/semicolon +6 -0
- data/test/multipart/text +15 -0
- data/test/multipart/three_files_three_fields +31 -0
- data/test/multipart/webkit +32 -0
- data/test/rackup/config.ru +31 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
- data/test/{spec_rack_auth_basic.rb → spec_auth_basic.rb} +23 -15
- data/test/{spec_rack_auth_digest.rb → spec_auth_digest.rb} +56 -29
- data/test/spec_body_proxy.rb +85 -0
- data/test/spec_builder.rb +223 -0
- data/test/{spec_rack_cascade.rb → spec_cascade.rb} +28 -15
- data/test/{spec_rack_cgi.rb → spec_cgi.rb} +44 -31
- data/test/spec_chunked.rb +101 -0
- data/test/spec_commonlogger.rb +93 -0
- data/test/spec_conditionalget.rb +102 -0
- data/test/{spec_rack_config.rb → spec_config.rb} +6 -8
- data/test/spec_content_length.rb +85 -0
- data/test/spec_content_type.rb +45 -0
- data/test/spec_deflater.rb +339 -0
- data/test/{spec_rack_directory.rb → spec_directory.rb} +37 -10
- data/test/spec_etag.rb +107 -0
- data/test/{spec_rack_fastcgi.rb → spec_fastcgi.rb} +47 -29
- data/test/spec_file.rb +221 -0
- data/test/spec_handler.rb +72 -0
- data/test/spec_head.rb +45 -0
- data/test/{spec_rack_lint.rb → spec_lint.rb} +82 -60
- data/test/spec_lobster.rb +58 -0
- data/test/spec_lock.rb +164 -0
- data/test/spec_logger.rb +23 -0
- data/test/spec_methodoverride.rb +95 -0
- data/test/spec_mime.rb +51 -0
- data/test/{spec_rack_mock.rb → spec_mock.rb} +92 -38
- data/test/{spec_rack_mongrel.rb → spec_mongrel.rb} +46 -53
- data/test/spec_multipart.rb +600 -0
- data/test/spec_nulllogger.rb +20 -0
- data/test/spec_recursive.rb +72 -0
- data/test/spec_request.rb +1227 -0
- data/test/spec_response.rb +407 -0
- data/test/spec_rewindable_input.rb +118 -0
- data/test/spec_runtime.rb +49 -0
- data/test/spec_sendfile.rb +130 -0
- data/test/spec_server.rb +167 -0
- data/test/spec_session_abstract_id.rb +53 -0
- data/test/spec_session_cookie.rb +410 -0
- data/test/{spec_rack_session_memcache.rb → spec_session_memcache.rb} +119 -71
- data/test/{spec_rack_session_pool.rb → spec_session_pool.rb} +106 -69
- data/test/spec_showexceptions.rb +85 -0
- data/test/spec_showstatus.rb +103 -0
- data/test/spec_static.rb +145 -0
- data/test/spec_tempfile_reaper.rb +63 -0
- data/test/{spec_rack_thin.rb → spec_thin.rb} +35 -35
- data/test/{spec_rack_urlmap.rb → spec_urlmap.rb} +40 -19
- data/test/spec_utils.rb +647 -0
- data/test/spec_version.rb +17 -0
- data/test/spec_webrick.rb +184 -0
- data/test/static/another/index.html +1 -0
- data/test/static/index.html +1 -0
- data/test/testrequest.rb +78 -0
- data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
- metadata +220 -239
- data/RDOX +0 -0
- data/README +0 -592
- data/lib/rack/adapter/camping.rb +0 -22
- data/test/spec_auth.rb +0 -57
- data/test/spec_rack_builder.rb +0 -84
- data/test/spec_rack_camping.rb +0 -55
- data/test/spec_rack_chunked.rb +0 -62
- data/test/spec_rack_commonlogger.rb +0 -61
- data/test/spec_rack_conditionalget.rb +0 -41
- data/test/spec_rack_content_length.rb +0 -43
- data/test/spec_rack_content_type.rb +0 -30
- data/test/spec_rack_deflater.rb +0 -127
- data/test/spec_rack_etag.rb +0 -17
- data/test/spec_rack_file.rb +0 -75
- data/test/spec_rack_handler.rb +0 -43
- data/test/spec_rack_head.rb +0 -30
- data/test/spec_rack_lobster.rb +0 -45
- data/test/spec_rack_lock.rb +0 -38
- data/test/spec_rack_logger.rb +0 -21
- data/test/spec_rack_methodoverride.rb +0 -60
- data/test/spec_rack_nulllogger.rb +0 -13
- data/test/spec_rack_recursive.rb +0 -77
- data/test/spec_rack_request.rb +0 -594
- data/test/spec_rack_response.rb +0 -221
- data/test/spec_rack_rewindable_input.rb +0 -118
- data/test/spec_rack_runtime.rb +0 -35
- data/test/spec_rack_sendfile.rb +0 -86
- data/test/spec_rack_session_cookie.rb +0 -92
- data/test/spec_rack_showexceptions.rb +0 -21
- data/test/spec_rack_showstatus.rb +0 -72
- data/test/spec_rack_static.rb +0 -37
- data/test/spec_rack_utils.rb +0 -557
- data/test/spec_rack_webrick.rb +0 -130
- data/test/spec_rackup.rb +0 -164
|
@@ -21,6 +21,7 @@ module Rack
|
|
|
21
21
|
|
|
22
22
|
class Memcache < Abstract::ID
|
|
23
23
|
attr_reader :mutex, :pool
|
|
24
|
+
|
|
24
25
|
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
|
|
25
26
|
:namespace => 'rack:session',
|
|
26
27
|
:memcache_server => 'localhost:11211'
|
|
@@ -30,9 +31,9 @@ module Rack
|
|
|
30
31
|
|
|
31
32
|
@mutex = Mutex.new
|
|
32
33
|
mserv = @default_options[:memcache_server]
|
|
33
|
-
mopts = @default_options.
|
|
34
|
-
|
|
35
|
-
@pool = MemCache.new
|
|
34
|
+
mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
|
|
35
|
+
|
|
36
|
+
@pool = options[:cache] || MemCache.new(mserv, mopts)
|
|
36
37
|
unless @pool.active? and @pool.servers.any?{|c| c.alive? }
|
|
37
38
|
raise 'No memcache servers'
|
|
38
39
|
end
|
|
@@ -45,75 +46,48 @@ module Rack
|
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
|
48
|
-
def get_session(env,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
54
56
|
end
|
|
57
|
+
[sid, session]
|
|
55
58
|
end
|
|
56
|
-
session.instance_variable_set '@old', @pool.get(session_id, true)
|
|
57
|
-
return [session_id, session]
|
|
58
|
-
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
|
59
|
-
# MemCache server cannot be contacted
|
|
60
|
-
warn "#{self} is unable to find memcached server."
|
|
61
|
-
warn $!.inspect
|
|
62
|
-
return [ nil, {} ]
|
|
63
|
-
ensure
|
|
64
|
-
@mutex.unlock if @mutex.locked?
|
|
65
59
|
end
|
|
66
60
|
|
|
67
61
|
def set_session(env, session_id, new_session, options)
|
|
68
62
|
expiry = options[:expire_after]
|
|
69
63
|
expiry = expiry.nil? ? 0 : expiry + 1
|
|
70
64
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return false if options[:drop]
|
|
75
|
-
session_id = generate_sid
|
|
76
|
-
@pool.add session_id, {} # so we don't worry about cache miss on #set
|
|
65
|
+
with_lock(env) do
|
|
66
|
+
@pool.set session_id, new_session, expiry
|
|
67
|
+
session_id
|
|
77
68
|
end
|
|
69
|
+
end
|
|
78
70
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
unless Hash === old_session and Hash === new_session
|
|
84
|
-
env['rack.errors'].
|
|
85
|
-
puts 'Bad old_session or new_session sessions provided.'
|
|
86
|
-
else # merge sessions
|
|
87
|
-
# alterations are either update or delete, making as few changes as
|
|
88
|
-
# possible to prevent possible issues.
|
|
89
|
-
|
|
90
|
-
# removed keys
|
|
91
|
-
delete = old_session.keys - new_session.keys
|
|
92
|
-
if $VERBOSE and not delete.empty?
|
|
93
|
-
env['rack.errors'].
|
|
94
|
-
puts "//@#{session_id}: delete #{delete*','}"
|
|
95
|
-
end
|
|
96
|
-
delete.each{|k| session.delete k }
|
|
97
|
-
|
|
98
|
-
# added or altered keys
|
|
99
|
-
update = new_session.keys.
|
|
100
|
-
select{|k| new_session[k] != old_session[k] }
|
|
101
|
-
if $VERBOSE and not update.empty?
|
|
102
|
-
env['rack.errors'].puts "//@#{session_id}: update #{update*','}"
|
|
103
|
-
end
|
|
104
|
-
update.each{|k| session[k] = new_session[k] }
|
|
71
|
+
def destroy_session(env, session_id, options)
|
|
72
|
+
with_lock(env) do
|
|
73
|
+
@pool.delete(session_id)
|
|
74
|
+
generate_sid unless options[:drop]
|
|
105
75
|
end
|
|
76
|
+
end
|
|
106
77
|
|
|
107
|
-
|
|
108
|
-
|
|
78
|
+
def with_lock(env)
|
|
79
|
+
@mutex.lock if env['rack.multithread']
|
|
80
|
+
yield
|
|
109
81
|
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
82
|
+
if $VERBOSE
|
|
83
|
+
warn "#{self} is unable to find memcached server."
|
|
84
|
+
warn $!.inspect
|
|
85
|
+
end
|
|
86
|
+
raise
|
|
114
87
|
ensure
|
|
115
88
|
@mutex.unlock if @mutex.locked?
|
|
116
89
|
end
|
|
90
|
+
|
|
117
91
|
end
|
|
118
92
|
end
|
|
119
93
|
end
|
data/lib/rack/session/pool.rb
CHANGED
|
@@ -42,58 +42,34 @@ module Rack
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def get_session(env, sid)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
sid
|
|
51
|
-
@pool.store sid, session
|
|
45
|
+
with_lock(env) do
|
|
46
|
+
unless sid and session = @pool[sid]
|
|
47
|
+
sid, session = generate_sid, {}
|
|
48
|
+
@pool.store sid, session
|
|
49
|
+
end
|
|
50
|
+
[sid, session]
|
|
52
51
|
end
|
|
53
|
-
session.instance_variable_set('@old', {}.merge(session))
|
|
54
|
-
return [sid, session]
|
|
55
|
-
ensure
|
|
56
|
-
@mutex.unlock if env['rack.multithread']
|
|
57
52
|
end
|
|
58
53
|
|
|
59
54
|
def set_session(env, session_id, new_session, options)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@pool.delete session_id
|
|
64
|
-
return false if options[:drop]
|
|
65
|
-
session_id = generate_sid
|
|
66
|
-
@pool.store session_id, 0
|
|
55
|
+
with_lock(env) do
|
|
56
|
+
@pool.store session_id, new_session
|
|
57
|
+
session_id
|
|
67
58
|
end
|
|
68
|
-
old_session = new_session.instance_variable_get('@old') || {}
|
|
69
|
-
session = merge_sessions session_id, old_session, new_session, session
|
|
70
|
-
@pool.store session_id, session
|
|
71
|
-
return session_id
|
|
72
|
-
rescue
|
|
73
|
-
warn "#{new_session.inspect} has been lost."
|
|
74
|
-
warn $!.inspect
|
|
75
|
-
ensure
|
|
76
|
-
@mutex.unlock if env['rack.multithread']
|
|
77
59
|
end
|
|
78
60
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
unless Hash === old and Hash === new
|
|
84
|
-
warn 'Bad old or new sessions provided.'
|
|
85
|
-
return cur
|
|
61
|
+
def destroy_session(env, session_id, options)
|
|
62
|
+
with_lock(env) do
|
|
63
|
+
@pool.delete(session_id)
|
|
64
|
+
generate_sid unless options[:drop]
|
|
86
65
|
end
|
|
66
|
+
end
|
|
87
67
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
warn "//@#{sid}: updating #{update*','}" if $DEBUG and not update.empty?
|
|
94
|
-
update.each{|k| cur[k] = new[k] }
|
|
95
|
-
|
|
96
|
-
cur
|
|
68
|
+
def with_lock(env)
|
|
69
|
+
@mutex.lock if env['rack.multithread']
|
|
70
|
+
yield
|
|
71
|
+
ensure
|
|
72
|
+
@mutex.unlock if @mutex.locked?
|
|
97
73
|
end
|
|
98
74
|
end
|
|
99
75
|
end
|
data/lib/rack/showexceptions.rb
CHANGED
|
@@ -23,18 +23,54 @@ module Rack
|
|
|
23
23
|
def call(env)
|
|
24
24
|
@app.call(env)
|
|
25
25
|
rescue StandardError, LoadError, SyntaxError => e
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
exception_string = dump_exception(e)
|
|
27
|
+
|
|
28
|
+
env["rack.errors"].puts(exception_string)
|
|
29
|
+
env["rack.errors"].flush
|
|
30
|
+
|
|
31
|
+
if accepts_html?(env)
|
|
32
|
+
content_type = "text/html"
|
|
33
|
+
body = pretty(env, e)
|
|
34
|
+
else
|
|
35
|
+
content_type = "text/plain"
|
|
36
|
+
body = exception_string
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
[
|
|
40
|
+
500,
|
|
41
|
+
{
|
|
42
|
+
CONTENT_TYPE => content_type,
|
|
43
|
+
CONTENT_LENGTH => Rack::Utils.bytesize(body).to_s,
|
|
44
|
+
},
|
|
45
|
+
[body],
|
|
46
|
+
]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def prefers_plaintext?(env)
|
|
50
|
+
!accepts_html(env)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def accepts_html?(env)
|
|
54
|
+
Rack::Utils.best_q_match(env["HTTP_ACCEPT"], %w[text/html])
|
|
55
|
+
end
|
|
56
|
+
private :accepts_html?
|
|
57
|
+
|
|
58
|
+
def dump_exception(exception)
|
|
59
|
+
string = "#{exception.class}: #{exception.message}\n"
|
|
60
|
+
string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
|
|
61
|
+
string
|
|
31
62
|
end
|
|
32
63
|
|
|
33
64
|
def pretty(env, exception)
|
|
34
65
|
req = Rack::Request.new(env)
|
|
35
|
-
path = (req.script_name + req.path_info).squeeze("/")
|
|
36
66
|
|
|
37
|
-
|
|
67
|
+
# This double assignment is to prevent an "unused variable" warning on
|
|
68
|
+
# Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
|
|
69
|
+
path = path = (req.script_name + req.path_info).squeeze("/")
|
|
70
|
+
|
|
71
|
+
# This double assignment is to prevent an "unused variable" warning on
|
|
72
|
+
# Ruby 1.9.3. Yes, it is dumb, but I don't like Ruby yelling at me.
|
|
73
|
+
frames = frames = exception.backtrace.map { |line|
|
|
38
74
|
frame = OpenStruct.new
|
|
39
75
|
if line =~ /(.*?):(\d+)(:in `(.*)')?/
|
|
40
76
|
frame.filename = $1
|
|
@@ -58,11 +94,7 @@ module Rack
|
|
|
58
94
|
end
|
|
59
95
|
}.compact
|
|
60
96
|
|
|
61
|
-
|
|
62
|
-
env["rack.errors"].puts exception.backtrace.map { |l| "\t" + l }
|
|
63
|
-
env["rack.errors"].flush
|
|
64
|
-
|
|
65
|
-
[@template.result(binding)]
|
|
97
|
+
@template.result(binding)
|
|
66
98
|
end
|
|
67
99
|
|
|
68
100
|
def h(obj) # :nodoc:
|
|
@@ -195,7 +227,13 @@ TEMPLATE = <<'HTML'
|
|
|
195
227
|
<h2><%=h exception.message %></h2>
|
|
196
228
|
<table><tr>
|
|
197
229
|
<th>Ruby</th>
|
|
198
|
-
<td
|
|
230
|
+
<td>
|
|
231
|
+
<% if first = frames.first %>
|
|
232
|
+
<code><%=h first.filename %></code>: in <code><%=h first.function %></code>, line <%=h frames.first.lineno %>
|
|
233
|
+
<% else %>
|
|
234
|
+
unknown location
|
|
235
|
+
<% end %>
|
|
236
|
+
</td>
|
|
199
237
|
</tr><tr>
|
|
200
238
|
<th>Web</th>
|
|
201
239
|
<td><code><%=h req.request_method %> <%=h(req.host + path)%></code></td>
|
|
@@ -248,7 +286,7 @@ TEMPLATE = <<'HTML'
|
|
|
248
286
|
<h2>Request information</h2>
|
|
249
287
|
|
|
250
288
|
<h3 id="get-info">GET</h3>
|
|
251
|
-
<%
|
|
289
|
+
<% if req.GET and not req.GET.empty? %>
|
|
252
290
|
<table class="req">
|
|
253
291
|
<thead>
|
|
254
292
|
<tr>
|
|
@@ -270,7 +308,7 @@ TEMPLATE = <<'HTML'
|
|
|
270
308
|
<% end %>
|
|
271
309
|
|
|
272
310
|
<h3 id="post-info">POST</h3>
|
|
273
|
-
<%
|
|
311
|
+
<% if req.POST and not req.POST.empty? %>
|
|
274
312
|
<table class="req">
|
|
275
313
|
<thead>
|
|
276
314
|
<tr>
|
data/lib/rack/showstatus.rb
CHANGED
|
@@ -3,8 +3,8 @@ require 'rack/request'
|
|
|
3
3
|
require 'rack/utils'
|
|
4
4
|
|
|
5
5
|
module Rack
|
|
6
|
-
# Rack::ShowStatus catches all empty responses
|
|
7
|
-
#
|
|
6
|
+
# Rack::ShowStatus catches all empty responses and replaces them
|
|
7
|
+
# with a site explaining the error.
|
|
8
8
|
#
|
|
9
9
|
# Additional details can be put into <tt>rack.showstatus.detail</tt>
|
|
10
10
|
# and will be shown as HTML. If such details exist, the error page
|
|
@@ -19,16 +19,23 @@ module Rack
|
|
|
19
19
|
def call(env)
|
|
20
20
|
status, headers, body = @app.call(env)
|
|
21
21
|
headers = Utils::HeaderHash.new(headers)
|
|
22
|
-
empty = headers[
|
|
22
|
+
empty = headers[CONTENT_LENGTH].to_i <= 0
|
|
23
23
|
|
|
24
24
|
# client or server error, or explicit message
|
|
25
25
|
if (status.to_i >= 400 && empty) || env["rack.showstatus.detail"]
|
|
26
|
-
|
|
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.
|
|
28
|
+
req = req = Rack::Request.new(env)
|
|
29
|
+
|
|
27
30
|
message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s
|
|
28
|
-
|
|
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
|
|
35
|
+
|
|
29
36
|
body = @template.result(binding)
|
|
30
37
|
size = Rack::Utils.bytesize(body)
|
|
31
|
-
[status, headers.merge(
|
|
38
|
+
[status, headers.merge(CONTENT_TYPE => "text/html", CONTENT_LENGTH => size.to_s), [body]]
|
|
32
39
|
else
|
|
33
40
|
[status, headers, body]
|
|
34
41
|
end
|
|
@@ -89,7 +96,7 @@ TEMPLATE = <<'HTML'
|
|
|
89
96
|
</table>
|
|
90
97
|
</div>
|
|
91
98
|
<div id="info">
|
|
92
|
-
<p><%= detail %></p>
|
|
99
|
+
<p><%=h detail %></p>
|
|
93
100
|
</div>
|
|
94
101
|
|
|
95
102
|
<div id="explanation">
|
data/lib/rack/static.rb
CHANGED
|
@@ -1,38 +1,150 @@
|
|
|
1
1
|
module Rack
|
|
2
2
|
|
|
3
3
|
# The Rack::Static middleware intercepts requests for static files
|
|
4
|
-
# (javascript files, images, stylesheets, etc) based on the url prefixes
|
|
5
|
-
# passed in the options, and serves them using a Rack::File
|
|
6
|
-
# allows a Rack stack to serve both static and dynamic content.
|
|
4
|
+
# (javascript files, images, stylesheets, etc) based on the url prefixes or
|
|
5
|
+
# route mappings passed in the options, and serves them using a Rack::File
|
|
6
|
+
# object. This allows a Rack stack to serve both static and dynamic content.
|
|
7
7
|
#
|
|
8
8
|
# Examples:
|
|
9
|
+
#
|
|
10
|
+
# Serve all requests beginning with /media from the "media" folder located
|
|
11
|
+
# in the current directory (ie media/*):
|
|
12
|
+
#
|
|
9
13
|
# use Rack::Static, :urls => ["/media"]
|
|
10
|
-
#
|
|
11
|
-
#
|
|
14
|
+
#
|
|
15
|
+
# Serve all requests beginning with /css or /images from the folder "public"
|
|
16
|
+
# in the current directory (ie public/css/* and public/images/*):
|
|
12
17
|
#
|
|
13
18
|
# use Rack::Static, :urls => ["/css", "/images"], :root => "public"
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
|
|
19
|
+
#
|
|
20
|
+
# Serve all requests to / with "index.html" from the folder "public" in the
|
|
21
|
+
# current directory (ie public/index.html):
|
|
22
|
+
#
|
|
23
|
+
# use Rack::Static, :urls => {"/" => 'index.html'}, :root => 'public'
|
|
24
|
+
#
|
|
25
|
+
# Serve all requests normally from the folder "public" in the current
|
|
26
|
+
# directory but uses index.html as default route for "/"
|
|
27
|
+
#
|
|
28
|
+
# use Rack::Static, :urls => [""], :root => 'public', :index =>
|
|
29
|
+
# 'index.html'
|
|
30
|
+
#
|
|
31
|
+
# Set custom HTTP Headers for based on rules:
|
|
32
|
+
#
|
|
33
|
+
# use Rack::Static, :root => 'public',
|
|
34
|
+
# :header_rules => [
|
|
35
|
+
# [rule, {header_field => content, header_field => content}],
|
|
36
|
+
# [rule, {header_field => content}]
|
|
37
|
+
# ]
|
|
38
|
+
#
|
|
39
|
+
# Rules for selecting files:
|
|
40
|
+
#
|
|
41
|
+
# 1) All files
|
|
42
|
+
# Provide the :all symbol
|
|
43
|
+
# :all => Matches every file
|
|
44
|
+
#
|
|
45
|
+
# 2) Folders
|
|
46
|
+
# Provide the folder path as a string
|
|
47
|
+
# '/folder' or '/folder/subfolder' => Matches files in a certain folder
|
|
48
|
+
#
|
|
49
|
+
# 3) File Extensions
|
|
50
|
+
# Provide the file extensions as an array
|
|
51
|
+
# ['css', 'js'] or %w(css js) => Matches files ending in .css or .js
|
|
52
|
+
#
|
|
53
|
+
# 4) Regular Expressions / Regexp
|
|
54
|
+
# Provide a regular expression
|
|
55
|
+
# %r{\.(?:css|js)\z} => Matches files ending in .css or .js
|
|
56
|
+
# /\.(?:eot|ttf|otf|woff2|woff|svg)\z/ => Matches files ending in
|
|
57
|
+
# the most common web font formats (.eot, .ttf, .otf, .woff2, .woff, .svg)
|
|
58
|
+
# Note: This Regexp is available as a shortcut, using the :fonts rule
|
|
59
|
+
#
|
|
60
|
+
# 5) Font Shortcut
|
|
61
|
+
# Provide the :fonts symbol
|
|
62
|
+
# :fonts => Uses the Regexp rule stated right above to match all common web font endings
|
|
63
|
+
#
|
|
64
|
+
# Rule Ordering:
|
|
65
|
+
# Rules are applied in the order that they are provided.
|
|
66
|
+
# List rather general rules above special ones.
|
|
67
|
+
#
|
|
68
|
+
# Complete example use case including HTTP header rules:
|
|
69
|
+
#
|
|
70
|
+
# use Rack::Static, :root => 'public',
|
|
71
|
+
# :header_rules => [
|
|
72
|
+
# # Cache all static files in public caches (e.g. Rack::Cache)
|
|
73
|
+
# # as well as in the browser
|
|
74
|
+
# [:all, {'Cache-Control' => 'public, max-age=31536000'}],
|
|
75
|
+
#
|
|
76
|
+
# # Provide web fonts with cross-origin access-control-headers
|
|
77
|
+
# # Firefox requires this when serving assets using a Content Delivery Network
|
|
78
|
+
# [:fonts, {'Access-Control-Allow-Origin' => '*'}]
|
|
79
|
+
# ]
|
|
80
|
+
#
|
|
17
81
|
class Static
|
|
18
82
|
|
|
19
83
|
def initialize(app, options={})
|
|
20
84
|
@app = app
|
|
21
85
|
@urls = options[:urls] || ["/favicon.ico"]
|
|
86
|
+
@index = options[:index]
|
|
22
87
|
root = options[:root] || Dir.pwd
|
|
88
|
+
|
|
89
|
+
# HTTP Headers
|
|
90
|
+
@header_rules = options[:header_rules] || []
|
|
91
|
+
# Allow for legacy :cache_control option while prioritizing global header_rules setting
|
|
92
|
+
@header_rules.insert(0, [:all, {'Cache-Control' => options[:cache_control]}]) if options[:cache_control]
|
|
93
|
+
|
|
23
94
|
@file_server = Rack::File.new(root)
|
|
24
95
|
end
|
|
25
96
|
|
|
97
|
+
def overwrite_file_path(path)
|
|
98
|
+
@urls.kind_of?(Hash) && @urls.key?(path) || @index && path =~ /\/$/
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def route_file(path)
|
|
102
|
+
@urls.kind_of?(Array) && @urls.any? { |url| path.index(url) == 0 }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def can_serve(path)
|
|
106
|
+
route_file(path) || overwrite_file_path(path)
|
|
107
|
+
end
|
|
108
|
+
|
|
26
109
|
def call(env)
|
|
27
|
-
path = env[
|
|
28
|
-
|
|
110
|
+
path = env[PATH_INFO]
|
|
111
|
+
|
|
112
|
+
if can_serve(path)
|
|
113
|
+
env["PATH_INFO"] = (path =~ /\/$/ ? path + @index : @urls[path]) if overwrite_file_path(path)
|
|
114
|
+
path = env["PATH_INFO"]
|
|
115
|
+
response = @file_server.call(env)
|
|
29
116
|
|
|
30
|
-
|
|
31
|
-
|
|
117
|
+
headers = response[1]
|
|
118
|
+
applicable_rules(path).each do |rule, new_headers|
|
|
119
|
+
new_headers.each { |field, content| headers[field] = content }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
response
|
|
32
123
|
else
|
|
33
124
|
@app.call(env)
|
|
34
125
|
end
|
|
35
126
|
end
|
|
36
127
|
|
|
128
|
+
# Convert HTTP header rules to HTTP headers
|
|
129
|
+
def applicable_rules(path)
|
|
130
|
+
@header_rules.find_all do |rule, new_headers|
|
|
131
|
+
case rule
|
|
132
|
+
when :all
|
|
133
|
+
true
|
|
134
|
+
when :fonts
|
|
135
|
+
path =~ /\.(?:ttf|otf|eot|woff2|woff|svg)\z/
|
|
136
|
+
when String
|
|
137
|
+
path = ::Rack::Utils.unescape(path)
|
|
138
|
+
path.start_with?(rule) || path.start_with?('/' + rule)
|
|
139
|
+
when Array
|
|
140
|
+
path =~ /\.(#{rule.join('|')})\z/
|
|
141
|
+
when Regexp
|
|
142
|
+
path =~ rule
|
|
143
|
+
else
|
|
144
|
+
false
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
37
149
|
end
|
|
38
150
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'rack/body_proxy'
|
|
2
|
+
|
|
3
|
+
module Rack
|
|
4
|
+
|
|
5
|
+
# Middleware tracks and cleans Tempfiles created throughout a request (i.e. Rack::Multipart)
|
|
6
|
+
# Ideas/strategy based on posts by Eric Wong and Charles Oliver Nutter
|
|
7
|
+
# https://groups.google.com/forum/#!searchin/rack-devel/temp/rack-devel/brK8eh-MByw/sw61oJJCGRMJ
|
|
8
|
+
class TempfileReaper
|
|
9
|
+
def initialize(app)
|
|
10
|
+
@app = app
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(env)
|
|
14
|
+
env['rack.tempfiles'] ||= []
|
|
15
|
+
status, headers, body = @app.call(env)
|
|
16
|
+
body_proxy = BodyProxy.new(body) do
|
|
17
|
+
env['rack.tempfiles'].each { |f| f.close! } unless env['rack.tempfiles'].nil?
|
|
18
|
+
end
|
|
19
|
+
[status, headers, body_proxy]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/rack/urlmap.rb
CHANGED
|
@@ -12,6 +12,9 @@ module Rack
|
|
|
12
12
|
# first, since they are most specific.
|
|
13
13
|
|
|
14
14
|
class URLMap
|
|
15
|
+
NEGATIVE_INFINITY = -1.0 / 0.0
|
|
16
|
+
INFINITY = 1.0 / 0.0
|
|
17
|
+
|
|
15
18
|
def initialize(map = {})
|
|
16
19
|
remap(map)
|
|
17
20
|
end
|
|
@@ -27,29 +30,60 @@ module Rack
|
|
|
27
30
|
unless location[0] == ?/
|
|
28
31
|
raise ArgumentError, "paths need to start with /"
|
|
29
32
|
end
|
|
33
|
+
|
|
30
34
|
location = location.chomp('/')
|
|
31
35
|
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n')
|
|
32
36
|
|
|
33
37
|
[host, location, match, app]
|
|
34
|
-
}.sort_by
|
|
38
|
+
}.sort_by do |(host, location, _, _)|
|
|
39
|
+
[host ? -host.size : INFINITY, -location.size]
|
|
40
|
+
end
|
|
35
41
|
end
|
|
36
42
|
|
|
37
43
|
def call(env)
|
|
38
|
-
path = env[
|
|
44
|
+
path = env[PATH_INFO]
|
|
39
45
|
script_name = env['SCRIPT_NAME']
|
|
40
|
-
hHost
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
hHost = env['HTTP_HOST']
|
|
47
|
+
sName = env['SERVER_NAME']
|
|
48
|
+
sPort = env['SERVER_PORT']
|
|
49
|
+
|
|
50
|
+
@mapping.each do |host, location, match, app|
|
|
51
|
+
unless casecmp?(hHost, host) \
|
|
52
|
+
|| casecmp?(sName, host) \
|
|
53
|
+
|| (!host && (casecmp?(hHost, sName) ||
|
|
54
|
+
casecmp?(hHost, sName+':'+sPort)))
|
|
55
|
+
next
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
next unless m = match.match(path.to_s)
|
|
59
|
+
|
|
60
|
+
rest = m[1]
|
|
61
|
+
next unless !rest || rest.empty? || rest[0] == ?/
|
|
62
|
+
|
|
63
|
+
env['SCRIPT_NAME'] = (script_name + location)
|
|
64
|
+
env['PATH_INFO'] = rest
|
|
65
|
+
|
|
66
|
+
return app.call(env)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
[404, {CONTENT_TYPE => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
|
|
70
|
+
|
|
71
|
+
ensure
|
|
72
|
+
env['PATH_INFO'] = path
|
|
73
|
+
env['SCRIPT_NAME'] = script_name
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
def casecmp?(v1, v2)
|
|
78
|
+
# if both nil, or they're the same string
|
|
79
|
+
return true if v1 == v2
|
|
80
|
+
|
|
81
|
+
# if either are nil... (but they're not the same)
|
|
82
|
+
return false if v1.nil?
|
|
83
|
+
return false if v2.nil?
|
|
84
|
+
|
|
85
|
+
# otherwise check they're not case-insensitive the same
|
|
86
|
+
v1.casecmp(v2).zero?
|
|
53
87
|
end
|
|
54
88
|
end
|
|
55
89
|
end
|