unicorn 3.3.1 → 3.4.0

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.
@@ -7,24 +7,11 @@ require 'tmpdir'
7
7
 
8
8
  default_port = 8080
9
9
  addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
10
- retries = 100
11
- base = 5000
12
10
  port = sock = lock_path = nil
13
11
 
14
12
  begin
15
- begin
16
- port = base + rand(32768 - base)
17
- while port == default_port
18
- port = base + rand(32768 - base)
19
- end
20
-
21
- sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
22
- sock.bind(Socket.pack_sockaddr_in(port, addr))
23
- sock.listen(5)
24
- rescue Errno::EADDRINUSE, Errno::EACCES
25
- sock.close rescue nil
26
- retry if (retries -= 1) >= 0
27
- end
13
+ sock = TCPServer.new(addr, 0)
14
+ port = sock.addr[1]
28
15
 
29
16
  # since we'll end up closing the random port we just got, there's a race
30
17
  # condition could allow the random port we just chose to reselect itself
@@ -797,7 +797,6 @@ EOF
797
797
 
798
798
  def test_daemonize_redirect_fail
799
799
  pid_file = "#{@tmpdir}/test.pid"
800
- log = Tempfile.new('unicorn_test_log')
801
800
  ucfg = Tempfile.new('unicorn_test_config')
802
801
  ucfg.syswrite("pid #{pid_file}\"\n")
803
802
  err = Tempfile.new('stderr')
@@ -1040,7 +1039,7 @@ EOF
1040
1039
  lock_path = "#{Dir::tmpdir}/unicorn_test." \
1041
1040
  "#{Unicorn::Const::DEFAULT_LISTEN}.lock"
1042
1041
  begin
1043
- lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
1042
+ File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
1044
1043
  yield
1045
1044
  rescue Errno::EEXIST
1046
1045
  lock_path = nil
@@ -265,7 +265,7 @@ logger Logger.new('#{COMMON_TMP.path}')
265
265
 
266
266
  if @pid
267
267
  Process.kill(:QUIT, @pid)
268
- pid2, status = Process.waitpid2(@pid)
268
+ _, status = Process.waitpid2(@pid)
269
269
  assert status.success?
270
270
  end
271
271
 
@@ -100,30 +100,17 @@ end
100
100
  # for a race condition is very small). You may also set UNICORN_TEST_ADDR
101
101
  # to override the default test address (127.0.0.1).
102
102
  def unused_port(addr = '127.0.0.1')
103
- retries = 100
104
- base = 5000
105
103
  port = sock = nil
106
104
  begin
107
- begin
108
- port = base + rand(32768 - base)
109
- while port == Unicorn::Const::DEFAULT_PORT
110
- port = base + rand(32768 - base)
111
- end
112
-
113
- sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
114
- sock.bind(Socket.pack_sockaddr_in(port, addr))
115
- sock.listen(5)
116
- rescue Errno::EADDRINUSE, Errno::EACCES
117
- sock.close rescue nil
118
- retry if (retries -= 1) >= 0
119
- end
105
+ sock = TCPServer.new(addr, 0)
106
+ port = sock.addr[1]
120
107
 
121
108
  # since we'll end up closing the random port we just got, there's a race
122
109
  # condition could allow the random port we just chose to reselect itself
123
110
  # when running tests in parallel with gmake. Create a lock file while
124
111
  # we have the port here to ensure that does not happen .
125
112
  lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
126
- lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
113
+ File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
127
114
  at_exit { File.unlink(lock_path) rescue nil }
128
115
  rescue Errno::EEXIST
129
116
  sock.close rescue nil
@@ -33,6 +33,14 @@ class TestConfigurator < Test::Unit::TestCase
33
33
  assert_equal "0.0.0.0:2007", meth.call('2007')
34
34
  assert_equal "0.0.0.0:2007", meth.call(2007)
35
35
 
36
+ %w([::1]:2007 [::]:2007).each do |addr|
37
+ assert_equal addr, meth.call(addr.dup)
38
+ end
39
+
40
+ # for Rainbows! users only
41
+ assert_equal "[::]:80", meth.call("[::]")
42
+ assert_equal "127.6.6.6:80", meth.call("127.6.6.6")
43
+
36
44
  # the next two aren't portable, consider them unsupported for now
37
45
  # assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('1:2007')
38
46
  # assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('2:2007')
@@ -14,9 +14,10 @@ class HttpParserTest < Test::Unit::TestCase
14
14
 
15
15
  def test_parse_simple
16
16
  parser = HttpParser.new
17
- req = {}
18
- http = "GET / HTTP/1.1\r\n\r\n"
19
- assert_equal req, parser.headers(req, http)
17
+ req = parser.env
18
+ http = parser.buf
19
+ http << "GET / HTTP/1.1\r\n\r\n"
20
+ assert_equal req, parser.parse
20
21
  assert_equal '', http
21
22
 
22
23
  assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
@@ -31,14 +32,14 @@ class HttpParserTest < Test::Unit::TestCase
31
32
  parser.clear
