grockit-thin 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. data/CHANGELOG +220 -0
  2. data/COMMITTERS +3 -0
  3. data/COPYING +18 -0
  4. data/README +77 -0
  5. data/Rakefile +13 -0
  6. data/benchmark/abc +51 -0
  7. data/benchmark/benchmarker.rb +80 -0
  8. data/benchmark/runner +79 -0
  9. data/bin/thin +6 -0
  10. data/example/adapter.rb +32 -0
  11. data/example/config.ru +23 -0
  12. data/example/monit_sockets +20 -0
  13. data/example/monit_unixsock +20 -0
  14. data/example/myapp.rb +1 -0
  15. data/example/ramaze.ru +12 -0
  16. data/example/thin.god +80 -0
  17. data/example/thin_solaris_smf.erb +36 -0
  18. data/example/thin_solaris_smf.readme.txt +150 -0
  19. data/example/vlad.rake +61 -0
  20. data/ext/thin_parser/common.rl +55 -0
  21. data/ext/thin_parser/ext_help.h +14 -0
  22. data/ext/thin_parser/extconf.rb +6 -0
  23. data/ext/thin_parser/parser.c +1218 -0
  24. data/ext/thin_parser/parser.h +49 -0
  25. data/ext/thin_parser/parser.rl +159 -0
  26. data/ext/thin_parser/thin.c +433 -0
  27. data/lib/rack/adapter/loader.rb +78 -0
  28. data/lib/rack/adapter/rails.rb +167 -0
  29. data/lib/rack/handler/thin.rb +18 -0
  30. data/lib/thin.rb +49 -0
  31. data/lib/thin/backends/base.rb +135 -0
  32. data/lib/thin/backends/swiftiply_client.rb +56 -0
  33. data/lib/thin/backends/tcp_server.rb +29 -0
  34. data/lib/thin/backends/unix_server.rb +51 -0
  35. data/lib/thin/command.rb +52 -0
  36. data/lib/thin/connection.rb +178 -0
  37. data/lib/thin/controllers/cluster.rb +121 -0
  38. data/lib/thin/controllers/controller.rb +182 -0
  39. data/lib/thin/controllers/service.rb +75 -0
  40. data/lib/thin/controllers/service.sh.erb +39 -0
  41. data/lib/thin/daemonizing.rb +163 -0
  42. data/lib/thin/headers.rb +31 -0
  43. data/lib/thin/logging.rb +54 -0
  44. data/lib/thin/request.rb +144 -0
  45. data/lib/thin/response.rb +96 -0
  46. data/lib/thin/runner.rb +208 -0
  47. data/lib/thin/server.rb +241 -0
  48. data/lib/thin/stats.html.erb +216 -0
  49. data/lib/thin/stats.rb +52 -0
  50. data/lib/thin/statuses.rb +43 -0
  51. data/lib/thin/version.rb +32 -0
  52. data/spec/backends/swiftiply_client_spec.rb +66 -0
  53. data/spec/backends/tcp_server_spec.rb +33 -0
  54. data/spec/backends/unix_server_spec.rb +37 -0
  55. data/spec/command_spec.rb +20 -0
  56. data/spec/configs/cluster.yml +9 -0
  57. data/spec/configs/single.yml +9 -0
  58. data/spec/connection_spec.rb +105 -0
  59. data/spec/controllers/cluster_spec.rb +179 -0
  60. data/spec/controllers/controller_spec.rb +121 -0
  61. data/spec/controllers/service_spec.rb +50 -0
  62. data/spec/daemonizing_spec.rb +192 -0
  63. data/spec/headers_spec.rb +29 -0
  64. data/spec/logging_spec.rb +46 -0
  65. data/spec/perf/request_perf_spec.rb +50 -0
  66. data/spec/perf/response_perf_spec.rb +19 -0
  67. data/spec/perf/server_perf_spec.rb +39 -0
  68. data/spec/rack/loader_spec.rb +29 -0
  69. data/spec/rack/rails_adapter_spec.rb +106 -0
  70. data/spec/rails_app/app/controllers/application.rb +10 -0
  71. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  72. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  73. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  74. data/spec/rails_app/config/boot.rb +109 -0
  75. data/spec/rails_app/config/environment.rb +64 -0
  76. data/spec/rails_app/config/environments/development.rb +18 -0
  77. data/spec/rails_app/config/environments/production.rb +19 -0
  78. data/spec/rails_app/config/environments/test.rb +22 -0
  79. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  80. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  81. data/spec/rails_app/config/routes.rb +35 -0
  82. data/spec/rails_app/public/404.html +30 -0
  83. data/spec/rails_app/public/422.html +30 -0
  84. data/spec/rails_app/public/500.html +30 -0
  85. data/spec/rails_app/public/dispatch.cgi +10 -0
  86. data/spec/rails_app/public/dispatch.fcgi +24 -0
  87. data/spec/rails_app/public/dispatch.rb +10 -0
  88. data/spec/rails_app/public/favicon.ico +0 -0
  89. data/spec/rails_app/public/images/rails.png +0 -0
  90. data/spec/rails_app/public/index.html +277 -0
  91. data/spec/rails_app/public/javascripts/application.js +2 -0
  92. data/spec/rails_app/public/javascripts/controls.js +963 -0
  93. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  94. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  95. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  96. data/spec/rails_app/public/robots.txt +5 -0
  97. data/spec/rails_app/script/about +3 -0
  98. data/spec/rails_app/script/console +3 -0
  99. data/spec/rails_app/script/destroy +3 -0
  100. data/spec/rails_app/script/generate +3 -0
  101. data/spec/rails_app/script/performance/benchmarker +3 -0
  102. data/spec/rails_app/script/performance/profiler +3 -0
  103. data/spec/rails_app/script/performance/request +3 -0
  104. data/spec/rails_app/script/plugin +3 -0
  105. data/spec/rails_app/script/process/inspector +3 -0
  106. data/spec/rails_app/script/process/reaper +3 -0
  107. data/spec/rails_app/script/process/spawner +3 -0
  108. data/spec/rails_app/script/runner +3 -0
  109. data/spec/rails_app/script/server +3 -0
  110. data/spec/request/mongrel_spec.rb +39 -0
  111. data/spec/request/parser_spec.rb +191 -0
  112. data/spec/request/persistent_spec.rb +35 -0
  113. data/spec/request/processing_spec.rb +45 -0
  114. data/spec/response_spec.rb +76 -0
  115. data/spec/runner_spec.rb +167 -0
  116. data/spec/server/builder_spec.rb +44 -0
  117. data/spec/server/pipelining_spec.rb +109 -0
  118. data/spec/server/robustness_spec.rb +34 -0
  119. data/spec/server/stopping_spec.rb +45 -0
  120. data/spec/server/swiftiply.yml +6 -0
  121. data/spec/server/swiftiply_spec.rb +32 -0
  122. data/spec/server/tcp_spec.rb +57 -0
  123. data/spec/server/threaded_spec.rb +27 -0
  124. data/spec/server/unix_socket_spec.rb +26 -0
  125. data/spec/server_spec.rb +96 -0
  126. data/spec/spec_helper.rb +219 -0
  127. data/tasks/announce.rake +22 -0
  128. data/tasks/deploy.rake +16 -0
  129. data/tasks/email.erb +30 -0
  130. data/tasks/ext.rake +42 -0
  131. data/tasks/gem.rake +102 -0
  132. data/tasks/rdoc.rake +25 -0
  133. data/tasks/site.rake +15 -0
  134. data/tasks/spec.rake +48 -0
  135. data/tasks/stats.rake +28 -0
  136. metadata +240 -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,191 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Request, 'parser' do
