lint.rb 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. require 'rack/utils'
  2. module Rack
  3. # Rack::Lint validates your application and the requests and
  4. # responses according to the Rack spec.
  5. class Lint
  6. def initialize(app)
  7. @app = app
  8. @content_length = nil
  9. end
  10. # :stopdoc:
  11. class LintError < RuntimeError; end
  12. module Assertion
  13. def assert(message, &block)
  14. unless block.call
  15. raise LintError, message
  16. end
  17. end
  18. end
  19. include Assertion
  20. ## This specification aims to formalize the Rack protocol. You
  21. ## can (and should) use Rack::Lint to enforce it.
  22. ##
  23. ## When you develop middleware, be sure to add a Lint before and
  24. ## after to catch all mistakes.
  25. ## = Rack applications
  26. ## A Rack application is a Ruby object (not a class) that
  27. ## responds to +call+.
  28. def call(env=nil)
  29. dup._call(env)
  30. end
  31. def _call(env)
  32. ## It takes exactly one argument, the *environment*
  33. assert("No env given") { env }
  34. check_env env
  35. env['rack.input'] = InputWrapper.new(env['rack.input'])
  36. env['rack.errors'] = ErrorWrapper.new(env['rack.errors'])
  37. ## and returns an Array of exactly three values:
  38. status, headers, @body = @app.call(env)
  39. ## The *status*,
  40. check_status status
  41. ## the *headers*,
  42. check_headers headers
  43. ## and the *body*.
  44. check_content_type status, headers
  45. check_content_length status, headers
  46. @head_request = env["REQUEST_METHOD"] == "HEAD"
  47. [status, headers, self]
  48. end
  49. ## == The Environment
  50. def check_env(env)
  51. ## The environment must be an instance of Hash that includes
  52. ## CGI-like headers. The application is free to modify the
  53. ## environment.
  54. assert("env #{env.inspect} is not a Hash, but #{env.class}") {
  55. env.kind_of? Hash
  56. }
  57. ##
  58. ## The environment is required to include these variables
  59. ## (adopted from PEP333), except when they'd be empty, but see
  60. ## below.
  61. ## <tt>REQUEST_METHOD</tt>:: The HTTP request method, such as
  62. ## "GET" or "POST". This cannot ever
  63. ## be an empty string, and so is
  64. ## always required.
  65. ## <tt>SCRIPT_NAME</tt>:: The initial portion of the request
  66. ## URL's "path" that corresponds to the
  67. ## application object, so that the
  68. ## application knows its virtual
  69. ## "location". This may be an empty
  70. ## string, if the application corresponds
  71. ## to the "root" of the server.
  72. ## <tt>PATH_INFO</tt>:: The remainder of the request URL's
  73. ## "path", designating the virtual
  74. ## "location" of the request's target
  75. ## within the application. This may be an
  76. ## empty string, if the request URL targets
  77. ## the application root and does not have a
  78. ## trailing slash. This value may be
  79. ## percent-encoded when I originating from
  80. ## a URL.
  81. ## <tt>QUERY_STRING</tt>:: The portion of the request URL that
  82. ## follows the <tt>?</tt>, if any. May be
  83. ## empty, but is always required!
  84. ## <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>:: When combined with <tt>SCRIPT_NAME</tt> and <tt>PATH_INFO</tt>, these variables can be used to complete the URL. Note, however, that <tt>HTTP_HOST</tt>, if present, should be used in preference to <tt>SERVER_NAME</tt> for reconstructing the request URL. <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt> can never be empty strings, and so are always required.
  85. ## <tt>HTTP_</tt> Variables:: Variables corresponding to the
  86. ## client-supplied HTTP request
  87. ## headers (i.e., variables whose
  88. ## names begin with <tt>HTTP_</tt>). The
  89. ## presence or absence of these
  90. ## variables should correspond with
  91. ## the presence or absence of the
  92. ## appropriate HTTP header in the
  93. ## request.
  94. ## In addition to this, the Rack environment must include these
  95. ## Rack-specific variables:
  96. ## <tt>rack.version</tt>:: The Array [1,1], representing this version of Rack.
  97. ## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
  98. ## <tt>rack.input</tt>:: See below, the input stream.
  99. ## <tt>rack.errors</tt>:: See below, the error stream.
  100. ## <tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise.
  101. ## <tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise.
  102. ## <tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar).
  103. ##
  104. ## Additional environment specifications have approved to
  105. ## standardized middleware APIs. None of these are required to
  106. ## be implemented by the server.
  107. ## <tt>rack.session</tt>:: A hash like interface for storing request session data.
  108. ## The store must implement:
  109. if session = env['rack.session']
  110. ## store(key, value) (aliased as []=);
  111. assert("session #{session.inspect} must respond to store and []=") {
  112. session.respond_to?(:store) && session.respond_to?(:[]=)
  113. }
  114. ## fetch(key, default = nil) (aliased as []);
  115. assert("session #{session.inspect} must respond to fetch and []") {
  116. session.respond_to?(:fetch) && session.respond_to?(:[])
  117. }
  118. ## delete(key);
  119. assert("session #{session.inspect} must respond to delete") {
  120. session.respond_to?(:delete)
  121. }
  122. ## clear;
  123. assert("session #{session.inspect} must respond to clear") {
  124. session.respond_to?(:clear)
  125. }
  126. end
  127. ## <tt>rack.logger</tt>:: A common object interface for logging messages.
  128. ## The object must implement:
  129. if logger = env['rack.logger']
  130. ## info(message, &block)
  131. assert("logger #{logger.inspect} must respond to info") {
  132. logger.respond_to?(:info)
  133. }
  134. ## debug(message, &block)
  135. assert("logger #{logger.inspect} must respond to debug") {
  136. logger.respond_to?(:debug)
  137. }
  138. ## warn(message, &block)
  139. assert("logger #{logger.inspect} must respond to warn") {
  140. logger.respond_to?(:warn)
  141. }
  142. ## error(message, &block)
  143. assert("logger #{logger.inspect} must respond to error") {
  144. logger.respond_to?(:error)
  145. }
  146. ## fatal(message, &block)
  147. assert("logger #{logger.inspect} must respond to fatal") {
  148. logger.respond_to?(:fatal)
  149. }
  150. end
  151. ## The server or the application can store their own data in the
  152. ## environment, too. The keys must contain at least one dot,
  153. ## and should be prefixed uniquely. The prefix <tt>rack.</tt>
  154. ## is reserved for use with the Rack core distribution and other
  155. ## accepted specifications and must not be used otherwise.
  156. ##
  157. %w[REQUEST_METHOD SERVER_NAME SERVER_PORT
  158. QUERY_STRING
  159. rack.version rack.input rack.errors
  160. rack.multithread rack.multiprocess rack.run_once].each { |header|
  161. assert("env missing required key #{header}") { env.include? header }
  162. }
  163. ## The environment must not contain the keys
  164. ## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
  165. ## (use the versions without <tt>HTTP_</tt>).
  166. %w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header|
  167. assert("env contains #{header}, must use #{header[5,-1]}") {
  168. not env.include? header
  169. }
  170. }
  171. ## The CGI keys (named without a period) must have String values.
  172. env.each { |key, value|
  173. next if key.include? "." # Skip extensions
  174. assert("env variable #{key} has non-string value #{value.inspect}") {
  175. value.kind_of? String
  176. }
  177. }
  178. ##
  179. ## There are the following restrictions:
  180. ## * <tt>rack.version</tt> must be an array of Integers.
  181. assert("rack.version must be an Array, was #{env["rack.version"].class}") {
  182. env["rack.version"].kind_of? Array
  183. }
  184. ## * <tt>rack.url_scheme</tt> must either be +http+ or +https+.
  185. assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") {
  186. %w[http https].include? env["rack.url_scheme"]
  187. }
  188. ## * There must be a valid input stream in <tt>rack.input</tt>.
  189. check_input env["rack.input"]
  190. ## * There must be a valid error stream in <tt>rack.errors</tt>.
  191. check_error env["rack.errors"]
  192. ## * The <tt>REQUEST_METHOD</tt> must be a valid token.
  193. assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") {
  194. env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
  195. }
  196. ## * The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
  197. assert("SCRIPT_NAME must start with /") {
  198. !env.include?("SCRIPT_NAME") ||
  199. env["SCRIPT_NAME"] == "" ||
  200. env["SCRIPT_NAME"] =~ /\A\//
  201. }
  202. ## * The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt>
  203. assert("PATH_INFO must start with /") {
  204. !env.include?("PATH_INFO") ||
  205. env["PATH_INFO"] == "" ||
  206. env["PATH_INFO"] =~ /\A\//
  207. }
  208. ## * The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only.
  209. assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") {
  210. !env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/
  211. }
  212. ## * One of <tt>SCRIPT_NAME</tt> or <tt>PATH_INFO</tt> must be
  213. ## set. <tt>PATH_INFO</tt> should be <tt>/</tt> if
  214. ## <tt>SCRIPT_NAME</tt> is empty.
  215. assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") {
  216. env["SCRIPT_NAME"] || env["PATH_INFO"]
  217. }
  218. ## <tt>SCRIPT_NAME</tt> never should be <tt>/</tt>, but instead be empty.
  219. assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") {
  220. env["SCRIPT_NAME"] != "/"
  221. }
  222. end
  223. ## === The Input Stream
  224. ##
  225. ## The input stream is an IO-like object which contains the raw HTTP
  226. ## POST data.
  227. def check_input(input)
  228. ## When applicable, its external encoding must be "ASCII-8BIT" and it
  229. ## must be opened in binary mode, for Ruby 1.9 compatibility.
  230. assert("rack.input #{input} does not have ASCII-8BIT as its external encoding") {
  231. input.external_encoding.name == "ASCII-8BIT"
  232. } if input.respond_to?(:external_encoding)
  233. assert("rack.input #{input} is not opened in binary mode") {
  234. input.binmode?
  235. } if input.respond_to?(:binmode?)
  236. ## The input stream must respond to +gets+, +each+, +read+ and +rewind+.
  237. [:gets, :each, :read, :rewind].each { |method|
  238. assert("rack.input #{input} does not respond to ##{method}") {
  239. input.respond_to? method
  240. }
  241. }
  242. end
  243. class InputWrapper
  244. include Assertion
  245. def initialize(input)
  246. @input = input
  247. end
  248. ## * +gets+ must be called without arguments and return a string,
  249. ## or +nil+ on EOF.
  250. def gets(*args)
  251. assert("rack.input#gets called with arguments") { args.size == 0 }
  252. v = @input.gets
  253. assert("rack.input#gets didn't return a String") {
  254. v.nil? or v.kind_of? String
  255. }
  256. v
  257. end
  258. ## * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
  259. ## If given, +length+ must be a non-negative Integer (>= 0) or +nil+, and +buffer+ must
  260. ## be a String and may not be nil. If +length+ is given and not nil, then this method
  261. ## reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
  262. ## then this method reads all data until EOF.
  263. ## When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
  264. ## if +length+ is not given or is nil.
  265. ## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
  266. ## newly created String object.
  267. def read(*args)
  268. assert("rack.input#read called with too many arguments") {
  269. args.size <= 2
  270. }
  271. if args.size >= 1
  272. assert("rack.input#read called with non-integer and non-nil length") {
  273. args.first.kind_of?(Integer) || args.first.nil?
  274. }
  275. assert("rack.input#read called with a negative length") {
  276. args.first.nil? || args.first >= 0
  277. }
  278. end
  279. if args.size >= 2
  280. assert("rack.input#read called with non-String buffer") {
  281. args[1].kind_of?(String)
  282. }
  283. end
  284. v = @input.read(*args)
  285. assert("rack.input#read didn't return nil or a String") {
  286. v.nil? or v.kind_of? String
  287. }
  288. if args[0].nil?
  289. assert("rack.input#read(nil) returned nil on EOF") {
  290. !v.nil?
  291. }
  292. end
  293. v
  294. end
  295. ## * +each+ must be called without arguments and only yield Strings.
  296. def each(*args)
  297. assert("rack.input#each called with arguments") { args.size == 0 }
  298. @input.each { |line|
  299. assert("rack.input#each didn't yield a String") {
  300. line.kind_of? String
  301. }
  302. yield line
  303. }
  304. end
  305. ## * +rewind+ must be called without arguments. It rewinds the input
  306. ## stream back to the beginning. It must not raise Errno::ESPIPE:
  307. ## that is, it may not be a pipe or a socket. Therefore, handler
  308. ## developers must buffer the input data into some rewindable object
  309. ## if the underlying input stream is not rewindable.
  310. def rewind(*args)
  311. assert("rack.input#rewind called with arguments") { args.size == 0 }
  312. assert("rack.input#rewind raised Errno::ESPIPE") {
  313. begin
  314. @input.rewind
  315. true
  316. rescue Errno::ESPIPE
  317. false
  318. end
  319. }
  320. end
  321. ## * +close+ must never be called on the input stream.
  322. def close(*args)
  323. assert("rack.input#close must not be called") { false }
  324. end
  325. end
  326. ## === The Error Stream
  327. def check_error(error)
  328. ## The error stream must respond to +puts+, +write+ and +flush+.
  329. [:puts, :write, :flush].each { |method|
  330. assert("rack.error #{error} does not respond to ##{method}") {
  331. error.respond_to? method
  332. }
  333. }
  334. end
  335. class ErrorWrapper
  336. include Assertion
  337. def initialize(error)
  338. @error = error
  339. end
  340. ## * +puts+ must be called with a single argument that responds to +to_s+.
  341. def puts(str)
  342. @error.puts str
  343. end
  344. ## * +write+ must be called with a single argument that is a String.
  345. def write(str)
  346. assert("rack.errors#write not called with a String") { str.kind_of? String }
  347. @error.write str
  348. end
  349. ## * +flush+ must be called without arguments and must be called
  350. ## in order to make the error appear for sure.
  351. def flush
  352. @error.flush
  353. end
  354. ## * +close+ must never be called on the error stream.
  355. def close(*args)
  356. assert("rack.errors#close must not be called") { false }
  357. end
  358. end
  359. ## == The Response
  360. ## === The Status
  361. def check_status(status)
  362. ## This is an HTTP status. When parsed as integer (+to_i+), it must be
  363. ## greater than or equal to 100.
  364. assert("Status must be >=100 seen as integer") { status.to_i >= 100 }
  365. end
  366. ## === The Headers
  367. def check_headers(header)
  368. ## The header must respond to +each+, and yield values of key and value.
  369. assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
  370. header.respond_to? :each
  371. }
  372. header.each { |key, value|
  373. ## The header keys must be Strings.
  374. assert("header key must be a string, was #{key.class}") {
  375. key.kind_of? String
  376. }
  377. ## The header must not contain a +Status+ key,
  378. assert("header must not contain Status") { key.downcase != "status" }
  379. ## contain keys with <tt>:</tt> or newlines in their name,
  380. assert("header names must not contain : or \\n") { key !~ /[:\n]/ }
  381. ## contain keys names that end in <tt>-</tt> or <tt>_</tt>,
  382. assert("header names must not end in - or _") { key !~ /[-_]\z/ }
  383. ## but only contain keys that consist of
  384. ## letters, digits, <tt>_</tt> or <tt>-</tt> and start with a letter.
  385. assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ }
  386. ## The values of the header must be Strings,
  387. assert("a header value must be a String, but the value of " +
  388. "'#{key}' is a #{value.class}") { value.kind_of? String }
  389. ## consisting of lines (for multiple header values, e.g. multiple
  390. ## <tt>Set-Cookie</tt> values) seperated by "\n".
  391. value.split("\n").each { |item|
  392. ## The lines must not contain characters below 037.
  393. assert("invalid header value #{key}: #{item.inspect}") {
  394. item !~ /[\000-\037]/
  395. }
  396. }
  397. }
  398. end
  399. ## === The Content-Type
  400. def check_content_type(status, headers)
  401. headers.each { |key, value|
  402. ## There must be a <tt>Content-Type</tt>, except when the
  403. ## +Status+ is 1xx, 204, 205 or 304, in which case there must be none
  404. ## given.
  405. if key.downcase == "content-type"
  406. assert("Content-Type header found in #{status} response, not allowed") {
  407. not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
  408. }
  409. return
  410. end
  411. }
  412. assert("No Content-Type header found") {
  413. Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
  414. }
  415. end
  416. ## === The Content-Length
  417. def check_content_length(status, headers)
  418. headers.each { |key, value|
  419. if key.downcase == 'content-length'
  420. ## There must not be a <tt>Content-Length</tt> header when the
  421. ## +Status+ is 1xx, 204, 205 or 304.
  422. assert("Content-Length header found in #{status} response, not allowed") {
  423. not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
  424. }
  425. @content_length = value
  426. end
  427. }
  428. end
  429. def verify_content_length(bytes)
  430. if @head_request
  431. assert("Response body was given for HEAD request, but should be empty") {
  432. bytes == 0
  433. }
  434. elsif @content_length
  435. assert("Content-Length header was #{@content_length}, but should be #{bytes}") {
  436. @content_length == bytes.to_s
  437. }
  438. end
  439. end
  440. ## === The Body
  441. def each
  442. @closed = false
  443. bytes = 0
  444. ## The Body must respond to +each+
  445. assert("Response body must respond to each") do
  446. @body.respond_to?(:each)
  447. end
  448. @body.each { |part|
  449. ## and must only yield String values.
  450. assert("Body yielded non-string value #{part.inspect}") {
  451. part.kind_of? String
  452. }
  453. bytes += Rack::Utils.bytesize(part)
  454. yield part
  455. }
  456. verify_content_length(bytes)
  457. ##
  458. ## The Body itself should not be an instance of String, as this will
  459. ## break in Ruby 1.9.
  460. ##
  461. ## If the Body responds to +close+, it will be called after iteration.
  462. # XXX howto: assert("Body has not been closed") { @closed }
  463. ##
  464. ## If the Body responds to +to_path+, it must return a String
  465. ## identifying the location of a file whose contents are identical
  466. ## to that produced by calling +each+; this may be used by the
  467. ## server as an alternative, possibly more efficient way to
  468. ## transport the response.
  469. if @body.respond_to?(:to_path)
  470. assert("The file identified by body.to_path does not exist") {
  471. ::File.exist? @body.to_path
  472. }
  473. end
  474. ##
  475. ## The Body commonly is an Array of Strings, the application
  476. ## instance itself, or a File-like object.
  477. end
  478. def close
  479. @closed = true
  480. @body.close if @body.respond_to?(:close)
  481. end
  482. # :startdoc:
  483. end
  484. end
  485. ## == Thanks
  486. ## Some parts of this specification are adopted from PEP333: Python
  487. ## Web Server Gateway Interface
  488. ## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank
  489. ## everyone involved in that effort.