rack 1.4.7 → 2.1.4
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +77 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +122 -456
- data/Rakefile +32 -31
- data/SPEC +119 -29
- data/bin/rackup +1 -0
- data/contrib/rack_logo.svg +164 -111
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +4 -2
- data/example/protectedlobster.ru +3 -1
- data/lib/rack/auth/abstract/handler.rb +7 -5
- data/lib/rack/auth/abstract/request.rb +8 -6
- data/lib/rack/auth/basic.rb +5 -2
- data/lib/rack/auth/digest/md5.rb +10 -8
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +5 -4
- data/lib/rack/auth/digest/request.rb +4 -2
- data/lib/rack/body_proxy.rb +11 -9
- data/lib/rack/builder.rb +63 -20
- data/lib/rack/cascade.rb +10 -9
- data/lib/rack/chunked.rb +45 -11
- data/lib/rack/{commonlogger.rb → common_logger.rb} +24 -15
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -6
- data/lib/rack/config.rb +7 -0
- data/lib/rack/content_length.rb +12 -6
- data/lib/rack/content_type.rb +4 -2
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +73 -42
- data/lib/rack/directory.rb +77 -56
- data/lib/rack/etag.rb +25 -13
- data/lib/rack/events.rb +156 -0
- data/lib/rack/file.rb +4 -143
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler/cgi.rb +18 -17
- data/lib/rack/handler/fastcgi.rb +21 -17
- data/lib/rack/handler/lsws.rb +14 -12
- data/lib/rack/handler/scgi.rb +27 -21
- data/lib/rack/handler/thin.rb +19 -5
- data/lib/rack/handler/webrick.rb +66 -24
- data/lib/rack/handler.rb +29 -19
- data/lib/rack/head.rb +21 -14
- data/lib/rack/lint.rb +259 -65
- data/lib/rack/lobster.rb +17 -10
- data/lib/rack/lock.rb +19 -10
- data/lib/rack/logger.rb +4 -2
- data/lib/rack/media_type.rb +43 -0
- data/lib/rack/method_override.rb +52 -0
- data/lib/rack/mime.rb +43 -6
- data/lib/rack/mock.rb +109 -44
- data/lib/rack/multipart/generator.rb +11 -12
- data/lib/rack/multipart/parser.rb +302 -115
- data/lib/rack/multipart/uploaded_file.rb +4 -3
- data/lib/rack/multipart.rb +40 -9
- data/lib/rack/null_logger.rb +39 -0
- data/lib/rack/query_parser.rb +218 -0
- data/lib/rack/recursive.rb +14 -11
- data/lib/rack/reloader.rb +12 -5
- data/lib/rack/request.rb +484 -270
- data/lib/rack/response.rb +196 -77
- data/lib/rack/rewindable_input.rb +5 -14
- data/lib/rack/runtime.rb +13 -6
- data/lib/rack/sendfile.rb +44 -20
- data/lib/rack/server.rb +175 -61
- data/lib/rack/session/abstract/id.rb +276 -133
- data/lib/rack/session/cookie.rb +75 -40
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +24 -18
- data/lib/rack/show_exceptions.rb +392 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +11 -9
- data/lib/rack/static.rb +65 -38
- data/lib/rack/tempfile_reaper.rb +24 -0
- data/lib/rack/urlmap.rb +40 -15
- data/lib/rack/utils.rb +316 -285
- data/lib/rack.rb +78 -23
- data/rack.gemspec +26 -19
- metadata +44 -209
- data/KNOWN-ISSUES +0 -30
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -100
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/methodoverride.rb +0 -33
- data/lib/rack/nulllogger.rb +0 -18
- data/lib/rack/showexceptions.rb +0 -378
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/lighttpd.errors +0 -1
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -8
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth.rb +0 -57
- data/test/spec_auth_basic.rb +0 -81
- data/test/spec_auth_digest.rb +0 -259
- data/test/spec_body_proxy.rb +0 -69
- data/test/spec_builder.rb +0 -207
- data/test/spec_cascade.rb +0 -61
- data/test/spec_cgi.rb +0 -102
- data/test/spec_chunked.rb +0 -87
- data/test/spec_commonlogger.rb +0 -57
- data/test/spec_conditionalget.rb +0 -102
- data/test/spec_config.rb +0 -22
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -45
- data/test/spec_deflater.rb +0 -187
- data/test/spec_directory.rb +0 -88
- data/test/spec_etag.rb +0 -98
- data/test/spec_fastcgi.rb +0 -107
- data/test/spec_file.rb +0 -200
- data/test/spec_handler.rb +0 -59
- data/test/spec_head.rb +0 -48
- data/test/spec_lint.rb +0 -515
- data/test/spec_lobster.rb +0 -58
- data/test/spec_lock.rb +0 -167
- data/test/spec_logger.rb +0 -23
- data/test/spec_methodoverride.rb +0 -72
- data/test/spec_mock.rb +0 -269
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_multipart.rb +0 -479
- data/test/spec_nulllogger.rb +0 -23
- data/test/spec_recursive.rb +0 -72
- data/test/spec_request.rb +0 -955
- data/test/spec_response.rb +0 -313
- data/test/spec_rewindable_input.rb +0 -118
- data/test/spec_runtime.rb +0 -49
- data/test/spec_sendfile.rb +0 -90
- data/test/spec_server.rb +0 -121
- data/test/spec_session_abstract_id.rb +0 -43
- data/test/spec_session_cookie.rb +0 -361
- data/test/spec_session_memcache.rb +0 -321
- data/test/spec_session_pool.rb +0 -209
- data/test/spec_showexceptions.rb +0 -92
- data/test/spec_showstatus.rb +0 -84
- data/test/spec_static.rb +0 -145
- data/test/spec_thin.rb +0 -86
- data/test/spec_urlmap.rb +0 -213
- data/test/spec_utils.rb +0 -554
- data/test/spec_webrick.rb +0 -143
- data/test/static/another/index.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
module Auth
|
3
5
|
module Digest
|
@@ -17,7 +19,7 @@ module Rack
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def self.split_header_value(str)
|
20
|
-
str.scan(
|
22
|
+
str.scan(/\w+\=(?:"[^\"]+"|[^,]+)/n)
|
21
23
|
end
|
22
24
|
|
23
25
|
def initialize
|
@@ -38,16 +40,15 @@ module Rack
|
|
38
40
|
|
39
41
|
def to_s
|
40
42
|
map do |k, v|
|
41
|
-
"#{k}
|
43
|
+
"#{k}=#{(UNQUOTED.include?(k) ? v.to_s : quote(v))}"
|
42
44
|
end.join(', ')
|
43
45
|
end
|
44
46
|
|
45
47
|
def quote(str) # From WEBrick::HTTPUtils
|
46
|
-
'"'
|
48
|
+
'"' + str.gsub(/[\\\"]/o, "\\\1") + '"'
|
47
49
|
end
|
48
50
|
|
49
51
|
end
|
50
52
|
end
|
51
53
|
end
|
52
54
|
end
|
53
|
-
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/auth/abstract/request'
|
2
4
|
require 'rack/auth/digest/params'
|
3
5
|
require 'rack/auth/digest/nonce'
|
@@ -7,11 +9,11 @@ module Rack
|
|
7
9
|
module Digest
|
8
10
|
class Request < Auth::AbstractRequest
|
9
11
|
def method
|
10
|
-
@env[
|
12
|
+
@env[RACK_METHODOVERRIDE_ORIGINAL_METHOD] || @env[REQUEST_METHOD]
|
11
13
|
end
|
12
14
|
|
13
15
|
def digest?
|
14
|
-
|
16
|
+
"digest" == scheme
|
15
17
|
end
|
16
18
|
|
17
19
|
def correct_uri?
|
data/lib/rack/body_proxy.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class BodyProxy
|
3
5
|
def initialize(body, &block)
|
4
|
-
@body
|
6
|
+
@body = body
|
7
|
+
@block = block
|
8
|
+
@closed = false
|
5
9
|
end
|
6
10
|
|
7
|
-
def respond_to?(
|
8
|
-
|
9
|
-
super or @body.respond_to?(*args)
|
11
|
+
def respond_to?(method_name, include_all = false)
|
12
|
+
super or @body.respond_to?(method_name, include_all)
|
10
13
|
end
|
11
14
|
|
12
15
|
def close
|
@@ -27,13 +30,12 @@ module Rack
|
|
27
30
|
# We are applying this special case for #each only. Future bugs of this
|
28
31
|
# class will be handled by requesting users to patch their ruby
|
29
32
|
# implementation, to save adding too many methods in this class.
|
30
|
-
def each
|
31
|
-
@body.each
|
33
|
+
def each
|
34
|
+
@body.each { |body| yield body }
|
32
35
|
end
|
33
36
|
|
34
|
-
def method_missing(*args, &block)
|
35
|
-
|
36
|
-
@body.__send__(*args, &block)
|
37
|
+
def method_missing(method_name, *args, &block)
|
38
|
+
@body.__send__(method_name, *args, &block)
|
37
39
|
end
|
38
40
|
end
|
39
41
|
end
|
data/lib/rack/builder.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
# Rack::Builder implements a small DSL to iteratively construct Rack
|
3
5
|
# applications.
|
@@ -25,29 +27,47 @@ module Rack
|
|
25
27
|
#
|
26
28
|
# run app
|
27
29
|
#
|
28
|
-
# +use+ adds
|
30
|
+
# +use+ adds middleware to the stack, +run+ dispatches to an application.
|
29
31
|
# You can use +map+ to construct a Rack::URLMap in a convenient way.
|
30
32
|
|
31
33
|
class Builder
|
34
|
+
|
35
|
+
# https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom
|
36
|
+
UTF_8_BOM = '\xef\xbb\xbf'
|
37
|
+
|
32
38
|
def self.parse_file(config, opts = Server::Options.new)
|
33
|
-
|
34
|
-
|
35
|
-
cfgfile = ::File.read(config)
|
36
|
-
if cfgfile[/^#\\(.*)/] && opts
|
37
|
-
options = opts.parse! $1.split(/\s+/)
|
38
|
-
end
|
39
|
-
cfgfile.sub!(/^__END__\n.*\Z/m, '')
|
40
|
-
app = eval "Rack::Builder.new {\n" + cfgfile + "\n}.to_app",
|
41
|
-
TOPLEVEL_BINDING, config, 0
|
39
|
+
if config.end_with?('.ru')
|
40
|
+
return self.load_file(config, opts)
|
42
41
|
else
|
43
42
|
require config
|
44
|
-
app = Object.const_get(::File.basename(config, '.rb').capitalize)
|
43
|
+
app = Object.const_get(::File.basename(config, '.rb').split('_').map(&:capitalize).join(''))
|
44
|
+
return app, {}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.load_file(path, opts = Server::Options.new)
|
49
|
+
options = {}
|
50
|
+
|
51
|
+
cfgfile = ::File.read(path)
|
52
|
+
cfgfile.slice!(/\A#{UTF_8_BOM}/) if cfgfile.encoding == Encoding::UTF_8
|
53
|
+
|
54
|
+
if cfgfile[/^#\\(.*)/] && opts
|
55
|
+
options = opts.parse! $1.split(/\s+/)
|
45
56
|
end
|
57
|
+
|
58
|
+
cfgfile.sub!(/^__END__\n.*\Z/m, '')
|
59
|
+
app = new_from_string cfgfile, path
|
60
|
+
|
46
61
|
return app, options
|
47
62
|
end
|
48
63
|
|
49
|
-
def
|
50
|
-
|
64
|
+
def self.new_from_string(builder_script, file = "(rackup)")
|
65
|
+
eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
|
66
|
+
TOPLEVEL_BINDING, file, 0
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize(default_app = nil, &block)
|
70
|
+
@use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
|
51
71
|
instance_eval(&block) if block_given?
|
52
72
|
end
|
53
73
|
|
@@ -55,7 +75,7 @@ module Rack
|
|
55
75
|
self.new(default_app, &block).to_app
|
56
76
|
end
|
57
77
|
|
58
|
-
# Specifies
|
78
|
+
# Specifies middleware to use in a stack.
|
59
79
|
#
|
60
80
|
# class Middleware
|
61
81
|
# def initialize(app)
|
@@ -69,7 +89,7 @@ module Rack
|
|
69
89
|
# end
|
70
90
|
#
|
71
91
|
# use Middleware
|
72
|
-
# run lambda { |env| [200, { "Content-Type => "text/plain" }, ["OK"]] }
|
92
|
+
# run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
|
73
93
|
#
|
74
94
|
# All requests through to this application will first be processed by the middleware class.
|
75
95
|
# The +call+ method in this example sets an additional environment key which then can be
|
@@ -77,10 +97,11 @@ module Rack
|
|
77
97
|
def use(middleware, *args, &block)
|
78
98
|
if @map
|
79
99
|
mapping, @map = @map, nil
|
80
|
-
@use << proc { |app| generate_map
|
100
|
+
@use << proc { |app| generate_map(app, mapping) }
|
81
101
|
end
|
82
102
|
@use << proc { |app| middleware.new(app, *args, &block) }
|
83
103
|
end
|
104
|
+
ruby2_keywords(:use) if respond_to?(:ruby2_keywords, true)
|
84
105
|
|
85
106
|
# Takes an argument that is an object that responds to #call and returns a Rack response.
|
86
107
|
# The simplest form of this is a lambda object:
|
@@ -92,7 +113,7 @@ module Rack
|
|
92
113
|
# class Heartbeat
|
93
114
|
# def self.call(env)
|
94
115
|
# [200, { "Content-Type" => "text/plain" }, ["OK"]]
|
95
|
-
#
|
116
|
+
# end
|
96
117
|
# end
|
97
118
|
#
|
98
119
|
# run Heartbeat
|
@@ -100,6 +121,19 @@ module Rack
|
|
100
121
|
@run = app
|
101
122
|
end
|
102
123
|
|
124
|
+
# Takes a lambda or block that is used to warm-up the application.
|
125
|
+
#
|
126
|
+
# warmup do |app|
|
127
|
+
# client = Rack::MockRequest.new(app)
|
128
|
+
# client.get('/')
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# use SomeMiddleware
|
132
|
+
# run MyApp
|
133
|
+
def warmup(prc = nil, &block)
|
134
|
+
@warmup = prc || block
|
135
|
+
end
|
136
|
+
|
103
137
|
# Creates a route within the application.
|
104
138
|
#
|
105
139
|
# Rack::Builder.app do
|
@@ -124,10 +158,19 @@ module Rack
|
|
124
158
|
@map[path] = block
|
125
159
|
end
|
126
160
|
|
161
|
+
# Freeze the app (set using run) and all middleware instances when building the application
|
162
|
+
# in to_app.
|
163
|
+
def freeze_app
|
164
|
+
@freeze_app = true
|
165
|
+
end
|
166
|
+
|
127
167
|
def to_app
|
128
168
|
app = @map ? generate_map(@run, @map) : @run
|
129
169
|
fail "missing run or map statement" unless app
|
130
|
-
|
170
|
+
app.freeze if @freeze_app
|
171
|
+
app = @use.reverse.inject(app) { |a, e| e[a].tap { |x| x.freeze if @freeze_app } }
|
172
|
+
@warmup.call(app) if @warmup
|
173
|
+
app
|
131
174
|
end
|
132
175
|
|
133
176
|
def call(env)
|
@@ -137,8 +180,8 @@ module Rack
|
|
137
180
|
private
|
138
181
|
|
139
182
|
def generate_map(default_app, mapping)
|
140
|
-
mapped = default_app ? {'/' => default_app} : {}
|
141
|
-
mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b) }
|
183
|
+
mapped = default_app ? { '/' => default_app } : {}
|
184
|
+
mapping.each { |r, b| mapped[r] = self.class.new(default_app, &b).to_app }
|
142
185
|
URLMap.new(mapped)
|
143
186
|
end
|
144
187
|
end
|
data/lib/rack/cascade.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
|
-
# Rack::Cascade tries
|
3
|
-
# first response that is not 404 (or in a list of configurable
|
4
|
+
# Rack::Cascade tries a request on several apps, and returns the
|
5
|
+
# first response that is not 404 or 405 (or in a list of configurable
|
4
6
|
# status codes).
|
5
7
|
|
6
8
|
class Cascade
|
7
|
-
NotFound = [404, {
|
9
|
+
NotFound = [404, { CONTENT_TYPE => "text/plain" }, []]
|
8
10
|
|
9
11
|
attr_reader :apps
|
10
12
|
|
11
|
-
def initialize(apps, catch=[404, 405])
|
12
|
-
@apps = []
|
13
|
+
def initialize(apps, catch = [404, 405])
|
14
|
+
@apps = []
|
13
15
|
apps.each { |app| add app }
|
14
16
|
|
15
17
|
@catch = {}
|
@@ -38,13 +40,12 @@ module Rack
|
|
38
40
|
result
|
39
41
|
end
|
40
42
|
|
41
|
-
def add
|
42
|
-
@has_app[app] = true
|
43
|
+
def add(app)
|
43
44
|
@apps << app
|
44
45
|
end
|
45
46
|
|
46
|
-
def include?
|
47
|
-
@
|
47
|
+
def include?(app)
|
48
|
+
@apps.include?(app)
|
48
49
|
end
|
49
50
|
|
50
51
|
alias_method :<<, :add
|
data/lib/rack/chunked.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
|
3
5
|
module Rack
|
@@ -10,7 +12,7 @@ module Rack
|
|
10
12
|
# A body wrapper that emits chunked responses
|
11
13
|
class Body
|
12
14
|
TERM = "\r\n"
|
13
|
-
TAIL = "0#{TERM}
|
15
|
+
TAIL = "0#{TERM}"
|
14
16
|
|
15
17
|
include Rack::Utils
|
16
18
|
|
@@ -18,40 +20,72 @@ module Rack
|
|
18
20
|
@body = body
|
19
21
|
end
|
20
22
|
|
21
|
-
def each
|
23
|
+
def each(&block)
|
22
24
|
term = TERM
|
23
25
|
@body.each do |chunk|
|
24
|
-
size = bytesize
|
26
|
+
size = chunk.bytesize
|
25
27
|
next if size == 0
|
26
28
|
|
27
|
-
chunk = chunk.
|
29
|
+
chunk = chunk.b
|
28
30
|
yield [size.to_s(16), term, chunk, term].join
|
29
31
|
end
|
30
32
|
yield TAIL
|
33
|
+
insert_trailers(&block)
|
34
|
+
yield TERM
|
31
35
|
end
|
32
36
|
|
33
37
|
def close
|
34
38
|
@body.close if @body.respond_to?(:close)
|
35
39
|
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def insert_trailers(&block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class TrailerBody < Body
|
48
|
+
private
|
49
|
+
|
50
|
+
def insert_trailers(&block)
|
51
|
+
@body.trailers.each_pair do |k, v|
|
52
|
+
yield "#{k}: #{v}\r\n"
|
53
|
+
end
|
54
|
+
end
|
36
55
|
end
|
37
56
|
|
38
57
|
def initialize(app)
|
39
58
|
@app = app
|
40
59
|
end
|
41
60
|
|
61
|
+
# pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
|
62
|
+
# a version (nor response headers)
|
63
|
+
def chunkable_version?(ver)
|
64
|
+
case ver
|
65
|
+
when 'HTTP/1.0', nil, 'HTTP/0.9'
|
66
|
+
false
|
67
|
+
else
|
68
|
+
true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
42
72
|
def call(env)
|
43
73
|
status, headers, body = @app.call(env)
|
44
74
|
headers = HeaderHash.new(headers)
|
45
75
|
|
46
|
-
if env[
|
47
|
-
STATUS_WITH_NO_ENTITY_BODY.
|
48
|
-
headers[
|
49
|
-
headers[
|
76
|
+
if ! chunkable_version?(env[SERVER_PROTOCOL]) ||
|
77
|
+
STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) ||
|
78
|
+
headers[CONTENT_LENGTH] ||
|
79
|
+
headers[TRANSFER_ENCODING]
|
50
80
|
[status, headers, body]
|
51
81
|
else
|
52
|
-
headers.delete(
|
53
|
-
headers[
|
54
|
-
|
82
|
+
headers.delete(CONTENT_LENGTH)
|
83
|
+
headers[TRANSFER_ENCODING] = 'chunked'
|
84
|
+
if headers['Trailer']
|
85
|
+
[status, headers, TrailerBody.new(body)]
|
86
|
+
else
|
87
|
+
[status, headers, Body.new(body)]
|
88
|
+
end
|
55
89
|
end
|
56
90
|
end
|
57
91
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/body_proxy'
|
2
4
|
|
3
5
|
module Rack
|
@@ -10,7 +12,7 @@ module Rack
|
|
10
12
|
# an instance of Rack::NullLogger.
|
11
13
|
#
|
12
14
|
# +logger+ can be any class, including the standard library Logger, and is
|
13
|
-
# expected to have
|
15
|
+
# expected to have either +write+ or +<<+ method, which accepts the CommonLogger::FORMAT.
|
14
16
|
# According to the SPEC, the error stream must also respond to +puts+
|
15
17
|
# (which takes a single argument that responds to +to_s+), and +flush+
|
16
18
|
# (which is called without arguments in order to make the error appear for
|
@@ -18,18 +20,18 @@ module Rack
|
|
18
20
|
class CommonLogger
|
19
21
|
# Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
|
20
22
|
#
|
21
|
-
# lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
|
23
|
+
# lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
|
22
24
|
#
|
23
25
|
# %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
|
24
26
|
FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
|
25
27
|
|
26
|
-
def initialize(app, logger=nil)
|
28
|
+
def initialize(app, logger = nil)
|
27
29
|
@app = app
|
28
30
|
@logger = logger
|
29
31
|
end
|
30
32
|
|
31
33
|
def call(env)
|
32
|
-
began_at =
|
34
|
+
began_at = Utils.clock_time
|
33
35
|
status, header, body = @app.call(env)
|
34
36
|
header = Utils::HeaderHash.new(header)
|
35
37
|
body = BodyProxy.new(body) { log(env, status, header, began_at) }
|
@@ -39,26 +41,33 @@ module Rack
|
|
39
41
|
private
|
40
42
|
|
41
43
|
def log(env, status, header, began_at)
|
42
|
-
now = Time.now
|
43
44
|
length = extract_content_length(header)
|
44
45
|
|
45
|
-
|
46
|
-
logger.write FORMAT % [
|
46
|
+
msg = FORMAT % [
|
47
47
|
env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
|
48
48
|
env["REMOTE_USER"] || "-",
|
49
|
-
now.strftime("%d/%b/%Y
|
50
|
-
env[
|
51
|
-
env[
|
52
|
-
env[
|
53
|
-
env[
|
49
|
+
Time.now.strftime("%d/%b/%Y:%H:%M:%S %z"),
|
50
|
+
env[REQUEST_METHOD],
|
51
|
+
env[PATH_INFO],
|
52
|
+
env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
|
53
|
+
env[SERVER_PROTOCOL],
|
54
54
|
status.to_s[0..3],
|
55
55
|
length,
|
56
|
-
|
56
|
+
Utils.clock_time - began_at ]
|
57
|
+
|
58
|
+
logger = @logger || env[RACK_ERRORS]
|
59
|
+
# Standard library logger doesn't support write but it supports << which actually
|
60
|
+
# calls to write on the log device without formatting
|
61
|
+
if logger.respond_to?(:write)
|
62
|
+
logger.write(msg)
|
63
|
+
else
|
64
|
+
logger << msg
|
65
|
+
end
|
57
66
|
end
|
58
67
|
|
59
68
|
def extract_content_length(headers)
|
60
|
-
value = headers[
|
61
|
-
value.to_s == '0' ? '-' : value
|
69
|
+
value = headers[CONTENT_LENGTH]
|
70
|
+
!value || value.to_s == '0' ? '-' : value
|
62
71
|
end
|
63
72
|
end
|
64
73
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
|
3
5
|
module Rack
|
@@ -13,22 +15,25 @@ module Rack
|
|
13
15
|
# a conditional GET matches.
|
14
16
|
#
|
15
17
|
# Adapted from Michael Klishin's Merb implementation:
|
16
|
-
#
|
18
|
+
# https://github.com/wycats/merb/blob/master/merb-core/lib/merb-core/rack/middleware/conditional_get.rb
|
17
19
|
class ConditionalGet
|
18
20
|
def initialize(app)
|
19
21
|
@app = app
|
20
22
|
end
|
21
23
|
|
22
24
|
def call(env)
|
23
|
-
case env[
|
25
|
+
case env[REQUEST_METHOD]
|
24
26
|
when "GET", "HEAD"
|
25
27
|
status, headers, body = @app.call(env)
|
26
28
|
headers = Utils::HeaderHash.new(headers)
|
27
29
|
if status == 200 && fresh?(env, headers)
|
28
30
|
status = 304
|
29
|
-
headers.delete(
|
30
|
-
headers.delete(
|
31
|
-
|
31
|
+
headers.delete(CONTENT_TYPE)
|
32
|
+
headers.delete(CONTENT_LENGTH)
|
33
|
+
original_body = body
|
34
|
+
body = Rack::BodyProxy.new([]) do
|
35
|
+
original_body.close if original_body.respond_to?(:close)
|
36
|
+
end
|
32
37
|
end
|
33
38
|
[status, headers, body]
|
34
39
|
else
|
@@ -61,7 +66,16 @@ module Rack
|
|
61
66
|
end
|
62
67
|
|
63
68
|
def to_rfc2822(since)
|
64
|
-
|
69
|
+
# shortest possible valid date is the obsolete: 1 Nov 97 09:55 A
|
70
|
+
# anything shorter is invalid, this avoids exceptions for common cases
|
71
|
+
# most common being the empty string
|
72
|
+
if since && since.length >= 16
|
73
|
+
# NOTE: there is no trivial way to write this in a non exception way
|
74
|
+
# _rfc2822 returns a hash but is not that usable
|
75
|
+
Time.rfc2822(since) rescue nil
|
76
|
+
else
|
77
|
+
nil
|
78
|
+
end
|
65
79
|
end
|
66
80
|
end
|
67
81
|
end
|
data/lib/rack/config.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
# Rack::Config modifies the environment using the block given during
|
3
5
|
# initialization.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# use Rack::Config do |env|
|
9
|
+
# env['my-key'] = 'some-value'
|
10
|
+
# end
|
4
11
|
class Config
|
5
12
|
def initialize(app, &block)
|
6
13
|
@app = app
|
data/lib/rack/content_length.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
4
|
+
require 'rack/body_proxy'
|
2
5
|
|
3
6
|
module Rack
|
4
7
|
|
@@ -14,17 +17,20 @@ module Rack
|
|
14
17
|
status, headers, body = @app.call(env)
|
15
18
|
headers = HeaderHash.new(headers)
|
16
19
|
|
17
|
-
if !STATUS_WITH_NO_ENTITY_BODY.
|
18
|
-
!headers[
|
19
|
-
!headers[
|
20
|
+
if !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
|
21
|
+
!headers[CONTENT_LENGTH] &&
|
22
|
+
!headers[TRANSFER_ENCODING] &&
|
20
23
|
body.respond_to?(:to_ary)
|
21
24
|
|
22
25
|
obody = body
|
23
26
|
body, length = [], 0
|
24
|
-
obody.each { |part| body << part; length += bytesize
|
25
|
-
|
27
|
+
obody.each { |part| body << part; length += part.bytesize }
|
28
|
+
|
29
|
+
body = BodyProxy.new(body) do
|
30
|
+
obody.close if obody.respond_to?(:close)
|
31
|
+
end
|
26
32
|
|
27
|
-
headers[
|
33
|
+
headers[CONTENT_LENGTH] = length.to_s
|
28
34
|
end
|
29
35
|
|
30
36
|
[status, headers, body]
|
data/lib/rack/content_type.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
|
3
5
|
module Rack
|
@@ -19,8 +21,8 @@ module Rack
|
|
19
21
|
status, headers, body = @app.call(env)
|
20
22
|
headers = Utils::HeaderHash.new(headers)
|
21
23
|
|
22
|
-
unless STATUS_WITH_NO_ENTITY_BODY.
|
23
|
-
headers[
|
24
|
+
unless STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i)
|
25
|
+
headers[CONTENT_TYPE] ||= @content_type
|
24
26
|
end
|
25
27
|
|
26
28
|
[status, headers, body]
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Regexp has `match?` since Ruby 2.4
|
4
|
+
# so to support Ruby < 2.4 we need to define this method
|
5
|
+
|
6
|
+
module Rack
|
7
|
+
module RegexpExtensions
|
8
|
+
refine Regexp do
|
9
|
+
def match?(string, pos = 0)
|
10
|
+
!!match(string, pos)
|
11
|
+
end
|
12
|
+
end unless //.respond_to?(:match?)
|
13
|
+
end
|
14
|
+
end
|