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.

Files changed (117) hide show
  1. data/CHANGELOG +40 -0
  2. data/COPYING +18 -0
  3. data/README +60 -0
  4. data/Rakefile +11 -0
  5. data/benchmark/simple.rb +48 -0
  6. data/bin/thin +123 -0
  7. data/doc/benchmarks.txt +86 -0
  8. data/doc/rdoc/classes/Process.html +181 -0
  9. data/doc/rdoc/classes/Rack.html +156 -0
  10. data/doc/rdoc/classes/Rack/Adapter.html +155 -0
  11. data/doc/rdoc/classes/Rack/Adapter/Rails.html +289 -0
  12. data/doc/rdoc/classes/Rack/Adapter/Rails/CGIWrapper.html +359 -0
  13. data/doc/rdoc/classes/Rack/Handler.html +155 -0
  14. data/doc/rdoc/classes/Rack/Handler/Thin.html +175 -0
  15. data/doc/rdoc/classes/Thin.html +164 -0
  16. data/doc/rdoc/classes/Thin/Cluster.html +399 -0
  17. data/doc/rdoc/classes/Thin/Connection.html +223 -0
  18. data/doc/rdoc/classes/Thin/Daemonizable.html +260 -0
  19. data/doc/rdoc/classes/Thin/Daemonizable/ClassMethods.html +197 -0
  20. data/doc/rdoc/classes/Thin/Headers.html +238 -0
  21. data/doc/rdoc/classes/Thin/InvalidRequest.html +144 -0
  22. data/doc/rdoc/classes/Thin/Logging.html +201 -0
  23. data/doc/rdoc/classes/Thin/Request.html +231 -0
  24. data/doc/rdoc/classes/Thin/Response.html +271 -0
  25. data/doc/rdoc/classes/Thin/Server.html +295 -0
  26. data/doc/rdoc/classes/Thin/StopServer.html +143 -0
  27. data/doc/rdoc/created.rid +1 -0
  28. data/doc/rdoc/files/README.html +226 -0
  29. data/doc/rdoc/files/bin/thin.html +245 -0
  30. data/doc/rdoc/files/lib/rack/adapter/rails_rb.html +146 -0
  31. data/doc/rdoc/files/lib/rack/handler/thin_rb.html +146 -0
  32. data/doc/rdoc/files/lib/thin/cluster_rb.html +146 -0
  33. data/doc/rdoc/files/lib/thin/connection_rb.html +145 -0
  34. data/doc/rdoc/files/lib/thin/daemonizing_rb.html +146 -0
  35. data/doc/rdoc/files/lib/thin/headers_rb.html +146 -0
  36. data/doc/rdoc/files/lib/thin/logging_rb.html +146 -0
  37. data/doc/rdoc/files/lib/thin/request_rb.html +145 -0
  38. data/doc/rdoc/files/lib/thin/response_rb.html +145 -0
  39. data/doc/rdoc/files/lib/thin/server_rb.html +145 -0
  40. data/doc/rdoc/files/lib/thin/statuses_rb.html +145 -0
  41. data/doc/rdoc/files/lib/thin/version_rb.html +145 -0
  42. data/doc/rdoc/files/lib/thin_rb.html +152 -0
  43. data/doc/rdoc/index.html +10 -0
  44. data/doc/rdoc/logo.gif +0 -0
  45. data/doc/rdoc/rdoc-style.css +55 -0
  46. data/example/config.ru +9 -0
  47. data/example/thin.god +72 -0
  48. data/ext/thin_parser/common.rl +54 -0
  49. data/ext/thin_parser/ext_help.h +14 -0
  50. data/ext/thin_parser/extconf.rb +6 -0
  51. data/ext/thin_parser/parser.c +1199 -0
  52. data/ext/thin_parser/parser.h +49 -0
  53. data/ext/thin_parser/parser.rl +143 -0
  54. data/ext/thin_parser/thin.c +424 -0
  55. data/lib/rack/adapter/rails.rb +155 -0
  56. data/lib/rack/handler/thin.rb +13 -0
  57. data/lib/thin.rb +36 -0
  58. data/lib/thin/cluster.rb +106 -0
  59. data/lib/thin/connection.rb +46 -0
  60. data/lib/thin/daemonizing.rb +112 -0
  61. data/lib/thin/headers.rb +37 -0
  62. data/lib/thin/logging.rb +23 -0
  63. data/lib/thin/request.rb +72 -0
  64. data/lib/thin/response.rb +48 -0
  65. data/lib/thin/server.rb +80 -0
  66. data/lib/thin/statuses.rb +43 -0
  67. data/lib/thin/version.rb +11 -0
  68. data/lib/thin_parser.so +0 -0
  69. data/spec/cluster_spec.rb +58 -0
  70. data/spec/daemonizing_spec.rb +93 -0
  71. data/spec/headers_spec.rb +35 -0
  72. data/spec/rack_rails_spec.rb +92 -0
  73. data/spec/rails_app/app/controllers/application.rb +10 -0
  74. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  75. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  76. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  77. data/spec/rails_app/config/boot.rb +109 -0
  78. data/spec/rails_app/config/environment.rb +64 -0
  79. data/spec/rails_app/config/environments/development.rb +18 -0
  80. data/spec/rails_app/config/environments/production.rb +19 -0
  81. data/spec/rails_app/config/environments/test.rb +22 -0
  82. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  83. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  84. data/spec/rails_app/config/routes.rb +35 -0
  85. data/spec/rails_app/public/404.html +30 -0
  86. data/spec/rails_app/public/422.html +30 -0
  87. data/spec/rails_app/public/500.html +30 -0
  88. data/spec/rails_app/public/dispatch.cgi +10 -0
  89. data/spec/rails_app/public/dispatch.fcgi +24 -0
  90. data/spec/rails_app/public/dispatch.rb +10 -0
  91. data/spec/rails_app/public/favicon.ico +0 -0
  92. data/spec/rails_app/public/images/rails.png +0 -0
  93. data/spec/rails_app/public/index.html +277 -0
  94. data/spec/rails_app/public/javascripts/application.js +2 -0
  95. data/spec/rails_app/public/javascripts/controls.js +963 -0
  96. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  97. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  98. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  99. data/spec/rails_app/public/robots.txt +5 -0
  100. data/spec/rails_app/script/about +3 -0
  101. data/spec/rails_app/script/console +3 -0
  102. data/spec/rails_app/script/destroy +3 -0
  103. data/spec/rails_app/script/generate +3 -0
  104. data/spec/rails_app/script/performance/benchmarker +3 -0
  105. data/spec/rails_app/script/performance/profiler +3 -0
  106. data/spec/rails_app/script/performance/request +3 -0
  107. data/spec/rails_app/script/plugin +3 -0
  108. data/spec/rails_app/script/process/inspector +3 -0
  109. data/spec/rails_app/script/process/reaper +3 -0
  110. data/spec/rails_app/script/process/spawner +3 -0
  111. data/spec/rails_app/script/runner +3 -0
  112. data/spec/rails_app/script/server +3 -0
  113. data/spec/request_spec.rb +258 -0
  114. data/spec/response_spec.rb +56 -0
  115. data/spec/server_spec.rb +75 -0
  116. data/spec/spec_helper.rb +127 -0
  117. metadata +219 -0
@@ -0,0 +1,5 @@
1
+ # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2
+ #
3
+ # To ban all spiders from the entire site uncomment the next two lines:
4
+ # User-Agent: *
5
+ # Disallow: /
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/about'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/console'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/destroy'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/generate'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/performance/benchmarker'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/performance/profiler'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/performance/request'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/plugin'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/inspector'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/reaper'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../../config/boot'
3
+ require 'commands/process/spawner'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/runner'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/server'
@@ -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
@@ -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