From 5d86d0912ad07fbd41381ae7487827b545ac340e Mon Sep 17 00:00:00 2001 From: Thibault Charbonnier Date: Tue, 21 Mar 2017 17:08:04 -0700 Subject: [PATCH] tests(proxy) rewrite preserve_hosts suite * tests(fixtures) udpate custom Nginx template Updated to use the latest Kong 0.10.0 Nginx template configuration. * tests(helpers) introduce 'nginx_conf' option `helpers.start_kong()` now receives an `nginx_conf` option, similar to the CLI's `--nginx-conf`, which makes the test instance start with a custom Nginx template configuration. * tests(proxy) rewrite 'preserve_host' suite We'd rather not depend on any third-party service for testing such a behavior since `preserve_host` typically creates invalid HTTP requests to upstream services. Here we introduce custom Nginx locations that allow us to mimic (and improve) httpbin.org behavior locally. The long term goal is to transition all of the tests relying on httpbin.org to such local code, so this is a step forwards, regardless of the testing framework or Nginx configuration handling we might have in the future. --- .../05-proxy/01-router_spec.lua | 105 ++++++++++-------- spec/fixtures/custom_nginx.template | 83 ++++++++++++-- spec/helpers.lua | 7 +- 3 files changed, 140 insertions(+), 55 deletions(-) diff --git a/spec/02-integration/05-proxy/01-router_spec.lua b/spec/02-integration/05-proxy/01-router_spec.lua index e990926f45c7..c62a8b6f5fac 100644 --- a/spec/02-integration/05-proxy/01-router_spec.lua +++ b/spec/02-integration/05-proxy/01-router_spec.lua @@ -241,73 +241,88 @@ describe("Router", function() { name = "api-1", preserve_host = true, - upstream_url = "http://httpbin.org", + upstream_url = "http://localhost:9999/headers-inspect", hosts = "preserved.com", }, { name = "api-2", preserve_host = false, - upstream_url = "http://httpbin.org", + upstream_url = "http://localhost:9999/headers-inspect", hosts = "discarded.com", - } + }, } - assert(helpers.start_kong()) + assert(helpers.start_kong { + nginx_conf = "spec/fixtures/custom_nginx.template", + }) end) teardown(function() helpers.stop_kong() end) - it("preserves downstream host if enabled", function() - local res = assert(client:send { - method = "GET", - path = "/get", - headers = { ["Host"] = "preserved.com" }, - }) + describe(" = false (default)", function() + it("uses hostname from upstream_url", function() + local res = assert(client:send { + method = "GET", + path = "/get", + headers = { ["Host"] = "discarded.com" }, + }) - assert.res_status(200, res) - local json = cjson.decode((res:read_body())) - assert.equal("preserved.com", json.headers.Host) - end) + local body = assert.res_status(200, res) + local json = cjson.decode(body) + assert.matches("localhost", json.host, nil, true) -- not testing :port + end) - pending("preserves downstream host+port if enabled", function() - -- pending because httpbin.org seems to not return the exact header - -- but a parsed one, with the port stripped. - local res = assert(client:send { - method = "GET", - path = "/get", - headers = { ["Host"] = "preserved.com:80" }, - }) + it("uses port value from upstream_url if not default", function() + local res = assert(client:send { + method = "GET", + path = "/get", + headers = { ["Host"] = "discarded.com" }, + }) - assert.res_status(200, res) - local json = cjson.decode((res:read_body())) - assert.equal("preserved.com:80", json.headers.Host) + local body = assert.res_status(200, res) + local json = cjson.decode(body) + assert.matches(":9999", json.host, nil, true) -- not testing hostname + end) end) - pending("preserves downstream host+non_default_port if enabled", function() - -- pending because httpbin.org seems to not return the exact header - -- but a parsed one, with the port stripped. - local res = assert(client:send { - method = "GET", - path = "/get", - headers = { ["Host"] = "preserved.com:123" }, - }) + describe(" = true", function() + it("forwards request Host", function() + local res = assert(client:send { + method = "GET", + path = "/", + headers = { ["Host"] = "preserved.com" }, + }) - assert.res_status(200, res) - local json = cjson.decode((res:read_body())) - assert.equal("preserved.com:123", json.headers.Host) - end) + local body = assert.res_status(200, res) + local json = cjson.decode(body) + assert.equal("preserved.com", json.host) + end) - it("discards downstream host if disabled", function() - local res = assert(client:send { - method = "GET", - path = "/get", - headers = { ["Host"] = "discarded.com" }, - }) + it("forwards request Host:Port even if port is default", function() + local res = assert(client:send { + method = "GET", + path = "/get", + headers = { ["Host"] = "preserved.com:80" }, + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + assert.equal("preserved.com:80", json.host) + end) - assert.response(res).has.status(200) - assert.equal("httpbin.org", assert.request(res).has.header("Host")) + it("forwards request Host:Port if port isn't default", function() + local res = assert(client:send { + method = "GET", + path = "/get", + headers = { ["Host"] = "preserved.com:123" }, + }) + + local body = assert.res_status(200, res) + local json = cjson.decode(body) + assert.equal("preserved.com:123", json.host) + end) end) end) diff --git a/spec/fixtures/custom_nginx.template b/spec/fixtures/custom_nginx.template index 94172f78e120..aa39ac4064dc 100644 --- a/spec/fixtures/custom_nginx.template +++ b/spec/fixtures/custom_nginx.template @@ -12,12 +12,23 @@ http { charset UTF-8; error_log logs/error.log ${{LOG_LEVEL}}; - access_log logs/access.log; > if anonymous_reports then ${{SYSLOG_REPORTS}} > end +> if nginx_optimizations then +>-- send_timeout 60s; # default value +>-- keepalive_timeout 75s; # default value +>-- client_body_timeout 60s; # default value +>-- client_header_timeout 60s; # default value +>-- tcp_nopush on; # disabled until benchmarked +>-- proxy_buffer_size 128k; # disabled until benchmarked +>-- proxy_buffers 4 256k; # disabled until benchmarked +>-- proxy_busy_buffers_size 256k; # disabled until benchmarked +>-- reset_timedout_connection on; # disabled until benchmarked +> end + client_max_body_size 0; proxy_ssl_server_name on; underscores_in_headers on; @@ -29,18 +40,22 @@ http { lua_package_path '${{LUA_PACKAGE_PATH}};;'; lua_package_cpath '${{LUA_PACKAGE_CPATH}};;'; lua_code_cache ${{LUA_CODE_CACHE}}; + lua_socket_pool_size ${{LUA_SOCKET_POOL_SIZE}}; lua_max_running_timers 4096; lua_max_pending_timers 16384; lua_shared_dict kong 4m; lua_shared_dict cache ${{MEM_CACHE_SIZE}}; - lua_shared_dict cassandra 1m; - lua_shared_dict cassandra_prepared 5m; + lua_shared_dict cache_locks 100k; + lua_shared_dict process_events 1m; + lua_shared_dict cassandra 5m; lua_socket_log_errors off; > if lua_ssl_trusted_certificate then - lua_ssl_trusted_certificate '${{lua_ssl_trusted_certificate}}'; + lua_ssl_trusted_certificate '${{LUA_SSL_TRUSTED_CERTIFICATE}}'; + lua_ssl_verify_depth ${{LUA_SSL_VERIFY_DEPTH}}; > end init_by_lua_block { + require 'resty.core' kong = require 'kong' kong.init() } @@ -49,16 +64,39 @@ http { kong.init_worker() } + proxy_next_upstream_tries 999; + + upstream kong_upstream { + server 0.0.0.1; + balancer_by_lua_block { + kong.balancer() + } + keepalive ${{UPSTREAM_KEEPALIVE}}; + } + + map $http_upgrade $upstream_connection { + default keep-alive; + websocket upgrade; + } + + map $http_upgrade $upstream_upgrade { + default ''; + websocket websocket; + } + server { server_name kong; listen ${{PROXY_LISTEN}}; - error_page 500 502 503 504 /50x; + error_page 404 408 411 412 413 414 417 /kong_error_handler; + error_page 500 502 503 504 /kong_error_handler; + + access_log logs/access.log; > if ssl then listen ${{PROXY_LISTEN_SSL}} ssl; ssl_certificate ${{SSL_CERT}}; ssl_certificate_key ${{SSL_CERT_KEY}}; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_protocols TLSv1.1 TLSv1.2; ssl_certificate_by_lua_block { kong.ssl_certificate() } @@ -66,18 +104,22 @@ http { location / { set $upstream_host nil; - set $upstream_url nil; + set $upstream_scheme nil; access_by_lua_block { kong.access() } + proxy_http_version 1.1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $upstream_host; + proxy_set_header Upgrade $upstream_upgrade; + proxy_set_header Connection $upstream_connection; + proxy_pass_header Server; - proxy_pass $upstream_url; + proxy_pass $upstream_scheme://kong_upstream; header_filter_by_lua_block { kong.header_filter() @@ -92,7 +134,7 @@ http { } } - location = /50x { + location = /kong_error_handler { internal; content_by_lua_block { require('kong.core.error_handlers')(ngx) @@ -104,13 +146,23 @@ http { server_name kong_admin; listen ${{ADMIN_LISTEN}}; + access_log logs/admin_access.log; + client_max_body_size 10m; client_body_buffer_size 10m; +> if admin_ssl then + listen ${{ADMIN_LISTEN_SSL}} ssl; + ssl_certificate ${{ADMIN_SSL_CERT}}; + ssl_certificate_key ${{ADMIN_SSL_CERT_KEY}}; + ssl_protocols TLSv1.1 TLSv1.2; +> end + location / { default_type application/json; content_by_lua_block { ngx.header['Access-Control-Allow-Origin'] = '*' + ngx.header['Access-Control-Allow-Credentials'] = 'false' if ngx.req.get_method() == 'OPTIONS' then ngx.header['Access-Control-Allow-Methods'] = 'GET,HEAD,PUT,PATCH,POST,DELETE' ngx.header['Access-Control-Allow-Headers'] = 'Content-Type' @@ -139,5 +191,18 @@ http { location /custom_server_path { return 200; } + + location /headers-inspect { + content_by_lua_block { + local cjson = require "cjson" + + local headers = ngx.req.get_headers() + local json = cjson.encode(headers) + + ngx.status = 200 + ngx.say(json) + ngx.exit(200) + } + } } } diff --git a/spec/helpers.lua b/spec/helpers.lua index c17a06fe8b88..36d153c52585 100644 --- a/spec/helpers.lua +++ b/spec/helpers.lua @@ -853,7 +853,12 @@ return { local ok, err = prepare_prefix(env.prefix) if not ok then return nil, err end - return kong_exec("start --conf "..TEST_CONF_PATH, env) + local nginx_conf = "" + if env.nginx_conf then + nginx_conf = " --nginx-conf " .. env.nginx_conf + end + + return kong_exec("start --conf " .. TEST_CONF_PATH .. nginx_conf, env) end, stop_kong = function(prefix, preserve_prefix) prefix = prefix or conf.prefix