macournoyer-thin 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. data/CHANGELOG +233 -0
  2. data/COPYING +18 -0
  3. data/README +77 -0
  4. data/Rakefile +13 -0
  5. data/benchmark/abc +51 -0
  6. data/benchmark/benchmarker.rb +80 -0
  7. data/benchmark/runner +79 -0
  8. data/bin/thin +6 -0
  9. data/example/adapter.rb +32 -0
  10. data/example/config.ru +23 -0
  11. data/example/monit_sockets +20 -0
  12. data/example/monit_unixsock +20 -0
  13. data/example/myapp.rb +1 -0
  14. data/example/ramaze.ru +12 -0
  15. data/example/thin.god +80 -0
  16. data/example/thin_solaris_smf.erb +36 -0
  17. data/example/thin_solaris_smf.readme.txt +150 -0
  18. data/example/vlad.rake +64 -0
  19. data/ext/thin_parser/common.rl +55 -0
  20. data/ext/thin_parser/ext_help.h +14 -0
  21. data/ext/thin_parser/extconf.rb +6 -0
  22. data/ext/thin_parser/parser.c +452 -0
  23. data/ext/thin_parser/parser.h +49 -0
  24. data/ext/thin_parser/parser.rl +157 -0
  25. data/ext/thin_parser/thin.c +433 -0
  26. data/lib/rack/adapter/loader.rb +79 -0
  27. data/lib/rack/adapter/rails.rb +173 -0
  28. data/lib/rack/handler/thin.rb +18 -0
  29. data/lib/thin.rb +50 -0
  30. data/lib/thin/backends/base.rb +141 -0
  31. data/lib/thin/backends/swiftiply_client.rb +56 -0
  32. data/lib/thin/backends/tcp_server.rb +29 -0
  33. data/lib/thin/backends/unix_server.rb +51 -0
  34. data/lib/thin/command.rb +52 -0
  35. data/lib/thin/connection.rb +186 -0
  36. data/lib/thin/controllers/cluster.rb +127 -0
  37. data/lib/thin/controllers/controller.rb +183 -0
  38. data/lib/thin/controllers/service.rb +75 -0
  39. data/lib/thin/controllers/service.sh.erb +39 -0
  40. data/lib/thin/daemonizing.rb +163 -0
  41. data/lib/thin/headers.rb +39 -0
  42. data/lib/thin/logging.rb +54 -0
  43. data/lib/thin/request.rb +147 -0
  44. data/lib/thin/response.rb +99 -0
  45. data/lib/thin/runner.rb +208 -0
  46. data/lib/thin/server.rb +241 -0
  47. data/lib/thin/stats.html.erb +216 -0
  48. data/lib/thin/stats.rb +52 -0
  49. data/lib/thin/statuses.rb +43 -0
  50. data/lib/thin/version.rb +32 -0
  51. data/spec/backends/swiftiply_client_spec.rb +66 -0
  52. data/spec/backends/tcp_server_spec.rb +33 -0
  53. data/spec/backends/unix_server_spec.rb +37 -0
  54. data/spec/command_spec.rb +20 -0
  55. data/spec/configs/cluster.yml +9 -0
  56. data/spec/configs/single.yml +9 -0
  57. data/spec/connection_spec.rb +105 -0
  58. data/spec/controllers/cluster_spec.rb +212 -0
  59. data/spec/controllers/controller_spec.rb +129 -0
  60. data/spec/controllers/service_spec.rb +50 -0
  61. data/spec/daemonizing_spec.rb +192 -0
  62. data/spec/headers_spec.rb +40 -0
  63. data/spec/logging_spec.rb +46 -0
  64. data/spec/perf/request_perf_spec.rb +50 -0
  65. data/spec/perf/response_perf_spec.rb +19 -0
  66. data/spec/perf/server_perf_spec.rb +39 -0
  67. data/spec/rack/loader_spec.rb +29 -0
  68. data/spec/rack/rails_adapter_spec.rb +106 -0
  69. data/spec/rails_app/app/controllers/application.rb +10 -0
  70. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  71. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  72. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  73. data/spec/rails_app/config/boot.rb +109 -0
  74. data/spec/rails_app/config/environment.rb +64 -0
  75. data/spec/rails_app/config/environments/development.rb +18 -0
  76. data/spec/rails_app/config/environments/production.rb +19 -0
  77. data/spec/rails_app/config/environments/test.rb +22 -0
  78. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  79. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  80. data/spec/rails_app/config/routes.rb +35 -0
  81. data/spec/rails_app/public/404.html +30 -0
  82. data/spec/rails_app/public/422.html +30 -0
  83. data/spec/rails_app/public/500.html +30 -0
  84. data/spec/rails_app/public/dispatch.cgi +10 -0
  85. data/spec/rails_app/public/dispatch.fcgi +24 -0
  86. data/spec/rails_app/public/dispatch.rb +10 -0
  87. data/spec/rails_app/public/favicon.ico +0 -0
  88. data/spec/rails_app/public/images/rails.png +0 -0
  89. data/spec/rails_app/public/index.html +277 -0
  90. data/spec/rails_app/public/javascripts/application.js +2 -0
  91. data/spec/rails_app/public/javascripts/controls.js +963 -0
  92. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  93. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  94. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  95. data/spec/rails_app/public/robots.txt +5 -0
  96. data/spec/rails_app/script/about +3 -0
  97. data/spec/rails_app/script/console +3 -0
  98. data/spec/rails_app/script/destroy +3 -0
  99. data/spec/rails_app/script/generate +3 -0
  100. data/spec/rails_app/script/performance/benchmarker +3 -0
  101. data/spec/rails_app/script/performance/profiler +3 -0
  102. data/spec/rails_app/script/performance/request +3 -0
  103. data/spec/rails_app/script/plugin +3 -0
  104. data/spec/rails_app/script/process/inspector +3 -0
  105. data/spec/rails_app/script/process/reaper +3 -0
  106. data/spec/rails_app/script/process/spawner +3 -0
  107. data/spec/rails_app/script/runner +3 -0
  108. data/spec/rails_app/script/server +3 -0
  109. data/spec/request/mongrel_spec.rb +39 -0
  110. data/spec/request/parser_spec.rb +215 -0
  111. data/spec/request/persistent_spec.rb +35 -0
  112. data/spec/request/processing_spec.rb +45 -0
  113. data/spec/response_spec.rb +83 -0
  114. data/spec/runner_spec.rb +167 -0
  115. data/spec/server/builder_spec.rb +44 -0
  116. data/spec/server/pipelining_spec.rb +109 -0
  117. data/spec/server/robustness_spec.rb +34 -0
  118. data/spec/server/stopping_spec.rb +45 -0
  119. data/spec/server/swiftiply.yml +6 -0
  120. data/spec/server/swiftiply_spec.rb +32 -0
  121. data/spec/server/tcp_spec.rb +57 -0
  122. data/spec/server/threaded_spec.rb +27 -0
  123. data/spec/server/unix_socket_spec.rb +26 -0
  124. data/spec/server_spec.rb +96 -0
  125. data/spec/spec_helper.rb +219 -0
  126. data/tasks/announce.rake +22 -0
  127. data/tasks/deploy.rake +16 -0
  128. data/tasks/email.erb +30 -0
  129. data/tasks/ext.rake +42 -0
  130. data/tasks/gem.rake +108 -0
  131. data/tasks/rdoc.rake +25 -0
  132. data/tasks/site.rake +15 -0
  133. data/tasks/spec.rake +48 -0
  134. data/tasks/stats.rake +28 -0
  135. metadata +248 -0
