<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -8,10 +8,7 @@ class SimpleAdapter
     body = [&quot;hello!&quot;]
     [
       200,
-      {
-        'Content-Type'   =&gt; 'text/plain',
-        'Content-Length' =&gt; body.join.size.to_s,
-      },
+      { 'Content-Type' =&gt; 'text/plain' },
       body
     ]
   end</diff>
      <filename>example/adapter.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,10 +15,7 @@ app = proc do |env|
   
   [
     200,                                        # Status code
-    {
-      'Content-Type' =&gt; 'text/html',            # Reponse headers
-      'Content-Length' =&gt; body.join.size.to_s
-    },
+    { 'Content-Type' =&gt; 'text/html' },          # Reponse headers
     body                                        # Body of the response
   ]
 end</diff>
      <filename>example/config.ru</filename>
    </modified>
    <modified>
      <diff>@@ -5,6 +5,10 @@ module Thin
   # This class is instanciated by EventMachine on each new connection
   # that is opened.
   class Connection &lt; EventMachine::Connection
+    CONTENT_LENGTH    = 'Content-Length'.freeze
+    TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
+    CHUNKED_REGEXP    = /\bchunked\b/i.freeze
+
     include Logging
     
     # Rack application (adapter) served by this connection.
@@ -66,6 +70,9 @@ module Thin
     def post_process(result)
       return unless result
       
+      # Set the Content-Length header if possible
+      set_content_length(result) if need_content_length?(result)
+      
       @response.status, @response.headers, @response.body = result
       
       # Make the response persistent if requested by the client
@@ -142,5 +149,29 @@ module Thin
       def socket_address
         Socket.unpack_sockaddr_in(get_peername)[1]
       end
+      
+    private
+      def need_content_length?(result)
+        status, headers, body = result
+        return false if headers.has_key?(CONTENT_LENGTH)
+        return false if (100..199).include?(status) || status == 204 || status == 304
+        return false if headers.has_key?(TRANSFER_ENCODING) &amp;&amp; headers[TRANSFER_ENCODING] =~ CHUNKED_REGEXP
+        return false unless body.kind_of?(String) || body.kind_of?(Array)
+        true
+      end
+      
+      def set_content_length(result)
+        headers, body = result[1..2]
+        case body
+        when String
+          headers[CONTENT_LENGTH] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
+        when Array
+           bytes = 0
+           body.each do |p|
+             bytes += p.respond_to?(:bytesize) ? p.bytesize : p.size
+           end
+           headers[CONTENT_LENGTH] = bytes.to_s
+        end
+      end
   end
 end
\ No newline at end of file</diff>
      <filename>lib/thin/connection.rb</filename>
    </modified>
    <modified>
      <diff>@@ -22,8 +22,8 @@ module Thin
     FORWARDED_FOR     = 'HTTP_X_FORWARDED_FOR'.freeze
     CONTENT_LENGTH    = 'CONTENT_LENGTH'.freeze
     CONNECTION        = 'HTTP_CONNECTION'.freeze
-    KEEP_ALIVE_REGEXP = /keep-alive/i
-    CLOSE_REGEXP      = /close/i
+    KEEP_ALIVE_REGEXP = /\bkeep-alive\b/i.freeze
+    CLOSE_REGEXP      = /\bclose\b/i.freeze
     
     # Freeze some Rack header names
     RACK_INPUT        = 'rack.input'.freeze</diff>
      <filename>lib/thin/request.rb</filename>
    </modified>
    <modified>
      <diff>@@ -43,10 +43,7 @@ module Thin
         
         [
           200,
-          {
-            'Content-Type' =&gt; 'text/html',
-            'Content-Length' =&gt; body.size.to_s
-          },
+          { 'Content-Type' =&gt; 'text/html' },
           [body]
         ]
       end</diff>
      <filename>lib/thin/stats.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@ describe Server, &quot;HTTP pipelining&quot; do
     start_server do |env|
       calls += 1
       body = env['PATH_INFO'] + '-' + calls.to_s
-      [200, { 'Content-Type' =&gt; 'text/html', 'Content-Length' =&gt; body.size.to_s }, body]
+      [200, { 'Content-Type' =&gt; 'text/html' }, body]
     end
     @server.maximum_persistent_connections = 1024
   end</diff>
      <filename>spec/server/pipelining_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,7 +4,7 @@ describe Server, 'robustness' do
   before do
     start_server do |env|
       body = 'hello!'
-      [200, { 'Content-Type' =&gt; 'text/html', 'Content-Length' =&gt; body.size.to_s }, body]
+      [200, { 'Content-Type' =&gt; 'text/html' }, body]
     end
   end
   </diff>
      <filename>spec/server/robustness_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../spec_helper'
 describe Server, &quot;stopping&quot; do
   before do
     start_server do |env|
-      [200, { 'Content-Type' =&gt; 'text/html', 'Content-Length' =&gt; '2' }, ['ok']]
+      [200, { 'Content-Type' =&gt; 'text/html' }, ['ok']]
     end
   end
   </diff>
      <filename>spec/server/stopping_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,7 +12,7 @@ else
       sleep 2 # HACK ooh boy, I wish I knew how to make those specs more stable...
       start_server('0.0.0.0', 5555, :backend =&gt; Backends::SwiftiplyClient, :wait_for_socket =&gt; false) do |env|
         body = env.inspect + env['rack.input'].read
