thin 0.5.3-x86-mswin32-60
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 +40 -0
- data/COPYING +18 -0
- data/README +60 -0
- data/Rakefile +11 -0
- data/benchmark/simple.rb +48 -0
- data/bin/thin +123 -0
- data/doc/benchmarks.txt +86 -0
- data/doc/rdoc/classes/Process.html +181 -0
- data/doc/rdoc/classes/Rack.html +156 -0
- data/doc/rdoc/classes/Rack/Adapter.html +155 -0
- data/doc/rdoc/classes/Rack/Adapter/Rails.html +289 -0
- data/doc/rdoc/classes/Rack/Adapter/Rails/CGIWrapper.html +359 -0
- data/doc/rdoc/classes/Rack/Handler.html +155 -0
- data/doc/rdoc/classes/Rack/Handler/Thin.html +175 -0
- data/doc/rdoc/classes/Thin.html +164 -0
- data/doc/rdoc/classes/Thin/Cluster.html +399 -0
- data/doc/rdoc/classes/Thin/Connection.html +223 -0
- data/doc/rdoc/classes/Thin/Daemonizable.html +260 -0
- data/doc/rdoc/classes/Thin/Daemonizable/ClassMethods.html +197 -0
- data/doc/rdoc/classes/Thin/Headers.html +238 -0
- data/doc/rdoc/classes/Thin/InvalidRequest.html +144 -0
- data/doc/rdoc/classes/Thin/Logging.html +201 -0
- data/doc/rdoc/classes/Thin/Request.html +231 -0
- data/doc/rdoc/classes/Thin/Response.html +271 -0
- data/doc/rdoc/classes/Thin/Server.html +295 -0
- data/doc/rdoc/classes/Thin/StopServer.html +143 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/README.html +226 -0
- data/doc/rdoc/files/bin/thin.html +245 -0
- data/doc/rdoc/files/lib/rack/adapter/rails_rb.html +146 -0
- data/doc/rdoc/files/lib/rack/handler/thin_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/cluster_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/connection_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/daemonizing_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/headers_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/logging_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/request_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/response_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/server_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/statuses_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/version_rb.html +145 -0
- data/doc/rdoc/files/lib/thin_rb.html +152 -0
- data/doc/rdoc/index.html +10 -0
- data/doc/rdoc/logo.gif +0 -0
- data/doc/rdoc/rdoc-style.css +55 -0
- data/example/config.ru +9 -0
- data/example/thin.god +72 -0
- data/ext/thin_parser/common.rl +54 -0
- data/ext/thin_parser/ext_help.h +14 -0
- data/ext/thin_parser/extconf.rb +6 -0
- data/ext/thin_parser/parser.c +1199 -0
- data/ext/thin_parser/parser.h +49 -0
- data/ext/thin_parser/parser.rl +143 -0
- data/ext/thin_parser/thin.c +424 -0
- data/lib/rack/adapter/rails.rb +155 -0
- data/lib/rack/handler/thin.rb +13 -0
- data/lib/thin.rb +36 -0
- data/lib/thin/cluster.rb +106 -0
- data/lib/thin/connection.rb +46 -0
- data/lib/thin/daemonizing.rb +112 -0
- data/lib/thin/headers.rb +37 -0
- data/lib/thin/logging.rb +23 -0
- data/lib/thin/request.rb +72 -0
- data/lib/thin/response.rb +48 -0
- data/lib/thin/server.rb +80 -0
- data/lib/thin/statuses.rb +43 -0
- data/lib/thin/version.rb +11 -0
- data/lib/thin_parser.so +0 -0
- data/spec/cluster_spec.rb +58 -0
- data/spec/daemonizing_spec.rb +93 -0
- data/spec/headers_spec.rb +35 -0
- data/spec/rack_rails_spec.rb +92 -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_spec.rb +258 -0
- data/spec/response_spec.rb +56 -0
- data/spec/server_spec.rb +75 -0
- data/spec/spec_helper.rb +127 -0
- metadata +219 -0
@@ -0,0 +1,258 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
describe Request do
|
5
|
+
it 'should include basic headers' do
|
6
|
+
request = R("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
7
|
+
request.env['SERVER_PROTOCOL'].should == 'HTTP/1.1'
|
8
|
+
request.env['REQUEST_PATH'].should == '/'
|
9
|
+
request.env['HTTP_VERSION'].should == 'HTTP/1.1'
|
10
|
+
request.env['REQUEST_URI'].should == '/'
|
11
|
+
request.env['GATEWAY_INTERFACE'].should == 'CGI/1.2'
|
12
|
+
request.env['REQUEST_METHOD'].should == 'GET'
|
13
|
+
request.env["rack.url_scheme"].should == 'http'
|
14
|
+
request.env['FRAGMENT'].to_s.should be_empty
|
15
|
+
request.env['QUERY_STRING'].to_s.should be_empty
|
16
|
+
|
17
|
+
request.should validate_with_lint
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should not prepend HTTP_ to Content-Type and Content-Length' do
|
21
|
+
request = R("POST / HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/html\r\nContent-Length: 2\r\n\r\naa")
|
22
|
+
request.env.keys.should_not include('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH')
|
23
|
+
request.env.keys.should include('CONTENT_TYPE', 'CONTENT_LENGTH')
|
24
|
+
|
25
|
+
request.should validate_with_lint
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should raise error on invalid request line' do
|
29
|
+
proc { R("GET / SsUTF/1.1") }.should raise_error(InvalidRequest)
|
30
|
+
proc { R("GET / HTTP/1.1yousmelllikecheeze") }.should raise_error(InvalidRequest)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should support fragment in uri' do
|
34
|
+
request = R("GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
35
|
+
|
36
|
+
request.env['REQUEST_URI'].should == '/forums/1/topics/2375?page=1'
|
37
|
+
request.env['PATH_INFO'].should == '/forums/1/topics/2375'
|
38
|
+
request.env['QUERY_STRING'].should == 'page=1'
|
39
|
+
request.env['FRAGMENT'].should == 'posts-17408'
|
40
|
+
|
41
|
+
request.should validate_with_lint
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should parse path with query string' do
|
45
|
+
request = R("GET /index.html?234235 HTTP/1.1\r\nHost: localhost\r\n\r\n")
|
46
|
+
request.env['REQUEST_PATH'].should == '/index.html'
|
47
|
+
request.env['QUERY_STRING'].should == '234235'
|
48
|
+
request.env['FRAGMENT'].should be_nil
|
49
|
+
|
50
|
+
request.should validate_with_lint
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should raise error on large header names' do
|
54
|
+
proc { R("GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(1024))}: Test\r\n\r\n") }.
|
55
|
+
should raise_error(InvalidRequest)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should raise error on large mangled field values' do
|
59
|
+
proc { R("GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 100*1024+(1024), false)}\r\n\r\n") }.
|
60
|
+
should raise_error(InvalidRequest)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should raise error on big fat ugly headers' do
|
64
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
65
|
+
get << "X-Test: test\r\n" * (80 * 1024)
|
66
|
+
proc { R(get) }.should raise_error(InvalidRequest)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should raise error on random garbage' do
|
70
|
+
proc { R("GET #{rand_data(1024, 1024+(1024), false)} #{rand_data(1024, 1024+(1024), false)}\r\n\r\n") }.
|
71
|
+
should raise_error(InvalidRequest)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should parse headers from GET request' do
|
75
|
+
request = R(<<-EOS, true)
|
76
|
+
GET / HTTP/1.1
|
77
|
+
Host: localhost:3000
|
78
|
+
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
|
79
|
+
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
|
80
|
+
Accept-Language: en-us,en;q=0.5
|
81
|
+
Accept-Encoding: gzip,deflate
|
82
|
+
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
|
83
|
+
Cookie: mium=7
|
84
|
+
Keep-Alive: 300
|
85
|
+
Connection: keep-alive
|
86
|
+
|
87
|
+
EOS
|
88
|
+
request.env['HTTP_HOST'].should == 'localhost:3000'
|
89
|
+
request.env['SERVER_NAME'].should == 'localhost'
|
90
|
+
request.env['SERVER_PORT'].should == '3000'
|
91
|
+
request.env['HTTP_COOKIE'].should == 'mium=7'
|
92
|
+
|
93
|
+
request.should validate_with_lint
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should parse POST request with data' do
|
97
|
+
request = R(<<-EOS.chomp, true)
|
98
|
+
POST /postit HTTP/1.1
|
99
|
+
Host: localhost:3000
|
100
|
+
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
|
101
|
+
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
|
102
|
+
Accept-Language: en-us,en;q=0.5
|
103
|
+
Accept-Encoding: gzip,deflate
|
104
|
+
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
|
105
|
+
Keep-Alive: 300
|
106
|
+
Connection: keep-alive
|
107
|
+
Content-Type: text/html
|
108
|
+
Content-Length: 37
|
109
|
+
|
110
|
+
name=marc&email=macournoyer@gmail.com
|
111
|
+
EOS
|
112
|
+
|
113
|
+
request.env['REQUEST_METHOD'].should == 'POST'
|
114
|
+
request.env['REQUEST_URI'].should == '/postit'
|
115
|
+
request.env['CONTENT_TYPE'].should == 'text/html'
|
116
|
+
request.env['CONTENT_LENGTH'].should == '37'
|
117
|
+
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'
|
118
|
+
request.env['HTTP_ACCEPT_LANGUAGE'].should == 'en-us,en;q=0.5'
|
119
|
+
|
120
|
+
request.body.rewind
|
121
|
+
request.body.read.should == 'name=marc&email=macournoyer@gmail.com'
|
122
|
+
|
123
|
+
request.should validate_with_lint
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should not fuck up on stupid fucked IE6 headers' do
|
127
|
+
body = <<-EOS
|
128
|
+
POST /codes/58-tracking-file-downloads-automatically-in-google-analytics-with-prototype/refactors HTTP/1.0
|
129
|
+
X-Real-IP: 62.24.71.95
|
130
|
+
X-Forwarded-For: 62.24.71.95
|
131
|
+
Host: refactormycode.com
|
132
|
+
Connection: close
|
133
|
+
TE: deflate,gzip;q=0.3
|
134
|
+
Accept: */*
|
135
|
+
Range: bytes=0-499999
|
136
|
+
Referer: http://refactormycode.com/codes/58-tracking-file-downloads-automatically-in-google-analytics-with-prototype
|
137
|
+
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
|
138
|
+
Content-Length: 1
|
139
|
+
Content-Type: application/x-www-form-urlencoded
|
140
|
+
Cookie: _refactormycode_session_id=a1b2n3jk4k5; flash=%7B%7D
|
141
|
+
Cookie2: $Version="1"
|
142
|
+
|
143
|
+
a
|
144
|
+
EOS
|
145
|
+
request = R(body, true)
|
146
|
+
request.env['HTTP_COOKIE2'].should == '$Version="1"'
|
147
|
+
|
148
|
+
request.should validate_with_lint
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'shoud accept long query string' do
|
152
|
+
body = <<-EOS
|
153
|
+
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
|
154
|
+
Host: localhost:3000
|
155
|
+
|
156
|
+
EOS
|
157
|
+
request = R(body, true)
|
158
|
+
|
159
|
+
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'
|
160
|
+
|
161
|
+
request.should validate_with_lint
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should parse even with stupid Content-Length' do
|
165
|
+
body = <<-EOS.chomp
|
166
|
+
POST / HTTP/1.1
|
167
|
+
Host: localhost:3000
|
168
|
+
Content-Length: 300
|
169
|
+
|
170
|
+
aye
|
171
|
+
EOS
|
172
|
+
request = R(body, true)
|
173
|
+
|
174
|
+
request.body.rewind
|
175
|
+
request.body.read.should == 'aye'
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should parse in chunks' do
|
179
|
+
request = Request.new
|
180
|
+
request.parse("POST / HTTP/1.1\r\n").should be_false
|
181
|
+
request.parse("Host: localhost\r\n").should be_false
|
182
|
+
request.parse("Content-Length: 9\r\n").should be_false
|
183
|
+
request.parse("\r\nvery ").should be_false
|
184
|
+
request.parse("cool").should be_true
|
185
|
+
|
186
|
+
request.env['CONTENT_LENGTH'].should == '9'
|
187
|
+
request.body.read.should == 'very cool'
|
188
|
+
request.should validate_with_lint
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should be faster then #{max_parsing_time = 0.2} ms" do
|
192
|
+
body = <<-EOS.chomp
|
193
|
+
POST /postit HTTP/1.1
|
194
|
+
Host: localhost:3000
|
195
|
+
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
|
196
|
+
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
|
197
|
+
Accept-Language: en-us,en;q=0.5
|
198
|
+
Accept-Encoding: gzip,deflate
|
199
|
+
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
|
200
|
+
Keep-Alive: 300
|
201
|
+
Connection: keep-alive
|
202
|
+
Content-Type: text/html
|
203
|
+
Content-Length: 37
|
204
|
+
|
205
|
+
hi=there&name=marc&email=macournoyer@gmail.com
|
206
|
+
EOS
|
207
|
+
|
208
|
+
proc { R(body, true) }.should be_faster_then(max_parsing_time)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should be comparable to Mongrel parser' do
|
212
|
+
require 'http11'
|
213
|
+
|
214
|
+
body = <<-EOS.chomp.gsub("\n", "\r\n")
|
215
|
+
POST /postit HTTP/1.1
|
216
|
+
Host: localhost:3000
|
217
|
+
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
|
218
|
+
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
|
219
|
+
Accept-Language: en-us,en;q=0.5
|
220
|
+
Accept-Encoding: gzip,deflate
|
221
|
+
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
|
222
|
+
Keep-Alive: 300
|
223
|
+
Connection: keep-alive
|
224
|
+
Content-Type: text/html
|
225
|
+
Content-Length: 37
|
226
|
+
|
227
|
+
hi=there&name=marc&email=macournoyer@gmail.com
|
228
|
+
EOS
|
229
|
+
|
230
|
+
tests = 10_000
|
231
|
+
puts
|
232
|
+
Benchmark.bmbm(10) do |results|
|
233
|
+
results.report("mongrel:") { tests.times { Mongrel::HttpParser.new.execute({}, body.dup, 0) } }
|
234
|
+
results.report("thin:") { tests.times { Thin::HttpParser.new.execute({'rack.input' => StringIO.new}, body.dup, 0) } }
|
235
|
+
end
|
236
|
+
end if ENV['BM']
|
237
|
+
|
238
|
+
private
|
239
|
+
def rand_data(min, max, readable=true)
|
240
|
+
count = min + ((rand(max)+1) *10).to_i
|
241
|
+
res = count.to_s + "/"
|
242
|
+
|
243
|
+
if readable
|
244
|
+
res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
|
245
|
+
else
|
246
|
+
res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
|
247
|
+
end
|
248
|
+
|
249
|
+
return res
|
250
|
+
end
|
251
|
+
|
252
|
+
def R(raw, convert_line_feed=false)
|
253
|
+
raw.gsub!("\n", "\r\n") if convert_line_feed
|
254
|
+
request = Thin::Request.new
|
255
|
+
request.parse(raw)
|
256
|
+
request
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,56 @@
|
|
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
|
+
end
|
8
|
+
|
9
|
+
it 'should output headers' do
|
10
|
+
@response.headers_output.should == "Content-Type: text/html\r\nContent-Length: 0\r\nConnection: close\r\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
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"
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should allow duplicates in headers' do
|
18
|
+
@response.headers['Set-Cookie'] = 'mium=7'
|
19
|
+
@response.headers['Set-Cookie'] = 'hi=there'
|
20
|
+
|
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"
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should parse simple header values' do
|
25
|
+
@response.headers = {
|
26
|
+
'Host' => 'localhost'
|
27
|
+
}
|
28
|
+
|
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"
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should parse multiline header values in several headers' do
|
33
|
+
@response.headers = {
|
34
|
+
'Set-Cookie' => "mium=7\nhi=there"
|
35
|
+
}
|
36
|
+
|
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"
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should output body' do
|
41
|
+
@response.body << '<html></html>'
|
42
|
+
|
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>"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should be faster then #{max_parsing_time = 0.06} ms" do
|
47
|
+
@response.body << <<-EOS
|
48
|
+
<html><head><title>Dir listing</title></head>
|
49
|
+
<body><h1>Listing stuff</h1><ul>
|
50
|
+
#{'<li>Hi!</li>' * 100}
|
51
|
+
</ul></body></html>
|
52
|
+
EOS
|
53
|
+
|
54
|
+
proc { @response.to_s }.should be_faster_then(max_parsing_time)
|
55
|
+
end
|
56
|
+
end
|
data/spec/server_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'net/http'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
describe Server do
|
6
|
+
before do
|
7
|
+
app = proc do |env|
|
8
|
+
body = [env['QUERY_STRING'], env['rack.input'].read].compact
|
9
|
+
[200, { 'Content-Type' => 'text/html' }, body]
|
10
|
+
end
|
11
|
+
server = Thin::Server.new('0.0.0.0', 3333, app)
|
12
|
+
server.timeout = 3
|
13
|
+
server.silent = true
|
14
|
+
|
15
|
+
server.start
|
16
|
+
@thread = Thread.new do
|
17
|
+
server.listen!
|
18
|
+
end
|
19
|
+
sleep 0.1 until @thread.status == 'sleep'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should GET from Net::HTTP' do
|
23
|
+
get('/?cthis').should == 'cthis'
|
24
|
+
end
|
25
|
+
|
26
|
+
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"
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should return empty string on incomplete headers' do
|
31
|
+
raw('0.0.0.0', 3333, "GET /?this HTTP/1.1\r\nHost:").should be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should return empty string on incorrect Content-Length' do
|
35
|
+
raw('0.0.0.0', 3333, "POST / HTTP/1.1\r\nContent-Length: 300\r\n\r\naye").should be_empty
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should POST from Net::HTTP' do
|
39
|
+
post('/', :arg => 'pirate').should == 'arg=pirate'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should handle big POST' do
|
43
|
+
big = 'X' * (20 * 1024)
|
44
|
+
post('/', :big => big).size.should == big.size + 4
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should handle GET in less then #{get_request_time = 5} ms" do
|
48
|
+
proc { get('/') }.should be_faster_then(get_request_time)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should handle POST in less then #{post_request_time = 6} ms" do
|
52
|
+
proc { post('/', :file => 'X' * 1000) }.should be_faster_then(get_request_time)
|
53
|
+
end
|
54
|
+
|
55
|
+
after do
|
56
|
+
@thread.kill
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def get(url)
|
61
|
+
Net::HTTP.get(URI.parse('http://0.0.0.0:3333' + url))
|
62
|
+
end
|
63
|
+
|
64
|
+
def raw(host, port, data)
|
65
|
+
socket = TCPSocket.new(host, port)
|
66
|
+
socket.write data
|
67
|
+
out = socket.read
|
68
|
+
socket.close
|
69
|
+
out
|
70
|
+
end
|
71
|
+
|
72
|
+
def post(url, params={})
|
73
|
+
Net::HTTP.post_form(URI.parse('http://0.0.0.0:3333' + url), params).body
|
74
|
+
end
|
75
|
+
end
|