Skip to content

Commit fc75276

Browse files
authored
Merge pull request #1106 from eloycoto/THREESCALE-2886
Gateway: Add a option to enable keepalive-timeout
2 parents 107d8bc + 3044596 commit fc75276

File tree

6 files changed

+263
-1
lines changed

6 files changed

+263
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1616
- Remove dnsmasq process for APIcast [PR #1090](https://github.com/3scale/APIcast/pull/1090), [THREESCALE-1555](https://issues.jboss.org/browse/THREESCALE-1555)
1717
- Allow to use capture function in liquid templates. [PR #1107](https://github.com/3scale/APIcast/pull/1107), [THREESCALE-1911](https://issues.jboss.org/browse/THREESCALE-1911)
1818
- OAuth 2.0 MTLS policy [PR #1101](https://github.com/3scale/APIcast/pull/1101) [Issue #1003](https://github.com/3scale/APIcast/issues/1003)
19+
- Add an option to enable keepalive_timeout on gateway [THREESCALE-2886](https://issues.jboss.org/browse/THREESCALE-2886) [PR #1106](https://github.com/3scale/APIcast/pull/1106)
1920

2021

2122
### Fixed

doc/parameters.md

+13
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,16 @@ The metrics that will have extended information are:
432432
- total_response_time_seconds: labels service_id and service_system_name
433433
- upstream_response_time_seconds: labels service_id and service_system_name
434434
- upstream_status: labels service_id and service_system_name
435+
436+
### `HTTP_KEEPALIVE_TIMEOUT`
437+
438+
**Default:** 75
439+
**Value:** positive integers
440+
**Example:** "1"
441+
442+
This parameter sets a timeout during which a keep-alive client connection will
443+
stay open on the server side. The zero value disables keep-alive client
444+
connections.
445+
446+
By default Gateway does not enable it, and the keepalive timeout on nginx is set
447+
to [75 seconds](http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout)

gateway/conf.d/apicast.conf

+7-1
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,14 @@ location / {
152152
set $post_action_impact '';
153153
set $original_request_id '';
154154

155-
proxy_ignore_client_abort on;
155+
# {% if http_keepalive_timeout != empty %}
156+
# {% capture keepalive_timeout %}
157+
#{#} keepalive_timeout {{ http_keepalive_timeout}};
158+
# {% endcapture %}
159+
# {{ keepalive_timeout | replace: "#{#}", "" }}
160+
# {% endif %}
156161

162+
proxy_ignore_client_abort on;
157163
rewrite_by_lua_block {
158164
require('resty.ctx').stash()
159165
require('apicast.executor'):rewrite()

gateway/http.d/apicast.conf.liquid

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ server {
8585

8686
server_name _;
8787

88+
{% if http_keepalive_timeout != empty %}
89+
keepalive_timeout {{ http_keepalive_timeout}};
90+
{% endif %}
91+
8892
{% if opentracing_tracer != empty %}
8993
opentracing_operation_name "apicast";
9094
opentracing_trace_locations on;

gateway/src/apicast/cli/environment.lua

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ _M.default_config = {
117117
opentracing_config = env_value_ref('OPENTRACING_CONFIG'),
118118
opentracing_forward_header = env_value_ref('OPENTRACING_FORWARD_HEADER'),
119119
upstream_retry_cases = env_value_ref('APICAST_UPSTREAM_RETRY_CASES'),
120+
http_keepalive_timeout = env_value_ref('HTTP_KEEPALIVE_TIMEOUT'),
120121
policy_chain = require('apicast.policy_chain').default(),
121122
nameservers = parse_nameservers(),
122123
worker_processes = cpus() or 'auto',

t/http-keepalive-timeout.t

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
use lib 't';
2+
use Test::APIcast::Blackbox 'no_plan';
3+
4+
repeat_each(1);
5+
6+
run_tests();
7+
8+
__DATA__
9+
10+
=== TEST 1: Keepalive is not set if no env variable
11+
--- configuration
12+
{
13+
"services": [
14+
{
15+
"id": 42,
16+
"backend_version": 1,
17+
"backend_authentication_type": "service_token",
18+
"backend_authentication_value": "token-value",
19+
"proxy": {
20+
"hosts": [
21+
"one"
22+
],
23+
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
24+
"proxy_rules": [
25+
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 1 }
26+
],
27+
"policy_chain": [
28+
{ "name": "apicast.policy.apicast" }
29+
]
30+
}
31+
}
32+
]
33+
}
34+
--- upstream env
35+
location / {
36+
content_by_lua_block {
37+
ngx.say('yay, api backend');
38+
}
39+
}
40+
--- backend
41+
location /transactions/authrep.xml {
42+
content_by_lua_block {
43+
ngx.exit(200)
44+
}
45+
}
46+
--- test env
47+
content_by_lua_block {
48+
local sock = ngx.socket.tcp()
49+
local ok, err = sock:connect(ngx.var.server_addr, ngx.var.apicast_port)
50+
if not ok then
51+
ngx.say("failed to connect: ", err)
52+
ngx.exit(400)
53+
return
54+
end
55+
56+
local function send_request(sock)
57+
local bytes, err = sock:send("GET /?user_key=foo HTTP/1.1\r\nHost:one\r\n\r\n")
58+
if not bytes then
59+
return false, string.format("failed to send bytes: %s", err)
60+
end
61+
62+
local data, err = sock:receiveany(10 * 1024) -- read any data, at most 10K
63+
if not data then
64+
return false, string.format("failed to receive data: %s", err)
65+
end
66+
67+
return true, nil
68+
end
69+
70+
local result, err = send_request(sock)
71+
ngx.say("First request status: ", result, " err:", err)
72+
ngx.sleep(1)
73+
local result, err = send_request(sock)
74+
ngx.say("Second request status: ", result, " err:", err)
75+
}
76+
--- response_body
77+
First request status: true err:nil
78+
Second request status: true err:nil
79+
--- no_error_log
80+
[error]
81+
82+
=== TEST 2: Keepalive timeout cleans correctly on timeout.
83+
--- env eval
84+
("HTTP_KEEPALIVE_TIMEOUT", "0")
85+
--- configuration
86+
{
87+
"services": [
88+
{
89+
"id": 42,
90+
"backend_version": 1,
91+
"backend_authentication_type": "service_token",
92+
"backend_authentication_value": "token-value",
93+
"proxy": {
94+
"hosts": [
95+
"one"
96+
],
97+
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
98+
"proxy_rules": [
99+
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 1 }
100+
],
101+
"policy_chain": [
102+
{ "name": "apicast.policy.apicast" }
103+
]
104+
}
105+
}
106+
]
107+
}
108+
--- upstream env
109+
location / {
110+
content_by_lua_block {
111+
ngx.say('yay, api backend');
112+
}
113+
}
114+
--- backend
115+
location /transactions/authrep.xml {
116+
content_by_lua_block {
117+
ngx.exit(200)
118+
}
119+
}
120+
--- test env
121+
content_by_lua_block {
122+
local sock = ngx.socket.tcp()
123+
local ok, err = sock:connect(ngx.var.server_addr, ngx.var.apicast_port)
124+
if not ok then
125+
ngx.say("failed to connect: ", err)
126+
ngx.exit(400)
127+
return
128+
end
129+
130+
local function send_request(sock)
131+
local bytes, err = sock:send("GET /?user_key=foo HTTP/1.1\r\nHost:one\r\n\r\n")
132+
if not bytes then
133+
return false, string.format("failed to send bytes: %s", err)
134+
end
135+
136+
local data, err = sock:receiveany(10 * 1024) -- read any data, at most 10K
137+
if not data then
138+
return false, string.format("failed to receive data: %s", err)
139+
end
140+
141+
return true, nil
142+
end
143+
144+
local result, err = send_request(sock)
145+
ngx.say("First request status: ", result, " err:", err)
146+
ngx.sleep(1)
147+
148+
result, err = send_request(sock)
149+
ngx.say("Second request status: ", result, " err:", err)
150+
}
151+
--- response_body
152+
First request status: true err:nil
153+
Second request status: false err:failed to receive data: closed
154+
--- no_error_log
155+
[error]
156+
157+
=== TEST 3: Keepalive timeout can be set correctly.
158+
--- env eval
159+
(
160+
"HTTP_KEEPALIVE_TIMEOUT" => 2
161+
)
162+
--- timeout: 10s
163+
--- configuration
164+
{
165+
"services": [
166+
{
167+
"id": 42,
168+
"backend_version": 1,
169+
"backend_authentication_type": "service_token",
170+
"backend_authentication_value": "token-value",
171+
"proxy": {
172+
"hosts": [
173+
"one"
174+
],
175+
"api_backend": "http://test:$TEST_NGINX_SERVER_PORT/",
176+
"proxy_rules": [
177+
{ "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 1 }
178+
],
179+
"policy_chain": [
180+
{ "name": "apicast.policy.apicast" }
181+
]
182+
}
183+
}
184+
]
185+
}
186+
--- upstream env
187+
location / {
188+
content_by_lua_block {
189+
ngx.say('yay, api backend');
190+
}
191+
}
192+
--- backend
193+
location /transactions/authrep.xml {
194+
content_by_lua_block {
195+
ngx.exit(200)
196+
}
197+
}
198+
--- test env
199+
content_by_lua_block {
200+
local sock = ngx.socket.tcp()
201+
local ok, err = sock:connect(ngx.var.server_addr, ngx.var.apicast_port)
202+
if not ok then
203+
ngx.say("failed to connect: ", err)
204+
ngx.exit(400)
205+
return
206+
end
207+
208+
local function send_request(sock)
209+
local bytes, err = sock:send("GET /?user_key=foo HTTP/1.1\r\nHost:one\r\n\r\n")
210+
if not bytes then
211+
return false, string.format("failed to send bytes: %s", err)
212+
end
213+
214+
local data, err = sock:receiveany(10 * 1024) -- read any data, at most 10K
215+
if not data then
216+
return false, string.format("failed to receive data: %s", err)
217+
end
218+
219+
return true, nil
220+
end
221+
222+
local result, err = send_request(sock)
223+
ngx.say("First request status: ", result, " err:", err)
224+
ngx.sleep(1)
225+
result, err = send_request(sock)
226+
ngx.say("Second request status: ", result, " err:", err)
227+
228+
ngx.sleep(3)
229+
result, err = send_request(sock)
230+
ngx.say("Third request status: ", result, " err:", err)
231+
}
232+
--- response_body
233+
First request status: true err:nil
234+
Second request status: true err:nil
235+
Third request status: false err:failed to receive data: closed
236+
--- no_error_log
237+
[error]

0 commit comments

Comments
 (0)