Commit c84bfc34 authored by Łukasz Nowak's avatar Łukasz Nowak

rapid-cdn: c->h: Implement type:websocket

haproxy supports websocket by default everywhere, so reuse this and make
type=websocket only needed if some specific configuration like
websocket-path-list or websocket-transparent have to be set.

As a result of implementing this feature X-Forwarded-Proto and
X-Forwarded-Port header support is now enabled per slave and not just
globally.

X-Real-Ip is only available for websocket paths, previous implementation
was simply wrong, and this has been fixed and asserted in tests.

Tests have been slightly updated to make it easier to follow the real
websocket logic.
parent 0b81971c
...@@ -38,7 +38,7 @@ md5sum = cba4d995962f7fbeae3f61c9372c4181 ...@@ -38,7 +38,7 @@ md5sum = cba4d995962f7fbeae3f61c9372c4181
[template-frontend-haproxy-configuration] [template-frontend-haproxy-configuration]
_update_hash_filename_ = templates/frontend-haproxy.cfg.in _update_hash_filename_ = templates/frontend-haproxy.cfg.in
md5sum = 3c4ddc00f6dc609ec830648d1f394b81 md5sum = 1777541cb76d1e3982a4b19bef8b2d55
[template-frontend-haproxy-crt-list] [template-frontend-haproxy-crt-list]
_update_hash_filename_ = templates/frontend-haproxy-crt-list.in _update_hash_filename_ = templates/frontend-haproxy-crt-list.in
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
}, },
"type": { "type": {
"default": "", "default": "",
"description": "Type of slave. If redirect, the slave will redirect to the given URL. If zope, the rewrite rules will be compatible with Virtual Host Monster.", "description": "Type of slave. If redirect, the slave will redirect to the given URL. If zope, the rewrite rules will be compatible with Virtual Host Monster. All frontends support websocket by default and under the hood, but switch to type:websocket allow to configure websocket-path-list and websocket-transparent options.",
"enum": [ "enum": [
"", "",
"zope", "zope",
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
}, },
"websocket-transparent": { "websocket-transparent": {
"default": "true", "default": "true",
"description": "If set to false, websocket slave will be without Caddy's transparent proxy mode. Depending on the application the setting shall be false or true. Defaults to true for transparent proxying.", "description": "If set to false, websocket slave will be without passing X-Real-Ip, X-Forwarded-Proto and X-Forwarded-Port. Depending on the application the setting shall be false or true. Defaults to true for transparent proxying.",
"enum": [ "enum": [
"false", "false",
"true" "true"
......
...@@ -140,36 +140,6 @@ ...@@ -140,36 +140,6 @@
websocket websocket
without /proxy/ without /proxy/
} }
{%- elif slave_parameter['type'] == 'websocket' %}
{%- if slave_parameter['websocket-path-list'] %}
proxy / {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
{%- if slave_parameter['websocket-transparent'] %}
transparent
{%- else %}
header_upstream Host {host}
{%- endif %}
}
{%- for websocket_path in slave_parameter['websocket-path-list'] %}
proxy "/{{ websocket_path }}" {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
websocket
{%- if slave_parameter['websocket-transparent'] %}
transparent
{%- else %}
header_upstream Host {host}
{%- endif %}
}
{%- endfor %}
{%- else %}
proxy / {{ backend_url }} {
{{ proxy_header() }}
{{ hsts_header(tls) }}
websocket
{%- if slave_parameter['websocket-transparent'] %}
transparent
{%- else %} {%- else %}
header_upstream Host {host} header_upstream Host {host}
{%- endif %} {%- endif %}
......
...@@ -124,6 +124,30 @@ backend {{ slave_instance['slave_reference'] }}-{{ scheme }} ...@@ -124,6 +124,30 @@ backend {{ slave_instance['slave_reference'] }}-{{ scheme }}
{%- if slave_instance['prefer-gzip-encoding-to-backend'] %} {%- if slave_instance['prefer-gzip-encoding-to-backend'] %}
http-request set-header Accept-Encoding gzip if { hdr(Accept-Encoding) -m sub gzip } http-request set-header Accept-Encoding gzip if { hdr(Accept-Encoding) -m sub gzip }
{%- endif %} {%- endif %}
{%- if slave_instance['type'] == 'websocket' %}
{%- if slave_instance['websocket-path-list'] %}
{%- set acl_entry = ['acl is_websocket '] %}
{%- for path in slave_instance['websocket-path-list'] %}
{%- do acl_entry.append('path -i -m beg /%s || ' % (path.replace('%', '%%'),)) %}
{%- endfor %}
{%- do acl_entry.append('always_false') %}
{{ ''.join(acl_entry) }}
{%- else %}
acl is_websocket always_true
{%- endif %}
http-request set-header X-Forwarded-Proto {{ scheme }} if !is_websocket
http-request set-header X-Forwarded-Port {{ configuration[scheme + '-port'] }} if !is_websocket
{%- if slave_instance['websocket-transparent'] %}
http-request set-header X-Real-Ip "%ci" if is_websocket
http-request set-header X-Forwarded-Proto {{ scheme }} if is_websocket
http-request set-header X-Forwarded-Port {{ configuration[scheme + '-port'] }} if is_websocket
{%- else %}
{#- Pass-thourgh: X-Forwarded-Proto, X-Forwarded-Port #}
{%- endif %}
{%- else %}
http-request set-header X-Forwarded-Proto {{ scheme }}
http-request set-header X-Forwarded-Port {{ configuration[scheme + '-port'] }}
{%- endif %} {# if slave_instance['type'] == 'websocket' #}
{%- if info_dict['path'] %} {%- if info_dict['path'] %}
http-request set-path {{ info_dict['path'] }}%[path] http-request set-path {{ info_dict['path'] }}%[path]
{%- endif %} {# if info_dict['path'] #} {%- endif %} {# if info_dict['path'] #}
......
...@@ -2277,11 +2277,9 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin): ...@@ -2277,11 +2277,9 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin):
self.assertFalse('timestamp' in line) self.assertFalse('timestamp' in line)
def assertBackendHeaders( def assertBackendHeaders(
self, backend_header_dict, domain, source_ip=SOURCE_IP, port=HTTPS_PORT, self, backend_header_dict, domain=None, source_ip=SOURCE_IP,
proto='https', ignore_header_list=None, cached=False): port=HTTPS_PORT, proto='https', cached=False):
if ignore_header_list is None: if domain is not None:
ignore_header_list = []
if 'Host' not in ignore_header_list:
self.assertEqual( self.assertEqual(
backend_header_dict['host'], backend_header_dict['host'],
'%s:%s' % (domain, port)) '%s:%s' % (domain, port))
...@@ -3350,10 +3348,8 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin): ...@@ -3350,10 +3348,8 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin):
j = result.json() j = result.json()
except Exception: except Exception:
raise ValueError('JSON decode problem in:\n%s' % (result.text,)) raise ValueError('JSON decode problem in:\n%s' % (result.text,))
parsed = urllib.parse.urlparse(self.backend_url)
self.assertBackendHeaders( self.assertBackendHeaders(
j['Incoming Headers'], parsed.hostname, port='17', proto='irc', j['Incoming Headers'], port='17', proto='irc')
ignore_header_list=['Host'])
self.assertEqual( self.assertEqual(
'Upgrade', 'Upgrade',
j['Incoming Headers']['connection'] j['Incoming Headers']['connection']
...@@ -3386,7 +3382,7 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin): ...@@ -3386,7 +3382,7 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin):
except Exception: except Exception:
raise ValueError('JSON decode problem in:\n%s' % (result.text,)) raise ValueError('JSON decode problem in:\n%s' % (result.text,))
self.assertBackendHeaders(j['Incoming Headers'], parameter_dict['domain']) self.assertBackendHeaders(j['Incoming Headers'], parameter_dict['domain'])
self.assertTrue('x-real-ip' in j['Incoming Headers']) self.assertFalse('x-real-ip' in j['Incoming Headers'])
result = fakeHTTPSResult( result = fakeHTTPSResult(
parameter_dict['domain'], 'ws/test-path', parameter_dict['domain'], 'ws/test-path',
...@@ -3456,10 +3452,7 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin): ...@@ -3456,10 +3452,7 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin):
j = result.json() j = result.json()
except Exception: except Exception:
raise ValueError('JSON decode problem in:\n%s' % (result.text,)) raise ValueError('JSON decode problem in:\n%s' % (result.text,))
parsed = urllib.parse.urlparse(self.backend_url) self.assertBackendHeaders(j['Incoming Headers'], parameter_dict['domain'])
self.assertBackendHeaders(
j['Incoming Headers'], parsed.hostname, port='17', proto='irc',
ignore_header_list=['Host'])
self.assertFalse('x-real-ip' in j['Incoming Headers']) self.assertFalse('x-real-ip' in j['Incoming Headers'])
result = fakeHTTPSResult( result = fakeHTTPSResult(
...@@ -3478,8 +3471,7 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin): ...@@ -3478,8 +3471,7 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin):
except Exception: except Exception:
raise ValueError('JSON decode problem in:\n%s' % (result.text,)) raise ValueError('JSON decode problem in:\n%s' % (result.text,))
self.assertBackendHeaders( self.assertBackendHeaders(
j['Incoming Headers'], parsed.hostname, port='17', proto='irc', j['Incoming Headers'], port='17', proto='irc')
ignore_header_list=['Host'])
self.assertEqual( self.assertEqual(
'Upgrade', 'Upgrade',
j['Incoming Headers']['connection'] j['Incoming Headers']['connection']
...@@ -3502,8 +3494,7 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin): ...@@ -3502,8 +3494,7 @@ class TestSlave(SlaveHttpFrontendTestCase, TestDataMixin, AtsMixin):
except Exception: except Exception:
raise ValueError('JSON decode problem in:\n%s' % (result.text,)) raise ValueError('JSON decode problem in:\n%s' % (result.text,))
self.assertBackendHeaders( self.assertBackendHeaders(
j['Incoming Headers'], parsed.hostname, port='17', proto='irc', j['Incoming Headers'], port='17', proto='irc')
ignore_header_list=['Host'])
self.assertEqual( self.assertEqual(
'Upgrade', 'Upgrade',
j['Incoming Headers']['connection'] j['Incoming Headers']['connection']
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment