summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViacheslav Hletenko <v.gletenko@vyos.io>2026-06-23 18:45:17 +0300
committerGitHub <noreply@github.com>2026-06-23 18:45:17 +0300
commit105c2470df751f9bff280255506640e63204e3bb (patch)
tree18e25c127bf66f6316e91cc8646718ff6f34a9a1
parent4e74f21299c77a35115a61908a2c84e390e1f0d1 (diff)
parent4f550fc5184f7d583f5976e801e9cae4d06e0cb6 (diff)
downloadvyos-1x-105c2470df751f9bff280255506640e63204e3bb.tar.gz
vyos-1x-105c2470df751f9bff280255506640e63204e3bb.zip
Merge pull request #5226 from indrajitr/haproxy-websocket
haproxy: T8931: Improve WebSocket support for HAProxy
-rw-r--r--data/templates/load-balancing/haproxy.cfg.j27
-rw-r--r--interface-definitions/include/haproxy/timeout-tunnel.xml.i14
-rw-r--r--interface-definitions/include/haproxy/timeout.xml.i1
-rw-r--r--interface-definitions/load-balancing_haproxy.xml.in10
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_haproxy.py33
-rw-r--r--src/conf_mode/load-balancing_haproxy.py7
6 files changed, 71 insertions, 1 deletions
diff --git a/data/templates/load-balancing/haproxy.cfg.j2 b/data/templates/load-balancing/haproxy.cfg.j2
index 79fb5e369..438796cee 100644
--- a/data/templates/load-balancing/haproxy.cfg.j2
+++ b/data/templates/load-balancing/haproxy.cfg.j2
@@ -42,6 +42,7 @@ defaults
timeout connect {{ timeout.connect }}s
timeout client {{ timeout.client }}s
timeout server {{ timeout.server }}s
+ timeout tunnel {{ timeout.tunnel }}s
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
@@ -205,6 +206,9 @@ backend {{ back }}
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
+{% if back_config.http_server_close is vyos_defined %}
+ option http-server-close
+{% endif %}
{% endif %}
{% if back_config.logging is vyos_defined %}
{% for facility, facility_config in back_config.logging.facility.items() %}
@@ -285,5 +289,8 @@ backend {{ back }}
{% if back_config.timeout.server is vyos_defined %}
timeout server {{ back_config.timeout.server }}s
{% endif %}
+{% if back_config.timeout.tunnel is vyos_defined %}
+ timeout tunnel {{ back_config.timeout.tunnel }}s
+{% endif %}
{% endfor %}
{% endif %}
diff --git a/interface-definitions/include/haproxy/timeout-tunnel.xml.i b/interface-definitions/include/haproxy/timeout-tunnel.xml.i
new file mode 100644
index 000000000..ea6be1505
--- /dev/null
+++ b/interface-definitions/include/haproxy/timeout-tunnel.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from haproxy/timeout-tunnel.xml.i -->
+<leafNode name="tunnel">
+ <properties>
+ <help>Set the maximum inactivity time on the client and server side for tunnels</help>
+ <valueHelp>
+ <format>u32:1-86400</format>
+ <description>Tunnel timeout in seconds</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-86400"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/haproxy/timeout.xml.i b/interface-definitions/include/haproxy/timeout.xml.i
index a3a5a8a3e..a84e32001 100644
--- a/interface-definitions/include/haproxy/timeout.xml.i
+++ b/interface-definitions/include/haproxy/timeout.xml.i
@@ -7,6 +7,7 @@
#include <include/haproxy/timeout-check.xml.i>
#include <include/haproxy/timeout-connect.xml.i>
#include <include/haproxy/timeout-server.xml.i>
+ #include <include/haproxy/timeout-tunnel.xml.i>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/load-balancing_haproxy.xml.in b/interface-definitions/load-balancing_haproxy.xml.in
index f3baf86ca..8111d7506 100644
--- a/interface-definitions/load-balancing_haproxy.xml.in
+++ b/interface-definitions/load-balancing_haproxy.xml.in
@@ -225,6 +225,12 @@
</constraint>
</properties>
</leafNode>
+ <leafNode name="http-server-close">
+ <properties>
+ <help>Enable HTTP/1.x connection closing on the server side</help>
+ <valueless/>
+ </properties>
+ </leafNode>
#include <include/haproxy/rule-backend.xml.i>
<tagNode name="server">
<properties>
@@ -399,6 +405,10 @@
<leafNode name="server">
<defaultValue>50</defaultValue>
</leafNode>
+ #include <include/haproxy/timeout-tunnel.xml.i>
+ <leafNode name="tunnel">
+ <defaultValue>300</defaultValue>
+ </leafNode>
</children>
</node>
#include <include/interface/vrf.xml.i>
diff --git a/smoketest/scripts/cli/test_load-balancing_haproxy.py b/smoketest/scripts/cli/test_load-balancing_haproxy.py
index 9cb031276..118d42773 100755
--- a/smoketest/scripts/cli/test_load-balancing_haproxy.py
+++ b/smoketest/scripts/cli/test_load-balancing_haproxy.py
@@ -335,6 +335,34 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
+ def test_reverse_proxy_backend_websocket(self):
+ t_tunnel = '3600'
+ opt_server_close = 'http-server-close'
+
+ # Setup base
+ self.configure_pki()
+ self.base_config()
+
+ # Set minimal backend websocket configuration
+ self.cli_set(base_path + ['backend', haproxy_backend_name, opt_server_close])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'timeout', 'tunnel', t_tunnel])
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'ssl', 'no-verify'])
+
+ self.cli_commit()
+
+ # Ensure 'http-server-close' is not used in tcp mode, to test config validation
+ self.cli_set(base_path + ['backend', haproxy_backend_name, 'mode', 'tcp'])
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+
+ config = read_file(HAPROXY_CONF)
+ self.assertIn(f'option {opt_server_close}', config)
+ self.assertIn(f'timeout tunnel {t_tunnel}s', config)
+ self.assertIn('option forwardfor', config)
+ self.assertIn(' http-request set-header X-Forwarded-Port %[dst_port]', config)
+ self.assertIn('http-request add-header X-Forwarded-Proto https if { ssl_fc }', config)
+ self.assertIn(f'server {haproxy_backend_name} 192.0.2.11:9090 send-proxy ssl verify none', config)
+
def test_reverse_proxy_backend_http_check(self):
# Setup base
self.base_config()
@@ -619,10 +647,12 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
t_default_client = '50'
t_default_connect = '10'
t_default_server ='50'
+ t_default_tunnel ='300'
t_check = '4'
t_client = '300'
t_connect = '12'
t_server ='120'
+ t_tunnel ='600'
t_front_client = '600'
self.base_config()
@@ -633,6 +663,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
f'timeout connect {t_default_connect}s',
f'timeout client {t_default_client}s',
f'timeout server {t_default_server}s',
+ f'timeout tunnel {t_default_tunnel}s'
)
# Check default timeout options
config = read_file(HAPROXY_CONF)
@@ -644,6 +675,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.cli_set(base_path + ['timeout', 'client', t_client])
self.cli_set(base_path + ['timeout', 'connect', t_connect])
self.cli_set(base_path + ['timeout', 'server', t_server])
+ self.cli_set(base_path + ['timeout', 'tunnel', t_tunnel])
self.cli_set(base_path + ['service', haproxy_service_name, 'timeout', 'client', t_front_client])
self.cli_commit()
@@ -654,6 +686,7 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
f'timeout connect {t_connect}s',
f'timeout client {t_client}s',
f'timeout server {t_server}s',
+ f'timeout tunnel {t_tunnel}s',
f'timeout client {t_front_client}s',
)
diff --git a/src/conf_mode/load-balancing_haproxy.py b/src/conf_mode/load-balancing_haproxy.py
index 0f31880ba..f95d05151 100644
--- a/src/conf_mode/load-balancing_haproxy.py
+++ b/src/conf_mode/load-balancing_haproxy.py
@@ -149,7 +149,12 @@ def verify(lb):
for group in ['service', 'backend']:
for config_name, config in lb[group].items():
if 'http_response_headers' in config and config['mode'] != 'http':
- raise ConfigError(f'{group} {config_name} must be set to http mode to use http_response_headers!')
+ raise ConfigError(f'{group} {config_name} must be set to http mode to use http-response headers!')
+
+ # Check if http-server-close is configured in any backend where mode != http
+ for config_name, config in lb['backend'].items():
+ if 'http_server_close' in config and config['mode'] != 'http':
+ raise ConfigError(f'backend {config_name} must be set to http mode to use http-server-close!')
def generate(lb):