Skip to content

Commit 04e8c9c

Browse files
committed
Fix nginx duplicate server name warnings in certain cases.
If the api-umbrella.yml config manually defined a wildcard host, then two hosts could end up defined for the wildcard server name ("_"). This cleans up the implementation a bit, and prevents this duplicate server name warning. This doesn't actually change the existing behavior, so there's still some certain configuration situations that might not really make sense (non-default wildcards), but we'll keep everything defined as-is to match the hosts config.
1 parent 2b0c8ac commit 04e8c9c

File tree

4 files changed

+189
-11
lines changed

4 files changed

+189
-11
lines changed

src/api-umbrella/cli/read_config.lua

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,23 @@ local function set_computed_config()
202202
end
203203
end
204204

205-
-- Add a default fallback host that will match any hostname, but doesn't
206-
-- include any host-specific settings in nginx (like rewrites). This host can
207-
-- still then be used to match APIs for unknown hosts.
208-
table.insert(config["hosts"], {
209-
hostname = "*",
210-
_nginx_server_name = "_",
211-
default = (not default_host_exists),
212-
})
205+
-- If a default host hasn't been explicitly defined, then add a default
206+
-- fallback host that will match any hostname (but doesn't include any
207+
-- host-specific settings in nginx, like rewrites). A default host is
208+
-- necessary so nginx handles all hostnames, allowing APIs to be matched for
209+
-- hosts that are only defined in the API backend configuration.
210+
if not default_host_exists then
211+
table.insert(config["hosts"], {
212+
hostname = "*",
213+
-- Use a slightly different nginx server name to avoid any conflicts with
214+
-- explicitly defined wildcard hosts (but aren't the default, which
215+
-- doesn't seem particularly likely). There's nothing actually special
216+
-- about "_" in nginx, it's just a hostname that won't match anything
217+
-- real.
218+
_nginx_server_name = "__",
219+
default = true,
220+
})
221+
end
213222

214223
local default_hostname
215224
if config["hosts"] then

templates/etc/nginx/router.conf.mustache

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,12 +221,10 @@ http {
221221
server {
222222
{{#listen.addresses}}
223223
listen {{.}}:{{http_port}}{{#default}} default_server so_keepalive=on{{/default}};
224+
listen {{.}}:{{https_port}} ssl{{#default}} default_server so_keepalive=on{{/default}};
224225
{{/listen.addresses}}
225226
server_name {{_nginx_server_name}};
226227

227-
{{#listen.addresses}}
228-
listen {{.}}:{{https_port}} ssl{{#default}} default_server so_keepalive=on{{/default}};
229-
{{/listen.addresses}}
230228
{{#ssl_cert}}
231229
ssl_certificate {{ssl_cert}};
232230
ssl_certificate_key {{ssl_cert_key}};

test/proxy/test_nginx_rewrites.rb

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ def setup
99
end
1010

1111
def test_basic_rewrites
12+
log_tail = LogTail.new("nginx/current")
1213
override_config({
1314
"hosts" => [
1415
{
@@ -24,6 +25,8 @@ def test_basic_rewrites
2425
},
2526
],
2627
}, "--router") do
28+
refute_nginx_duplicate_server_name_warnings(log_tail)
29+
2730
# Basic rewrite
2831
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options)
2932
assert_response_code(301, response)
@@ -37,6 +40,7 @@ def test_basic_rewrites
3740
end
3841

3942
def test_default_host
43+
log_tail = LogTail.new("nginx/current")
4044
override_config({
4145
"hosts" => [
4246
{
@@ -51,6 +55,8 @@ def test_default_host
5155
},
5256
],
5357
}, "--router") do
58+
refute_nginx_duplicate_server_name_warnings(log_tail)
59+
5460
# Known host without rewrites
5561
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
5662
:headers => { "Host" => "default.foo" },
@@ -74,6 +80,7 @@ def test_default_host
7480
end
7581

7682
def test_no_default_host
83+
log_tail = LogTail.new("nginx/current")
7784
override_config({
7885
"hosts" => [
7986
{
@@ -84,6 +91,8 @@ def test_no_default_host
8491
},
8592
],
8693
}, "--router") do
94+
refute_nginx_duplicate_server_name_warnings(log_tail)
95+
8796
# Known host without rewrites
8897
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
8998
:headers => { "Host" => "default.foo" },
@@ -99,7 +108,138 @@ def test_no_default_host
99108
end
100109
end
101110

111+
# When nginx has no default_server defined, then the first server defined
112+
# becomes the default one. So test to see how this ordering also interacts
113+
# with a wildcard host.
114+
def test_no_default_host_wildcard_first
115+
log_tail = LogTail.new("nginx/current")
116+
override_config({
117+
"hosts" => [
118+
{
119+
"hostname" => "*",
120+
"rewrites" => [
121+
"^/#{unique_test_id}/hello/rewrite https://example.com/something/ permanent",
122+
],
123+
},
124+
{
125+
"hostname" => "known.foo",
126+
},
127+
],
128+
}, "--router") do
129+
refute_nginx_duplicate_server_name_warnings(log_tail)
130+
131+
# Known host without rewrites
132+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
133+
:headers => { "Host" => "known.foo" },
134+
}))
135+
assert_response_code(404, response)
136+
137+
# Unknown host
138+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
139+
:headers => { "Host" => "unknown.foo" },
140+
}))
141+
assert_response_code(404, response)
142+
end
143+
end
144+
145+
def test_no_default_host_wildcard_last
146+
log_tail = LogTail.new("nginx/current")
147+
override_config({
148+
"hosts" => [
149+
{
150+
"hostname" => "known.foo",
151+
},
152+
{
153+
"hostname" => "*",
154+
"rewrites" => [
155+
"^/#{unique_test_id}/hello/rewrite https://example.com/something/ permanent",
156+
],
157+
},
158+
],
159+
}, "--router") do
160+
refute_nginx_duplicate_server_name_warnings(log_tail)
161+
162+
# Known host without rewrites
163+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
164+
:headers => { "Host" => "known.foo" },
165+
}))
166+
assert_response_code(404, response)
167+
168+
# Unknown host
169+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
170+
:headers => { "Host" => "unknown.foo" },
171+
}))
172+
assert_response_code(404, response)
173+
end
174+
end
175+
176+
def test_wildcard_is_default
177+
log_tail = LogTail.new("nginx/current")
178+
override_config({
179+
"hosts" => [
180+
{
181+
"hostname" => "*",
182+
"default" => true,
183+
"rewrites" => [
184+
"^/#{unique_test_id}/hello/rewrite https://example.com/something/ permanent",
185+
],
186+
},
187+
{
188+
"hostname" => "known.foo",
189+
},
190+
],
191+
}, "--router") do
192+
refute_nginx_duplicate_server_name_warnings(log_tail)
193+
194+
# Known host without rewrites
195+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
196+
:headers => { "Host" => "known.foo" },
197+
}))
198+
assert_response_code(404, response)
199+
200+
# Unknown host
201+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
202+
:headers => { "Host" => "unknown.foo" },
203+
}))
204+
assert_response_code(301, response)
205+
assert_equal("https://example.com/something/?foo=bar", response.headers["location"])
206+
end
207+
end
208+
209+
def test_wildcard_is_not_default
210+
log_tail = LogTail.new("nginx/current")
211+
override_config({
212+
"hosts" => [
213+
{
214+
"hostname" => "*",
215+
"rewrites" => [
216+
"^/#{unique_test_id}/hello/rewrite https://example.com/something/ permanent",
217+
],
218+
},
219+
{
220+
"hostname" => "known.foo",
221+
"default" => true,
222+
},
223+
],
224+
}, "--router") do
225+
refute_nginx_duplicate_server_name_warnings(log_tail)
226+
227+
# Known host without rewrites
228+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
229+
:headers => { "Host" => "known.foo" },
230+
}))
231+
assert_response_code(404, response)
232+
233+
# Unknown host
234+
response = Typhoeus.get("https://127.0.0.1:9081/#{unique_test_id}/hello/rewrite?foo=bar", http_options.deep_merge({
235+
:headers => { "Host" => "unknown.foo" },
236+
}))
237+
assert_response_code(404, response)
238+
end
239+
end
240+
102241
def test_precedence
242+
log_tail = LogTail.new("nginx/current")
103243
override_config({
104244
"apis" => [
105245
{
@@ -129,6 +269,8 @@ def test_precedence
129269
},
130270
],
131271
}, "--router") do
272+
refute_nginx_duplicate_server_name_warnings(log_tail)
273+
132274
http_opts = http_options.deep_merge({
133275
:headers => { "Host" => "with-apis-and-website.foo" },
134276
})
@@ -166,4 +308,13 @@ def test_precedence
166308
assert_match("<center>API Umbrella</center>", response.body)
167309
end
168310
end
311+
312+
private
313+
314+
def refute_nginx_duplicate_server_name_warnings(log_tail)
315+
log_output = log_tail.read
316+
assert_match(/\[notice\].*: reconfiguring/, log_output)
317+
refute_match("conflicting server name", log_output)
318+
refute_match("warn", log_output)
319+
end
169320
end

test/support/log_tail.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class LogTail
2+
def initialize(filename)
3+
@path = File.join($config["log_dir"], filename)
4+
File.open(@path) do |file|
5+
file.seek(0, IO::SEEK_END)
6+
@pos = file.pos
7+
end
8+
end
9+
10+
def read
11+
output = nil
12+
File.open(@path) do |file|
13+
file.seek(@pos)
14+
output = file.read
15+
@pos = file.pos
16+
end
17+
18+
output
19+
end
20+
end

0 commit comments

Comments
 (0)