32
33
  req.clear
33
34
 
34
- http = "G"
35
- assert_nil parser.headers(req, http)
35
+ http << "G"
36
+ assert_nil parser.parse
36
37
  assert_equal "G", http
37
38
  assert req.empty?
38
39
 
39
40
  # 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)
41
+ http << "ET /hello-world HTTP/1.1\r\n\r\n"
42
+ assert parser.parse
42
43
 
43
44
  assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
44
45
  assert_equal '/hello-world', req['REQUEST_PATH']
@@ -53,161 +54,161 @@ class HttpParserTest < Test::Unit::TestCase
53
54
 
54
55
  def test_tab_lws
55
56
  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
57
+ req = parser.env
58
+ parser.buf << "GET / HTTP/1.1\r\nHost:\tfoo.bar\r\n\r\n"
59
+ assert_equal req.object_id, parser.parse.object_id
59
60
  assert_equal "foo.bar", req['HTTP_HOST']
60
61
  end
61
62
 
62
63
  def test_connection_close_no_ka
63
64
  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
65
+ req = parser.env
66
+ parser.buf << "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
67
+ assert_equal req.object_id, parser.parse.object_id
67
68
  assert_equal "GET", req['REQUEST_METHOD']
68
69
  assert ! parser.keepalive?
69
70
  end
70
71
 
71
72
  def test_connection_keep_alive_ka
72
73
  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
74
+ req = parser.env
75
+ parser.buf << "HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
76
+ assert_equal req.object_id, parser.parse.object_id
76
77
  assert parser.keepalive?
77
78
  end
78
79
 
79
80
  def test_connection_keep_alive_no_body
80
81
  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
82
+ req = parser.env
83
+ parser.buf << "POST / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
84
+ assert_equal req.object_id, parser.parse.object_id
84
85
  assert parser.keepalive?
85
86
  end
86
87
 
87
88
  def test_connection_keep_alive_no_body_empty
88
89
  parser = HttpParser.new
89
- req = {}
90
- tmp = "POST / HTTP/1.1\r\n" \
91
- "Content-Length: 0\r\n" \
92
- "Connection: keep-alive\r\n\r\n"
93
- assert_equal req.object_id, parser.headers(req, tmp).object_id
90
+ req = parser.env
91
+ parser.buf << "POST / HTTP/1.1\r\n" \
92
+ "Content-Length: 0\r\n" \
93
+ "Connection: keep-alive\r\n\r\n"
94
+ assert_equal req.object_id, parser.parse.object_id
94
95
  assert parser.keepalive?
95
96
  end
96
97
 
97
98
  def test_connection_keep_alive_ka_bad_version
98
99
  parser = HttpParser.new
99
- req = {}
100
- tmp = "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"
101
- assert_equal req.object_id, parser.headers(req, tmp).object_id
100
+ req = parser.env
101
+ parser.buf << "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"
102
+ assert_equal req.object_id, parser.parse.object_id
102
103
  assert parser.keepalive?
103
104
  end
104
105
 
105
106
  def test_parse_server_host_default_port
106
107
  parser = HttpParser.new
107
- req = {}
108
- tmp = "GET / HTTP/1.1\r\nHost: foo\r\n\r\n"
109
- assert_equal req, parser.headers(req, tmp)
108
+ req = parser.env
109
+ parser.buf << "GET / HTTP/1.1\r\nHost: foo\r\n\r\n"
110
+ assert_equal req, parser.parse
110
111
  assert_equal 'foo', req['SERVER_NAME']
111
112
  assert_equal '80', req['SERVER_PORT']
112
- assert_equal '', tmp
113
+ assert_equal '', parser.buf
113
114
  assert parser.keepalive?
114
115
  end
115
116
 
116
117
  def test_parse_server_host_alt_port
117
118
  parser = HttpParser.new
118
- req = {}
119
- tmp = "GET / HTTP/1.1\r\nHost: foo:999\r\n\r\n"
120
- assert_equal req, parser.headers(req, tmp)
119
+ req = parser.env
120
+ parser.buf << "GET / HTTP/1.1\r\nHost: foo:999\r\n\r\n"
121
+ assert_equal req, parser.parse
121
122
  assert_equal 'foo', req['SERVER_NAME']
122
123
  assert_equal '999', req['SERVER_PORT']
123
- assert_equal '', tmp
124
+ assert_equal '', parser.buf
124
125
  assert parser.keepalive?
125
126
  end
126
127
 
127
128
  def test_parse_server_host_empty_port
128
129
  parser = HttpParser.new
129
- req = {}
130
- tmp = "GET / HTTP/1.1\r\nHost: foo:\r\n\r\n"
131
- assert_equal req, parser.headers(req, tmp)
130
+ req = parser.env
131
+ parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n\r\n"
132
+ assert_equal req, parser.parse
132
133
  assert_equal 'foo', req['SERVER_NAME']
133
134
  assert_equal '80', req['SERVER_PORT']
134
- assert_equal '', tmp
135
+ assert_equal '', parser.buf
135
136
  assert parser.keepalive?
136
137
  end
137
138
 
138
139
  def test_parse_server_host_xfp_https
139
140
  parser = HttpParser.new
140
- req = {}
141
- tmp = "GET / HTTP/1.1\r\nHost: foo:\r\n" \
142
- "X-Forwarded-Proto: https\r\n\r\n"
143
- assert_equal req, parser.headers(req, tmp)
141
+ req = parser.env
142
+ parser.buf << "GET / HTTP/1.1\r\nHost: foo:\r\n" \
143
+ "X-Forwarded-Proto: https\r\n\r\n"
144
+ assert_equal req, parser.parse
144
145
  assert_equal 'foo', req['SERVER_NAME']
145
146
  assert_equal '443', req['SERVER_PORT']
146
- assert_equal '', tmp
147
+ assert_equal '', parser.buf
147
148
  assert parser.keepalive?
148
149
  end
149
150
 
150
151
  def test_parse_xfp_https_chained
151
152
  parser = HttpParser.new
152
- req = {}
153
- tmp = "GET / HTTP/1.0\r\n" \
154
- "X-Forwarded-Proto: https,http\r\n\r\n"
155
- assert_equal req, parser.headers(req, tmp)
153
+ req = parser.env
154
+ parser.buf << "GET / HTTP/1.0\r\n" \
155
+ "X-Forwarded-Proto: https,http\r\n\r\n"
156
+ assert_equal req, parser.parse
156
157
  assert_equal '443', req['SERVER_PORT'], req.inspect
157
158
  assert_equal 'https', req['rack.url_scheme'], req.inspect
158
- assert_equal '', tmp
159
+ assert_equal '', parser.buf
159
160
  end
160
161
 
161
162
  def test_parse_xfp_https_chained_backwards
162
163
  parser = HttpParser.new
163
- req = {}
164
- tmp = "GET / HTTP/1.0\r\n" \
164
+ req = parser.env
165
+ parser.buf << "GET / HTTP/1.0\r\n" \
165
166
  "X-Forwarded-Proto: http,https\r\n\r\n"
166
- assert_equal req, parser.headers(req, tmp)
167
+ assert_equal req, parser.parse
167
168
  assert_equal '80', req['SERVER_PORT'], req.inspect
168
169
  assert_equal 'http', req['rack.url_scheme'], req.inspect
169
- assert_equal '', tmp
170
+ assert_equal '', parser.buf
170
171
  end
171
172
 
172
173
  def test_parse_xfp_gopher_is_ignored
173
174
  parser = HttpParser.new
174
- req = {}
175
- tmp = "GET / HTTP/1.0\r\n" \
176
- "X-Forwarded-Proto: gopher\r\n\r\n"
177
- assert_equal req, parser.headers(req, tmp)
175
+ req = parser.env
176
+ parser.buf << "GET / HTTP/1.0\r\n" \
177
+ "X-Forwarded-Proto: gopher\r\n\r\n"
178
+ assert_equal req, parser.parse
178
179
  assert_equal '80', req['SERVER_PORT'], req.inspect
179
180
  assert_equal 'http', req['rack.url_scheme'], req.inspect
180
- assert_equal '', tmp
181
+ assert_equal '', parser.buf
181
182
  end
182
183
 
183
184
  def test_parse_x_forwarded_ssl_on
184
185
  parser = HttpParser.new
185
- req = {}
186
- tmp = "GET / HTTP/1.0\r\n" \
187
- "X-Forwarded-Ssl: on\r\n\r\n"
188
- assert_equal req, parser.headers(req, tmp)
186
+ req = parser.env
187
+ parser.buf << "GET / HTTP/1.0\r\n" \
188
+ "X-Forwarded-Ssl: on\r\n\r\n"
189
+ assert_equal req, parser.parse
189
190
  assert_equal '443', req['SERVER_PORT'], req.inspect
190
191
  assert_equal 'https', req['rack.url_scheme'], req.inspect
191
- assert_equal '', tmp
192
+ assert_equal '', parser.buf
192
193
  end
193
194
 
194
195
  def test_parse_x_forwarded_ssl_off
195
196
  parser = HttpParser.new
196
- req = {}
197
- tmp = "GET / HTTP/1.0\r\n" \
198
- "X-Forwarded-Ssl: off\r\n\r\n"
199
- assert_equal req, parser.headers(req, tmp)
197
+ req = parser.env
198
+ parser.buf << "GET / HTTP/1.0\r\nX-Forwarded-Ssl: off\r\n\r\n"
199
+ assert_equal req, parser.parse
200
200
  assert_equal '80', req['SERVER_PORT'], req.inspect
201
201
  assert_equal 'http', req['rack.url_scheme'], req.inspect
202
- assert_equal '', tmp
202
+ assert_equal '', parser.buf
203
203
  end
204
204
 
205
205
  def test_parse_strange_headers
