unicorn-simon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +7 -0
  2. data/.CHANGELOG.old +25 -0
  3. data/.document +28 -0
  4. data/.gitattributes +5 -0
  5. data/.gitignore +25 -0
  6. data/.mailmap +26 -0
  7. data/.manifest +156 -0
  8. data/.olddoc.yml +18 -0
  9. data/Application_Timeouts +77 -0
  10. data/CONTRIBUTORS +35 -0
  11. data/COPYING +674 -0
  12. data/DESIGN +95 -0
  13. data/Documentation/.gitignore +5 -0
  14. data/Documentation/GNUmakefile +30 -0
  15. data/Documentation/unicorn.1.txt +187 -0
  16. data/Documentation/unicorn_rails.1.txt +175 -0
  17. data/FAQ +70 -0
  18. data/GIT-VERSION-FILE +1 -0
  19. data/GIT-VERSION-GEN +39 -0
  20. data/GNUmakefile +253 -0
  21. data/HACKING +120 -0
  22. data/ISSUES +90 -0
  23. data/KNOWN_ISSUES +79 -0
  24. data/LATEST +30 -0
  25. data/LICENSE +67 -0
  26. data/Links +56 -0
  27. data/NEWS +2465 -0
  28. data/PHILOSOPHY +139 -0
  29. data/README +138 -0
  30. data/Rakefile +16 -0
  31. data/SIGNALS +123 -0
  32. data/Sandbox +104 -0
  33. data/TODO +3 -0
  34. data/TUNING +119 -0
  35. data/archive/.gitignore +3 -0
  36. data/archive/slrnpull.conf +4 -0
  37. data/bin/unicorn +126 -0
  38. data/bin/unicorn_rails +209 -0
  39. data/examples/big_app_gc.rb +2 -0
  40. data/examples/echo.ru +27 -0
  41. data/examples/init.sh +102 -0
  42. data/examples/logger_mp_safe.rb +25 -0
  43. data/examples/logrotate.conf +44 -0
  44. data/examples/nginx.conf +155 -0
  45. data/examples/unicorn.conf.minimal.rb +13 -0
  46. data/examples/unicorn.conf.rb +110 -0
  47. data/examples/unicorn.socket +11 -0
  48. data/examples/unicorn@.service +33 -0
  49. data/ext/unicorn_http/CFLAGS +13 -0
  50. data/ext/unicorn_http/c_util.h +124 -0
  51. data/ext/unicorn_http/common_field_optimization.h +111 -0
  52. data/ext/unicorn_http/ext_help.h +62 -0
  53. data/ext/unicorn_http/extconf.rb +11 -0
  54. data/ext/unicorn_http/global_variables.h +97 -0
  55. data/ext/unicorn_http/httpdate.c +78 -0
  56. data/ext/unicorn_http/unicorn_http.c +4274 -0
  57. data/ext/unicorn_http/unicorn_http.rl +980 -0
  58. data/ext/unicorn_http/unicorn_http_common.rl +76 -0
  59. data/lib/unicorn/app/old_rails/static.rb +59 -0
  60. data/lib/unicorn/app/old_rails.rb +35 -0
  61. data/lib/unicorn/cgi_wrapper.rb +147 -0
  62. data/lib/unicorn/configurator.rb +664 -0
  63. data/lib/unicorn/const.rb +21 -0
  64. data/lib/unicorn/http_request.rb +122 -0
  65. data/lib/unicorn/http_response.rb +60 -0
  66. data/lib/unicorn/http_server.rb +824 -0
  67. data/lib/unicorn/launcher.rb +62 -0
  68. data/lib/unicorn/oob_gc.rb +82 -0
  69. data/lib/unicorn/preread_input.rb +33 -0
  70. data/lib/unicorn/socket_helper.rb +195 -0
  71. data/lib/unicorn/stream_input.rb +146 -0
  72. data/lib/unicorn/tee_input.rb +133 -0
  73. data/lib/unicorn/tmpio.rb +27 -0
  74. data/lib/unicorn/util.rb +90 -0
  75. data/lib/unicorn/version.rb +1 -0
  76. data/lib/unicorn/worker.rb +140 -0
  77. data/lib/unicorn.rb +123 -0
  78. data/man/man1/unicorn.1 +221 -0
  79. data/man/man1/unicorn_rails.1 +212 -0
  80. data/setup.rb +1586 -0
  81. data/t/.gitignore +4 -0
  82. data/t/GNUmakefile +74 -0
  83. data/t/README +42 -0
  84. data/t/bin/content-md5-put +36 -0
  85. data/t/bin/sha1sum.rb +17 -0
  86. data/t/bin/unused_listen +40 -0
  87. data/t/broken-app.ru +12 -0
  88. data/t/detach.ru +11 -0
  89. data/t/env.ru +3 -0
  90. data/t/fails-rack-lint.ru +5 -0
  91. data/t/heartbeat-timeout.ru +12 -0
  92. data/t/hijack.ru +43 -0
  93. data/t/listener_names.ru +4 -0
  94. data/t/my-tap-lib.sh +201 -0
  95. data/t/oob_gc.ru +20 -0
  96. data/t/oob_gc_path.ru +20 -0
  97. data/t/pid.ru +3 -0
  98. data/t/preread_input.ru +17 -0
  99. data/t/rack-input-tests.ru +21 -0
  100. data/t/t0000-http-basic.sh +50 -0
  101. data/t/t0001-reload-bad-config.sh +53 -0
  102. data/t/t0002-config-conflict.sh +49 -0
  103. data/t/t0002-parser-error.sh +94 -0
  104. data/t/t0003-working_directory.sh +51 -0
  105. data/t/t0004-heartbeat-timeout.sh +69 -0
  106. data/t/t0004-working_directory_broken.sh +24 -0
  107. data/t/t0005-working_directory_app.rb.sh +40 -0
  108. data/t/t0006-reopen-logs.sh +83 -0
  109. data/t/t0006.ru +13 -0
  110. data/t/t0007-working_directory_no_embed_cli.sh +44 -0
  111. data/t/t0008-back_out_of_upgrade.sh +110 -0
  112. data/t/t0009-broken-app.sh +56 -0
  113. data/t/t0009-winch_ttin.sh +59 -0
  114. data/t/t0010-reap-logging.sh +55 -0
  115. data/t/t0011-active-unix-socket.sh +79 -0
  116. data/t/t0012-reload-empty-config.sh +85 -0
  117. data/t/t0013-rewindable-input-false.sh +24 -0
  118. data/t/t0013.ru +12 -0
  119. data/t/t0014-rewindable-input-true.sh +24 -0
  120. data/t/t0014.ru +12 -0
  121. data/t/t0015-configurator-internals.sh +25 -0
  122. data/t/t0018-write-on-close.sh +23 -0
  123. data/t/t0019-max_header_len.sh +49 -0
  124. data/t/t0020-at_exit-handler.sh +49 -0
  125. data/t/t0021-process_detach.sh +29 -0
  126. data/t/t0022-listener_names-preload_app.sh +32 -0
  127. data/t/t0100-rack-input-tests.sh +124 -0
  128. data/t/t0116-client_body_buffer_size.sh +80 -0
  129. data/t/t0116.ru +16 -0
  130. data/t/t0200-rack-hijack.sh +30 -0
  131. data/t/t0300-no-default-middleware.sh +20 -0
  132. data/t/t9000-preread-input.sh +48 -0
  133. data/t/t9001-oob_gc.sh +47 -0
  134. data/t/t9002-oob_gc-path.sh +75 -0
  135. data/t/test-lib.sh +128 -0
  136. data/t/write-on-close.ru +11 -0
  137. data/test/aggregate.rb +15 -0
  138. data/test/benchmark/README +50 -0
  139. data/test/benchmark/dd.ru +18 -0
  140. data/test/benchmark/stack.ru +8 -0
  141. data/test/exec/README +5 -0
  142. data/test/exec/test_exec.rb +1099 -0
  143. data/test/test_helper.rb +298 -0
  144. data/test/unit/test_configurator.rb +175 -0
  145. data/test/unit/test_droplet.rb +28 -0
  146. data/test/unit/test_http_parser.rb +886 -0
  147. data/test/unit/test_http_parser_ng.rb +633 -0
  148. data/test/unit/test_request.rb +182 -0
  149. data/test/unit/test_response.rb +111 -0
  150. data/test/unit/test_server.rb +268 -0
  151. data/test/unit/test_signals.rb +188 -0
  152. data/test/unit/test_socket_helper.rb +197 -0
  153. data/test/unit/test_stream_input.rb +203 -0
  154. data/test/unit/test_tee_input.rb +304 -0
  155. data/test/unit/test_upload.rb +306 -0
  156. data/test/unit/test_util.rb +105 -0
  157. data/unicorn.gemspec +50 -0
  158. metadata +310 -0
