spec_deflater.rb 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. require 'stringio'
  2. require 'time' # for Time#httpdate
  3. require 'rack/deflater'
  4. require 'rack/mock'
  5. require 'zlib'
  6. describe Rack::Deflater do
  7. def build_response(status, body, accept_encoding, headers = {})
  8. body = [body] if body.respond_to? :to_str
  9. app = lambda { |env| [status, {}, body] }
  10. request = Rack::MockRequest.env_for("", headers.merge("HTTP_ACCEPT_ENCODING" => accept_encoding))
  11. response = Rack::Deflater.new(app).call(request)
  12. return response
  13. end
  14. def inflate(buf)
  15. inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
  16. inflater.inflate(buf) << inflater.finish
  17. end
  18. should "be able to deflate bodies that respond to each" do
  19. body = Object.new
  20. class << body; def each; yield("foo"); yield("bar"); end; end
  21. response = build_response(200, body, "deflate")
  22. response[0].should.equal(200)
  23. response[1].should.equal({
  24. "Content-Encoding" => "deflate",
  25. "Vary" => "Accept-Encoding"
  26. })
  27. buf = ''
  28. response[2].each { |part| buf << part }
  29. inflate(buf).should.equal("foobar")
  30. end
  31. should "flush deflated chunks to the client as they become ready" do
  32. body = Object.new
  33. class << body; def each; yield("foo"); yield("bar"); end; end
  34. response = build_response(200, body, "deflate")
  35. response[0].should.equal(200)
  36. response[1].should.equal({
  37. "Content-Encoding" => "deflate",
  38. "Vary" => "Accept-Encoding"
  39. })
  40. buf = []
  41. inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
  42. response[2].each { |part| buf << inflater.inflate(part) }
  43. buf << inflater.finish
  44. buf.delete_if { |part| part.empty? }
  45. buf.join.should.equal("foobar")
  46. end
  47. # TODO: This is really just a special case of the above...
  48. should "be able to deflate String bodies" do
  49. response = build_response(200, "Hello world!", "deflate")
  50. response[0].should.equal(200)
  51. response[1].should.equal({
  52. "Content-Encoding" => "deflate",
  53. "Vary" => "Accept-Encoding"
  54. })
  55. buf = ''
  56. response[2].each { |part| buf << part }
  57. inflate(buf).should.equal("Hello world!")
  58. end
  59. should "be able to gzip bodies that respond to each" do
  60. body = Object.new
  61. class << body; def each; yield("foo"); yield("bar"); end; end
  62. response = build_response(200, body, "gzip")
  63. response[0].should.equal(200)
  64. response[1].should.equal({
  65. "Content-Encoding" => "gzip",
  66. "Vary" => "Accept-Encoding",
  67. })
  68. buf = ''
  69. response[2].each { |part| buf << part }
  70. io = StringIO.new(buf)
  71. gz = Zlib::GzipReader.new(io)
  72. gz.read.should.equal("foobar")
  73. gz.close
  74. end
  75. should "flush gzipped chunks to the client as they become ready" do
  76. body = Object.new
  77. class << body; def each; yield("foo"); yield("bar"); end; end
  78. response = build_response(200, body, "gzip")
  79. response[0].should.equal(200)
  80. response[1].should.equal({
  81. "Content-Encoding" => "gzip",
  82. "Vary" => "Accept-Encoding"
  83. })
  84. buf = []
  85. inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
  86. response[2].each { |part| buf << inflater.inflate(part) }
  87. buf << inflater.finish
  88. buf.delete_if { |part| part.empty? }
  89. buf.join.should.equal("foobar")
  90. end
  91. should "be able to fallback to no deflation" do
  92. response = build_response(200, "Hello world!", "superzip")
  93. response[0].should.equal(200)
  94. response[1].should.equal({ "Vary" => "Accept-Encoding" })
  95. response[2].should.equal(["Hello world!"])
  96. end
  97. should "be able to skip when there is no response entity body" do
  98. response = build_response(304, [], "gzip")
  99. response[0].should.equal(304)
  100. response[1].should.equal({})
  101. response[2].should.equal([])
  102. end
  103. should "handle the lack of an acceptable encoding" do
  104. response1 = build_response(200, "Hello world!", "identity;q=0", "PATH_INFO" => "/")
  105. response1[0].should.equal(406)
  106. response1[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "71"})
  107. response1[2].should.equal(["An acceptable encoding for the requested resource / could not be found."])
  108. response2 = build_response(200, "Hello world!", "identity;q=0", "SCRIPT_NAME" => "/foo", "PATH_INFO" => "/bar")
  109. response2[0].should.equal(406)
  110. response2[1].should.equal({"Content-Type" => "text/plain", "Content-Length" => "78"})
  111. response2[2].should.equal(["An acceptable encoding for the requested resource /foo/bar could not be found."])
  112. end
  113. should "handle gzip response with Last-Modified header" do
  114. last_modified = Time.now.httpdate
  115. app = lambda { |env| [200, { "Last-Modified" => last_modified }, ["Hello World!"]] }
  116. request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
  117. response = Rack::Deflater.new(app).call(request)
  118. response[0].should.equal(200)
  119. response[1].should.equal({
  120. "Content-Encoding" => "gzip",
  121. "Vary" => "Accept-Encoding",
  122. "Last-Modified" => last_modified
  123. })
  124. buf = ''
  125. response[2].each { |part| buf << part }
  126. io = StringIO.new(buf)
  127. gz = Zlib::GzipReader.new(io)
  128. gz.read.should.equal("Hello World!")
  129. gz.close
  130. end
  131. should "do nothing when no-transform Cache-Control directive present" do
  132. app = lambda { |env| [200, {'Cache-Control' => 'no-transform'}, ['Hello World!']] }
  133. request = Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => "gzip")
  134. response = Rack::Deflater.new(app).call(request)
  135. response[0].should.equal(200)
  136. response[1].should.not.include "Content-Encoding"
  137. response[2].join.should.equal("Hello World!")
  138. end
  139. end