206
206
  parser = HttpParser.new
207
- req = {}
207
+ req = parser.env
208
208
  should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
209
- assert_equal req, parser.headers(req, should_be_good)
210
- assert_equal '', should_be_good
209
+ parser.buf << should_be_good
210
+ assert_equal req, parser.parse
211
+ assert_equal '', parser.buf
211
212
  assert parser.keepalive?
212
213
  end
213
214
 
@@ -217,14 +218,14 @@ class HttpParserTest < Test::Unit::TestCase
217
218
  def test_nasty_pound_header
218
219
  parser = HttpParser.new
219
220
  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"
220
- req = {}
221
- buf = nasty_pound_header.dup
221
+ req = parser.env
222
+ parser.buf << nasty_pound_header.dup
222
223
 
223
224
  assert nasty_pound_header =~ /(-----BEGIN .*--END CERTIFICATE-----)/m
224
225
  expect = $1.dup
225
226
  expect.gsub!(/\r\n\t/, ' ')
226
- assert_equal req, parser.headers(req, buf)
227
- assert_equal '', buf
227
+ assert_equal req, parser.parse
228
+ assert_equal '', parser.buf
228
229
  assert_equal expect, req['HTTP_X_SSL_BULLSHIT']
229
230
  end
230
231
 
@@ -235,9 +236,10 @@ class HttpParserTest < Test::Unit::TestCase
235
236
  "\t\r\n" \
236
237
  " \r\n" \
237
238
  " ASDF\r\n\r\n"
238
- req = {}
239
- assert_equal req, parser.headers(req, header)
240
- assert_equal '', header
239
+ parser.buf << header
240
+ req = parser.env
241
+ assert_equal req, parser.parse
242
+ assert_equal '', parser.buf
241
243
  assert_equal 'ASDF', req['HTTP_X_ASDF']
242
244
  end
243
245
 
@@ -249,9 +251,10 @@ class HttpParserTest < Test::Unit::TestCase
249
251
  "\t\r\n" \
250
252
  " x\r\n" \
251
253
  " ASDF\r\n\r\n"
252
- req = {}
253
- assert_equal req, parser.headers(req, header)
254
- assert_equal '', header
254
+ req = parser.env
255
+ parser.buf << header
256
+ assert_equal req, parser.parse
257
+ assert_equal '', parser.buf
255
258
  assert_equal 'hi y x ASDF', req['HTTP_X_ASDF']
256
259
  end
257
260
 
@@ -261,8 +264,9 @@ class HttpParserTest < Test::Unit::TestCase
261
264
  "Host: \r\n" \
262
265
  " YHBT.net\r\n" \
263
266
  "\r\n"
264
- req = {}
265
- assert_equal req, parser.headers(req, header)
267
+ parser.buf << header
268
+ req = parser.env
269
+ assert_equal req, parser.parse
266
270
  assert_equal 'example.com', req['HTTP_HOST']
267
271
  end
268
272
 
@@ -271,21 +275,22 @@ class HttpParserTest < Test::Unit::TestCase
271
275
  # in case we ever go multithreaded/evented...
272
276
  def test_resumable_continuations
273
277
  nr = 1000
274
- req = {}
275
278
  header = "GET / HTTP/1.1\r\n" \
276
279
  "X-ASDF: \r\n" \
277
280
  " hello\r\n"
278
281
  tmp = []
279
282
  nr.times { |i|
280
283
  parser = HttpParser.new
281
- assert parser.headers(req, "#{header} #{i}\r\n").nil?
284
+ req = parser.env
285
+ parser.buf << "#{header} #{i}\r\n"
286
+ assert parser.parse.nil?
282
287
  asdf = req['HTTP_X_ASDF']
283
288
  assert_equal "hello #{i}", asdf
284
289
  tmp << [ parser, asdf ]
285
- req.clear
286
290
  }
287
291
  tmp.each_with_index { |(parser, asdf), i|
288
- assert_equal req, parser.headers(req, "#{header} #{i}\r\n .\r\n\r\n")
292
+ parser.buf << " .\r\n\r\n"
293
+ assert parser.parse
289
294
  assert_equal "hello #{i} .", asdf
290
295
  }
291
296
  end
@@ -296,8 +301,9 @@ class HttpParserTest < Test::Unit::TestCase
296
301
  " y\r\n" \
297
302
  "Host: hello\r\n" \
298
303
  "\r\n"
299
- req = {}
300
- assert_raises(HttpParserError) { parser.headers(req, header) }
304
+ parser.buf << header
305
+ req = parser.env
306
+ assert_raises(HttpParserError) { parser.parse }
301
307
  end
302
308
 
303
309
  def test_parse_ie6_urls
@@ -309,7 +315,7 @@ class HttpParserTest < Test::Unit::TestCase
309
315
  /mal"formed"?
310
316
  ).each do |path|
311
317
  parser = HttpParser.new
312
- req = {}
318
+ req = parser.env
313
319
  sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
314
320
  assert_equal req, parser.headers(req, sorta_safe)