@@ -0,0 +1,633 @@
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_parser_max_len
15
+ assert_raises(RangeError) do
16
+ HttpParser.max_header_len = 0xffffffff + 1
17
+ end
18
+ end
19
+
20
+ def test_next_clear
21
+ r = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
22
+ @parser.buf << r
23
+ @parser.parse
24
+ @parser.response_start_sent = true
25
+ assert @parser.keepalive?
26
+ assert @parser.next?
27
+ assert @parser.response_start_sent
28
+
29
+ # persistent client makes another request:
30
+ @parser.buf << r
31
+ @parser.parse
32
+ assert @parser.keepalive?
33
+ assert @parser.next?
34
+ assert_equal false, @parser.response_start_sent
35
+ end
36
+
37
+ def test_response_start_sent
38
+ assert_equal false, @parser.response_start_sent, "default is false"
39
+ @parser.response_start_sent = true
40
+ assert_equal true, @parser.response_start_sent
41
+ @parser.response_start_sent = false
42
+ assert_equal false, @parser.response_start_sent
43
+ @parser.response_start_sent = true
44
+ @parser.clear
45
+ assert_equal false, @parser.response_start_sent
46
+ end
47
+
48
+ def test_connection_TE
49
+ @parser.buf << "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: TE\r\n"
50
+ @parser.buf << "TE: trailers\r\n\r\n"
51
+ @parser.parse
52
+ assert @parser.keepalive?
53
+ assert @parser.next?
54
+ end
55
+
56
+ def test_keepalive_requests_with_next?
57
+ req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
58
+ expect = {
59
+ "SERVER_NAME" => "example.com",
60
+ "HTTP_HOST" => "example.com",
61
+ "rack.url_scheme" => "http",
62
+ "REQUEST_PATH" => "/",
63
+ "SERVER_PROTOCOL" => "HTTP/1.1",
64
+ "PATH_INFO" => "/",
65
+ "HTTP_VERSION" => "HTTP/1.1",
66
+ "REQUEST_URI" => "/",
67
+ "SERVER_PORT" => "80",
68
+ "REQUEST_METHOD" => "GET",
69
+ "QUERY_STRING" => ""
70
+ }.freeze
71
+ 100.times do |nr|
72
+ @parser.buf << req
73
+ assert_equal expect, @parser.parse
74
+ assert @parser.next?
75
+ end
76
+ end
77
+
78
+ def test_default_keepalive_is_off
79
+ assert ! @parser.keepalive?
80
+ assert ! @parser.next?
81
+ @parser.buf << "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
82
+ @parser.parse
83
+ assert @parser.keepalive?
84
+ @parser.clear
85
+ assert ! @parser.keepalive?
86
+ assert ! @parser.next?
87
+ end
88
+
89
+ def test_identity_byte_headers
90
+ req = @parser.env
91
+ str = "PUT / HTTP/1.1\r\n"
92
+ str << "Content-Length: 123\r\n"
93
+ str << "\r"
94
+ hdr = @parser.buf
95
+ str.each_byte { |byte|
96
+ hdr << byte.chr
97
+ assert_nil @parser.parse
98
+ }
99
+ hdr << "\n"
100
+ assert_equal req.object_id, @parser.parse.object_id
101
+ assert_equal '123', req['CONTENT_LENGTH']
102
+ assert_equal 0, hdr.size
103
+ assert ! @parser.keepalive?
104
+ assert @parser.headers?
105
+ assert_equal 123, @parser.content_length
106
+ dst = ""
107
+ buf = '.' * 123
108
+ @parser.filter_body(dst, buf)
109
+ assert_equal '.' * 123, dst
110
+ assert_equal "", buf
111
+ assert @parser.keepalive?
112
+ end
113
+
114
+ def test_identity_step_headers
115
+ req = @parser.env
116
+ str = @parser.buf
117
+ str << "PUT / HTTP/1.1\r\n"
118
+ assert ! @parser.parse
119
+ str << "Content-Length: 123\r\n"
120
+ assert ! @parser.parse
121
+ str << "\r\n"
122
+ assert_equal req.object_id, @parser.parse.object_id
123
+ assert_equal '123', req['CONTENT_LENGTH']
124
+ assert_equal 0, str.size
125
+ assert ! @parser.keepalive?
126
+ assert @parser.headers?
127
+ dst = ""
128
+ buf = '.' * 123
129
+ @parser.filter_body(dst, buf)
130
+ assert_equal '.' * 123, dst
131
+ assert_equal "", buf
132
+ assert @parser.keepalive?
133
+ end
134
+
135
+ def test_identity_oneshot_header
136
+ req = @parser.env
137
+ str = @parser.buf
138
+ str << "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\n"
139
+ assert_equal req.object_id, @parser.parse.object_id
140
+ assert_equal '123', req['CONTENT_LENGTH']
141
+ assert_equal 0, str.size
142
+ assert ! @parser.keepalive?
143
+ assert @parser.headers?
144
+ dst = ""
145
+ buf = '.' * 123
146
+ @parser.filter_body(dst, buf)
147
+ assert_equal '.' * 123, dst
148
+ assert_equal "", buf
149
+ end
150
+
151
+ def test_identity_oneshot_header_with_body
152
+ body = ('a' * 123).freeze
153
+ req = @parser.env
154
+ str = @parser.buf
155
+ str << "PUT / HTTP/1.1\r\n" \
156
+ "Content-Length: #{body.length}\r\n" \
157
+ "\r\n#{body}"
158
+ assert_equal req.object_id, @parser.parse.object_id
159
+ assert_equal '123', req['CONTENT_LENGTH']
160
+ assert_equal 123, str.size
161
+ assert_equal body, str
162
+ tmp = ''
163
+ assert_nil @parser.filter_body(tmp, str)
164
+ assert_equal 0, str.size
165
+ assert_equal tmp, body
166
+ assert_equal "", @parser.filter_body(tmp, str)
167
+ assert @parser.keepalive?
168
+ end
169
+
170
+ def test_identity_oneshot_header_with_body_partial
171
+ str = @parser.buf
172
+ str << "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\na"
173
+ assert_equal Hash, @parser.parse.class
174
+ assert_equal 1, str.size
175
+ assert_equal 'a', str
176
+ tmp = ''
177
+ assert_nil @parser.filter_body(tmp, str)
178
+ assert_equal "", str
179
+ assert_equal "a", tmp
180
+ str << ' ' * 122
181
+ rv = @parser.filter_body(tmp, str)
182
+ assert_equal 122, tmp.size
183
+ assert_nil rv
184
+ assert_equal "", str
185
+ assert_equal str.object_id, @parser.filter_body(tmp, str).object_id
186
+ assert @parser.keepalive?
187
+ end
188
+
189
+ def test_identity_oneshot_header_with_body_slop
190
+ str = @parser.buf
191
+ str << "PUT / HTTP/1.1\r\nContent-Length: 1\r\n\r\naG"
192
+ assert_equal Hash, @parser.parse.class
193
+ assert_equal 2, str.size
194
+ assert_equal 'aG', str
195
+ tmp = ''
196
+ assert_nil @parser.filter_body(tmp, str)
197
+ assert_equal "G", str
198
+ assert_equal "G", @parser.filter_body(tmp, str)
199
+ assert_equal 1, tmp.size
200
+ assert_equal "a", tmp
201
+ assert @parser.keepalive?
202
+ end
203
+
204
+ def test_chunked
205
+ str = @parser.buf
206
+ req = @parser.env
207
+ str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
208
+ assert_equal req, @parser.parse, "msg=#{str}"
209
+ assert_equal 0, str.size
210
+ tmp = ""
211
+ assert_nil @parser.filter_body(tmp, str << "6")
212
+ assert_equal 0, tmp.size
213
+ assert_nil @parser.filter_body(tmp, str << "\r\n")
214
+ assert_equal 0, str.size
215
+ assert_equal 0, tmp.size
216
+ tmp = ""
217
+ assert_nil @parser.filter_body(tmp, str << "..")
218
+ assert_equal "..", tmp
219
+ assert_nil @parser.filter_body(tmp, str << "abcd\r\n0\r\n")
220
+ assert_equal "abcd", tmp
221
+ assert_equal str.object_id, @parser.filter_body(tmp, str << "PUT").object_id
222
+ assert_equal "PUT", str
223
+ assert ! @parser.keepalive?
224
+ str << "TY: FOO\r\n\r\n"
225
+ assert_equal req, @parser.parse
226
+ assert_equal "FOO", req["HTTP_PUTTY"]
227
+ assert @parser.keepalive?
228
+ end
229
+
230
+ def test_chunked_empty
231
+ str = @parser.buf
232
+ req = @parser.env
233
+ str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
234
+ assert_equal req, @parser.parse, "msg=#{str}"
235
+ assert_equal 0, str.size
236
+ tmp = ""
237
+ assert_equal str, @parser.filter_body(tmp, str << "0\r\n\r\n")
238
+ assert_equal "", tmp
239
+ end
240
+
241
+ def test_two_chunks
242
+ str = @parser.buf
243
+ str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
244
+ req = @parser.env
245
+ assert_equal req, @parser.parse
246
+ assert_equal 0, str.size
247
+ tmp = ""
248
+ assert_nil @parser.filter_body(tmp, str << "6")
249
+ assert_equal 0, tmp.size
250
+ assert_nil @parser.filter_body(tmp, str << "\r\n")
251
+ assert_equal "", str
252
+ assert_equal 0, tmp.size
253
+ tmp = ""
254
+ assert_nil @parser.filter_body(tmp, str << "..")
255
+ assert_equal 2, tmp.size
256
+ assert_equal "..", tmp
257
+ assert_nil @parser.filter_body(tmp, str << "abcd\r\n1")
258
+ assert_equal "abcd", tmp
259
+ assert_nil @parser.filter_body(tmp, str << "\r")
260
+ assert_equal "", tmp
261
+ assert_nil @parser.filter_body(tmp, str << "\n")
262
+ assert_equal "", tmp
263
+ assert_nil @parser.filter_body(tmp, str << "z")
264
+ assert_equal "z", tmp
265
+ assert_nil @parser.filter_body(tmp, str << "\r\n")
266
+ assert_nil @parser.filter_body(tmp, str << "0")
267
+ assert_nil @parser.filter_body(tmp, str << "\r")
268
+ rv = @parser.filter_body(tmp, str << "\nGET")
269
+ assert_equal "GET", rv
270
+ assert_equal str.object_id, rv.object_id
271
+ assert ! @parser.keepalive?
272
+ end
273
+
274
+ def test_big_chunk
275
+ str = @parser.buf
276
+ str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
277
+ "4000\r\nabcd"
278
+ req = @parser.env
279
+ assert_equal req, @parser.parse
280
+ tmp = ''
281
+ assert_nil @parser.filter_body(tmp, str)
282
+ assert_equal '', str
283
+ str << ' ' * 16300
284
+ assert_nil @parser.filter_body(tmp, str)
285
+ assert_equal '', str
286
+ str << ' ' * 80
287
+ assert_nil @parser.filter_body(tmp, str)
288
+ assert_equal '', str
289
+ assert ! @parser.body_eof?
290
+ assert_equal "", @parser.filter_body(tmp, str << "\r\n0\r\n")
291
+ assert_equal "", tmp
292
+ assert @parser.body_eof?
293
+ str << "\r\n"
294
+ assert_equal req, @parser.parse
295
+ assert_equal "", str
296
+ assert @parser.body_eof?
297
+ assert @parser.keepalive?
298
+ end
299
+
300
+ def test_two_chunks_oneshot
301
+ str = @parser.buf
302
+ req = @parser.env
303
+ str << "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
304
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
305
+ assert_equal req, @parser.parse
306
+ tmp = ''
307
+ assert_nil @parser.filter_body(tmp, str)
308
+ assert_equal 'a..', tmp
309
+ rv = @parser.filter_body(tmp, str)
310
+ assert_equal rv.object_id, str.object_id
311
+ assert ! @parser.keepalive?
312
+ end
313
+
314
+ def test_chunks_bytewise
315
+ chunked = "10\r\nabcdefghijklmnop\r\n11\r\n0123456789abcdefg\r\n0\r\n"
316
+ str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
317
+ buf = @parser.buf
318
+ buf << str
319
+ req = @parser.env
320
+ assert_equal req, @parser.parse
321
+ assert_equal "", buf
322
+ tmp = ''
323
+ body = ''
324
+ str = chunked[0..-2]
325
+ str.each_byte { |byte|
326
+ assert_nil @parser.filter_body(tmp, buf << byte.chr)
327
+ body << tmp
328
+ }
329
+ assert_equal 'abcdefghijklmnop0123456789abcdefg', body
330
+ rv = @parser.filter_body(tmp, buf<< "\n")
331
+ assert_equal rv.object_id, buf.object_id
332
+ assert ! @parser.keepalive?
333
+ end
334
+
335
+ def test_trailers
336
+ req = @parser.env
337
+ str = @parser.buf
338
+ str << "PUT / HTTP/1.1\r\n" \
339
+ "Trailer: Content-MD5\r\n" \
340
+ "transfer-Encoding: chunked\r\n\r\n" \
341
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
342
+ assert_equal req, @parser.parse
343
+ assert_equal 'Content-MD5', req['HTTP_TRAILER']
344
+ assert_nil req['HTTP_CONTENT_MD5']
345
+ tmp = ''
346
+ assert_nil @parser.filter_body(tmp, str)
347
+ assert_equal 'a..', tmp
348
+ md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
349
+ rv = @parser.filter_body(tmp, str)
350
+ assert_equal rv.object_id, str.object_id
351
+ assert_equal '', str
352
+ md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
353
+ str << md5_hdr
354
+ assert_nil @parser.trailers(req, str)
355
+ assert_equal md5_b64, req['HTTP_CONTENT_MD5']
356
+ assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
357
+ str << "\r"
358
+ assert_nil @parser.parse
359
+ str << "\nGET / "
360
+ assert_equal req, @parser.parse
361
+ assert_equal "GET / ", str
362
+ assert @parser.keepalive?
363
+ end
364
+
365
+ def test_trailers_slowly
366
+ str = @parser.buf
367
+ str << "PUT / HTTP/1.1\r\n" \
368
+ "Trailer: Content-MD5\r\n" \
369
+ "transfer-Encoding: chunked\r\n\r\n" \
370
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
371
+ req = @parser.env
372
+ assert_equal req, @parser.parse
373
+ assert_equal 'Content-MD5', req['HTTP_TRAILER']
374
+ assert_nil req['HTTP_CONTENT_MD5']
375
+ tmp = ''
376
+ assert_nil @parser.filter_body(tmp, str)
377
+ assert_equal 'a..', tmp
378
+ md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
379
+ rv = @parser.filter_body(tmp, str)
380
+ assert_equal rv.object_id, str.object_id
381
+ assert_equal '', str
382
+ assert_nil @parser.trailers(req, str)
383
+ md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
384
+ md5_hdr.each_byte { |byte|
385
+ str << byte.chr
386
+ assert_nil @parser.trailers(req, str)
387
+ }
388
+ assert_equal md5_b64, req['HTTP_CONTENT_MD5']
389
+ assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
390
+ str << "\r"
391
+ assert_nil @parser.parse
392
+ str << "\n"
393
+ assert_equal req, @parser.parse
394
+ end
395
+
396
+ def test_max_chunk
397
+ str = @parser.buf
398
+ str << "PUT / HTTP/1.1\r\n" \
399
+ "transfer-Encoding: chunked\r\n\r\n" \
400
+ "#{HttpParser::CHUNK_MAX.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
401
+ req = @parser.env
402
+ assert_equal req, @parser.parse
403
+ assert_nil @parser.content_length
404
+ @parser.filter_body('', str)
405
+ assert ! @parser.keepalive?
406
+ end
407
+
408
+ def test_max_body
409
+ n = HttpParser::LENGTH_MAX
410
+ @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
411
+ req = @parser.env
412
+ @parser.headers(req, @parser.buf)
413
+ assert_equal n, req['CONTENT_LENGTH'].to_i
414
+ assert ! @parser.keepalive?
415
+ end
416
+
417
+ def test_overflow_chunk
418
+ n = HttpParser::CHUNK_MAX + 1
419
+ str = @parser.buf
420
+ req = @parser.env
421
+ str << "PUT / HTTP/1.1\r\n" \
422
+ "transfer-Encoding: chunked\r\n\r\n" \
423
+ "#{n.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
424
+ assert_equal req, @parser.parse
425
+ assert_nil @parser.content_length
426
+ assert_raise(HttpParserError) { @parser.filter_body('', str) }
427
+ end
428
+
429
+ def test_overflow_content_length
430
+ n = HttpParser::LENGTH_MAX + 1
431
+ @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
432
+ assert_raise(HttpParserError) { @parser.parse }
433
+ end
434
+
435
+ def test_bad_chunk
436
+ @parser.buf << "PUT / HTTP/1.1\r\n" \
437
+ "transfer-Encoding: chunked\r\n\r\n" \
438
+ "#zzz\r\na\r\n2\r\n..\r\n0\r\n"
439
+ req = @parser.env
440
+ assert_equal req, @parser.parse
441
+ assert_nil @parser.content_length
442
+ assert_raise(HttpParserError) { @parser.filter_body("", @parser.buf) }
443
+ end
444
+
445
+ def test_bad_content_length
446
+ @parser.buf << "PUT / HTTP/1.1\r\nContent-Length: 7ff\r\n\r\n"
447
+ assert_raise(HttpParserError) { @parser.parse }
448
+ end
449
+
450
+ def test_bad_trailers
451
+ str = @parser.buf
452
+ req = @parser.env
453
+ str << "PUT / HTTP/1.1\r\n" \
454
+ "Trailer: Transfer-Encoding\r\n" \
455
+ "transfer-Encoding: chunked\r\n\r\n" \
456
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
457
+ assert_equal req, @parser.parse
458
+ assert_equal 'Transfer-Encoding', req['HTTP_TRAILER']
459
+ tmp = ''
460
+ assert_nil @parser.filter_body(tmp, str)
461
+ assert_equal 'a..', tmp
462
+ assert_equal '', str
463
+ str << "Transfer-Encoding: identity\r\n\r\n"
464
+ assert_raise(HttpParserError) { @parser.parse }
465
+ end
466
+
467
+ def test_repeat_headers
468
+ str = "PUT / HTTP/1.1\r\n" \
469
+ "Trailer: Content-MD5\r\n" \
470
+ "Trailer: Content-SHA1\r\n" \
471
+ "transfer-Encoding: chunked\r\n\r\n" \
472
+ "1\r\na\r\n2\r\n..\r\n0\r\n"
473
+ req = @parser.env
474
+ @parser.buf << str
475
+ assert_equal req, @parser.parse
476
+ assert_equal 'Content-MD5,Content-SHA1', req['HTTP_TRAILER']
477
+ assert ! @parser.keepalive?
478
+ end
479
+
480
+ def test_parse_simple_request
481
+ parser = HttpParser.new
482
+ req = parser.env
483
+ parser.buf << "GET /read-rfc1945-if-you-dont-believe-me\r\n"
484
+ assert_equal req, parser.parse
485
+ assert_equal '', parser.buf
486
+ expect = {
487
+ "SERVER_NAME"=>"localhost",
488
+ "rack.url_scheme"=>"http",
489
+ "REQUEST_PATH"=>"/read-rfc1945-if-you-dont-believe-me",
490
+ "PATH_INFO"=>"/read-rfc1945-if-you-dont-believe-me",
491
+ "REQUEST_URI"=>"/read-rfc1945-if-you-dont-believe-me",
492
+ "SERVER_PORT"=>"80",
493
+ "SERVER_PROTOCOL"=>"HTTP/0.9",
494
+ "REQUEST_METHOD"=>"GET",
495
+ "QUERY_STRING"=>""
496
+ }
497
+ assert_equal expect, req
498
+ assert ! parser.headers?
499
+ end
500
+
501
+ def test_path_info_semicolon
502
+ qs = "QUERY_STRING"
503
+ pi = "PATH_INFO"
504
+ req = {}
505
+ str = "GET %s HTTP/1.1\r\nHost: example.com\r\n\r\n"
506
+ {
507
+ "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
508
+ "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
509
+ "/1;a=b" => { qs => "", pi => "/1;a=b" },
510
+ "/1;a=b?" => { qs => "", pi => "/1;a=b" },
511
+ "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
512
+ "*" => { qs => "", pi => "" },
513
+ }.each do |uri,expect|
514
+ assert_equal req, @parser.headers(req.clear, str % [ uri ])
515
+ req = req.dup
516
+ @parser.clear
517
+ assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
518
+ assert_equal expect[qs], req[qs], "#{qs} mismatch"
519
+ assert_equal expect[pi], req[pi], "#{pi} mismatch"
520
+ next if uri == "*"
521
+ uri = URI.parse("http://example.com#{uri}")
522
+ assert_equal uri.query.to_s, req[qs], "#{qs} mismatch URI.parse disagrees"
523
+ assert_equal uri.path, req[pi], "#{pi} mismatch URI.parse disagrees"
524
+ end
525
+ end
526
+
527
+ def test_path_info_semicolon_absolute
528
+ qs = "QUERY_STRING"
529
+ pi = "PATH_INFO"
530
+ req = {}
531
+ str = "GET http://example.com%s HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
532
+ {
533
+ "/1;a=b?c=d&e=f" => { qs => "c=d&e=f", pi => "/1;a=b" },
534
+ "/1?c=d&e=f" => { qs => "c=d&e=f", pi => "/1" },
535
+ "/1;a=b" => { qs => "", pi => "/1;a=b" },
536
+ "/1;a=b?" => { qs => "", pi => "/1;a=b" },
537
+ "/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
538
+ }.each do |uri,expect|
539
+ assert_equal req, @parser.headers(req.clear, str % [ uri ])
540
+ req = req.dup
541
+ @parser.clear
542
+ assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
543
+ assert_equal "example.com", req["HTTP_HOST"], "Host: mismatch"
544
+ assert_equal expect[qs], req[qs], "#{qs} mismatch"
545
+ assert_equal expect[pi], req[pi], "#{pi} mismatch"
546
+ end
547
+ end
548
+
549
+ def test_negative_content_length
550
+ req = {}
551
+ str = "PUT / HTTP/1.1\r\n" \
552
+ "Content-Length: -1\r\n" \
553
+ "\r\n"
554
+ assert_raises(HttpParserError) do
555
+ @parser.headers(req, str)
556
+ end
557
+ end
558
+
559
+ def test_invalid_content_length
560
+ req = {}
561
+ str = "PUT / HTTP/1.1\r\n" \
562
+ "Content-Length: zzzzz\r\n" \
563
+ "\r\n"
564
+ assert_raises(HttpParserError) do
565
+ @parser.headers(req, str)
566
+ end
567
+ end
568
+
569
+ def test_backtrace_is_empty
570
+ begin
571
+ @parser.headers({}, "AAADFSFDSFD\r\n\r\n")
572
+ assert false, "should never get here line:#{__LINE__}"
573
+ rescue HttpParserError => e
574
+ assert_equal [], e.backtrace
575
+ return
576
+ end
577
+ assert false, "should never get here line:#{__LINE__}"
578
+ end
579
+
580
+ def test_ignore_version_header
581
+ @parser.buf << "GET / HTTP/1.1\r\nVersion: hello\r\n\r\n"
582
+ req = @parser.env
583
+ assert_equal req, @parser.parse
584
+ assert_equal '', @parser.buf
585
+ expect = {
586
+ "SERVER_NAME" => "localhost",
587
+ "rack.url_scheme" => "http",
588
+ "REQUEST_PATH" => "/",
589
+ "SERVER_PROTOCOL" => "HTTP/1.1",
590
+ "PATH_INFO" => "/",
591
+ "HTTP_VERSION" => "HTTP/1.1",
592
+ "REQUEST_URI" => "/",
593
+ "SERVER_PORT" => "80",
594
+ "REQUEST_METHOD" => "GET",
595
+ "QUERY_STRING" => ""
596
+ }
597
+ assert_equal expect, req
598
+ end
599
+
600
+ def test_pipelined_requests
601
+ host = "example.com"
602
+ expect = {
603
+ "HTTP_HOST" => host,
604
+ "SERVER_NAME" => host,
605
+ "REQUEST_PATH" => "/",
606
+ "rack.url_scheme" => "http",
607
+ "SERVER_PROTOCOL" => "HTTP/1.1",
608
+ "PATH_INFO" => "/",
609
+ "HTTP_VERSION" => "HTTP/1.1",
610
+ "REQUEST_URI" => "/",
611
+ "SERVER_PORT" => "80",
612
+ "REQUEST_METHOD" => "GET",
613
+ "QUERY_STRING" => ""
614
+ }
615
+ req1 = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
616
+ req2 = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"
617
+ @parser.buf << (req1 + req2)
618
+ env1 = @parser.parse.dup
619
+ assert_equal expect, env1
620
+ assert_equal req2, @parser.buf
621
+ assert ! @parser.env.empty?
622
+ assert @parser.next?
623
+ assert @parser.keepalive?
624
+ assert @parser.headers?
625
+ assert_equal expect, @parser.env
626
+ env2 = @parser.parse.dup
627
+ host.replace "www.example.com"
628
+ assert_equal "www.example.com", expect["HTTP_HOST"]
629
+ assert_equal "www.example.com", expect["SERVER_NAME"]
630
+ assert_equal expect, env2
631
+ assert_equal "", @parser.buf
632
+ end
633
+ end