@@ -0,0 +1,167 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Runner do
4
+ it "should parse options" do
5
+ runner = Runner.new(%w(start --pid test.pid --port 5000))
6
+
7
+ runner.options[:pid].should == 'test.pid'
8
+ runner.options[:port].should == 5000
9
+ end
10
+
11
+ it "should parse specified command" do
12
+ Runner.new(%w(start)).command.should == 'start'
13
+ Runner.new(%w(stop)).command.should == 'stop'
14
+ Runner.new(%w(restart)).command.should == 'restart'
15
+ end
16
+
17
+ it "should abort on unknow command" do
18
+ runner = Runner.new(%w(poop))
19
+
20
+ runner.should_receive(:abort)
21
+ runner.run!
22
+ end
23
+
24
+ it "should exit on empty command" do
25
+ runner = Runner.new([])
26
+
27
+ runner.should_receive(:exit).with(1)
28
+
29
+ silence_stream(STDOUT) do
30
+ runner.run!
31
+ end
32
+ end
33
+
34
+ it "should use Controller when controlling a single server" do
35
+ runner = Runner.new(%w(start))
36
+
37
+ controller = mock('controller')
38
+ controller.should_receive(:start)
39
+ Controllers::Controller.should_receive(:new).and_return(controller)
40
+
41
+ runner.run!
42
+ end
43
+
44
+ it "should use Cluster controller when controlling multiple servers" do
45
+ runner = Runner.new(%w(start --servers 3))
46
+
47
+ controller = mock('cluster')
48
+ controller.should_receive(:start)
49
+ Controllers::Cluster.should_receive(:new).and_return(controller)
50
+
51
+ runner.run!
52
+ end
53
+
54
+ it "should default to single server controller" do
55
+ Runner.new(%w(start)).should_not be_a_cluster
56
+ end
57
+
58
+ it "should consider as a cluster with :servers option" do
59
+ Runner.new(%w(start --servers 3)).should be_a_cluster
60
+ end
61
+
62
+ it "should consider as a cluster with :only option" do
63
+ Runner.new(%w(start --only 3000)).should be_a_cluster
64
+ end
65
+
66
+ it "should warn when require a rack config file" do
67
+ STDERR.stub!(:write)
68
+ STDERR.should_receive(:write).with(/WARNING:/)
69
+
70
+ runner = Runner.new(%w(start -r config.ru))
71
+ runner.run! rescue nil
72
+
73
+ runner.options[:rackup].should == 'config.ru'
74
+ end
75
+
76
+ it "should require file" do
77
+ runner = Runner.new(%w(start -r unexisting))
78
+ proc { runner.run! }.should raise_error(LoadError)
79
+ end
80
+
81
+ it "should remember requires" do
82
+ runner = Runner.new(%w(start -r rubygems -r thin))
83
+ runner.options[:require].should == %w(rubygems thin)
84
+ end
85
+
86
+ it "should remember debug options" do
87
+ runner = Runner.new(%w(start -D -V))
88
+ runner.options[:debug].should be_true
89
+ runner.options[:trace].should be_true
90
+ end
91
+
92
+ it "should default debug and trace to false" do
93
+ runner = Runner.new(%w(start))
94
+ runner.options[:debug].should_not be_true
95
+ runner.options[:trace].should_not be_true
96
+ end
97
+ end
98
+
99
+ describe Runner, 'with config file' do
100
+ before do
101
+ @runner = Runner.new(%w(start --config spec/configs/cluster.yml))
102
+ end
103
+
104
+ it "should load options from file with :config option" do
105
+ @runner.send :load_options_from_config_file!
106
+
107
+ @runner.options[:environment].should == 'production'
108
+ @runner.options[:chdir].should == 'spec/rails_app'
109
+ @runner.options[:port].should == 5000
110
+ @runner.options[:servers].should == 3
111
+ end
112
+
113
+ it "should change directory after loading config" do
114
+ @orig_dir = Dir.pwd
115
+
116
+ controller = mock('controller')
117
+ controller.should_receive(:respond_to?).with('start').and_return(true)
118
+ controller.should_receive(:start)
119
+ Controllers::Cluster.should_receive(:new).and_return(controller)
120
+ expected_dir = File.expand_path('spec/rails_app')
121
+
122
+ begin
123
+ silence_stream(STDERR) do
124
+ @runner.run!
125
+ end
126
+
127
+ Dir.pwd.should == expected_dir
128
+
129
+ ensure
130
+ # any other spec using relative paths should work as expected
131
+ Dir.chdir(@orig_dir)
132
+ end
133
+ end
134
+ end
135
+
136
+ describe Runner, "service" do
137
+ before do
138
+ Thin.stub!(:linux?).and_return(true)
139
+
140
+ @controller = mock('service')
141
+ Controllers::Service.stub!(:new).and_return(@controller)
142
+ end
143
+
144
+ it "should use Service controller when controlling all servers" do
145
+ runner = Runner.new(%w(start --all))
146
+
147
+ @controller.should_receive(:start)
148
+
149
+ runner.run!
150
+ end
151
+
152
+ it "should call install with arguments" do
153
+ runner = Runner.new(%w(install /etc/cool))
154
+
155
+ @controller.should_receive(:install).with('/etc/cool')
156
+
157
+ runner.run!
158
+ end
159
+
160
+ it "should call install without arguments" do
161
+ runner = Runner.new(%w(install))
162
+
163
+ @controller.should_receive(:install).with()
164
+
165
+ runner.run!
166
+ end
167
+ end
@@ -0,0 +1,44 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Server, 'app builder' do
4
+ it "should build app from constructor" do
5
+ app = proc {}
6
+ server = Server.new('0.0.0.0', 3000, app)
7
+
8
+ server.app.should == app
9
+ end
10
+
11
+ it "should build app from builder block" do
12
+ server = Server.new '0.0.0.0', 3000 do
13
+ run(proc { |env| :works })
14
+ end
15
+
16
+ server.app.call({}).should == :works
17
+ end
18
+
19
+ it "should use middlewares in builder block" do
20
+ server = Server.new '0.0.0.0', 3000 do
21
+ use Rack::ShowExceptions
22
+ run(proc { |env| :works })
23
+ end
24
+
25
+ server.app.class.should == Rack::ShowExceptions
26
+ server.app.call({}).should == :works
27
+ end
28
+
29
+ it "should work with Rack url mapper" do
30
+ server = Server.new '0.0.0.0', 3000 do
31
+ map '/test' do
32
+ run(proc { |env| [200, {}, 'Found /test'] })
33
+ end
34
+ end
35
+
36
+ default_env = { 'SCRIPT_NAME' => '' }
37
+
38
+ server.app.call(default_env.update('PATH_INFO' => '/'))[0].should == 404
39
+
40
+ status, headers, body = server.app.call(default_env.update('PATH_INFO' => '/test'))
41
+ status.should == 200
42
+ body.should == 'Found /test'
43
+ end
44
+ end
@@ -0,0 +1,109 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Server, "HTTP pipelining" do
4
+ before do
5
+ calls = 0
6
+ start_server do |env|
7
+ calls += 1
8
+ body = env['PATH_INFO'] + '-' + calls.to_s
9
+ [200, { 'Content-Type' => 'text/html' }, body]
10
+ end
11
+ @server.maximum_persistent_connections = 1024
12
+ end
13
+
14
+ it "should pipeline request on same socket" do
15
+ socket = TCPSocket.new('0.0.0.0', 3333)
16
+ socket.write "GET /first HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
17
+ socket.flush
18
+ socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
19
+ response = socket.read
20
+ socket.close
21
+
22
+ wait_for_requests_to_complete!
23
+
24
+ response.should include('/first-1', '/second-2')
25
+ end
26
+
27
+ it "should pipeline requests by default on HTTP 1.1" do
28
+ socket = TCPSocket.new('0.0.0.0', 3333)
29
+ socket.write "GET /first HTTP/1.1\r\n\r\n"
30
+ socket.flush
31
+ socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
32
+ socket.flush
33
+ response = socket.read
34
+ socket.close
35
+
36
+ wait_for_requests_to_complete!
37
+
38
+ response.should include('/first-1', '/second-2')
39
+ end
40
+
41
+ it "should not pipeline request by default on HTTP 1.0" do
42
+ socket = TCPSocket.new('0.0.0.0', 3333)
43
+ socket.write "GET /first HTTP/1.0\r\n\r\n"
44
+ socket.flush
45
+ socket.write "GET /second HTTP/1.0\r\nConnection: close\r\n\r\n"
46
+ response = socket.read
47
+ socket.close
48
+
49
+ wait_for_requests_to_complete!
50
+
51
+ response.should include('/first-1')
52
+ response.should_not include('/second-2')
53
+ end
54
+
55
+ it "should not pipeline request on same socket when connection is closed" do
56
+ socket = TCPSocket.new('0.0.0.0', 3333)
57
+ socket.write "GET /first HTTP/1.1\r\nConnection: close\r\n\r\n"
58
+ socket.flush
59
+ socket.write "GET /second HTTP/1.1\r\nConnection: close\r\n\r\n"
60
+ response = socket.read
61
+ socket.close
62
+
63
+ wait_for_requests_to_complete!
64
+
65
+ response.should include('/first-1')
66
+ response.should_not include('/second-2')
67
+ end
68
+
69
+ it "should not allow more persistent connection then maximum" do
70
+ @server.maximum_persistent_connections = 1
71
+
72
+ socket1 = TCPSocket.new('0.0.0.0', 3333)
73
+ socket1.write "GET / HTTP/1.1\r\n\r\n"
74
+ socket1.flush
75
+ socket2 = TCPSocket.new('0.0.0.0', 3333)
76
+ socket2.write "GET / HTTP/1.1\r\n\r\n"
77
+ socket2.flush
78
+
79
+ @server.backend.persistent_connection_count.should == 1
80
+ @server.backend.size.should == 2
81
+
82
+ socket1.close
83
+ socket2.close
84
+ end
85
+
86
+ it "should decrement persistent connection on close" do
87
+ socket = TCPSocket.new('0.0.0.0', 3333)
88
+ socket.write "GET / HTTP/1.1\r\n\r\n"
89
+ socket.flush
90
+
91
+ @server.backend.persistent_connection_count.should == 1
92
+
93
+ socket.write "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
94
+ socket.close
95
+
96
+ wait_for_requests_to_complete!
97
+
98
+ @server.backend.persistent_connection_count.should == 0
99
+ end
100
+
101
+ after do
102
+ stop_server
103
+ end
104
+
105
+ private
106
+ def wait_for_requests_to_complete!
107
+ sleep 0.1 until @server.backend.size == 0
108
+ end
109
+ end
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Server, 'robustness' do
4
+ before do
5
+ start_server do |env|
6
+ body = 'hello!'
7
+ [200, { 'Content-Type' => 'text/html' }, body]
8
+ end
9
+ end
10
+
11
+ it "should not crash when header too large" do
12
+ 100.times do
13
+ begin
14
+ socket = TCPSocket.new(DEFAULT_TEST_ADDRESS, DEFAULT_TEST_PORT)
15
+ socket.write("GET / HTTP/1.1\r\n")
16
+ socket.write("Host: localhost\r\n")
17
+ socket.write("Connection: close\r\n")
18
+ 10000.times do
19
+ socket.write("X-Foo: #{'x' * 100}\r\n")
20
+ socket.flush
21
+ end
22
+ socket.write("\r\n")
23
+ socket.read
24
+ socket.close
25
+ rescue Errno::EPIPE, Errno::ECONNRESET
26
+ # Ignore.
27
+ end
28
+ end
29
+ end
30
+
31
+ after do
32
+ stop_server
33
+ end
34
+ end
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Server, "stopping" do
4
+ before do
5
+ start_server do |env|
6
+ [200, { 'Content-Type' => 'text/html' }, ['ok']]
7
+ end
8
+ end
9
+
10
+ it "should wait for current requests before soft stopping" do
11
+ socket = TCPSocket.new('0.0.0.0', 3333)
12
+ socket.write("GET / HTTP/1.1")
13
+ @server.stop # Stop the server in the middle of a request
14
+ socket.write("\r\n\r\n")
15
+
16
+ out = socket.read
17
+ socket.close
18
+
19
+ out.should_not be_empty
20
+ end
21
+
22
+ it "should not accept new requests when soft stopping" do
23
+ socket = TCPSocket.new('0.0.0.0', 3333)
24
+ socket.write("GET / HTTP/1.1")
25
+ @server.stop # Stop the server in the middle of a request
26
+
27
+ EventMachine.next_tick do
28
+ proc { get('/') }.should raise_error(Errno::ECONNRESET)
29
+ end
30
+
31
+ socket.close
32
+ end
33
+
34
+ it "should drop current requests when hard stopping" do
35
+ socket = TCPSocket.new('0.0.0.0', 3333)
36
+ socket.write("GET / HTTP/1.1")
37
+ @server.stop! # Force stop the server in the middle of a request
38
+
39
+ EventMachine.next_tick { socket.should be_closed }
40
+ end
41
+
42
+ after do
43
+ stop_server
44
+ end
45
+ end
@@ -0,0 +1,6 @@
1
+ cluster_address: 0.0.0.0
2
+ cluster_port: 3333
3
+ map:
4
+ - incoming: 127.0.0.1
5
+ outgoing: 127.0.0.1:5555
6
+ default: true
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ if SWIFTIPLY_PATH.empty?
4
+ warn "Ignoring Server on Swiftiply specs, gem install swiftiply to run"
5
+ else
6
+ describe Server, 'on Swiftiply' do
7
+ before do
8
+ @swiftiply = fork do
9
+ exec "#{SWIFTIPLY_PATH} -c #{File.dirname(__FILE__)}/swiftiply.yml"
10
+ end
11
+ wait_for_socket('0.0.0.0', 3333)
12
+ sleep 2 # HACK ooh boy, I wish I knew how to make those specs more stable...
13
+ start_server('0.0.0.0', 5555, :backend => Backends::SwiftiplyClient, :wait_for_socket => false) do |env|
14
+ body = env.inspect + env['rack.input'].read
15
+ [200, { 'Content-Type' => 'text/html' }, body]
16
+ end
17
+ end
18
+
19
+ it 'should GET from Net::HTTP' do
20
+ Net::HTTP.get(URI.parse("http://0.0.0.0:3333/?cthis")).should include('cthis')
21
+ end
22
+
23
+ it 'should POST from Net::HTTP' do
24
+ Net::HTTP.post_form(URI.parse("http://0.0.0.0:3333/"), :arg => 'pirate').body.should include('arg=pirate')
25
+ end
26
+
27
+ after do
28
+ stop_server
29
+ Process.kill(9, @swiftiply)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,57 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Server, 'on TCP socket' do
4
+ before do
5
+ start_server do |env|
6
+ body = env.inspect + env['rack.input'].read
7
+ [200, { 'Content-Type' => 'text/html' }, body]
8
+ end
9
+ end
10
+
11
+ it 'should GET from Net::HTTP' do
12
+ get('/?cthis').should include('cthis')
13
+ end
14
+
15
+ it 'should GET from TCPSocket' do
16
+ status, headers, body = parse_response(send_data("GET /?this HTTP/1.0\r\nConnection: close\r\n\r\n"))
17
+ status.should == 200
18
+ headers['Content-Type'].should == 'text/html'
19
+ headers['Connection'].should == 'close'
20
+ body.should include('this')
21
+ end
22
+
23
+ it "should add the Content-Length to the response when not present" do
24
+ status, headers, body = parse_response(send_data("GET / HTTP/1.0\r\nConnection: close\r\n\r\n"))
25
+ headers.should have_key('Content-Length')
26
+ end
27
+
28
+ it 'should set the Content-Length to equal the body size in bytes' do
29
+ status, headers, body = parse_response(send_data("GET / HTTP/1.0\r\nConnection: close\r\n\r\n"))
30
+ headers['Content-Length'].should == (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
31
+ end
32
+
33
+ it 'should return empty string on incomplete headers' do
34
+ send_data("GET /?this HTTP/1.1\r\nHost:").should be_empty
35
+ end
36
+
37
+ it 'should return empty string on incorrect Content-Length' do
38
+ send_data("POST / HTTP/1.1\r\nContent-Length: 300\r\nConnection: close\r\n\r\naye").should be_empty
39
+ end
40
+
41
+ it 'should POST from Net::HTTP' do
42
+ post('/', :arg => 'pirate').should include('arg=pirate')
43
+ end
44
+
45
+ it 'should handle big POST' do
46
+ big = 'X' * (20 * 1024)
47
+ post('/', :big => big).should include(big)
48
+ end
49
+
50
+ it "should retreive remote address" do
51
+ get('/').should include('"REMOTE_ADDR"=>"127.0.0.1"')
52
+ end
53
+
54
+ after do
55
+ stop_server
56
+ end
57
+ end