315
321
  assert_equal path, req['REQUEST_URI']
@@ -320,7 +326,7 @@ class HttpParserTest < Test::Unit::TestCase
320
326
 
321
327
  def test_parse_error
322
328
  parser = HttpParser.new
323
- req = {}
329
+ req = parser.env
324
330
  bad_http = "GET / SsUTF/1.1"
325
331
 
326
332
  assert_raises(HttpParserError) { parser.headers(req, bad_http) }
@@ -334,7 +340,7 @@ class HttpParserTest < Test::Unit::TestCase
334
340
 
335
341
  def test_piecemeal
336
342
  parser = HttpParser.new
337
- req = {}
343
+ req = parser.env
338
344
  http = "GET"
339
345
  assert_nil parser.headers(req, http)
340
346
  assert_nil parser.headers(req, http)
@@ -356,9 +362,10 @@ class HttpParserTest < Test::Unit::TestCase
356
362
  # not common, but underscores do appear in practice
357
363
  def test_absolute_uri_underscores
358
364
  parser = HttpParser.new
359
- req = {}
365
+ req = parser.env
360
366
  http = "GET http://under_score.example.com/foo?q=bar HTTP/1.0\r\n\r\n"
361
- assert_equal req, parser.headers(req, http)
367
+ parser.buf << http
368
+ assert_equal req, parser.parse
362
369
  assert_equal 'http', req['rack.url_scheme']
363
370
  assert_equal '/foo?q=bar', req['REQUEST_URI']
364
371
  assert_equal '/foo', req['REQUEST_PATH']
@@ -367,16 +374,17 @@ class HttpParserTest < Test::Unit::TestCase
367
374
  assert_equal 'under_score.example.com', req['HTTP_HOST']
368
375
  assert_equal 'under_score.example.com', req['SERVER_NAME']
369
376
  assert_equal '80', req['SERVER_PORT']
370
- assert_equal "", http
377
+ assert_equal "", parser.buf
371
378
  assert ! parser.keepalive?
372
379
  end
373
380
 
374
381
  # some dumb clients add users because they're stupid
375
382
  def test_absolute_uri_w_user
376
383
  parser = HttpParser.new
377
- req = {}
384
+ req = parser.env
378
385
  http = "GET http://user%20space@example.com/foo?q=bar HTTP/1.0\r\n\r\n"
379
- assert_equal req, parser.headers(req, http)
386
+ parser.buf << http
387
+ assert_equal req, parser.parse
380
388
  assert_equal 'http', req['rack.url_scheme']
381
389
  assert_equal '/foo?q=bar', req['REQUEST_URI']
382
390
  assert_equal '/foo', req['REQUEST_PATH']
@@ -385,7 +393,7 @@ class HttpParserTest < Test::Unit::TestCase
385
393
  assert_equal 'example.com', req['HTTP_HOST']
386
394
  assert_equal 'example.com', req['SERVER_NAME']
387
395
  assert_equal '80', req['SERVER_PORT']
388
- assert_equal "", http
396
+ assert_equal "", parser.buf
389
397
  assert ! parser.keepalive?
390
398
  end
391
399
 
@@ -394,7 +402,7 @@ class HttpParserTest < Test::Unit::TestCase
394
402
  def test_absolute_uri_uri_parse
395
403
  "#{URI::REGEXP::PATTERN::UNRESERVED};:&=+$,".split(//).each do |char|
396
404
  parser = HttpParser.new
397
- req = {}
405
+ req = parser.env
398
406
  http = "GET http://#{char}@example.com/ HTTP/1.0\r\n\r\n"
399
407
  assert_equal req, parser.headers(req, http)
400
408
  assert_equal 'http', req['rack.url_scheme']
@@ -412,9 +420,9 @@ class HttpParserTest < Test::Unit::TestCase
412
420
 
413
421
  def test_absolute_uri
414
422
  parser = HttpParser.new
415
- req = {}
416
- http = "GET http://example.com/foo?q=bar HTTP/1.0\r\n\r\n"
417
- assert_equal req, parser.headers(req, http)
423
+ req = parser.env
424
+ parser.buf << "GET http://example.com/foo?q=bar HTTP/1.0\r\n\r\n"
425
+ assert_equal req, parser.parse
418
426
  assert_equal 'http', req['rack.url_scheme']
419
427
  assert_equal '/foo?q=bar', req['REQUEST_URI']
420
428
  assert_equal '/foo', req['REQUEST_PATH']
@@ -423,17 +431,18 @@ class HttpParserTest < Test::Unit::TestCase
423
431
  assert_equal 'example.com', req['HTTP_HOST']
424
432
  assert_equal 'example.com', req['SERVER_NAME']
425
433
  assert_equal '80', req['SERVER_PORT']
426
- assert_equal "", http
434
+ assert_equal "", parser.buf
427
435
  assert ! parser.keepalive?
428
436
  end
429
437
 
430
438
  # X-Forwarded-Proto is not in rfc2616, absolute URIs are, however...
431
439
  def test_absolute_uri_https
432
440
  parser = HttpParser.new
433
- req = {}
441
+ req = parser.env
434
442
  http = "GET https://example.com/foo?q=bar HTTP/1.1\r\n" \
435
443
  "X-Forwarded-Proto: http\r\n\r\n"
436
- assert_equal req, parser.headers(req, http)
444
+ parser.buf << http
445
+ assert_equal req, parser.parse
437
446
  assert_equal 'https', req['rack.url_scheme']
438
447
  assert_equal '/foo?q=bar', req['REQUEST_URI']
439
448
  assert_equal '/foo', req['REQUEST_PATH']
@@ -442,17 +451,17 @@ class HttpParserTest < Test::Unit::TestCase
442
451
  assert_equal 'example.com', req['HTTP_HOST']
443
452
  assert_equal 'example.com', req['SERVER_NAME']
444
453
  assert_equal '443', req['SERVER_PORT']
445
- assert_equal "", http
454
+ assert_equal "", parser.buf
446
455
  assert parser.keepalive?
447
456
  end
448
457
 
449
458
  # Host: header should be ignored for absolute URIs
450
459
  def test_absolute_uri_with_port
451
460
  parser = HttpParser.new
452
- req = {}
453
- http = "GET http://example.com:8080/foo?q=bar HTTP/1.2\r\n" \
461
+ req = parser.env
462
+ parser.buf << "GET http://example.com:8080/foo?q=bar HTTP/1.2\r\n" \
454
463
  "Host: bad.example.com\r\n\r\n"
455
- assert_equal req, parser.headers(req, http)
464
+ assert_equal req, parser.parse
456
465
  assert_equal 'http', req['rack.url_scheme']
457
466
  assert_equal '/foo?q=bar', req['REQUEST_URI']
458
467
  assert_equal '/foo', req['REQUEST_PATH']
@@ -461,16 +470,16 @@ class HttpParserTest < Test::Unit::TestCase
461
470
  assert_equal 'example.com:8080', req['HTTP_HOST']
462
471
  assert_equal 'example.com', req['SERVER_NAME']
463
472
  assert_equal '8080', req['SERVER_PORT']
464
- assert_equal "", http
473
+ assert_equal "", parser.buf
465
474
  assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
466
475
  end
467
476
 
468
477
  def test_absolute_uri_with_empty_port
469
478
  parser = HttpParser.new
470
- req = {}
471
- http = "GET https://example.com:/foo?q=bar HTTP/1.1\r\n" \
479
+ req = parser.env
480
+ parser.buf << "GET https://example.com:/foo?q=bar HTTP/1.1\r\n" \
472
481
  "Host: bad.example.com\r\n\r\n"
473
- assert_equal req, parser.headers(req, http)
482
+ assert_equal req, parser.parse
474
483
  assert_equal 'https', req['rack.url_scheme']
475
484
  assert_equal '/foo?q=bar', req['REQUEST_URI']
476
485
  assert_equal '/foo', req['REQUEST_PATH']
@@ -479,42 +488,192 @@ class HttpParserTest < Test::Unit::TestCase
479
488
  assert_equal 'example.com:', req['HTTP_HOST']
480
489
  assert_equal 'example.com', req['SERVER_NAME']
481
490
  assert_equal '443', req['SERVER_PORT']
491
+ assert_equal "", parser.buf
492
+ assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
493
+ end
494
+
495
+ def test_absolute_ipv6_uri
496
+ parser = HttpParser.new
497
+ req = parser.env
498
+ url = "http://[::1]/foo?q=bar"
499
+ http = "GET #{url} HTTP/1.1\r\n" \
500
+ "Host: bad.example.com\r\n\r\n"
501
+ assert_equal req, parser.headers(req, http)
502
+ assert_equal 'http', req['rack.url_scheme']
503
+ assert_equal '/foo?q=bar', req['REQUEST_URI']
504
+ assert_equal '/foo', req['REQUEST_PATH']
505
+ assert_equal 'q=bar', req['QUERY_STRING']
506
+
507
+ uri = URI.parse(url)
508
+ assert_equal "[::1]", uri.host,
509
+ "URI.parse changed upstream for #{url}? host=#{uri.host}"
510
+ assert_equal "[::1]", req['HTTP_HOST']
511
+ assert_equal "[::1]", req['SERVER_NAME']
512
+ assert_equal '80', req['SERVER_PORT']
482
513
  assert_equal "", http
483
514
  assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
484
515
  end
485
516
 
486
- def test_put_body_oneshot
517
+ def test_absolute_ipv6_uri_alpha
518
+ parser = HttpParser.new
519
+ req = parser.env
520
+ url = "http://[::a]/"
521
+ http = "GET #{url} HTTP/1.1\r\n" \
522
+ "Host: bad.example.com\r\n\r\n"
523
+ assert_equal req, parser.headers(req, http)
524
+ assert_equal 'http', req['rack.url_scheme']
525
+
526
+ uri = URI.parse(url)
527
+ assert_equal "[::a]", uri.host,
528
+ "URI.parse changed upstream for #{url}? host=#{uri.host}"
529
+ assert_equal "[::a]", req['HTTP_HOST']
530
+ assert_equal "[::a]", req['SERVER_NAME']
531
+ assert_equal '80', req['SERVER_PORT']
532
+ end
533
+
534
+ def test_absolute_ipv6_uri_alpha_2
487
535
  parser = HttpParser.new
488
- req = {}
489
- http = "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nabcde"
536
+ req = parser.env
537
+ url = "http://[::B]/"
538
+ http = "GET #{url} HTTP/1.1\r\n" \
539
+ "Host: bad.example.com\r\n\r\n"
490
540
  assert_equal req, parser.headers(req, http)
541
+ assert_equal 'http', req['rack.url_scheme']
542
+
543
+ uri = URI.parse(url)
544
+ assert_equal "[::B]", uri.host,
545
+ "URI.parse changed upstream for #{url}? host=#{uri.host}"
546
+ assert_equal "[::B]", req['HTTP_HOST']
547
+ assert_equal "[::B]", req['SERVER_NAME']
548
+ assert_equal '80', req['SERVER_PORT']
549
+ end
550
+
551
+ def test_absolute_ipv6_uri_with_empty_port
552
+ parser = HttpParser.new
553
+ req = parser.env
554
+ url = "https://[::1]:/foo?q=bar"
555
+ http = "GET #{url} HTTP/1.1\r\n" \
556
+ "Host: bad.example.com\r\n\r\n"
557
+ assert_equal req, parser.headers(req, http)
558
+ assert_equal 'https', req['rack.url_scheme']
559
+ assert_equal '/foo?q=bar', req['REQUEST_URI']
560
+ assert_equal '/foo', req['REQUEST_PATH']
561
+ assert_equal 'q=bar', req['QUERY_STRING']
562
+
563
+ uri = URI.parse(url)
564
+ assert_equal "[::1]", uri.host,
565
+ "URI.parse changed upstream for #{url}? host=#{uri.host}"
566
+ assert_equal "[::1]:", req['HTTP_HOST']
567
+ assert_equal "[::1]", req['SERVER_NAME']
568
+ assert_equal '443', req['SERVER_PORT']
569
+ assert_equal "", http
570
+ assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
571
+ end
572
+
573
+ def test_absolute_ipv6_uri_with_port
574
+ parser = HttpParser.new
575
+ req = parser.env
576
+ url = "https://[::1]:666/foo?q=bar"
577
+ http = "GET #{url} HTTP/1.1\r\n" \
578
+ "Host: bad.example.com\r\n\r\n"
579
+ assert_equal req, parser.headers(req, http)
580
+ assert_equal 'https', req['rack.url_scheme']
581
+ assert_equal '/foo?q=bar', req['REQUEST_URI']
582
+ assert_equal '/foo', req['REQUEST_PATH']
583
+ assert_equal 'q=bar', req['QUERY_STRING']
584
+
585
+ uri = URI.parse(url)
586
+ assert_equal "[::1]", uri.host,
587
+ "URI.parse changed upstream for #{url}? host=#{uri.host}"
588
+ assert_equal "[::1]:666", req['HTTP_HOST']
589
+ assert_equal "[::1]", req['SERVER_NAME']
590
+ assert_equal '666', req['SERVER_PORT']
591
+ assert_equal "", http
592
+ assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
593
+ end
594
+
595
+ def test_ipv6_host_header
596
+ parser = HttpParser.new
597
+ req = parser.env
598
+ parser.buf << "GET / HTTP/1.1\r\n" \
599
+ "Host: [::1]\r\n\r\n"
600
+ assert_equal req, parser.parse
601
+ assert_equal "[::1]", req['HTTP_HOST']
602
+ assert_equal "[::1]", req['SERVER_NAME']
603
+ assert_equal '80', req['SERVER_PORT']
604
+ assert_equal "", parser.buf
605
+ assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
606
+ end
607
+
608
+ def test_ipv6_host_header_with_port
609
+ parser = HttpParser.new
610
+ req = parser.env
611
+ parser.buf << "GET / HTTP/1.1\r\n" \
612
+ "Host: [::1]:666\r\n\r\n"
613
+ assert_equal req, parser.parse
614
+ assert_equal "[::1]", req['SERVER_NAME']
615
+ assert_equal '666', req['SERVER_PORT']
616
+ assert_equal "[::1]:666", req['HTTP_HOST']
617
+ assert_equal "", parser.buf
618
+ assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
619
+ end
620
+
621
+ def test_ipv6_host_header_with_empty_port
622
+ parser = HttpParser.new
623
+ req = parser.env
624
+ parser.buf << "GET / HTTP/1.1\r\nHost: [::1]:\r\n\r\n"
625
+ assert_equal req, parser.parse
626
+ assert_equal "[::1]", req['SERVER_NAME']
627
+ assert_equal '80', req['SERVER_PORT']
628
+ assert_equal "[::1]:", req['HTTP_HOST']
629
+ assert_equal "", parser.buf
630
+ assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
631
+ end
632
+
633
+ # XXX Highly unlikely..., just make sure we don't segfault or assert on it
634
+ def test_broken_ipv6_host_header
635
+ parser = HttpParser.new
636
+ req = parser.env
637
+ parser.buf << "GET / HTTP/1.1\r\nHost: [::1:\r\n\r\n"
638
+ assert_equal req, parser.parse
639
+ assert_equal "[", req['SERVER_NAME']
640
+ assert_equal ':1:', req['SERVER_PORT']
641
+ assert_equal "[::1:", req['HTTP_HOST']
642
+ assert_equal "", parser.buf
643
+ end
644
+
645
+ def test_put_body_oneshot
646
+ parser = HttpParser.new
647
+ req = parser.env
648
+ parser.buf << "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nabcde"
649
+ assert_equal req, parser.parse
491
650
  assert_equal '/', req['REQUEST_PATH']
492
651
  assert_equal '/', req['REQUEST_URI']
493
652
  assert_equal 'PUT', req['REQUEST_METHOD']
494
653
  assert_equal 'HTTP/1.0', req['HTTP_VERSION']
495
654
  assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
496
- assert_equal "abcde", http
655
+ assert_equal "abcde", parser.buf
497
656
  assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
498
657
  end
499
658
 
500
659
  def test_put_body_later
501
660
  parser = HttpParser.new
502
- req = {}
503
- http = "PUT /l HTTP/1.0\r\nContent-Length: 5\r\n\r\n"
504
- assert_equal req, parser.headers(req, http)
661
+ req = parser.env
662
+ parser.buf << "PUT /l HTTP/1.0\r\nContent-Length: 5\r\n\r\n"
663
+ assert_equal req, parser.parse
505
664
  assert_equal '/l', req['REQUEST_PATH']
506
665
  assert_equal '/l', req['REQUEST_URI']
507
666
  assert_equal 'PUT', req['REQUEST_METHOD']
508
667
  assert_equal 'HTTP/1.0', req['HTTP_VERSION']
509
668
  assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
510
- assert_equal "", http
669
+ assert_equal "", parser.buf
511
670
  assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
512
671
  end
513
672
 
514
673
  def test_unknown_methods
515
674
  %w(GETT HEADR XGET XHEAD).each { |m|
516
675
  parser = HttpParser.new
517
- req = {}
676
+ req = parser.env
518
677
  s = "#{m} /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
519
678
  ok = false
520
679
  assert_nothing_raised do
@@ -532,17 +691,18 @@ class HttpParserTest < Test::Unit::TestCase
532
691
 
533
692
  def test_fragment_in_uri
534
693
  parser = HttpParser.new
535
- req = {}
694
+ req = parser.env
536
695
  get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
696
+ parser.buf << get
537
697
  ok = false
538
698
  assert_nothing_raised do
539
- ok = parser.headers(req, get)
699
+ ok = parser.parse
540
700
  end
541
701
  assert ok
542
702
  assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
543
703
  assert_equal 'posts-17408', req['FRAGMENT']
544
704
  assert_equal 'page=1', req['QUERY_STRING']
545
- assert_equal '', get
705
+ assert_equal '', parser.buf
546
706
  assert parser.keepalive?
547
707
  end
548
708
 
@@ -568,7 +728,8 @@ class HttpParserTest < Test::Unit::TestCase
568
728
  10.times do |c|
569
729
  get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
570
730
  assert_raises Unicorn::HttpParserError do
571
- parser.headers({}, get)
731
+ parser.buf << get
732
+ parser.parse
572
733
  parser.clear
573
734
  end
574
735
  end
@@ -577,7 +738,8 @@ class HttpParserTest < Test::Unit::TestCase
577
738
  10.times do |c|
578
739
  get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
579
740
  assert_raises Unicorn::HttpParserError do
580
- parser.headers({}, get)
741
+ parser.buf << get
742
+ parser.parse
581
743
  parser.clear
582
744
  end
583
745
  end
@@ -585,16 +747,18 @@ class HttpParserTest < Test::Unit::TestCase
585
747
  # then large headers are rejected too
586
748
  get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
587
749
  get << "X-Test: test\r\n" * (80 * 1024)
750
+ parser.buf << get
588
751
  assert_raises Unicorn::HttpParserError do
589
- parser.headers({}, get)
590
- parser.clear
752
+ parser.parse
591
753
  end
754
+ parser.clear
592
755
 
593
756
  # finally just that random garbage gets blocked all the time
594
757
  10.times do |c|
595
758
  get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
596
759
  assert_raises Unicorn::HttpParserError do
597
- parser.headers({}, get)
760
+ parser.buf << get
761
+ parser.parse
598
762
  parser.clear
599
763
  end
600
764
  end