unicorn-simon 0.0.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 (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