unicorn-fotopedia 0.99.1

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 (163) hide show
  1. data/.CHANGELOG.old +25 -0
  2. data/.document +19 -0
  3. data/.gitignore +21 -0
  4. data/.mailmap +26 -0
  5. data/CONTRIBUTORS +32 -0
  6. data/COPYING +339 -0
  7. data/DESIGN +105 -0
  8. data/Documentation/.gitignore +5 -0
  9. data/Documentation/GNUmakefile +30 -0
  10. data/Documentation/unicorn.1.txt +171 -0
  11. data/Documentation/unicorn_rails.1.txt +172 -0
  12. data/FAQ +52 -0
  13. data/GIT-VERSION-GEN +40 -0
  14. data/GNUmakefile +292 -0
  15. data/HACKING +116 -0
  16. data/ISSUES +36 -0
  17. data/KNOWN_ISSUES +50 -0
  18. data/LICENSE +55 -0
  19. data/PHILOSOPHY +145 -0
  20. data/README +149 -0
  21. data/Rakefile +191 -0
  22. data/SIGNALS +109 -0
  23. data/Sandbox +78 -0
  24. data/TODO +5 -0
  25. data/TUNING +70 -0
  26. data/bin/unicorn +126 -0
  27. data/bin/unicorn_rails +203 -0
  28. data/examples/big_app_gc.rb +33 -0
  29. data/examples/echo.ru +27 -0
  30. data/examples/git.ru +13 -0
  31. data/examples/init.sh +58 -0
  32. data/examples/logger_mp_safe.rb +25 -0
  33. data/examples/nginx.conf +139 -0
  34. data/examples/unicorn.conf.rb +78 -0
  35. data/ext/unicorn_http/CFLAGS +13 -0
  36. data/ext/unicorn_http/c_util.h +124 -0
  37. data/ext/unicorn_http/common_field_optimization.h +111 -0
  38. data/ext/unicorn_http/ext_help.h +77 -0
  39. data/ext/unicorn_http/extconf.rb +14 -0
  40. data/ext/unicorn_http/global_variables.h +89 -0
  41. data/ext/unicorn_http/unicorn_http.rl +714 -0
  42. data/ext/unicorn_http/unicorn_http_common.rl +75 -0
  43. data/lib/unicorn.rb +847 -0
  44. data/lib/unicorn/app/exec_cgi.rb +150 -0
  45. data/lib/unicorn/app/inetd.rb +109 -0
  46. data/lib/unicorn/app/old_rails.rb +33 -0
  47. data/lib/unicorn/app/old_rails/static.rb +58 -0
  48. data/lib/unicorn/cgi_wrapper.rb +145 -0
  49. data/lib/unicorn/configurator.rb +421 -0
  50. data/lib/unicorn/const.rb +34 -0
  51. data/lib/unicorn/http_request.rb +72 -0
  52. data/lib/unicorn/http_response.rb +75 -0
  53. data/lib/unicorn/launcher.rb +65 -0
  54. data/lib/unicorn/oob_gc.rb +58 -0
  55. data/lib/unicorn/socket_helper.rb +152 -0
  56. data/lib/unicorn/tee_input.rb +217 -0
  57. data/lib/unicorn/util.rb +90 -0
  58. data/local.mk.sample +62 -0
  59. data/setup.rb +1586 -0
  60. data/t/.gitignore +2 -0
  61. data/t/GNUmakefile +67 -0
  62. data/t/README +42 -0
  63. data/t/bin/content-md5-put +36 -0
  64. data/t/bin/sha1sum.rb +23 -0
  65. data/t/bin/unused_listen +40 -0
  66. data/t/bin/utee +12 -0
  67. data/t/env.ru +3 -0
  68. data/t/my-tap-lib.sh +200 -0
  69. data/t/t0000-http-basic.sh +50 -0
  70. data/t/t0001-reload-bad-config.sh +52 -0
  71. data/t/t0002-config-conflict.sh +49 -0
  72. data/t/test-lib.sh +100 -0
  73. data/test/aggregate.rb +15 -0
  74. data/test/benchmark/README +50 -0
  75. data/test/benchmark/dd.ru +18 -0
  76. data/test/exec/README +5 -0
  77. data/test/exec/test_exec.rb +1038 -0
  78. data/test/rails/app-1.2.3/.gitignore +2 -0
  79. data/test/rails/app-1.2.3/Rakefile +7 -0
  80. data/test/rails/app-1.2.3/app/controllers/application.rb +6 -0
  81. data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +36 -0
  82. data/test/rails/app-1.2.3/app/helpers/application_helper.rb +4 -0
  83. data/test/rails/app-1.2.3/config/boot.rb +11 -0
  84. data/test/rails/app-1.2.3/config/database.yml +12 -0
  85. data/test/rails/app-1.2.3/config/environment.rb +13 -0
  86. data/test/rails/app-1.2.3/config/environments/development.rb +9 -0
  87. data/test/rails/app-1.2.3/config/environments/production.rb +5 -0
  88. data/test/rails/app-1.2.3/config/routes.rb +6 -0
  89. data/test/rails/app-1.2.3/db/.gitignore +0 -0
  90. data/test/rails/app-1.2.3/public/404.html +1 -0
  91. data/test/rails/app-1.2.3/public/500.html +1 -0
  92. data/test/rails/app-2.0.2/.gitignore +2 -0
  93. data/test/rails/app-2.0.2/Rakefile +7 -0
  94. data/test/rails/app-2.0.2/app/controllers/application.rb +4 -0
  95. data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +36 -0
  96. data/test/rails/app-2.0.2/app/helpers/application_helper.rb +4 -0
  97. data/test/rails/app-2.0.2/config/boot.rb +11 -0
  98. data/test/rails/app-2.0.2/config/database.yml +12 -0
  99. data/test/rails/app-2.0.2/config/environment.rb +17 -0
  100. data/test/rails/app-2.0.2/config/environments/development.rb +8 -0
  101. data/test/rails/app-2.0.2/config/environments/production.rb +5 -0
  102. data/test/rails/app-2.0.2/config/routes.rb +6 -0
  103. data/test/rails/app-2.0.2/db/.gitignore +0 -0
  104. data/test/rails/app-2.0.2/public/404.html +1 -0
  105. data/test/rails/app-2.0.2/public/500.html +1 -0
  106. data/test/rails/app-2.1.2/.gitignore +2 -0
  107. data/test/rails/app-2.1.2/Rakefile +7 -0
  108. data/test/rails/app-2.1.2/app/controllers/application.rb +4 -0
  109. data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +36 -0
  110. data/test/rails/app-2.1.2/app/helpers/application_helper.rb +4 -0
  111. data/test/rails/app-2.1.2/config/boot.rb +111 -0
  112. data/test/rails/app-2.1.2/config/database.yml +12 -0
  113. data/test/rails/app-2.1.2/config/environment.rb +17 -0
  114. data/test/rails/app-2.1.2/config/environments/development.rb +7 -0
  115. data/test/rails/app-2.1.2/config/environments/production.rb +5 -0
  116. data/test/rails/app-2.1.2/config/routes.rb +6 -0
  117. data/test/rails/app-2.1.2/db/.gitignore +0 -0
  118. data/test/rails/app-2.1.2/public/404.html +1 -0
  119. data/test/rails/app-2.1.2/public/500.html +1 -0
  120. data/test/rails/app-2.2.2/.gitignore +2 -0
  121. data/test/rails/app-2.2.2/Rakefile +7 -0
  122. data/test/rails/app-2.2.2/app/controllers/application.rb +4 -0
  123. data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +36 -0
  124. data/test/rails/app-2.2.2/app/helpers/application_helper.rb +4 -0
  125. data/test/rails/app-2.2.2/config/boot.rb +111 -0
  126. data/test/rails/app-2.2.2/config/database.yml +12 -0
  127. data/test/rails/app-2.2.2/config/environment.rb +17 -0
  128. data/test/rails/app-2.2.2/config/environments/development.rb +7 -0
  129. data/test/rails/app-2.2.2/config/environments/production.rb +5 -0
  130. data/test/rails/app-2.2.2/config/routes.rb +6 -0
  131. data/test/rails/app-2.2.2/db/.gitignore +0 -0
  132. data/test/rails/app-2.2.2/public/404.html +1 -0
  133. data/test/rails/app-2.2.2/public/500.html +1 -0
  134. data/test/rails/app-2.3.5/.gitignore +2 -0
  135. data/test/rails/app-2.3.5/Rakefile +7 -0
  136. data/test/rails/app-2.3.5/app/controllers/application_controller.rb +5 -0
  137. data/test/rails/app-2.3.5/app/controllers/foo_controller.rb +36 -0
  138. data/test/rails/app-2.3.5/app/helpers/application_helper.rb +4 -0
  139. data/test/rails/app-2.3.5/config/boot.rb +109 -0
  140. data/test/rails/app-2.3.5/config/database.yml +12 -0
  141. data/test/rails/app-2.3.5/config/environment.rb +17 -0
  142. data/test/rails/app-2.3.5/config/environments/development.rb +7 -0
  143. data/test/rails/app-2.3.5/config/environments/production.rb +6 -0
  144. data/test/rails/app-2.3.5/config/routes.rb +6 -0
  145. data/test/rails/app-2.3.5/db/.gitignore +0 -0
  146. data/test/rails/app-2.3.5/public/404.html +1 -0
  147. data/test/rails/app-2.3.5/public/500.html +1 -0
  148. data/test/rails/app-2.3.5/public/x.txt +1 -0
  149. data/test/rails/test_rails.rb +280 -0
  150. data/test/test_helper.rb +301 -0
  151. data/test/unit/test_configurator.rb +150 -0
  152. data/test/unit/test_http_parser.rb +555 -0
  153. data/test/unit/test_http_parser_ng.rb +443 -0
  154. data/test/unit/test_request.rb +184 -0
  155. data/test/unit/test_response.rb +110 -0
  156. data/test/unit/test_server.rb +291 -0
  157. data/test/unit/test_signals.rb +206 -0
  158. data/test/unit/test_socket_helper.rb +147 -0
  159. data/test/unit/test_tee_input.rb +257 -0
  160. data/test/unit/test_upload.rb +298 -0
  161. data/test/unit/test_util.rb +96 -0
  162. data/unicorn.gemspec +52 -0
  163. metadata +283 -0
