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.

Files changed (137) hide show
  1. data/CHANGELOG +263 -0
  2. data/COPYING +18 -0
  3. data/README +69 -0
  4. data/Rakefile +36 -0
  5. data/benchmark/abc +51 -0
  6. data/benchmark/benchmarker.rb +80 -0
  7. data/benchmark/runner +82 -0
  8. data/bin/thin +6 -0
  9. data/example/adapter.rb +32 -0
  10. data/example/async_app.ru +126 -0
  11. data/example/async_chat.ru +247 -0
  12. data/example/async_tailer.ru +100 -0
  13. data/example/config.ru +22 -0
  14. data/example/monit_sockets +20 -0
  15. data/example/monit_unixsock +20 -0
  16. data/example/myapp.rb +1 -0
  17. data/example/ramaze.ru +12 -0
  18. data/example/thin.god +80 -0
  19. data/example/thin_solaris_smf.erb +36 -0
  20. data/example/thin_solaris_smf.readme.txt +150 -0
  21. data/example/vlad.rake +64 -0
  22. data/ext/thin_parser/common.rl +55 -0
  23. data/ext/thin_parser/ext_help.h +14 -0
  24. data/ext/thin_parser/extconf.rb +6 -0
  25. data/ext/thin_parser/parser.c +452 -0
  26. data/ext/thin_parser/parser.h +49 -0
  27. data/ext/thin_parser/parser.rl +157 -0
  28. data/ext/thin_parser/thin.c +433 -0
  29. data/lib/rack/adapter/loader.rb +79 -0
  30. data/lib/rack/adapter/rails.rb +181 -0
  31. data/lib/thin.rb +46 -0
  32. data/lib/thin/backends/base.rb +141 -0
  33. data/lib/thin/backends/swiftiply_client.rb +56 -0
  34. data/lib/thin/backends/tcp_server.rb +29 -0
  35. data/lib/thin/backends/unix_server.rb +51 -0
  36. data/lib/thin/command.rb +53 -0
  37. data/lib/thin/connection.rb +222 -0
  38. data/lib/thin/controllers/cluster.rb +127 -0
  39. data/lib/thin/controllers/controller.rb +183 -0
  40. data/lib/thin/controllers/service.rb +75 -0
  41. data/lib/thin/controllers/service.sh.erb +39 -0
  42. data/lib/thin/daemonizing.rb +174 -0
  43. data/lib/thin/headers.rb +39 -0
  44. data/lib/thin/logging.rb +54 -0
  45. data/lib/thin/request.rb +153 -0
  46. data/lib/thin/response.rb +101 -0
  47. data/lib/thin/runner.rb +209 -0
  48. data/lib/thin/server.rb +247 -0
  49. data/lib/thin/stats.html.erb +216 -0
  50. data/lib/thin/stats.rb +52 -0
  51. data/lib/thin/statuses.rb +43 -0
  52. data/lib/thin/version.rb +32 -0
  53. data/lib/thin_parser.so +0 -0
  54. data/spec/backends/swiftiply_client_spec.rb +66 -0
  55. data/spec/backends/tcp_server_spec.rb +33 -0
  56. data/spec/backends/unix_server_spec.rb +37 -0
  57. data/spec/command_spec.rb +25 -0
  58. data/spec/configs/cluster.yml +9 -0
  59. data/spec/configs/single.yml +9 -0
  60. data/spec/connection_spec.rb +106 -0
  61. data/spec/controllers/cluster_spec.rb +235 -0
  62. data/spec/controllers/controller_spec.rb +129 -0
  63. data/spec/controllers/service_spec.rb +50 -0
  64. data/spec/daemonizing_spec.rb +192 -0
  65. data/spec/headers_spec.rb +40 -0
  66. data/spec/logging_spec.rb +46 -0
  67. data/spec/perf/request_perf_spec.rb +50 -0
  68. data/spec/perf/response_perf_spec.rb +19 -0
  69. data/spec/perf/server_perf_spec.rb +39 -0
  70. data/spec/rack/loader_spec.rb +29 -0
  71. data/spec/rack/rails_adapter_spec.rb +106 -0
  72. data/spec/rails_app/app/controllers/application.rb +10 -0
  73. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  74. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  75. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  76. data/spec/rails_app/config/boot.rb +109 -0
  77. data/spec/rails_app/config/environment.rb +64 -0
  78. data/spec/rails_app/config/environments/development.rb +18 -0
  79. data/spec/rails_app/config/environments/production.rb +19 -0
  80. data/spec/rails_app/config/environments/test.rb +22 -0
  81. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  82. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  83. data/spec/rails_app/config/routes.rb +35 -0
  84. data/spec/rails_app/public/404.html +30 -0
  85. data/spec/rails_app/public/422.html +30 -0
  86. data/spec/rails_app/public/500.html +30 -0
  87. data/spec/rails_app/public/dispatch.cgi +10 -0
  88. data/spec/rails_app/public/dispatch.fcgi +24 -0
  89. data/spec/rails_app/public/dispatch.rb +10 -0
  90. data/spec/rails_app/public/favicon.ico +0 -0
  91. data/spec/rails_app/public/images/rails.png +0 -0
  92. data/spec/rails_app/public/index.html +277 -0
  93. data/spec/rails_app/public/javascripts/application.js +2 -0
  94. data/spec/rails_app/public/javascripts/controls.js +963 -0
  95. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  96. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  97. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  98. data/spec/rails_app/public/robots.txt +5 -0
  99. data/spec/rails_app/script/about +3 -0
  100. data/spec/rails_app/script/console +3 -0
  101. data/spec/rails_app/script/destroy +3 -0
  102. data/spec/rails_app/script/generate +3 -0
  103. data/spec/rails_app/script/performance/benchmarker +3 -0
  104. data/spec/rails_app/script/performance/profiler +3 -0
  105. data/spec/rails_app/script/performance/request +3 -0
  106. data/spec/rails_app/script/plugin +3 -0
  107. data/spec/rails_app/script/process/inspector +3 -0
  108. data/spec/rails_app/script/process/reaper +3 -0
  109. data/spec/rails_app/script/process/spawner +3 -0
  110. data/spec/rails_app/script/runner +3 -0
  111. data/spec/rails_app/script/server +3 -0
  112. data/spec/request/mongrel_spec.rb +39 -0
  113. data/spec/request/parser_spec.rb +215 -0
  114. data/spec/request/persistent_spec.rb +35 -0
  115. data/spec/request/processing_spec.rb +45 -0
  116. data/spec/response_spec.rb +91 -0
  117. data/spec/runner_spec.rb +168 -0
  118. data/spec/server/builder_spec.rb +44 -0
  119. data/spec/server/pipelining_spec.rb +110 -0
  120. data/spec/server/robustness_spec.rb +34 -0
  121. data/spec/server/stopping_spec.rb +55 -0
  122. data/spec/server/swiftiply.yml +6 -0
  123. data/spec/server/swiftiply_spec.rb +32 -0
  124. data/spec/server/tcp_spec.rb +57 -0
  125. data/spec/server/threaded_spec.rb +27 -0
  126. data/spec/server/unix_socket_spec.rb +26 -0
  127. data/spec/server_spec.rb +96 -0
  128. data/spec/spec_helper.rb +219 -0
  129. data/tasks/announce.rake +22 -0
  130. data/tasks/deploy.rake +13 -0
  131. data/tasks/email.erb +30 -0
  132. data/tasks/gem.rake +74 -0
  133. data/tasks/rdoc.rake +25 -0
  134. data/tasks/site.rake +15 -0
  135. data/tasks/spec.rake +49 -0
  136. data/tasks/stats.rake +28 -0
  137. metadata +246 -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,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