thin 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of thin might be problematic. Click here for more details.

data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ == 0.5.4 Flying Mustard release
2
+ * Don't read the full body, use direct streaming when sending response.
3
+ See: Response#each
4
+ As a result, the Content-Length can not be calculated anymore.
5
+ You have to do set this in your adapter. All frameworks do it anyway.
6
+ It improve memory usage and boost speed for low concurrency.
7
+ Thanks to Kent Sibilev and Ezra for their help on that one.
8
+ * Add 'Server' response header
9
+ * Fix --user and --group option not changing daemon process privileges
10
+
1
11
  == 0.5.3 Purple Yogurt release
2
12
  * win32 pre-compiled gem now available
3
13
  * change rake task configuration to allow win32 gem build
data/bin/thin CHANGED
@@ -73,8 +73,8 @@ def start(options)
73
73
  server.timeout = options[:timeout]
74
74
 
75
75
  if options[:daemonize]
76
- server.change_privilege options[:user], options[:group] if options[:user] && options[:group]
77
76
  server.daemonize
77
+ server.change_privilege options[:user], options[:group] if options[:user] && options[:group]
78
78
  end
79
79
 
80
80
  server.app = Rack::Adapter::Rails.new(options.merge(:root => options[:chdir]))
@@ -4,6 +4,20 @@
4
4
  # http://rack.rubyforge.org/doc/classes/Rack/Builder.html
5
5
 
6
6
  require File.dirname(__FILE__) + '/../lib/thin'
7
- require 'rack/lobster'
8
7
 
9
- run Rack::Lobster.new
8
+ app = proc do |env|
9
+ # Response body has to respond to each and yield strings
10
+ # See Rack specs for more info: http://rack.rubyforge.org/doc/files/SPEC.html
11
+ body = ['hi!']
12
+
13
+ [
14
+ 200, # Status code
15
+ {
16
+ 'Content-Type' => 'text/html', # Reponse headers
17
+ 'Content-Length' => body.join.size.to_s
18
+ },
19
+ body # Body of the response
20
+ ]
21
+ end
22
+
23
+ run app
@@ -1,4 +1,4 @@
1
- $: << File.expand_path(File.dirname(__FILE__))
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
2
 
3
3
  require 'fileutils'
4
4
  require 'timeout'
@@ -30,8 +30,10 @@ module Thin
30
30
  @response.status, @response.headers, @response.body = @app.call(env)
31
31
 
32
32
  # Send the response
33
- trace { @response.to_s }
34
- send_data @response.to_s
33
+ @response.each do |chunk|
34
+ trace { chunk }
35
+ send_data chunk
36
+ end
35
37
 
36
38
  close_connection_after_writing
37
39
 
@@ -1,25 +1,35 @@
1
1
  module Thin
2
2
  # A response sent to the client.
3
3
  class Response
4
- CONTENT_LENGTH = 'Content-Length'.freeze
5
4
  CONNECTION = 'Connection'.freeze
5
+ SERVER = 'Server'.freeze
6
6
  CLOSE = 'close'.freeze
7
7
 
8
+ # Status code
8
9
  attr_accessor :status
9
- attr_reader :headers, :body
10
+
11
+ # Response body, must respond to +each+.
12
+ attr_accessor :body
13
+
14
+ # Headers key-value hash
15
+ attr_reader :headers
10
16
 
11
17
  def initialize
12
18
  @headers = Headers.new
13
- @body = StringIO.new
14
19
  @status = 200
15
20
  end
16
21
 
22
+ # String representation of the headers
23
+ # to be sent in the response.
17
24
  def headers_output
18
- @headers[CONTENT_LENGTH] = @body.size
19
25
  @headers[CONNECTION] = CLOSE
26
+ @headers[SERVER] = Thin::SERVER
27
+
20
28
  @headers.to_s
21
29
  end
22
30
 
31
+ # Top header of the response,
32
+ # containing the status code and response headers.
23
33
  def head
24
34
  "HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status.to_i]}\r\n#{headers_output}\r\n"
25
35
  end
@@ -30,19 +40,19 @@ module Thin
30
40
  end
31
41
  end
32
42
 
33
- def body=(stream)
34
- stream.each do |part|
35
- @body << part
36
- end
37
- end
38
-
43
+ # Close any resource used by the response
39
44
  def close
40
- @body.close
45
+ @body.close if @body.respond_to?(:close)
41
46
  end
42
47
 
43
- def to_s
44
- @body.rewind
45
- head + @body.read
48
+ # Yields each chunk of the response.
49
+ # To controler the size of the chunks sent to the client
50
+ # define your own +each+ method on +body+.
51
+ def each
52
+ yield head
53
+ @body.each do |chunk|
54
+ yield chunk
55
+ end
46
56
  end
47
57
  end
48
58
  end
@@ -2,10 +2,10 @@ module Thin
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 5
5
- TINY = 3
5
+ TINY = 4
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
 
