response.rb 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. require 'rack/request'
  2. require 'rack/utils'
  3. require 'time'
  4. module Rack
  5. # Rack::Response provides a convenient interface to create a Rack
  6. # response.
  7. #
  8. # It allows setting of headers and cookies, and provides useful
  9. # defaults (a OK response containing HTML).
  10. #
  11. # You can use Response#write to iteratively generate your response,
  12. # but note that this is buffered by Rack::Response until you call
  13. # +finish+. +finish+ however can take a block inside which calls to
  14. # +write+ are synchronous with the Rack response.
  15. #
  16. # Your application's +call+ should end returning Response#finish.
  17. class Response
  18. attr_accessor :length
  19. def initialize(body=[], status=200, header={})
  20. @status = status.to_i
  21. @header = Utils::HeaderHash.new("Content-Type" => "text/html").
  22. merge(header)
  23. @chunked = "chunked" == @header['Transfer-Encoding']
  24. @writer = lambda { |x| @body << x }
  25. @block = nil
  26. @length = 0
  27. @body = []
  28. if body.respond_to? :to_str
  29. write body.to_str
  30. elsif body.respond_to?(:each)
  31. body.each { |part|
  32. write part.to_s
  33. }
  34. else
  35. raise TypeError, "stringable or iterable required"
  36. end
  37. yield self if block_given?
  38. end
  39. attr_reader :header
  40. attr_accessor :status, :body
  41. def [](key)
  42. header[key]
  43. end
  44. def []=(key, value)
  45. header[key] = value
  46. end
  47. def set_cookie(key, value)
  48. Utils.set_cookie_header!(header, key, value)
  49. end
  50. def delete_cookie(key, value={})
  51. Utils.delete_cookie_header!(header, key, value)
  52. end
  53. def redirect(target, status=302)
  54. self.status = status
  55. self["Location"] = target
  56. end
  57. def finish(&block)
  58. @block = block
  59. if [204, 205, 304].include?(status.to_i)
  60. header.delete "Content-Type"
  61. header.delete "Content-Length"
  62. [status.to_i, header, []]
  63. else
  64. [status.to_i, header, self]
  65. end
  66. end
  67. alias to_a finish # For *response
  68. alias to_ary finish # For implicit-splat on Ruby 1.9.2
  69. def each(&callback)
  70. @body.each(&callback)
  71. @writer = callback
  72. @block.call(self) if @block
  73. end
  74. # Append to body and update Content-Length.
  75. #
  76. # NOTE: Do not mix #write and direct #body access!
  77. #
  78. def write(str)
  79. s = str.to_s
  80. @length += Rack::Utils.bytesize(s) unless @chunked
  81. @writer.call s
  82. header["Content-Length"] = @length.to_s unless @chunked
  83. str
  84. end
  85. def close
  86. body.close if body.respond_to?(:close)
  87. end
  88. def empty?
  89. @block == nil && @body.empty?
  90. end
  91. alias headers header
  92. module Helpers
  93. def invalid?; status < 100 || status >= 600; end
  94. def informational?; status >= 100 && status < 200; end
  95. def successful?; status >= 200 && status < 300; end
  96. def redirection?; status >= 300 && status < 400; end
  97. def client_error?; status >= 400 && status < 500; end
  98. def server_error?; status >= 500 && status < 600; end
  99. def ok?; status == 200; end
  100. def bad_request?; status == 400; end
  101. def forbidden?; status == 403; end
  102. def not_found?; status == 404; end
  103. def method_not_allowed?; status == 405; end
  104. def unprocessable?; status == 422; end
  105. def redirect?; [301, 302, 303, 307].include? status; end
  106. # Headers
  107. attr_reader :headers, :original_headers
  108. def include?(header)
  109. !!headers[header]
  110. end
  111. def content_type
  112. headers["Content-Type"]
  113. end
  114. def content_length
  115. cl = headers["Content-Length"]
  116. cl ? cl.to_i : cl
  117. end
  118. def location
  119. headers["Location"]
  120. end
  121. end
  122. include Helpers
  123. end
  124. end