thin 1.2.3-x86-mswin32
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 +263 -0
- data/COPYING +18 -0
- data/README +69 -0
- data/Rakefile +36 -0
- data/benchmark/abc +51 -0
- data/benchmark/benchmarker.rb +80 -0
- data/benchmark/runner +82 -0
- data/bin/thin +6 -0
- data/example/adapter.rb +32 -0
- data/example/async_app.ru +126 -0
- data/example/async_chat.ru +247 -0
- data/example/async_tailer.ru +100 -0
- data/example/config.ru +22 -0
- data/example/monit_sockets +20 -0
- data/example/monit_unixsock +20 -0
- data/example/myapp.rb +1 -0
- data/example/ramaze.ru +12 -0
- data/example/thin.god +80 -0
- data/example/thin_solaris_smf.erb +36 -0
- data/example/thin_solaris_smf.readme.txt +150 -0
- data/example/vlad.rake +64 -0
- data/ext/thin_parser/common.rl +55 -0
- data/ext/thin_parser/ext_help.h +14 -0
- data/ext/thin_parser/extconf.rb +6 -0
- data/ext/thin_parser/parser.c +452 -0
- data/ext/thin_parser/parser.h +49 -0
- data/ext/thin_parser/parser.rl +157 -0
- data/ext/thin_parser/thin.c +433 -0
- data/lib/rack/adapter/loader.rb +79 -0
- data/lib/rack/adapter/rails.rb +181 -0
- data/lib/thin.rb +46 -0
- data/lib/thin/backends/base.rb +141 -0
- data/lib/thin/backends/swiftiply_client.rb +56 -0
- data/lib/thin/backends/tcp_server.rb +29 -0
- data/lib/thin/backends/unix_server.rb +51 -0
- data/lib/thin/command.rb +53 -0
- data/lib/thin/connection.rb +222 -0
- data/lib/thin/controllers/cluster.rb +127 -0
- data/lib/thin/controllers/controller.rb +183 -0
- data/lib/thin/controllers/service.rb +75 -0
- data/lib/thin/controllers/service.sh.erb +39 -0
- data/lib/thin/daemonizing.rb +174 -0
- data/lib/thin/headers.rb +39 -0
- data/lib/thin/logging.rb +54 -0
- data/lib/thin/request.rb +153 -0
- data/lib/thin/response.rb +101 -0
- data/lib/thin/runner.rb +209 -0
- data/lib/thin/server.rb +247 -0
- data/lib/thin/stats.html.erb +216 -0
- data/lib/thin/stats.rb +52 -0
- data/lib/thin/statuses.rb +43 -0
- data/lib/thin/version.rb +32 -0
- data/lib/thin_parser.so +0 -0
- data/spec/backends/swiftiply_client_spec.rb +66 -0
- data/spec/backends/tcp_server_spec.rb +33 -0
- data/spec/backends/unix_server_spec.rb +37 -0
- data/spec/command_spec.rb +25 -0
- data/spec/configs/cluster.yml +9 -0
- data/spec/configs/single.yml +9 -0
- data/spec/connection_spec.rb +106 -0
- data/spec/controllers/cluster_spec.rb +235 -0
- data/spec/controllers/controller_spec.rb +129 -0
- data/spec/controllers/service_spec.rb +50 -0
- data/spec/daemonizing_spec.rb +192 -0
- data/spec/headers_spec.rb +40 -0
- data/spec/logging_spec.rb +46 -0
- data/spec/perf/request_perf_spec.rb +50 -0
- data/spec/perf/response_perf_spec.rb +19 -0
- data/spec/perf/server_perf_spec.rb +39 -0
- data/spec/rack/loader_spec.rb +29 -0
- data/spec/rack/rails_adapter_spec.rb +106 -0
- data/spec/rails_app/app/controllers/application.rb +10 -0
- data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
- data/spec/rails_app/app/helpers/application_helper.rb +3 -0
- data/spec/rails_app/app/views/simple/index.html.erb +15 -0
- data/spec/rails_app/config/boot.rb +109 -0
- data/spec/rails_app/config/environment.rb +64 -0
- data/spec/rails_app/config/environments/development.rb +18 -0
- data/spec/rails_app/config/environments/production.rb +19 -0
- data/spec/rails_app/config/environments/test.rb +22 -0
- data/spec/rails_app/config/initializers/inflections.rb +10 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/routes.rb +35 -0
- data/spec/rails_app/public/404.html +30 -0
- data/spec/rails_app/public/422.html +30 -0
- data/spec/rails_app/public/500.html +30 -0
- data/spec/rails_app/public/dispatch.cgi +10 -0
- data/spec/rails_app/public/dispatch.fcgi +24 -0
- data/spec/rails_app/public/dispatch.rb +10 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/images/rails.png +0 -0
- data/spec/rails_app/public/index.html +277 -0
- data/spec/rails_app/public/javascripts/application.js +2 -0
- data/spec/rails_app/public/javascripts/controls.js +963 -0
- data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
- data/spec/rails_app/public/javascripts/effects.js +1120 -0
- data/spec/rails_app/public/javascripts/prototype.js +4225 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/script/about +3 -0
- data/spec/rails_app/script/console +3 -0
- data/spec/rails_app/script/destroy +3 -0
- data/spec/rails_app/script/generate +3 -0
- data/spec/rails_app/script/performance/benchmarker +3 -0
- data/spec/rails_app/script/performance/profiler +3 -0
- data/spec/rails_app/script/performance/request +3 -0
- data/spec/rails_app/script/plugin +3 -0
- data/spec/rails_app/script/process/inspector +3 -0
- data/spec/rails_app/script/process/reaper +3 -0
- data/spec/rails_app/script/process/spawner +3 -0
- data/spec/rails_app/script/runner +3 -0
- data/spec/rails_app/script/server +3 -0
- data/spec/request/mongrel_spec.rb +39 -0
- data/spec/request/parser_spec.rb +215 -0
- data/spec/request/persistent_spec.rb +35 -0
- data/spec/request/processing_spec.rb +45 -0
- data/spec/response_spec.rb +91 -0
- data/spec/runner_spec.rb +168 -0
- data/spec/server/builder_spec.rb +44 -0
- data/spec/server/pipelining_spec.rb +110 -0
- data/spec/server/robustness_spec.rb +34 -0
- data/spec/server/stopping_spec.rb +55 -0
- data/spec/server/swiftiply.yml +6 -0
- data/spec/server/swiftiply_spec.rb +32 -0
- data/spec/server/tcp_spec.rb +57 -0
- data/spec/server/threaded_spec.rb +27 -0
- data/spec/server/unix_socket_spec.rb +26 -0
- data/spec/server_spec.rb +96 -0
- data/spec/spec_helper.rb +219 -0
- data/tasks/announce.rake +22 -0
- data/tasks/deploy.rake +13 -0
- data/tasks/email.erb +30 -0
- data/tasks/gem.rake +74 -0
- data/tasks/rdoc.rake +25 -0
- data/tasks/site.rake +15 -0
- data/tasks/spec.rake +49 -0
- data/tasks/stats.rake +28 -0
- metadata +246 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
describe Request, 'legacy Mongrel tests' do
|
5
|
+
it 'should raise error on large header names' do
|
6
|
+
proc { R("GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(1024))}: Test\r\n\r\n") }.
|
7
|
+
should raise_error(InvalidRequest)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should raise error on large mangled field values' do
|
11
|
+
proc { R("GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024*1024, false)}\r\n\r\n") }.
|
12
|
+
should raise_error(InvalidRequest)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should raise error on big fat ugly headers' do
|
16
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
17
|
+
get << "X-Test: test\r\n" * (80 * 1024)
|
18
|
+
proc { R(get) }.should raise_error(InvalidRequest)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should raise error on random garbage' do
|
22
|
+
proc { R("GET #{rand_data(1024, 1024+(1024), false)} #{rand_data(1024, 1024+(1024), false)}\r\n\r\n") }.
|
23
|
+
should raise_error(InvalidRequest)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def rand_data(min, max, readable=true)
|
28
|
+
count = min + ((rand(max)+1) *10).to_i
|
29
|
+
res = count.to_s + "/"
|
30
|
+
|
31
|
+
if readable
|
32
|
+
res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
|
33
|
+
else
|
34
|
+
res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
|
35
|
+
end
|
36
|
+
|
37
|
+
return res
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
# Require mongrel so we can test that Thin parser don't clash w/ Mongrel parser.
|
4
|
+
begin
|
5
|
+
require 'mongrel'
|
6
|
+
rescue LoadError
|
7
|
+
warn "Install mongrel to test compatibility w/ it"
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Request, 'parser' do
|
11
|
+
it 'should include basic headers' do
|
12
|
+
request = R("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
13
|
+
request.env['SERVER_PROTOCOL'].should == 'HTTP/1.1'
|
14
|
+
request.env['REQUEST_PATH'].should == '/'
|
15
|
+
request.env['HTTP_VERSION'].should == 'HTTP/1.1'
|
16
|
+
request.env['REQUEST_URI'].should == '/'
|
17
|
+
request.env['GATEWAY_INTERFACE'].should == 'CGI/1.2'
|
18
|
+
request.env['REQUEST_METHOD'].should == 'GET'
|
19
|
+
request.env["rack.url_scheme"].should == 'http'
|
20
|
+
request.env['FRAGMENT'].to_s.should be_empty
|
21
|
+
request.env['QUERY_STRING'].to_s.should be_empty
|
22
|
+
|
23
|
+
request.should validate_with_lint
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should not prepend HTTP_ to Content-Type and Content-Length' do
|
27
|
+
request = R("POST / HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/html\r\nContent-Length: 2\r\n\r\naa")
|
28
|
+
request.env.keys.should_not include('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH')
|
29
|
+
request.env.keys.should include('CONTENT_TYPE', 'CONTENT_LENGTH')
|
30
|
+
|
31
|
+
request.should validate_with_lint
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should raise error on invalid request line' do
|
35
|
+
proc { R("GET / SsUTF/1.1") }.should raise_error(InvalidRequest)
|
36
|
+
proc { R("GET / HTTP/1.1yousmelllikecheeze") }.should raise_error(InvalidRequest)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should support fragment in uri' do
|
40
|
+
request = R("GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
41
|
+
|
42
|
+
request.env['REQUEST_URI'].should == '/forums/1/topics/2375?page=1'
|
43
|
+
request.env['PATH_INFO'].should == '/forums/1/topics/2375'
|
44
|
+
request.env['QUERY_STRING'].should == 'page=1'
|
45
|
+
request.env['FRAGMENT'].should == 'posts-17408'
|
46
|
+
|
47
|
+
request.should validate_with_lint
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should parse path with query string' do
|
51
|
+
request = R("GET /index.html?234235 HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
52
|
+
request.env['REQUEST_PATH'].should == '/index.html'
|
53
|
+
request.env['QUERY_STRING'].should == '234235'
|
54
|
+
request.env['FRAGMENT'].should be_nil
|
55
|
+
|
56
|
+
request.should validate_with_lint
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should parse headers from GET request' do
|
60
|
+
request = R(<<-EOS, true)
|
61
|
+
GET / HTTP/1.1
|
62
|
+
Host: myhost.com:3000
|
63
|
+
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9
|
64
|
+
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
|
65
|
+
Accept-Language: en-us,en;q=0.5
|
66
|
+
Accept-Encoding: gzip,deflate
|
67
|
+
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
|
68
|
+
Cookie: mium=7
|
69
|
+
Keep-Alive: 300
|
70
|
+
Connection: keep-alive
|
71
|
+
|
72
|
+
EOS
|
73
|
+
request.env['HTTP_HOST'].should == 'myhost.com:3000'
|
74
|
+
request.env['SERVER_NAME'].should == 'myhost.com'
|
75
|
+
request.env['SERVER_PORT'].should == '3000'
|
76
|
+
request.env['HTTP_COOKIE'].should == 'mium=7'
|
77
|
+
|
78
|
+
request.should validate_with_lint
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should parse POST request with data' do
|
82
|
+
request = R(<<-EOS.chomp, true)
|
83
|
+
POST /postit HTTP/1.1
|
84
|
+
Host: localhost:3000
|
85
|
+
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9
|
86
|
+
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
|
87
|
+
Accept-Language: en-us,en;q=0.5
|
88
|
+
Accept-Encoding: gzip,deflate
|
89
|
+
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
|
90
|
+
Keep-Alive: 300
|
91
|
+
Connection: keep-alive
|
92
|
+
Content-Type: text/html
|
93
|
+
Content-Length: 37
|
94
|
+
|
95
|
+
name=marc&email=macournoyer@gmail.com
|
96
|
+
EOS
|
97
|
+
|
98
|
+
request.env['REQUEST_METHOD'].should == 'POST'
|
99
|
+
request.env['REQUEST_URI'].should == '/postit'
|
100
|
+
request.env['CONTENT_TYPE'].should == 'text/html'
|
101
|
+
request.env['CONTENT_LENGTH'].should == '37'
|
102
|
+
request.env['HTTP_ACCEPT'].should == 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5'
|
103
|
+
request.env['HTTP_ACCEPT_LANGUAGE'].should == 'en-us,en;q=0.5'
|
104
|
+
|
105
|
+
request.body.rewind
|
106
|
+
request.body.read.should == 'name=marc&email=macournoyer@gmail.com'
|
107
|
+
request.body.class.should == StringIO
|
108
|
+
|
109
|
+
request.should validate_with_lint
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should not fuck up on stupid fucked IE6 headers' do
|
113
|
+
body = <<-EOS
|
114
|
+
POST /codes/58-tracking-file-downloads-automatically-in-google-analytics-with-prototype/refactors HTTP/1.0
|
115
|
+
X-Real-IP: 62.24.71.95
|
116
|
+
X-Forwarded-For: 62.24.71.95
|
117
|
+
Host: refactormycode.com
|
118
|
+
Connection: close
|
119
|
+
TE: deflate,gzip;q=0.3
|
120
|
+
Accept: */*
|
121
|
+
Range: bytes=0-499999
|
122
|
+
Referer: http://refactormycode.com/codes/58-tracking-file-downloads-automatically-in-google-analytics-with-prototype
|
123
|
+
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
|
124
|
+
Content-Length: 1
|
125
|
+
Content-Type: application/x-www-form-urlencoded
|
126
|
+
Cookie: _refactormycode_session_id=a1b2n3jk4k5; flash=%7B%7D
|
127
|
+
Cookie2: $Version="1"
|
128
|
+
|
129
|
+
a
|
130
|
+
EOS
|
131
|
+
request = R(body, true)
|
132
|
+
request.env['HTTP_COOKIE2'].should == '$Version="1"'
|
133
|
+
|
134
|
+
request.should validate_with_lint
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'shoud accept long query string' do
|
138
|
+
body = <<-EOS
|
139
|
+
GET /session?open_id_complete=1&nonce=ytPOcwni&nonce=ytPOcwni&openid.assoc_handle=%7BHMAC-SHA1%7D%7B473e38fe%7D%7BJTjJxA%3D%3D%7D&openid.identity=http%3A%2F%2Fmacournoyer.myopenid.com%2F&openid.mode=id_res&openid.op_endpoint=http%3A%2F%2Fwww.myopenid.com%2Fserver&openid.response_nonce=2007-11-29T01%3A19%3A35ZGA5FUU&openid.return_to=http%3A%2F%2Flocalhost%3A3000%2Fsession%3Fopen_id_complete%3D1%26nonce%3DytPOcwni%26nonce%3DytPOcwni&openid.sig=lPIRgwpfR6JAdGGnb0ZjcY%2FWjr8%3D&openid.signed=assoc_handle%2Cidentity%2Cmode%2Cop_endpoint%2Cresponse_nonce%2Creturn_to%2Csigned%2Csreg.email%2Csreg.nickname&openid.sreg.email=macournoyer%40yahoo.ca&openid.sreg.nickname=macournoyer HTTP/1.1
|
140
|
+
Host: localhost:3000
|
141
|
+
|
142
|
+
EOS
|
143
|
+
request = R(body, true)
|
144
|
+
|
145
|
+
request.env['QUERY_STRING'].should == 'open_id_complete=1&nonce=ytPOcwni&nonce=ytPOcwni&openid.assoc_handle=%7BHMAC-SHA1%7D%7B473e38fe%7D%7BJTjJxA%3D%3D%7D&openid.identity=http%3A%2F%2Fmacournoyer.myopenid.com%2F&openid.mode=id_res&openid.op_endpoint=http%3A%2F%2Fwww.myopenid.com%2Fserver&openid.response_nonce=2007-11-29T01%3A19%3A35ZGA5FUU&openid.return_to=http%3A%2F%2Flocalhost%3A3000%2Fsession%3Fopen_id_complete%3D1%26nonce%3DytPOcwni%26nonce%3DytPOcwni&openid.sig=lPIRgwpfR6JAdGGnb0ZjcY%2FWjr8%3D&openid.signed=assoc_handle%2Cidentity%2Cmode%2Cop_endpoint%2Cresponse_nonce%2Creturn_to%2Csigned%2Csreg.email%2Csreg.nickname&openid.sreg.email=macournoyer%40yahoo.ca&openid.sreg.nickname=macournoyer'
|
146
|
+
|
147
|
+
request.should validate_with_lint
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should parse even with stupid Content-Length' do
|
151
|
+
body = <<-EOS.chomp
|
152
|
+
POST / HTTP/1.1
|
153
|
+
Host: localhost:3000
|
154
|
+
Content-Length: 300
|
155
|
+
|
156
|
+
aye
|
157
|
+
EOS
|
158
|
+
request = R(body, true)
|
159
|
+
|
160
|
+
request.body.rewind
|
161
|
+
request.body.read.should == 'aye'
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should parse ie6 urls" do
|
165
|
+
%w(/some/random/path"
|
166
|
+
/some/random/path>
|
167
|
+
/some/random/path<
|
168
|
+
/we/love/you/ie6?q=<"">
|
169
|
+
/url?<="&>="
|
170
|
+
/mal"formed"?
|
171
|
+
).each do |path|
|
172
|
+
parser = HttpParser.new
|
173
|
+
req = {}
|
174
|
+
sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
|
175
|
+
nread = parser.execute(req, sorta_safe, 0)
|
176
|
+
|
177
|
+
sorta_safe.size.should == nread - 1 # Ragel 6 skips last linebreak
|
178
|
+
parser.should be_finished
|
179
|
+
parser.should_not be_error
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
xit "should parse absolute request URI" do
|
184
|
+
request = R(<<-EOS, true)
|
185
|
+
GET http://localhost:3000/hi HTTP/1.1
|
186
|
+
Host: localhost:3000
|
187
|
+
|
188
|
+
EOS
|
189
|
+
request.env['PATH_INFO'].should == '/hi'
|
190
|
+
|
191
|
+
request.should validate_with_lint
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
it "should fails on heders larger then MAX_HEADER" do
|
196
|
+
proc { R("GET / HTTP/1.1\r\nFoo: #{'X' * Request::MAX_HEADER}\r\n\r\n") }.should raise_error(InvalidRequest)
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should default SERVER_NAME to localhost" do
|
200
|
+
request = R("GET / HTTP/1.1\r\n\r\n")
|
201
|
+
request.env['SERVER_NAME'].should == "localhost"
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'should normalize http_fields' do
|
205
|
+
[ "GET /index.html HTTP/1.1\r\nhos-t: localhost\r\n\r\n",
|
206
|
+
"GET /index.html HTTP/1.1\r\nhOs_t: localhost\r\n\r\n",
|
207
|
+
"GET /index.html HTTP/1.1\r\nhoS-T: localhost\r\n\r\n"
|
208
|
+
].each { |req_str|
|
209
|
+
parser = HttpParser.new
|
210
|
+
req = {}
|
211
|
+
nread = parser.execute(req, req_str, 0)
|
212
|
+
req.should be_has_key('HTTP_HOS_T')
|
213
|
+
}
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Request, 'persistent' do
|
4
|
+
before do
|
5
|
+
@request = Request.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should not assume that a persistent connection is maintained for HTTP version 1.0" do
|
9
|
+
@request.env['HTTP_VERSION'] = 'HTTP/1.0'
|
10
|
+
@request.should_not be_persistent
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should assume that a persistent connection is maintained for HTTP version 1.0 when specified" do
|
14
|
+
@request.env['HTTP_VERSION'] = 'HTTP/1.0'
|
15
|
+
@request.env['HTTP_CONNECTION'] = 'Keep-Alive'
|
16
|
+
@request.should be_persistent
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should maintain a persistent connection for HTTP/1.1 client" do
|
20
|
+
@request.env['HTTP_VERSION'] = 'HTTP/1.1'
|
21
|
+
@request.env['HTTP_CONNECTION'] = 'Keep-Alive'
|
22
|
+
@request.should be_persistent
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should maintain a persistent connection for HTTP/1.1 client by default" do
|
26
|
+
@request.env['HTTP_VERSION'] = 'HTTP/1.1'
|
27
|
+
@request.should be_persistent
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not maintain a persistent connection for HTTP/1.1 client when Connection header include close" do
|
31
|
+
@request.env['HTTP_VERSION'] = 'HTTP/1.1'
|
32
|
+
@request.env['HTTP_CONNECTION'] = 'close'
|
33
|
+
@request.should_not be_persistent
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Request, 'processing' do
|
4
|
+
it 'should parse in chunks' do
|
5
|
+
request = Request.new
|
6
|
+
request.parse("POST / HTTP/1.1\r\n").should be_false
|
7
|
+
request.parse("Host: localhost\r\n").should be_false
|
8
|
+
request.parse("Content-Length: 9\r\n").should be_false
|
9
|
+
request.parse("\r\nvery ").should be_false
|
10
|
+
request.parse("cool").should be_true
|
11
|
+
|
12
|
+
request.env['CONTENT_LENGTH'].should == '9'
|
13
|
+
request.body.read.should == 'very cool'
|
14
|
+
request.should validate_with_lint
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should move body to tempfile when too big" do
|
18
|
+
len = Request::MAX_BODY + 2
|
19
|
+
request = Request.new
|
20
|
+
request.parse("POST /postit HTTP/1.1\r\nContent-Length: #{len}\r\n\r\n#{'X' * (len/2)}")
|
21
|
+
request.parse('X' * (len/2))
|
22
|
+
|
23
|
+
request.body.size.should == len
|
24
|
+
request.should be_finished
|
25
|
+
request.body.class.should == Tempfile
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should delete body tempfile when closing" do
|
29
|
+
body = 'X' * (Request::MAX_BODY + 1)
|
30
|
+
|
31
|
+
request = Request.new
|
32
|
+
request.parse("POST /postit HTTP/1.1\r\n")
|
33
|
+
request.parse("Content-Length: #{body.size}\r\n\r\n")
|
34
|
+
request.parse(body)
|
35
|
+
|
36
|
+
request.body.path.should_not be_nil
|
37
|
+
request.close
|
38
|
+
request.body.path.should be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should raise error when header is too big" do
|
42
|
+
big_headers = "X-Test: X\r\n" * (1024 * (80 + 32))
|
43
|
+
proc { R("GET / HTTP/1.1\r\n#{big_headers}\r\n") }.should raise_error(InvalidRequest)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Response do
|
4
|
+
before do
|
5
|
+
@response = Response.new
|
6
|
+
@response.headers['Content-Type'] = 'text/html'
|
7
|
+
@response.headers['Content-Length'] = '0'
|
8
|
+
@response.body = ''
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should output headers' do
|
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")
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should output head' do
|
20
|
+
@response.head.should include("HTTP/1.1 200 OK", "Content-Type: text/html", "Content-Length: 0",
|
21
|
+
"Connection: close", "\r\n\r\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should allow duplicates in headers' do
|
25
|
+
@response.headers['Set-Cookie'] = 'mium=7'
|
26
|
+
@response.headers['Set-Cookie'] = 'hi=there'
|
27
|
+
|
28
|
+
@response.head.should include("Set-Cookie: mium=7", "Set-Cookie: hi=there")
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should parse simple header values' do
|
32
|
+
@response.headers = {
|
33
|
+
'Host' => 'localhost'
|
34
|
+
}
|
35
|
+
|
36
|
+
@response.head.should include("Host: localhost")
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should parse multiline header values in several headers' do
|
40
|
+
@response.headers = {
|
41
|
+
'Set-Cookie' => "mium=7\nhi=there"
|
42
|
+
}
|
43
|
+
|
44
|
+
@response.head.should include("Set-Cookie: mium=7", "Set-Cookie: hi=there")
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should ignore nil headers' do
|
48
|
+
@response.headers = nil
|
49
|
+
@response.headers = { 'Host' => 'localhost' }
|
50
|
+
@response.headers = { 'Set-Cookie' => nil }
|
51
|
+
@response.head.should include('Host: localhost')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should output body' do
|
55
|
+
@response.body = ['<html>', '</html>']
|
56
|
+
|
57
|
+
out = ''
|
58
|
+
@response.each { |l| out << l }
|
59
|
+
out.should include("\r\n\r\n<html></html>")
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should output String body' do
|
63
|
+
@response.body = '<html></html>'
|
64
|
+
|
65
|
+
out = ''
|
66
|
+
@response.each { |l| out << l }
|
67
|
+
out.should include("\r\n\r\n<html></html>")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should not be persistent by default" do
|
71
|
+
@response.should_not be_persistent
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should not be persistent when no Content-Length" do
|
75
|
+
@response = Response.new
|
76
|
+
@response.headers['Content-Type'] = 'text/html'
|
77
|
+
@response.body = ''
|
78
|
+
|
79
|
+
@response.persistent!
|
80
|
+
@response.should_not be_persistent
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should be persistent when specified" do
|
84
|
+
@response.persistent!
|
85
|
+
@response.should be_persistent
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should be closeable" do
|
89
|
+
@response.close
|
90
|
+
end
|
91
|
+
end
|