tee_input: update documentation for Rack 1.2
[unicorn.git] / test / unit / test_http_parser.rb
blob222c2273343bed3b7251525be331d5d57954f1cb
1 # -*- encoding: binary -*-
3 # Copyright (c) 2005 Zed A. Shaw 
4 # You can redistribute it and/or modify it under the same terms as Ruby.
6 # Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html
7 # for more information.
9 require 'test/test_helper'
11 include Unicorn
13 class HttpParserTest < Test::Unit::TestCase
15   def test_parse_simple
16     parser = HttpParser.new
17     req = {}
18     http = "GET / HTTP/1.1\r\n\r\n"
19     assert_equal req, parser.headers(req, http)
20     assert_equal '', http
22     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
23     assert_equal '/', req['REQUEST_PATH']
24     assert_equal 'HTTP/1.1', req['HTTP_VERSION']
25     assert_equal '/', req['REQUEST_URI']
26     assert_equal 'GET', req['REQUEST_METHOD']
27     assert_nil req['FRAGMENT']
28     assert_equal '', req['QUERY_STRING']
30     assert parser.keepalive?
31     parser.reset
32     req.clear
34     http = "G"
35     assert_nil parser.headers(req, http)
36     assert_equal "G", http
37     assert req.empty?
39     # try parsing again to ensure we were reset correctly
40     http = "GET /hello-world HTTP/1.1\r\n\r\n"
41     assert parser.headers(req, http)
43     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
44     assert_equal '/hello-world', req['REQUEST_PATH']
45     assert_equal 'HTTP/1.1', req['HTTP_VERSION']
46     assert_equal '/hello-world', req['REQUEST_URI']
47     assert_equal 'GET', req['REQUEST_METHOD']
48     assert_nil req['FRAGMENT']
49     assert_equal '', req['QUERY_STRING']
50     assert_equal '', http
51     assert parser.keepalive?
52   end
54   def test_tab_lws
55     parser = HttpParser.new
56     req = {}
57     tmp = "GET / HTTP/1.1\r\nHost:\tfoo.bar\r\n\r\n"
58     assert_equal req.object_id, parser.headers(req, tmp).object_id
59     assert_equal "foo.bar", req['HTTP_HOST']
60   end
62   def test_connection_close_no_ka
63     parser = HttpParser.new
64     req = {}
65     tmp = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
66     assert_equal req.object_id, parser.headers(req, tmp).object_id
67     assert_equal "GET", req['REQUEST_METHOD']
68     assert ! parser.keepalive?
69   end
71   def test_connection_keep_alive_ka
72     parser = HttpParser.new
73     req = {}
74     tmp = "HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
75     assert_equal req.object_id, parser.headers(req, tmp).object_id
76     assert parser.keepalive?
77   end
79   def test_connection_keep_alive_ka_bad_method
80     parser = HttpParser.new
81     req = {}
82     tmp = "POST / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
83     assert_equal req.object_id, parser.headers(req, tmp).object_id
84     assert ! parser.keepalive?
85   end
87   def test_connection_keep_alive_ka_bad_version
88     parser = HttpParser.new
89     req = {}
90     tmp = "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"
91     assert_equal req.object_id, parser.headers(req, tmp).object_id
92     assert parser.keepalive?
93   end
95   def test_parse_server_host_default_port
96     parser = HttpParser.new
97     req = {}
98     tmp = "GET / HTTP/1.1\r\nHost: foo\r\n\r\n"
99     assert_equal req, parser.headers(req, tmp)
100     assert_equal 'foo', req['SERVER_NAME']
101     assert_equal '80', req['SERVER_PORT']
102     assert_equal '', tmp
103     assert parser.keepalive?
104   end
106   def test_parse_server_host_alt_port
107     parser = HttpParser.new
108     req = {}
109     tmp = "GET / HTTP/1.1\r\nHost: foo:999\r\n\r\n"
110     assert_equal req, parser.headers(req, tmp)
111     assert_equal 'foo', req['SERVER_NAME']
112     assert_equal '999', req['SERVER_PORT']
113     assert_equal '', tmp
114     assert parser.keepalive?
115   end
117   def test_parse_server_host_empty_port
118     parser = HttpParser.new
119     req = {}
120     tmp = "GET / HTTP/1.1\r\nHost: foo:\r\n\r\n"
121     assert_equal req, parser.headers(req, tmp)
122     assert_equal 'foo', req['SERVER_NAME']
123     assert_equal '80', req['SERVER_PORT']
124     assert_equal '', tmp
125     assert parser.keepalive?
126   end
128   def test_parse_server_host_xfp_https
129     parser = HttpParser.new
130     req = {}
131     tmp = "GET / HTTP/1.1\r\nHost: foo:\r\n" \
132           "X-Forwarded-Proto: https\r\n\r\n"
133     assert_equal req, parser.headers(req, tmp)
134     assert_equal 'foo', req['SERVER_NAME']
135     assert_equal '443', req['SERVER_PORT']
136     assert_equal '', tmp
137     assert parser.keepalive?
138   end
140   def test_parse_strange_headers
141     parser = HttpParser.new
142     req = {}
143     should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
144     assert_equal req, parser.headers(req, should_be_good)
145     assert_equal '', should_be_good
146     assert parser.keepalive?
147   end
149   # legacy test case from Mongrel that we never supported before...
150   # I still consider Pound irrelevant, unfortunately stupid clients that
151   # send extremely big headers do exist and they've managed to find Unicorn...
152   def test_nasty_pound_header
153     parser = HttpParser.new
154     nasty_pound_header = "GET / HTTP/1.1\r\nX-SSL-Bullshit:   -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"
155     req = {}
156     buf = nasty_pound_header.dup
158     assert nasty_pound_header =~ /(-----BEGIN .*--END CERTIFICATE-----)/m
159     expect = $1.dup
160     expect.gsub!(/\r\n\t/, ' ')
161     assert_equal req, parser.headers(req, buf)
162     assert_equal '', buf
163     assert_equal expect, req['HTTP_X_SSL_BULLSHIT']
164   end
166   def test_continuation_eats_leading_spaces
167     parser = HttpParser.new
168     header = "GET / HTTP/1.1\r\n" \
169              "X-ASDF:      \r\n" \
170              "\t\r\n" \
171              "    \r\n" \
172              "  ASDF\r\n\r\n"
173     req = {}
174     assert_equal req, parser.headers(req, header)
175     assert_equal '', header
176     assert_equal 'ASDF', req['HTTP_X_ASDF']
177   end
179   def test_continuation_eats_scattered_leading_spaces
180     parser = HttpParser.new
181     header = "GET / HTTP/1.1\r\n" \
182              "X-ASDF:   hi\r\n" \
183              "    y\r\n" \
184              "\t\r\n" \
185              "       x\r\n" \
186              "  ASDF\r\n\r\n"
187     req = {}
188     assert_equal req, parser.headers(req, header)
189     assert_equal '', header
190     assert_equal 'hi y x ASDF', req['HTTP_X_ASDF']
191   end
193   def test_continuation_with_absolute_uri_and_ignored_host_header
194     parser = HttpParser.new
195     header = "GET http://example.com/ HTTP/1.1\r\n" \
196              "Host: \r\n" \
197              "    YHBT.net\r\n" \
198              "\r\n"
199     req = {}
200     assert_equal req, parser.headers(req, header)
201     assert_equal 'example.com', req['HTTP_HOST']
202   end
204   # this may seem to be testing more of an implementation detail, but
205   # it also helps ensure we're safe in the presence of multiple parsers
206   # in case we ever go multithreaded/evented...
207   def test_resumable_continuations
208     nr = 1000
209     req = {}
210     header = "GET / HTTP/1.1\r\n" \
211              "X-ASDF:      \r\n" \
212              "  hello\r\n"
213     tmp = []
214     nr.times { |i|
215       parser = HttpParser.new
216       assert parser.headers(req, "#{header} #{i}\r\n").nil?
217       asdf = req['HTTP_X_ASDF']
218       assert_equal "hello #{i}", asdf
219       tmp << [ parser, asdf ]
220       req.clear
221     }
222     tmp.each_with_index { |(parser, asdf), i|
223       assert_equal req, parser.headers(req, "#{header} #{i}\r\n .\r\n\r\n")
224       assert_equal "hello #{i} .", asdf
225     }
226   end
228   def test_invalid_continuation
229     parser = HttpParser.new
230     header = "GET / HTTP/1.1\r\n" \
231              "    y\r\n" \
232              "Host: hello\r\n" \
233              "\r\n"
234     req = {}
235     assert_raises(HttpParserError) { parser.headers(req, header) }
236   end
238   def test_parse_ie6_urls
239     %w(/some/random/path"
240        /some/random/path>
241        /some/random/path<
242        /we/love/you/ie6?q=<"">
243        /url?<="&>="
244        /mal"formed"?
245     ).each do |path|
246       parser = HttpParser.new
247       req = {}
248       sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
249       assert_equal req, parser.headers(req, sorta_safe)
250       assert_equal path, req['REQUEST_URI']
251       assert_equal '', sorta_safe
252       assert parser.keepalive?
253     end
254   end
255   
256   def test_parse_error
257     parser = HttpParser.new
258     req = {}
259     bad_http = "GET / SsUTF/1.1"
261     assert_raises(HttpParserError) { parser.headers(req, bad_http) }
263     # make sure we can recover
264     parser.reset
265     req.clear
266     assert_equal req, parser.headers(req, "GET / HTTP/1.0\r\n\r\n")
267     assert ! parser.keepalive?
268   end
270   def test_piecemeal
271     parser = HttpParser.new
272     req = {}
273     http = "GET"
274     assert_nil parser.headers(req, http)
275     assert_nil parser.headers(req, http)
276     assert_nil parser.headers(req, http << " / HTTP/1.0")
277     assert_equal '/', req['REQUEST_PATH']
278     assert_equal '/', req['REQUEST_URI']
279     assert_equal 'GET', req['REQUEST_METHOD']
280     assert_nil parser.headers(req, http << "\r\n")
281     assert_equal 'HTTP/1.0', req['HTTP_VERSION']
282     assert_nil parser.headers(req, http << "\r")
283     assert_equal req, parser.headers(req, http << "\n")
284     assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
285     assert_nil req['FRAGMENT']
286     assert_equal '', req['QUERY_STRING']
287     assert_equal "", http
288     assert ! parser.keepalive?
289   end
291   # not common, but underscores do appear in practice
292   def test_absolute_uri_underscores
293     parser = HttpParser.new
294     req = {}
295     http = "GET http://under_score.example.com/foo?q=bar HTTP/1.0\r\n\r\n"
296     assert_equal req, parser.headers(req, http)
297     assert_equal 'http', req['rack.url_scheme']
298     assert_equal '/foo?q=bar', req['REQUEST_URI']
299     assert_equal '/foo', req['REQUEST_PATH']
300     assert_equal 'q=bar', req['QUERY_STRING']
302     assert_equal 'under_score.example.com', req['HTTP_HOST']
303     assert_equal 'under_score.example.com', req['SERVER_NAME']
304     assert_equal '80', req['SERVER_PORT']
305     assert_equal "", http
306     assert ! parser.keepalive?
307   end
309   # some dumb clients add users because they're stupid
310   def test_absolute_uri_w_user
311     parser = HttpParser.new
312     req = {}
313     http = "GET http://user%20space@example.com/foo?q=bar HTTP/1.0\r\n\r\n"
314     assert_equal req, parser.headers(req, http)
315     assert_equal 'http', req['rack.url_scheme']
316     assert_equal '/foo?q=bar', req['REQUEST_URI']
317     assert_equal '/foo', req['REQUEST_PATH']
318     assert_equal 'q=bar', req['QUERY_STRING']
320     assert_equal 'example.com', req['HTTP_HOST']
321     assert_equal 'example.com', req['SERVER_NAME']
322     assert_equal '80', req['SERVER_PORT']
323     assert_equal "", http
324     assert ! parser.keepalive?
325   end
327   # since Mongrel supported anything URI.parse supported, we're stuck
328   # supporting everything URI.parse supports
329   def test_absolute_uri_uri_parse
330     "#{URI::REGEXP::PATTERN::UNRESERVED};:&=+$,".split(//).each do |char|
331       parser = HttpParser.new
332       req = {}
333       http = "GET http://#{char}@example.com/ HTTP/1.0\r\n\r\n"
334       assert_equal req, parser.headers(req, http)
335       assert_equal 'http', req['rack.url_scheme']
336       assert_equal '/', req['REQUEST_URI']
337       assert_equal '/', req['REQUEST_PATH']
338       assert_equal '', req['QUERY_STRING']
340       assert_equal 'example.com', req['HTTP_HOST']
341       assert_equal 'example.com', req['SERVER_NAME']
342       assert_equal '80', req['SERVER_PORT']
343       assert_equal "", http
344       assert ! parser.keepalive?
345     end
346   end
348   def test_absolute_uri
349     parser = HttpParser.new
350     req = {}
351     http = "GET http://example.com/foo?q=bar HTTP/1.0\r\n\r\n"
352     assert_equal req, parser.headers(req, http)
353     assert_equal 'http', req['rack.url_scheme']
354     assert_equal '/foo?q=bar', req['REQUEST_URI']
355     assert_equal '/foo', req['REQUEST_PATH']
356     assert_equal 'q=bar', req['QUERY_STRING']
358     assert_equal 'example.com', req['HTTP_HOST']
359     assert_equal 'example.com', req['SERVER_NAME']
360     assert_equal '80', req['SERVER_PORT']
361     assert_equal "", http
362     assert ! parser.keepalive?
363   end
365   # X-Forwarded-Proto is not in rfc2616, absolute URIs are, however...
366   def test_absolute_uri_https
367     parser = HttpParser.new
368     req = {}
369     http = "GET https://example.com/foo?q=bar HTTP/1.1\r\n" \
370            "X-Forwarded-Proto: http\r\n\r\n"
371     assert_equal req, parser.headers(req, http)
372     assert_equal 'https', req['rack.url_scheme']
373     assert_equal '/foo?q=bar', req['REQUEST_URI']
374     assert_equal '/foo', req['REQUEST_PATH']
375     assert_equal 'q=bar', req['QUERY_STRING']
377     assert_equal 'example.com', req['HTTP_HOST']
378     assert_equal 'example.com', req['SERVER_NAME']
379     assert_equal '443', req['SERVER_PORT']
380     assert_equal "", http
381     assert parser.keepalive?
382   end
384   # Host: header should be ignored for absolute URIs
385   def test_absolute_uri_with_port
386     parser = HttpParser.new
387     req = {}
388     http = "GET http://example.com:8080/foo?q=bar HTTP/1.2\r\n" \
389            "Host: bad.example.com\r\n\r\n"
390     assert_equal req, parser.headers(req, http)
391     assert_equal 'http', req['rack.url_scheme']
392     assert_equal '/foo?q=bar', req['REQUEST_URI']
393     assert_equal '/foo', req['REQUEST_PATH']
394     assert_equal 'q=bar', req['QUERY_STRING']
396     assert_equal 'example.com:8080', req['HTTP_HOST']
397     assert_equal 'example.com', req['SERVER_NAME']
398     assert_equal '8080', req['SERVER_PORT']
399     assert_equal "", http
400     assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
401   end
403   def test_absolute_uri_with_empty_port
404     parser = HttpParser.new
405     req = {}
406     http = "GET https://example.com:/foo?q=bar HTTP/1.1\r\n" \
407            "Host: bad.example.com\r\n\r\n"
408     assert_equal req, parser.headers(req, http)
409     assert_equal 'https', req['rack.url_scheme']
410     assert_equal '/foo?q=bar', req['REQUEST_URI']
411     assert_equal '/foo', req['REQUEST_PATH']
412     assert_equal 'q=bar', req['QUERY_STRING']
414     assert_equal 'example.com:', req['HTTP_HOST']
415     assert_equal 'example.com', req['SERVER_NAME']
416     assert_equal '443', req['SERVER_PORT']
417     assert_equal "", http
418     assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
419   end
421   def test_put_body_oneshot
422     parser = HttpParser.new
423     req = {}
424     http = "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nabcde"
425     assert_equal req, parser.headers(req, http)
426     assert_equal '/', req['REQUEST_PATH']
427     assert_equal '/', req['REQUEST_URI']
428     assert_equal 'PUT', req['REQUEST_METHOD']
429     assert_equal 'HTTP/1.0', req['HTTP_VERSION']
430     assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
431     assert_equal "abcde", http
432     assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
433   end
435   def test_put_body_later
436     parser = HttpParser.new
437     req = {}
438     http = "PUT /l HTTP/1.0\r\nContent-Length: 5\r\n\r\n"
439     assert_equal req, parser.headers(req, http)
440     assert_equal '/l', req['REQUEST_PATH']
441     assert_equal '/l', req['REQUEST_URI']
442     assert_equal 'PUT', req['REQUEST_METHOD']
443     assert_equal 'HTTP/1.0', req['HTTP_VERSION']
444     assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
445     assert_equal "", http
446     assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
447   end
449   def test_unknown_methods
450     %w(GETT HEADR XGET XHEAD).each { |m|
451       parser = HttpParser.new
452       req = {}
453       s = "#{m} /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
454       ok = false
455       assert_nothing_raised do
456         ok = parser.headers(req, s)
457       end
458       assert ok
459       assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
460       assert_equal 'posts-17408', req['FRAGMENT']
461       assert_equal 'page=1', req['QUERY_STRING']
462       assert_equal "", s
463       assert_equal m, req['REQUEST_METHOD']
464       assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
465     }
466   end
468   def test_fragment_in_uri
469     parser = HttpParser.new
470     req = {}
471     get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
472     ok = false
473     assert_nothing_raised do
474       ok = parser.headers(req, get)
475     end
476     assert ok
477     assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
478     assert_equal 'posts-17408', req['FRAGMENT']
479     assert_equal 'page=1', req['QUERY_STRING']
480     assert_equal '', get
481     assert parser.keepalive?
482   end
484   # lame random garbage maker
485   def rand_data(min, max, readable=true)
486     count = min + ((rand(max)+1) *10).to_i
487     res = count.to_s + "/"
488     
489     if readable
490       res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
491     else
492       res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
493     end
495     return res
496   end
497   
499   def test_horrible_queries
500     parser = HttpParser.new
502     # then that large header names are caught
503     10.times do |c|
504       get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
505       assert_raises Unicorn::HttpParserError do
506         parser.headers({}, get)
507         parser.reset
508       end
509     end
511     # then that large mangled field values are caught
512     10.times do |c|
513       get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
514       assert_raises Unicorn::HttpParserError do
515         parser.headers({}, get)
516         parser.reset
517       end
518     end
520     # then large headers are rejected too
521     get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
522     get << "X-Test: test\r\n" * (80 * 1024)
523     assert_raises Unicorn::HttpParserError do
524       parser.headers({}, get)
525       parser.reset
526     end
528     # finally just that random garbage gets blocked all the time
529     10.times do |c|
530       get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
531       assert_raises Unicorn::HttpParserError do
532         parser.headers({}, get)
533         parser.reset
534       end
535     end
537   end
539   # so we don't  care about the portability of this test
540   # if it doesn't leak on Linux, it won't leak anywhere else
541   # unless your C compiler or platform is otherwise broken
542   LINUX_PROC_PID_STATUS = "/proc/self/status"
543   def test_memory_leak
544     match_rss = /^VmRSS:\s+(\d+)/
545     if File.read(LINUX_PROC_PID_STATUS) =~ match_rss
546       before = $1.to_i
547       1000000.times { Unicorn::HttpParser.new }
548       File.read(LINUX_PROC_PID_STATUS) =~ match_rss
549       after = $1.to_i
550       diff = after - before
551       assert(diff < 10000, "memory grew more than 10M: #{diff}")
552     end
553   end if RUBY_PLATFORM =~ /linux/ &&
554          File.readable?(LINUX_PROC_PID_STATUS) &&
555          !defined?(RUBY_ENGINE)