#95 ✓wontfix
Samuel Williams

Response#finish and ContentLength

Reported by Samuel Williams | May 16th, 2010 @ 05:34 AM

From Rack::ContentLength

12:     def call(env)
13:       status, headers, body = @app.call(env)
14:       headers = HeaderHash.new(headers)
15: 
16:       if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
17:          !headers['Content-Length'] &&
18:          !headers['Transfer-Encoding'] &&
19:          (body.respond_to?(:to_ary) || body.respond_to?(:to_str))
20: 
21:         body = [body] if body.respond_to?(:to_str) # rack 0.4 compat
22:         length = body.to_ary.inject(0) { |len, part| len + bytesize(part) }
23:         headers['Content-Length'] = length.to_s
24:       end
25: 
26:       [status, headers, body]
27:     end

Rack::Response doesn't respond to either :to_ary or :to_str.

This means that no content length is generated.

An alternative is to use Response#write, but it isn't clearly documented exactly how this should work.

Proposed Fixes:

  • Update the documentation to explain how to use Rack::Response(body, write)
  • Update Rack::Response so it provides the correct functionality for Content-Length generation
  • Update Rack::ContentLength so it knows about Rack::Response and generates the content length accordingly.
  • Update Rack::Response#finish so if there is no content length, it is generated from the body.

Comments and changes to this ticket

  • Jan Varwig

    Jan Varwig September 26th, 2010 @ 05:45 PM

    • Milestone order changed from “0” to “0”

    A result of this issue is that regular responses in Rails 3 NEVER generate a Content-Length header.
    This is a pretty serious problem

  • raggi

    raggi May 3rd, 2011 @ 07:24 AM

    • State changed from “new” to “wontfix”

    frameworks that send back iterative bodies need to find out themselves how to calculate the content-length header. enforcing this would break streaming.

  • Samuel Williams

    Samuel Williams May 3rd, 2011 @ 10:22 AM

    Raggi, with all respect, I think you've misunderstood the issue.

    Rack::Response#finish

    69:     def finish(&block)
    70:       @block = block
    71: 
    72:       if [204, 304].include?(status.to_i)
    73:         header.delete "Content-Type"
    74:         [status.to_i, header, []]
    75:       else
    76:         [status.to_i, header, self]
    77:       end
    78:     end
    

    At this point, the response sends itself as the body object.

    When this is passed to Rack::ContentLength#call,

    19:          (body.respond_to?(:to_ary) || body.respond_to?(:to_str))
    

    Then, no content length is generated. Actually, Rack::Response responds to :to_a, but not :to_ary. Weird huh?

    If the user sets the body directly, the result isn't always the same as if the object simply provided body rather than itself.

    The alternative is to use Rack::Response#write

    91:     def write(str)
    92:       s = str.to_s
    93:       @length += Rack::Utils.bytesize(s)
    94:       @writer.call s
    95: 
    96:       header["Content-Length"] = @length.to_s
    97:       str
    98:     end
    

    So, to summarise, if I have a Rack::Response and I set the body to ["Bob"], and then return Rack::Response#finish, Rack::ContentLength behaves differently than if I simply returned ["Bob"]. In my mind, this is inconsistent. If you require further details, please let me know.

    The key here is that even if the current behavior is desired, perhaps this should be documented somewhere.

    Thanks,
    Samuel

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile ยป

People watching this ticket

Pages