unicorn-shopify 4.8.2.5.23

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