unicorn-shopify 4.8.2.5.23

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 (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