-        [200, { 'Content-Type' =&gt; 'text/html', 'Content-Length' =&gt; body.size.to_s }, body]
+        [200, { 'Content-Type' =&gt; 'text/html' }, body]
       end
     end
     </diff>
      <filename>spec/server/swiftiply_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,19 +4,30 @@ describe Server, 'on TCP socket' do
   before do
     start_server do |env|
       body = env.inspect + env['rack.input'].read
-      [200, { 'Content-Type' =&gt; 'text/html', 'Content-Length' =&gt; body.size.to_s }, body]
+      [200, { 'Content-Type' =&gt; 'text/html' }, body]
     end
   end
-    
+  
   it 'should GET from Net::HTTP' do
     get('/?cthis').should include('cthis')
   end
   
   it 'should GET from TCPSocket' do
-    send_data(&quot;GET /?this HTTP/1.1\r\nConnection: close\r\n\r\n&quot;).
-      should include(&quot;HTTP/1.1 200 OK&quot;,
-                     &quot;Content-Type: text/html&quot;, &quot;Content-Length: &quot;,
-                     &quot;Connection: close&quot;, &quot;this&quot;)
+    status, headers, body = parse_response(send_data(&quot;GET /?this HTTP/1.0\r\nConnection: close\r\n\r\n&quot;))
+    status.should == 200
+    headers['Content-Type'].should == 'text/html'
+    headers['Connection'].should == 'close'
+    body.should include('this')
+  end
+  
+  it &quot;should add the Content-Length to the response when not present&quot; do
+    status, headers, body = parse_response(send_data(&quot;GET / HTTP/1.0\r\nConnection: close\r\n\r\n&quot;))
+    headers.should have_key('Content-Length')
+  end
+  
+  it 'should set the Content-Length to equal the body size in bytes' do
+    status, headers, body = parse_response(send_data(&quot;GET / HTTP/1.0\r\nConnection: close\r\n\r\n&quot;))
+    headers['Content-Length'].should == (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
   end
   
   it 'should return empty string on incomplete headers' do
@@ -35,7 +46,7 @@ describe Server, 'on TCP socket' do
     big = 'X' * (20 * 1024)
     post('/', :big =&gt; big).should include(big)
   end
-    
+  
   it &quot;should retreive remote address&quot; do
     get('/').should include('&quot;REMOTE_ADDR&quot;=&gt;&quot;127.0.0.1&quot;')
   end</diff>
      <filename>spec/server/tcp_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,7 +6,7 @@ describe Server, 'with threads' do
     start_server DEFAULT_TEST_ADDRESS, DEFAULT_TEST_PORT, :threaded =&gt; true do |env|
       sleep env['PATH_INFO'].delete('/').to_i
       @requests += 1
-      [200, { 'Content-Type' =&gt; 'text/html', 'Content-Length' =&gt; '2' }, 'hi']
+      [200, { 'Content-Type' =&gt; 'text/html' }, 'hi']
     end
   end
   </diff>
      <filename>spec/server/threaded_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../spec_helper'
 describe Server, &quot;on UNIX domain socket&quot; do
   before do
     start_server('/tmp/thin_test.sock') do |env|
-      [200, { 'Content-Type' =&gt; 'text/html', 'Content-Length' =&gt; env.inspect.size.to_s }, [env.inspect]]
+      [200, { 'Content-Type' =&gt; 'text/html' }, [env.inspect]]
     end
   end
   </diff>
      <filename>spec/server/unix_socket_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -190,6 +190,16 @@ module Helpers
     out
   end
   
+  def parse_response(response)
+    raw_headers, body = response.split(&quot;\r\n\r\n&quot;, 2)
+    raw_status, raw_headers = raw_headers.split(&quot;\r\n&quot;, 2)
+
+    status  = raw_status.match(%r{\AHTTP/1.1\s+(\d+)\b}).captures.first.to_i
+    headers = Hash[ *raw_headers.split(&quot;\r\n&quot;).map { |h| h.split(/:\s+/, 2) }.flatten ]
+
+    [ status, headers, body ]
+  end
+  
   def get(url)
     if @server.backend.class == Backends::UnixServer
       send_data(&quot;GET #{url} HTTP/1.1\r\nConnection: close\r\n\r\n&quot;)</diff>
      <filename>spec/spec_helper.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>f99bd54ab90485a5be1bdb3f86600f5fc68728a9</id>
    </parent>
  </parents>
  <author>
    <name>Dan Kubb</name>
    <email>dan.kubb@autopilotmarketing.com</email>
  </author>
  <url>http://github.com/macournoyer/thin/commit/affccc93679a419c8fca2f92fc11e006687bdf15</url>
  <id>affccc93679a419c8fca2f92fc11e006687bdf15</id>
  <committed-date>2008-07-19T06:34:08-07:00</committed-date>
  <authored-date>2008-07-18T12:40:18-07:00</authored-date>
  <message>Add Content-Length header to response automatically when possible [#74 state:resolved]</message>
  <tree>f2bdb3301514db8c1e743a5b7bdd3d721765e567</tree>
  <committer>
    <name>macournoyer</name>
    <email>macournoyer@gmail.com</email>
  </committer>
</commit>