9
- CODENAME = 'Purple Yogurt'
9
+ CODENAME = 'Flying Mustard'
10
10
  end
11
11
  end
@@ -4,21 +4,28 @@ describe Response do
4
4
  before do
5
5
  @response = Response.new
6
6
  @response.headers['Content-Type'] = 'text/html'
7
+ @response.headers['Content-Length'] = '0'
8
+ @response.body = ''
7
9
  end
8
10
 
9
11
  it 'should output headers' do
10
- @response.headers_output.should == "Content-Type: text/html\r\nContent-Length: 0\r\nConnection: close\r\n"
12
+ @response.headers_output.should include("Content-Type: text/html", "Content-Length: 0", "Connection: close")
13
+ end
14
+
15
+ it 'should include server name header' do
16
+ @response.headers_output.should include("Server: thin")
11
17
  end
12
18
 
13
19
  it 'should output head' do
14
- @response.head.should == "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
20
+ @response.head.should include("HTTP/1.1 200 OK", "Content-Type: text/html", "Content-Length: 0",
21
+ "Connection: close", "\r\n\r\n")
15
22
  end
16
23
 
17
24
  it 'should allow duplicates in headers' do
18
25
  @response.headers['Set-Cookie'] = 'mium=7'
19
26
  @response.headers['Set-Cookie'] = 'hi=there'
20
27
 
21
- @response.head.should == "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nSet-Cookie: mium=7\r\nSet-Cookie: hi=there\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
28
+ @response.head.should include("Set-Cookie: mium=7", "Set-Cookie: hi=there")
22
29
  end
23
30
 
24
31
  it 'should parse simple header values' do
@@ -26,7 +33,7 @@ describe Response do
26
33
  'Host' => 'localhost'
27
34
  }
28
35
 
29
- @response.head.should == "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nHost: localhost\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
36
+ @response.head.should include("Host: localhost")
30
37
  end
31
38
 
32
39
  it 'should parse multiline header values in several headers' do
@@ -34,16 +41,18 @@ describe Response do
34
41
  'Set-Cookie' => "mium=7\nhi=there"
35
42
  }
36
43
 
37
- @response.head.should == "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nSet-Cookie: mium=7\r\nSet-Cookie: hi=there\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
44
+ @response.head.should include("Set-Cookie: mium=7", "Set-Cookie: hi=there")
38
45
  end
39
46
 
40
47
  it 'should output body' do
41
- @response.body << '<html></html>'
48
+ @response.body = '<html></html>'
42
49
 
43
- @response.to_s.should == "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\n<html></html>"
50
+ out = ''
51
+ @response.each { |l| out << l }
52
+ out.should include("\r\n\r\n<html></html>")
44
53
  end
45
54
 
46
- it "should be faster then #{max_parsing_time = 0.06} ms" do
55
+ it "should be faster then #{max_parsing_time = 0.07} ms" do
47
56
  @response.body << <<-EOS
48
57
  <html><head><title>Dir listing</title></head>
49
58
  <body><h1>Listing stuff</h1><ul>
@@ -51,6 +60,10 @@ describe Response do
51
60
  </ul></body></html>
52
61
  EOS
53
62
 
54
- proc { @response.to_s }.should be_faster_then(max_parsing_time)
63
+ proc { @response.each { |l| l } }.should be_faster_then(max_parsing_time)
64
+ end
65
+
66
+ it "should be closeable" do
67
+ @response.close
55
68
  end
56
69
  end
@@ -5,8 +5,10 @@ require 'socket'
5
5
  describe Server do
6
6
  before do
7
7
  app = proc do |env|
8
- body = [env['QUERY_STRING'], env['rack.input'].read].compact
9
- [200, { 'Content-Type' => 'text/html' }, body]
8
+ body = ''
9
+ body << env['QUERY_STRING'].to_s
10
+ body << env['rack.input'].read.to_s
11
+ [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s }, body]
10
12
  end
11
13
  server = Thin::Server.new('0.0.0.0', 3333, app)
12
14
  server.timeout = 3
@@ -24,7 +26,10 @@ describe Server do
24
26
  end
25
27
 
26
28
  it 'should GET from TCPSocket' do
27
- raw('0.0.0.0', 3333, "GET /?this HTTP/1.1\r\n\r\n").should == "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 4\r\nConnection: close\r\n\r\nthis"
29
+ raw('0.0.0.0', 3333, "GET /?this HTTP/1.1\r\n\r\n").
30
+ should include("HTTP/1.1 200 OK",
31
+ "Content-Type: text/html", "Content-Length: 4",
32
+ "Connection: close", "\r\n\r\nthis")
28
33
  end
29
34
 
30
35
  it 'should return empty string on incomplete headers' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc-Andre Cournoyer
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-01-18 00:00:00 -05:00
12
+ date: 2008-01-19 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency