Add test for :preload_app config option
[unicorn.git] / test / unit / test_http_parser.rb
blob57c8db22a626ea708765272f15cae22c27815aa0
1 # Copyright (c) 2005 Zed A. Shaw 
2 # You can redistribute it and/or modify it under the same terms as Ruby.
4 # Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html
5 # for more information.
7 require 'test/test_helper'
9 include Unicorn
11 class HttpParserTest < Test::Unit::TestCase
12     
13   def test_parse_simple
14     parser = HttpParser.new
15     req = {}
16     http = "GET / HTTP/1.1\r\n\r\n"
17     assert parser.execute(req, http)
19     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
20     assert_equal '/', req['REQUEST_PATH']
21     assert_equal 'HTTP/1.1', req['HTTP_VERSION']
22     assert_equal '/', req['REQUEST_URI']
23     assert_equal 'GET', req['REQUEST_METHOD']
24     assert_nil req['FRAGMENT']
25     assert_nil req['QUERY_STRING']
27     parser.reset
28     req.clear
30     assert ! parser.execute(req, "G")
31     assert req.empty?
33     # try parsing again to ensure we were reset correctly
34     http = "GET /hello-world HTTP/1.1\r\n\r\n"
35     assert parser.execute(req, http)
37     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
38     assert_equal '/hello-world', req['REQUEST_PATH']
39     assert_equal 'HTTP/1.1', req['HTTP_VERSION']
40     assert_equal '/hello-world', req['REQUEST_URI']
41     assert_equal 'GET', req['REQUEST_METHOD']
42     assert_nil req['FRAGMENT']
43     assert_nil req['QUERY_STRING']
44   end
46   def test_parse_strange_headers
47     parser = HttpParser.new
48     req = {}
49     should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
50     assert parser.execute(req, should_be_good)
52     # ref: http://thread.gmane.org/gmane.comp.lang.ruby.Unicorn.devel/37/focus=45
53     # (note we got 'pen' mixed up with 'pound' in that thread,
54     # but the gist of it is still relevant: these nasty headers are irrelevant
55     #
56     # 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"
57     # parser = HttpParser.new
58     # req = {}
59     # assert parser.execute(req, nasty_pound_header, 0)
60   end
62   def test_parse_ie6_urls
63     %w(/some/random/path"
64        /some/random/path>
65        /some/random/path<
66        /we/love/you/ie6?q=<"">
67        /url?<="&>="
68        /mal"formed"?
69     ).each do |path|
70       parser = HttpParser.new
71       req = {}
72       sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
73       assert parser.execute(req, sorta_safe)
74     end
75   end
76   
77   def test_parse_error
78     parser = HttpParser.new
79     req = {}
80     bad_http = "GET / SsUTF/1.1"
82     assert_raises(HttpParserError) { parser.execute(req, bad_http) }
83     parser.reset
84     assert(parser.execute({}, "GET / HTTP/1.0\r\n\r\n"))
85   end
87   def test_piecemeal
88     parser = HttpParser.new
89     req = {}
90     http = "GET"
91     assert ! parser.execute(req, http)
92     assert_raises(HttpParserError) { parser.execute(req, http) }
93     assert ! parser.execute(req, http << " / HTTP/1.0")
94     assert_equal '/', req['REQUEST_PATH']
95     assert_equal '/', req['REQUEST_URI']
96     assert_equal 'GET', req['REQUEST_METHOD']
97     assert ! parser.execute(req, http << "\r\n")
98     assert_equal 'HTTP/1.0', req['HTTP_VERSION']
99     assert ! parser.execute(req, http << "\r")
100     assert parser.execute(req, http << "\n")
101     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
102     assert_nil req['FRAGMENT']
103     assert_nil req['QUERY_STRING']
104   end
106   def test_put_body_oneshot
107     parser = HttpParser.new
108     req = {}
109     http = "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nabcde"
110     assert parser.execute(req, http)
111     assert_equal '/', req['REQUEST_PATH']
112     assert_equal '/', req['REQUEST_URI']
113     assert_equal 'PUT', req['REQUEST_METHOD']
114     assert_equal 'HTTP/1.0', req['HTTP_VERSION']
115     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
116     assert_equal "abcde", req[:http_body]
117   end
119   def test_put_body_later
120     parser = HttpParser.new
121     req = {}
122     http = "PUT /l HTTP/1.0\r\nContent-Length: 5\r\n\r\n"
123     assert parser.execute(req, http)
124     assert_equal '/l', req['REQUEST_PATH']
125     assert_equal '/l', req['REQUEST_URI']
126     assert_equal 'PUT', req['REQUEST_METHOD']
127     assert_equal 'HTTP/1.0', req['HTTP_VERSION']
128     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
129     assert_equal "", req[:http_body]
130   end
132   def test_fragment_in_uri
133     parser = HttpParser.new
134     req = {}
135     get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
136     ok = false
137     assert_nothing_raised do
138       ok = parser.execute(req, get)
139     end
140     assert ok
141     assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
142     assert_equal 'posts-17408', req['FRAGMENT']
143     assert_equal 'page=1', req['QUERY_STRING']
144   end
146   # lame random garbage maker
147   def rand_data(min, max, readable=true)
148     count = min + ((rand(max)+1) *10).to_i
149     res = count.to_s + "/"
150     
151     if readable
152       res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
153     else
154       res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
155     end
157     return res
158   end
159   
161   def test_horrible_queries
162     parser = HttpParser.new
164     # then that large header names are caught
165     10.times do |c|
166       get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
167       assert_raises Unicorn::HttpParserError do
168         parser.execute({}, get)
169         parser.reset
170       end
171     end
173     # then that large mangled field values are caught
174     10.times do |c|
175       get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
176       assert_raises Unicorn::HttpParserError do
177         parser.execute({}, get)
178         parser.reset
179       end
180     end
182     # then large headers are rejected too
183     get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
184     get << "X-Test: test\r\n" * (80 * 1024)
185     assert_raises Unicorn::HttpParserError do
186       parser.execute({}, get)
187       parser.reset
188     end
190     # finally just that random garbage gets blocked all the time
191     10.times do |c|
192       get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
193       assert_raises Unicorn::HttpParserError do
194         parser.execute({}, get)
195         parser.reset
196       end
197     end
199   end