vendor/rack/lib/rack/lint.rb in relevance-castronaut-0.5.4 vs vendor/rack/lib/rack/lint.rb in relevance-castronaut-0.6.0
- old
+ new
@@ -27,12 +27,12 @@
## = Rack applications
## A Rack application is an Ruby object (not a class) that
## responds to +call+.
- def call(env=nil)
- dup._call(env)
+ def call(env=nil)
+ dup._call(env)
end
def _call(env)
## It takes exactly one argument, the *environment*
assert("No env given") { env }
@@ -47,10 +47,11 @@
check_status status
## the *headers*,
check_headers headers
## and the *body*.
check_content_type status, headers
+ check_content_length status, headers, env
[status, headers, self]
end
## == The Environment
def check_env(env)
@@ -160,15 +161,13 @@
## * There must be a valid input stream in <tt>rack.input</tt>.
check_input env["rack.input"]
## * There must be a valid error stream in <tt>rack.errors</tt>.
check_error env["rack.errors"]
- ## * The <tt>REQUEST_METHOD</tt> must be one of +GET+, +POST+, +PUT+,
- ## +DELETE+, +HEAD+, +OPTIONS+, +TRACE+.
+ ## * The <tt>REQUEST_METHOD</tt> must be a valid token.
assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") {
- %w[GET POST PUT DELETE
- HEAD OPTIONS TRACE].include?(env["REQUEST_METHOD"])
+ env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
}
## * The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
assert("SCRIPT_NAME must start with /") {
!env.include?("SCRIPT_NAME") ||
@@ -213,10 +212,18 @@
def initialize(input)
@input = input
end
+ def size
+ @input.size
+ end
+
+ def rewind
+ @input.rewind
+ end
+
## * +gets+ must be called without arguments and return a string,
## or +nil+ on EOF.
def gets(*args)
assert("rack.input#gets called with arguments") { args.size == 0 }
v = @input.gets
@@ -348,21 +355,78 @@
## === The Content-Type
def check_content_type(status, headers)
headers.each { |key, value|
## There must be a <tt>Content-Type</tt>, except when the
- ## +Status+ is 204 or 304, in which case there must be none
+ ## +Status+ is 1xx, 204 or 304, in which case there must be none
## given.
if key.downcase == "content-type"
- assert("Content-Type header found in #{status} response, not allowed"){
- not [204, 304].include? status.to_i
+ assert("Content-Type header found in #{status} response, not allowed") {
+ not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
return
end
}
assert("No Content-Type header found") {
- [204, 304].include? status.to_i
+ Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
+ end
+
+ ## === The Content-Length
+ def check_content_length(status, headers, env)
+ chunked_response = false
+ headers.each { |key, value|
+ if key.downcase == 'transfer-encoding'
+ chunked_response = value.downcase != 'identity'
+ end
+ }
+
+ headers.each { |key, value|
+ if key.downcase == 'content-length'
+ ## There must be a <tt>Content-Length</tt>, except when the
+ ## +Status+ is 1xx, 204 or 304, in which case there must be none
+ ## given.
+ assert("Content-Length header found in #{status} response, not allowed") {
+ not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
+ }
+
+ assert('Content-Length header should not be used if body is chunked') {
+ not chunked_response
+ }
+
+ bytes = 0
+ string_body = true
+
+ @body.each { |part|
+ unless part.kind_of?(String)
+ string_body = false
+ break
+ end
+
+ bytes += (part.respond_to?(:bytesize) ? part.bytesize : part.size)
+ }
+
+ if env["REQUEST_METHOD"] == "HEAD"
+ assert("Response body was given for HEAD request, but should be empty") {
+ bytes == 0
+ }
+ else
+ if string_body
+ assert("Content-Length header was #{value}, but should be #{bytes}") {
+ value == bytes.to_s
+ }
+ end
+ end
+
+ return
+ end
+ }
+
+ if [ String, Array ].include?(@body.class) && !chunked_response
+ assert('No Content-Length header found') {
+ Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
+ }
+ end
end
## === The Body
def each
@closed = false