@@ -0,0 +1,443 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ require 'test/test_helper'
4
+ require 'digest/md5'
5
+
6
+ include Unicorn
7
+
8
+ class HttpParserNgTest < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @parser = HttpParser.new
12
+ end
13
+
14
+ def test_identity_byte_headers
15
+ req = {}
16
+ str = "PUT / HTTP/1.1\r\n"
17
+ str << "Content-Length: 123\r\n"
18
+ str << "\r"
19
+ hdr = ""
20
+ str.each_byte { |byte|
21
+ assert_nil @parser.headers(req, hdr << byte.chr)
22
+ }
23
+ hdr << "\n"
24
+ assert_equal req.object_id, @parser.headers(req, hdr).object_id
25
+ assert_equal '123', req['CONTENT_LENGTH']
26
+ assert_equal 0, hdr.size
27
+ assert ! @parser.keepalive?
28
+ assert @parser.headers?
29
+ assert 123, @parser.content_length
30
+ end
31
+
32
+ def test_identity_step_headers
33
+ req = {}
34
+ str = "PUT / HTTP/1.1\r\n"
35
+ assert ! @parser.headers(req, str)
36
+ str << "Content-Length: 123\r\n"
37
+ assert ! @parser.headers(req, str)
38
+ str << "\r\n"
39
+ assert_equal req.object_id, @parser.headers(req, str).object_id
40
+ assert_equal '123', req['CONTENT_LENGTH']
41
+ assert_equal 0, str.size
42
+ assert ! @parser.keepalive?
43
+ assert @parser.headers?
44
+ end
45
+
46
+ def test_identity_oneshot_header
47
+ req = {}
48
+ str = "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\n"
49
+ assert_equal req.object_id, @parser.headers(req, str).object_id
50
+ assert_equal '123', req['CONTENT_LENGTH']
51
+ assert_equal 0, str.size
52
+ assert ! @parser.keepalive?
53
+ end
54
+
55
+ def test_identity_oneshot_header_with_body
56
+ body = ('a' * 123).freeze
57
+ req = {}
58
+ str = "PUT / HTTP/1.1\r\n" \
59
+ "Content-Length: #{body.length}\r\n" \
60
+ "\r\n#{body}"
61
+ assert_equal req.object_id, @parser.headers(req, str).object_id
62
+ assert_equal '123', req['CONTENT_LENGTH']
63
+ assert_equal 123, str.size
64
+ assert_equal body, str
65
+ tmp = ''
66
+ assert_nil @parser.filter_body(tmp, str)
67
+ assert_equal 0, str.size
68
+ assert_equal tmp, body
69
+ assert_equal "", @parser.filter_body(tmp, str)
70
+ assert ! @parser.keepalive?
71
+ end
72
+
73
+ def test_identity_oneshot_header_with_body_partial
74
+ str = "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\na"
75
+ assert_equal Hash, @parser.headers({}, str).class
76
+ assert_equal 1, str.size
77
+ assert_equal 'a', str
78
+ tmp = ''
79
+ assert_nil @parser.filter_body(tmp, str)
80
+ assert_equal "", str
81
+ assert_equal "a", tmp
82
+ str << ' ' * 122
83
+ rv = @parser.filter_body(tmp, str)
84
+ assert_equal 122, tmp.size
85
+ assert_nil rv
86
+ assert_equal "", str
87
+ assert_equal str.object_id, @parser.filter_body(tmp, str).object_id
88
+ assert ! @parser.keepalive?
89
+ end
90
+
91
+ def test_identity_oneshot_header_with_body_slop
92
+ str = "PUT / HTTP/1.1\r\nContent-Length: 1\r\n\r\naG"
93
+ assert_equal Hash, @parser.headers({}, str).class
94
+ assert_equal 2, str.size
95
+ assert_equal 'aG', str
96
+ tmp = ''
97
+ assert_nil @parser.filter_body(tmp, str)
98
+ assert_equal "G", str
99
+ assert_equal "G", @parser.filter_body(tmp, str)
100
+ assert_equal 1, tmp.size
101
+ assert_equal "a", tmp
102
+ assert ! @parser.keepalive?
103
+ end
104
+
105
+ def test_chunked
106
+ str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
107
+ req = {}
108
+ assert_equal req, @parser.headers(req, str)
109
+ assert_equal 0, str.size
110
+ tmp = ""
111
+ assert_nil @parser.filter_body(tmp, "6")
112
+ assert_equal 0, tmp.size
113
+ assert_nil @parser.filter_body(tmp, rv = "\r\n")
114
+ assert_equal 0, rv.size
115
+ assert_equal 0, tmp.size
116
+ tmp = ""
117
+ assert_nil @parser.filter_body(tmp, "..")
118
+ assert_equal "..", tmp
119
+ assert_nil @parser.filter_body(tmp, "abcd\r\n0\r\n")
120
+ assert_equal "abcd", tmp
121
+ rv = "PUT"
122
+ assert_equal rv.object_id, @parser.filter_body(tmp, rv).object_id
123
+ assert_equal "PUT", rv
124
+ assert ! @parser.keepalive?
125
+ end
126
+
127
+ def test_two_chunks
128
+ str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
129
+ req = {}
130
+ assert_equal req, @parser.headers(req, str)
131
+ assert_equal 0, str.size
132
+ tmp = ""
133
+ assert_nil @parser.filter_body(tmp, "6")
134
+ assert_equal 0, tmp.size
135
+ assert_nil @parser.filter_body(tmp, rv = "\r\n")
136
+ assert_equal "", rv
137
+ assert_equal 0, tmp.size
138
+ tmp = ""
139
+ assert_nil @parser.filter_body(tmp, "..")
140
+ assert_equal 2, tmp.size
141
+ assert_equal "..", tmp
142
+ assert_nil @parser.filter_body(tmp, "abcd\r\n1")
143
+ assert_equal "abcd", tmp
144
+ assert_nil @parser.filter_body(tmp, "\r")
145
+ assert_equal "", tmp
146
+ assert_nil @parser.filter_body(tmp, "\n")
147
+ assert_equal "", tmp
148
+ assert_nil @parser.filter_body(tmp, "z")
149
+ assert_equal "z", tmp
150
+ assert_nil @parser.filter_body(tmp, "\r\n")
151
+ assert_nil @parser.filter_body(tmp, "0")
152
+ assert_nil @parser.filter_body(tmp, "\r")
153
+ rv = @parser.filter_body(tmp, buf = "\nGET")
154
+ assert_equal "GET", rv
155
+ assert_equal buf.object_id, rv.object_id
156
+ assert ! @parser.keepalive?
157
+ end
158
+
159
+ def test_big_chunk
160
+ str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
161
+ "4000\r\nabcd"
162
+ req = {}
163
+ assert_equal req, @parser.headers(req, str)
164
+ tmp = ''
165
+ assert_nil @parser.filter_body(tmp, str)
166
+ assert_equal '', str
167
+ str = ' ' * 16300
168
+ assert_nil @parser.filter_body(tmp, str)
169
+ assert_equal '', str
170
+ str = ' ' * 80
171
+ assert_nil @parser.filter_body(tmp, str)
172
+ assert_equal '', str
173
+ assert ! @parser.body_eof?
174
+ assert_equal "", @parser.filter_body(tmp, "\r\n0\r\n")
175
+ assert_equal "", tmp
176
+ assert @parser.body_eof?
177
+ assert_equal req, @parser.trailers(req, moo = "\r\n")
178
+ assert_equal "", moo
179
+ assert @parser.body_eof?
180
+ assert ! @parser.keepalive?
181
+ end
182
+
183
+ def test_two_chunks_oneshot
184
+ str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
185
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
186
+ req = {}
187
+ assert_equal req, @parser.headers(req, str)
188
+ tmp = ''
189
+ assert_nil @parser.filter_body(tmp, str)
190
+ assert_equal 'a..', tmp
191
+ rv = @parser.filter_body(tmp, str)
192
+ assert_equal rv.object_id, str.object_id
193
+ assert ! @parser.keepalive?
194
+ end
195
+
196
+ def test_chunks_bytewise
197
+ chunked = "10\r\nabcdefghijklmnop\r\n11\r\n0123456789abcdefg\r\n0\r\n"
198
+ str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n#{chunked}"
199
+ req = {}
200
+ assert_equal req, @parser.headers(req, str)
201
+ assert_equal chunked, str
202
+ tmp = ''
203
+ buf = ''
204
+ body = ''
205
+ str = str[0..-2]
206
+ str.each_byte { |byte|
207
+ assert_nil @parser.filter_body(tmp, buf << byte.chr)
208
+ body << tmp
209
+ }
210
+ assert_equal 'abcdefghijklmnop0123456789abcdefg', body
211
+ rv = @parser.filter_body(tmp, buf << "\n")
212
+ assert_equal rv.object_id, buf.object_id
213
+ assert ! @parser.keepalive?
214
+ end
215
+
216
+ def test_trailers
217
+ str = "PUT / HTTP/1.1\r\n" \
218
+ "Trailer: Content-MD5\r\n" \
219
+ "transfer-Encoding: chunked\r\n\r\n" \
220
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
221
+ req = {}
222
+ assert_equal req, @parser.headers(req, str)
223
+ assert_equal 'Content-MD5', req['HTTP_TRAILER']
224
+ assert_nil req['HTTP_CONTENT_MD5']
225
+ tmp = ''
226
+ assert_nil @parser.filter_body(tmp, str)
227
+ assert_equal 'a..', tmp
228
+ md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
229
+ rv = @parser.filter_body(tmp, str)
230
+ assert_equal rv.object_id, str.object_id
231
+ assert_equal '', str
232
+ md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
233
+ str << md5_hdr
234
+ assert_nil @parser.trailers(req, str)
235
+ assert_equal md5_b64, req['HTTP_CONTENT_MD5']
236
+ assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
237
+ assert_nil @parser.trailers(req, str << "\r")
238
+ assert_equal req, @parser.trailers(req, str << "\nGET / ")
239
+ assert_equal "GET / ", str
240
+ assert ! @parser.keepalive?
241
+ end
242
+
243
+ def test_trailers_slowly
244
+ str = "PUT / HTTP/1.1\r\n" \
245
+ "Trailer: Content-MD5\r\n" \
246
+ "transfer-Encoding: chunked\r\n\r\n" \
247
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
248
+ req = {}
249
+ assert_equal req, @parser.headers(req, str)
250
+ assert_equal 'Content-MD5', req['HTTP_TRAILER']
251
+ assert_nil req['HTTP_CONTENT_MD5']
252
+ tmp = ''
253
+ assert_nil @parser.filter_body(tmp, str)
254
+ assert_equal 'a..', tmp
255
+ md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
256
+ rv = @parser.filter_body(tmp, str)
257
+ assert_equal rv.object_id, str.object_id
258
+ assert_equal '', str
259
+ assert_nil @parser.trailers(req, str)
260
+ md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
261
+ md5_hdr.each_byte { |byte|
262
+ str << byte.chr
263
+ assert_nil @parser.trailers(req, str)
264
+ }
265
+ assert_equal md5_b64, req['HTTP_CONTENT_MD5']
266
+ assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
267
+ assert_nil @parser.trailers(req, str << "\r")
268
+ assert_equal req, @parser.trailers(req, str << "\n")
269
+ end
270
+
271
+ def test_max_chunk
272
+ str = "PUT / HTTP/1.1\r\n" \
273
+ "transfer-Encoding: chunked\r\n\r\n" \
274
+ "#{HttpParser::CHUNK_MAX.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
275
+ req = {}
276
+ assert_equal req, @parser.headers(req, str)
277
+ assert_nil @parser.content_length
278
+ assert_nothing_raised { @parser.filter_body('', str) }
279
+ assert ! @parser.keepalive?
280
+ end
281
+
282
+ def test_max_body
283
+ n = HttpParser::LENGTH_MAX
284
+ str = "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
285
+ req = {}
286
+ assert_nothing_raised { @parser.headers(req, str) }
287
+ assert_equal n, req['CONTENT_LENGTH'].to_i
288
+ assert ! @parser.keepalive?
289
+ end
290
+
291
+ def test_overflow_chunk
292
+ n = HttpParser::CHUNK_MAX + 1
293
+ str = "PUT / HTTP/1.1\r\n" \
294
+ "transfer-Encoding: chunked\r\n\r\n" \
295
+ "#{n.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
296
+ req = {}
297
+ assert_equal req, @parser.headers(req, str)
298
+ assert_nil @parser.content_length
299
+ assert_raise(HttpParserError) { @parser.filter_body('', str) }
300
+ assert ! @parser.keepalive?
301
+ end
302
+
303
+ def test_overflow_content_length
304
+ n = HttpParser::LENGTH_MAX + 1
305
+ str = "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
306
+ assert_raise(HttpParserError) { @parser.headers({}, str) }
307
+ assert ! @parser.keepalive?
308
+ end
309
+
310
+ def test_bad_chunk
311
+ str = "PUT / HTTP/1.1\r\n" \
312
+ "transfer-Encoding: chunked\r\n\r\n" \
313
+ "#zzz\r\na\r\n2\r\n..\r\n0\r\n"
314
+ req = {}
315
+ assert_equal req, @parser.headers(req, str)
316
+ assert_nil @parser.content_length
317
+ assert_raise(HttpParserError) { @parser.filter_body('', str) }
318
+ assert ! @parser.keepalive?
319
+ end
320
+
321
+ def test_bad_content_length
322
+ str = "PUT / HTTP/1.1\r\nContent-Length: 7ff\r\n\r\n"
323
+ assert_raise(HttpParserError) { @parser.headers({}, str) }
324
+ assert ! @parser.keepalive?
325
+ end
326
+
327
+ def test_bad_trailers
328
+ str = "PUT / HTTP/1.1\r\n" \
329
+ "Trailer: Transfer-Encoding\r\n" \
330
+ "transfer-Encoding: chunked\r\n\r\n" \
331
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
332
+ req = {}
333
+ assert_equal req, @parser.headers(req, str)
334
+ assert_equal 'Transfer-Encoding', req['HTTP_TRAILER']
335
+ tmp = ''
336
+ assert_nil @parser.filter_body(tmp, str)
337
+ assert_equal 'a..', tmp
338
+ assert_equal '', str
339
+ str << "Transfer-Encoding: identity\r\n\r\n"
340
+ assert_raise(HttpParserError) { @parser.trailers(req, str) }
341
+ assert ! @parser.keepalive?
342
+ end
343
+
344
+ def test_repeat_headers
345
+ str = "PUT / HTTP/1.1\r\n" \
346
+ "Trailer: Content-MD5\r\n" \
347
+ "Trailer: Content-SHA1\r\n" \
348
+ "transfer-Encoding: chunked\r\n\r\n" \
349
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
350
+ req = {}
351
+ assert_equal req, @parser.headers(req, str)
352
+ assert_equal 'Content-MD5,Content-SHA1', req['HTTP_TRAILER']
353
+ assert ! @parser.keepalive?
354
+ end
355
+
356
+ def test_parse_simple_request
357
+ parser = HttpParser.new
358
+ req = {}
359
+ http = "GET /read-rfc1945-if-you-dont-believe-me\r\n"
360
+ assert_equal req, parser.headers(req, http)
361
+ assert_equal '', http
362
+ expect = {
363
+ "SERVER_NAME"=>"localhost",
364
+ "rack.url_scheme"=>"http",
365
+ "REQUEST_PATH"=>"/read-rfc1945-if-you-dont-believe-me",
366
+ "PATH_INFO"=>"/read-rfc1945-if-you-dont-believe-me",
367
+ "REQUEST_URI"=>"/read-rfc1945-if-you-dont-believe-me",
368
+ "SERVER_PORT"=>"80",
369
+ "SERVER_PROTOCOL"=>"HTTP/0.9",
370
+ "REQUEST_METHOD"=>"GET",
371
+ "QUERY_STRING"=>""
372
+ }
373
+ assert_equal expect, req
374
+ assert ! parser.headers?
375
+ end
376
+
377
+ def test_path_info_semicolon
378
+ qs = "QUERY_STRING"
379
+ pi = "PATH_INFO"
380
+ req = {}
381
+ str = "GET %s HTTP/1.1\r\nHost: example.com\r\n\r\n"
382
+ {
383
+ "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
384
+ "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
385
+ "/1;a=b" => { qs => "", pi => "/1;a=b" },
386
+ "/1;a=b?" => { qs => "", pi => "/1;a=b" },
387
+ "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
388
+ "*" => { qs => "", pi => "" },
389
+ }.each do |uri,expect|
390
+ assert_equal req, @parser.headers(req.clear, str % [ uri ])
391
+ @parser.reset
392
+ assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
393
+ assert_equal expect[qs], req[qs], "#{qs} mismatch"
394
+ assert_equal expect[pi], req[pi], "#{pi} mismatch"
395
+ next if uri == "*"
396
+ uri = URI.parse("http://example.com#{uri}")
397
+ assert_equal uri.query.to_s, req[qs], "#{qs} mismatch URI.parse disagrees"
398
+ assert_equal uri.path, req[pi], "#{pi} mismatch URI.parse disagrees"
399
+ end
400
+ end
401
+
402
+ def test_path_info_semicolon_absolute
403
+ qs = "QUERY_STRING"
404
+ pi = "PATH_INFO"
405
+ req = {}
406
+ str = "GET http://example.com%s HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
407
+ {
408
+ "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
409
+ "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
410
+ "/1;a=b" => { qs => "", pi => "/1;a=b" },
411
+ "/1;a=b?" => { qs => "", pi => "/1;a=b" },
412
+ "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
413
+ }.each do |uri,expect|
414
+ assert_equal req, @parser.headers(req.clear, str % [ uri ])
415
+ @parser.reset
416
+ assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
417
+ assert_equal "example.com", req["HTTP_HOST"], "Host: mismatch"
418
+ assert_equal expect[qs], req[qs], "#{qs} mismatch"
419
+ assert_equal expect[pi], req[pi], "#{pi} mismatch"
420
+ end
421
+ end
422
+
423
+ def test_negative_content_length
424
+ req = {}
425
+ str = "PUT / HTTP/1.1\r\n" \
426
+ "Content-Length: -1\r\n" \
427
+ "\r\n"
428
+ assert_raises(HttpParserError) do
429
+ @parser.headers(req, str)
430
+ end
431
+ end
432
+
433
+ def test_invalid_content_length
434
+ req = {}
435
+ str = "PUT / HTTP/1.1\r\n" \
436
+ "Content-Length: zzzzz\r\n" \
437
+ "\r\n"
438
+ assert_raises(HttpParserError) do
439
+ @parser.headers(req, str)
440
+ end
441
+ end
442
+
443
+ end
@@ -0,0 +1,184 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # Copyright (c) 2009 Eric Wong
4
+ # You can redistribute it and/or modify it under the same terms as Ruby.
5
+
6
+ require 'test/test_helper'
7
+
8
+ include Unicorn
9
+
10
+ class RequestTest < Test::Unit::TestCase
11
+
12
+ class MockRequest < StringIO
13
+ alias_method :readpartial, :sysread
14
+ alias_method :read_nonblock, :sysread
15
+ end
16
+
17
+ def setup
18
+ @request = HttpRequest.new
19
+ @app = lambda do |env|
20
+ [ 200, { 'Content-Length' => '0', 'Content-Type' => 'text/plain' }, [] ]
21
+ end
22
+ @lint = Rack::Lint.new(@app)
23
+ end
24
+
25
+ def test_options
26
+ client = MockRequest.new("OPTIONS * HTTP/1.1\r\n" \
27
+ "Host: foo\r\n\r\n")
28
+ res = env = nil
29
+ assert_nothing_raised { env = @request.read(client) }
30
+ assert_equal '', env['REQUEST_PATH']
31
+ assert_equal '', env['PATH_INFO']
32
+ assert_equal '*', env['REQUEST_URI']
33
+ assert_nothing_raised { res = @lint.call(env) }
34
+ end
35
+
36
+ def test_absolute_uri_with_query
37
+ client = MockRequest.new("GET http://e:3/x?y=z HTTP/1.1\r\n" \
38
+ "Host: foo\r\n\r\n")
39
+ res = env = nil
40
+ assert_nothing_raised { env = @request.read(client) }
41
+ assert_equal '/x', env['REQUEST_PATH']
42
+ assert_equal '/x', env['PATH_INFO']
43
+ assert_equal 'y=z', env['QUERY_STRING']
44
+ assert_nothing_raised { res = @lint.call(env) }
45
+ end
46
+
47
+ def test_absolute_uri_with_fragment
48
+ client = MockRequest.new("GET http://e:3/x#frag HTTP/1.1\r\n" \
49
+ "Host: foo\r\n\r\n")
50
+ res = env = nil
51
+ assert_nothing_raised { env = @request.read(client) }
52
+ assert_equal '/x', env['REQUEST_PATH']
53
+ assert_equal '/x', env['PATH_INFO']
54
+ assert_equal '', env['QUERY_STRING']
55
+ assert_equal 'frag', env['FRAGMENT']
56
+ assert_nothing_raised { res = @lint.call(env) }
57
+ end
58
+
59
+ def test_absolute_uri_with_query_and_fragment
60
+ client = MockRequest.new("GET http://e:3/x?a=b#frag HTTP/1.1\r\n" \
61
+ "Host: foo\r\n\r\n")
62
+ res = env = nil
63
+ assert_nothing_raised { env = @request.read(client) }
64
+ assert_equal '/x', env['REQUEST_PATH']
65
+ assert_equal '/x', env['PATH_INFO']
66
+ assert_equal 'a=b', env['QUERY_STRING']
67
+ assert_equal 'frag', env['FRAGMENT']
68
+ assert_nothing_raised { res = @lint.call(env) }
69
+ end
70
+
71
+ def test_absolute_uri_unsupported_schemes
72
+ %w(ssh+http://e/ ftp://e/x http+ssh://e/x).each do |abs_uri|
73
+ client = MockRequest.new("GET #{abs_uri} HTTP/1.1\r\n" \
74
+ "Host: foo\r\n\r\n")
75
+ assert_raises(HttpParserError) { @request.read(client) }
76
+ end
77
+ end
78
+
79
+ def test_x_forwarded_proto_https
80
+ res = env = nil
81
+ client = MockRequest.new("GET / HTTP/1.1\r\n" \
82
+ "X-Forwarded-Proto: https\r\n" \
83
+ "Host: foo\r\n\r\n")
84
+ assert_nothing_raised { env = @request.read(client) }
85
+ assert_equal "https", env['rack.url_scheme']
86
+ assert_nothing_raised { res = @lint.call(env) }
87
+ end
88
+
89
+ def test_x_forwarded_proto_http
90
+ res = env = nil
91
+ client = MockRequest.new("GET / HTTP/1.1\r\n" \
92
+ "X-Forwarded-Proto: http\r\n" \
93
+ "Host: foo\r\n\r\n")
94
+ assert_nothing_raised { env = @request.read(client) }
95
+ assert_equal "http", env['rack.url_scheme']
96
+ assert_nothing_raised { res = @lint.call(env) }
97
+ end
98
+
99
+ def test_x_forwarded_proto_invalid
100
+ res = env = nil
101
+ client = MockRequest.new("GET / HTTP/1.1\r\n" \
102
+ "X-Forwarded-Proto: ftp\r\n" \
103
+ "Host: foo\r\n\r\n")
104
+ assert_nothing_raised { env = @request.read(client) }
105
+ assert_equal "http", env['rack.url_scheme']
106
+ assert_nothing_raised { res = @lint.call(env) }
107
+ end
108
+
109
+ def test_rack_lint_get
110
+ client = MockRequest.new("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
111
+ res = env = nil
112
+ assert_nothing_raised { env = @request.read(client) }
113
+ assert_equal "http", env['rack.url_scheme']
114
+ assert_equal '127.0.0.1', env['REMOTE_ADDR']
115
+ assert_nothing_raised { res = @lint.call(env) }
116
+ end
117
+
118
+ def test_no_content_stringio
119
+ client = MockRequest.new("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
120
+ res = env = nil
121
+ assert_nothing_raised { env = @request.read(client) }
122
+ assert_equal StringIO, env['rack.input'].class
123
+ end
124
+
125
+ def test_zero_content_stringio
126
+ client = MockRequest.new("PUT / HTTP/1.1\r\n" \
127
+ "Content-Length: 0\r\n" \
128
+ "Host: foo\r\n\r\n")
129
+ res = env = nil
130
+ assert_nothing_raised { env = @request.read(client) }
131
+ assert_equal StringIO, env['rack.input'].class
132
+ end
133
+
134
+ def test_real_content_not_stringio
135
+ client = MockRequest.new("PUT / HTTP/1.1\r\n" \
136
+ "Content-Length: 1\r\n" \
137
+ "Host: foo\r\n\r\n")
138
+ res = env = nil
139
+ assert_nothing_raised { env = @request.read(client) }
140
+ assert_equal Unicorn::TeeInput, env['rack.input'].class
141
+ end
142
+
143
+ def test_rack_lint_put
144
+ client = MockRequest.new(
145
+ "PUT / HTTP/1.1\r\n" \
146
+ "Host: foo\r\n" \
147
+ "Content-Length: 5\r\n" \
148
+ "\r\n" \
149
+ "abcde")
150
+ res = env = nil
151
+ assert_nothing_raised { env = @request.read(client) }
152
+ assert ! env.include?(:http_body)
153
+ assert_nothing_raised { res = @lint.call(env) }
154
+ end
155
+
156
+ def test_rack_lint_big_put
157
+ count = 100
158
+ bs = 0x10000
159
+ buf = (' ' * bs).freeze
160
+ length = bs * count
161
+ client = Tempfile.new('big_put')
162
+ client.syswrite(
163
+ "PUT / HTTP/1.1\r\n" \
164
+ "Host: foo\r\n" \
165
+ "Content-Length: #{length}\r\n" \
166
+ "\r\n")
167
+ count.times { assert_equal bs, client.syswrite(buf) }
168
+ assert_equal 0, client.sysseek(0)
169
+ res = env = nil
170
+ assert_nothing_raised { env = @request.read(client) }
171
+ assert ! env.include?(:http_body)
172
+ assert_equal length, env['rack.input'].size
173
+ count.times {
174
+ tmp = env['rack.input'].read(bs)
175
+ tmp << env['rack.input'].read(bs - tmp.size) if tmp.size != bs
176
+ assert_equal buf, tmp
177
+ }
178
+ assert_nil env['rack.input'].read(bs)
179
+ assert_nothing_raised { env['rack.input'].rewind }
180
+ assert_nothing_raised { res = @lint.call(env) }
181
+ end
182
+
183
+ end
184
+