unicorn-academia 4.7.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 (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