chunked.rb 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. require 'rack/utils'
  2. module Rack
  3. # Middleware that applies chunked transfer encoding to response bodies
  4. # when the response does not include a Content-Length header.
  5. class Chunked
  6. include Rack::Utils
  7. # A body wrapper that emits chunked responses
  8. class Body
  9. TERM = "\r\n"
  10. TAIL = "0#{TERM}#{TERM}"
  11. include Rack::Utils
  12. def initialize(body)
  13. @body = body
  14. end
  15. def each
  16. term = TERM
  17. @body.each do |chunk|
  18. size = bytesize(chunk)
  19. next if size == 0
  20. chunk = chunk.dup.force_encoding(Encoding::BINARY) if chunk.respond_to?(:force_encoding)
  21. yield [size.to_s(16), term, chunk, term].join
  22. end
  23. yield TAIL
  24. end
  25. def close
  26. @body.close if @body.respond_to?(:close)
  27. end
  28. end
  29. def initialize(app)
  30. @app = app
  31. end
  32. def call(env)
  33. status, headers, body = @app.call(env)
  34. headers = HeaderHash.new(headers)
  35. if env['HTTP_VERSION'] == 'HTTP/1.0' ||
  36. STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
  37. headers['Content-Length'] ||
  38. headers['Transfer-Encoding']
  39. [status, headers, body]
  40. else
  41. headers.delete('Content-Length')
  42. headers['Transfer-Encoding'] = 'chunked'
  43. [status, headers, Body.new(body)]
  44. end
  45. end
  46. end
  47. end