unicorn-academia 4.7.0.1

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