4
+ it 'should include basic headers' do
5
+ request = R("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
6
+ request.env['SERVER_PROTOCOL'].should == 'HTTP/1.1'
7
+ request.env['REQUEST_PATH'].should == '/'
8
+ request.env['HTTP_VERSION'].should == 'HTTP/1.1'
9
+ request.env['REQUEST_URI'].should == '/'
10
+ request.env['GATEWAY_INTERFACE'].should == 'CGI/1.2'
11
+ request.env['REQUEST_METHOD'].should == 'GET'
12
+ request.env["rack.url_scheme"].should == 'http'
13
+ request.env['FRAGMENT'].to_s.should be_empty
14
+ request.env['QUERY_STRING'].to_s.should be_empty
15
+
16
+ request.should validate_with_lint
17
+ end
18
+
19
+ it 'should not prepend HTTP_ to Content-Type and Content-Length' do
20
+ request = R("POST / HTTP/1.1\r\nHost: localhost\r\nContent-Type: text/html\r\nContent-Length: 2\r\n\r\naa")
21
+ request.env.keys.should_not include('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH')
22
+ request.env.keys.should include('CONTENT_TYPE', 'CONTENT_LENGTH')
23
+
24
+ request.should validate_with_lint
25
+ end
26
+
27
+ it 'should raise error on invalid request line' do
28
+ proc { R("GET / SsUTF/1.1") }.should raise_error(InvalidRequest)
29
+ proc { R("GET / HTTP/1.1yousmelllikecheeze") }.should raise_error(InvalidRequest)
30
+ end
31
+
32
+ it 'should support fragment in uri' do
33
+ request = R("GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\nHost: localhost\r\n\r\n")
34
+
35
+ request.env['REQUEST_URI'].should == '/forums/1/topics/2375?page=1'
36
+ request.env['PATH_INFO'].should == '/forums/1/topics/2375'
37
+ request.env['QUERY_STRING'].should == 'page=1'
38
+ request.env['FRAGMENT'].should == 'posts-17408'
39
+
40
+ request.should validate_with_lint
41
+ end
42
+
43
+ it 'should parse path with query string' do
44
+ request = R("GET /index.html?234235 HTTP/1.1\r\nHost: localhost\r\n\r\n")
45
+ request.env['REQUEST_PATH'].should == '/index.html'
46
+ request.env['QUERY_STRING'].should == '234235'
47
+ request.env['FRAGMENT'].should be_nil
48
+
49
+ request.should validate_with_lint
50
+ end
51
+
52
+ it 'should parse headers from GET request' do
53
+ request = R(<<-EOS, true)
54
+ GET / HTTP/1.1
55
+ Host: localhost:3000
56
+ 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
57
+ Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
58
+ Accept-Language: en-us,en;q=0.5
59
+ Accept-Encoding: gzip,deflate
60
+ Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
61
+ Cookie: mium=7
62
+ Keep-Alive: 300
63
+ Connection: keep-alive
64
+
65
+ EOS
66
+ request.env['HTTP_HOST'].should == 'localhost:3000'
67
+ request.env['SERVER_NAME'].should == 'localhost'
68
+ request.env['SERVER_PORT'].should == '3000'
69
+ request.env['HTTP_COOKIE'].should == 'mium=7'
70
+
71
+ request.should validate_with_lint
72
+ end
73
+
74
+ it 'should parse POST request with data' do
75
+ request = R(<<-EOS.chomp, true)
76
+ POST /postit 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
+ Keep-Alive: 300
84
+ Connection: keep-alive
85
+ Content-Type: text/html
86
+ Content-Length: 37
87
+
88
+ name=marc&email=macournoyer@gmail.com
89
+ EOS
90
+
91
+ request.env['REQUEST_METHOD'].should == 'POST'
92
+ request.env['REQUEST_URI'].should == '/postit'
93
+ request.env['CONTENT_TYPE'].should == 'text/html'
94
+ request.env['CONTENT_LENGTH'].should == '37'
95
+ 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'
96
+ request.env['HTTP_ACCEPT_LANGUAGE'].should == 'en-us,en;q=0.5'
97
+
98
+ request.body.rewind
99
+ request.body.read.should == 'name=marc&email=macournoyer@gmail.com'
100
+ request.body.class.should == StringIO
101
+
102
+ request.should validate_with_lint
103
+ end
104
+
105
+ it 'should not fuck up on stupid fucked IE6 headers' do
106
+ body = <<-EOS
107
+ POST /codes/58-tracking-file-downloads-automatically-in-google-analytics-with-prototype/refactors HTTP/1.0
108
+ X-Real-IP: 62.24.71.95
109
+ X-Forwarded-For: 62.24.71.95
110
+ Host: refactormycode.com
111
+ Connection: close
112
+ TE: deflate,gzip;q=0.3
113
+ Accept: */*
114
+ Range: bytes=0-499999
115
+ Referer: http://refactormycode.com/codes/58-tracking-file-downloads-automatically-in-google-analytics-with-prototype
116
+ User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
117
+ Content-Length: 1
118
+ Content-Type: application/x-www-form-urlencoded
119
+ Cookie: _refactormycode_session_id=a1b2n3jk4k5; flash=%7B%7D
120
+ Cookie2: $Version="1"
121
+
122
+ a
123
+ EOS
124
+ request = R(body, true)
125
+ request.env['HTTP_COOKIE2'].should == '$Version="1"'
126
+
127
+ request.should validate_with_lint
128
+ end
129
+
130
+ it 'shoud accept long query string' do
131
+ body = <<-EOS
132
+ 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
133
+ Host: localhost:3000
134
+
135
+ EOS
136
+ request = R(body, true)
137
+
138
+ 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'
139
+
140
+ request.should validate_with_lint
141
+ end
142
+
143
+ it 'should parse even with stupid Content-Length' do
144
+ body = <<-EOS.chomp
145
+ POST / HTTP/1.1
146
+ Host: localhost:3000
147
+ Content-Length: 300
148
+
149
+ aye
150
+ EOS
151
+ request = R(body, true)
152
+
153
+ request.body.rewind
154
+ request.body.read.should == 'aye'
155
+ end
156
+
157
+ it "should parse ie6 urls" do
158
+ %w(/some/random/path"
159
+ /some/random/path>
160
+ /some/random/path<
161
+ /we/love/you/ie6?q=<"">
162
+ /url?<="&>="
163
+ /mal"formed"?
164
+ ).each do |path|
165
+ parser = HttpParser.new
166
+ req = {}
167
+ sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
168
+ nread = parser.execute(req, sorta_safe, 0)
169
+
170
+ sorta_safe.size.should == nread
171
+ parser.should be_finished
172
+ parser.should_not have_error
173
+ end
174
+ end
175
+
176
+ xit "should parse absolute request URI" do
177
+ request = R(<<-EOS, true)
178
+ GET http://localhost:3000/hi HTTP/1.1
179
+ Host: localhost:3000
180
+
181
+ EOS
182
+ request.env['PATH_INFO'].should == '/hi'
183
+
184
+ request.should validate_with_lint
185
+ end
186
+
187
+
188
+ it "should fails on heders larger then MAX_HEADER" do
189
+ proc { R("GET / HTTP/1.1\r\nFoo: #{'X' * Request::MAX_HEADER}\r\n\r\n") }.should raise_error(InvalidRequest)
190
+ end
191
+ 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,76 @@
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 output body' do
48
+ @response.body = '<html></html>'
49
+
50
+ out = ''
51
+ @response.each { |l| out << l }
52
+ out.should include("\r\n\r\n<html></html>")
53
+ end
54
+
55
+ it "should not be persistent by default" do
56
+ @response.should_not be_persistent
57
+ end
58
+
59
+ it "should not be persistent when no Content-Length" do
60
+ @response = Response.new
61
+ @response.headers['Content-Type'] = 'text/html'
62
+ @response.body = ''
63
+
64
+ @response.persistent!
65
+ @response.should_not be_persistent
66
+ end
67
+
68
+ it "should be persistent when specified" do
69
+ @response.persistent!
70
+ @response.should be_persistent
71
+ end
72
+
73
+ it "should be closeable" do
74
+ @response.close
75
+ end
76
+ end