{%- if configuration['http3-enable'] == 'True' %}
{%-   set HTTP3 = True %}
{%- else %}
{%-   set HTTP3 = False %}
{%- endif %}
global
  pidfile {{ configuration['pid-file'] }}
  # master-worker is compatible with foreground with process management
  master-worker
  expose-experimental-directives
  log {{ configuration['log-socket'] }} local0

defaults
  mode http
  log global
  option httplog
  timeout queue 60s
  timeout server {{ configuration['request-timeout'] }}s
  timeout client {{ configuration['request-timeout'] }}s
  timeout connect 2s
  retries 3
  timeout tunnel 1h
  default-server init-addr last,libc,none

{%- set SCHEME_PREFIX_MAPPING = { 'http': 'backend-http-info', 'https': 'backend-https-info'} %}
{%- macro frontend_entry(slave_instance, scheme, wildcard) %}
{#-   wildcard switch allows to put dangerous entries in the end, as haproxy parses with first match #}
{#-   if slave_instance[SCHEME_PREFIX_MAPPING[scheme]]['hostname'] and slave_instance[SCHEME_PREFIX_MAPPING[scheme]]['port'] #}
{%-     set host_list = (slave_instance.get('server-alias') or  '').split() %}
{%-     if slave_instance.get('custom_domain') not in host_list %}
{%-       do host_list.append(slave_instance.get('custom_domain')) %}
{%-     endif %}
{%-     set matched = {'count': 0} %}
{%-     for host in host_list %}
{#-       Match up to the end or optional port (starting with ':') #}
{#-       Please note that this matching is quite sensitive to changes and hard to test, so avoid needless changes #}
{%-       if wildcard and host.startswith('*.') %}
{%-         do matched.__setitem__('count', matched['count'] + 1) %}
# match wildcard {{ host }}
  acl is_{{ slave_instance['slave_reference'] }} hdr_reg(host) -i {{ host[2:] }}($|:.*)
{%-       elif not wildcard and not host.startswith('*.') %}
{%-         do matched.__setitem__('count', matched['count'] + 1) %}
  acl is_{{ slave_instance['slave_reference'] }} hdr_reg(host) -i ^{{ host }}($|:.*)
{%-       endif %}
{%-     endfor %}
{%-     if matched['count'] > 0 %}
  use_backend {{ slave_instance['slave_reference'] }}-{{ scheme }} if is_{{ slave_instance['slave_reference'] }}
{%-     endif %}
{#-   endif #}
{%- endmacro %}

{%- macro frontend_common() %}
  # normalize URIs as it's expected by the backends
  http-request normalize-uri path-merge-slashes
  http-request normalize-uri path-strip-dot
  http-request normalize-uri path-strip-dotdot
  # Combined Log Format
  capture request header REMOTE_USER len 255
  capture request header Referer len 255
  capture request header User-Agent len 255
  log-format "%{+E}o {{ '{' }}%b{{ '}' }} %ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq!!!%ci - %[capture.req.hdr(0)] [%trl] \"%HM %[capture.req.uri] %HV\" %ST %B \"%[capture.req.hdr(1)]\" \"%[capture.req.hdr(2)]\" %Ta"
  # setup Via
  http-request add-header Via "%HV rapid-cdn-frontend-{{ configuration['node-id'] }}-{{ configuration['version-hash'] }}"
  # setup X-Forwarded-For
  http-request set-header X-Forwarded-For "%ci"
{%- endmacro %}
frontend http-frontend
  bind {{ configuration['local-ipv4'] }}:{{ configuration['http-port'] }}
  bind {{ configuration['global-ipv6'] }}:{{ configuration['http-port'] }}
{{ frontend_common() }}
{%- for slave_instance in frontend_slave_list -%}
{{ frontend_entry(slave_instance, 'http', False) }}
{%- endfor %}
{%- for slave_instance in frontend_slave_list -%}
{{ frontend_entry(slave_instance, 'http', True) }}
{%- endfor %}
  default_backend BACKEND_NOT_FOUND

frontend https-frontend
  bind {{ configuration['local-ipv4'] }}:{{ configuration['https-port'] }} ssl crt-list {{ crt_list }}
  bind {{ configuration['global-ipv6'] }}:{{ configuration['https-port'] }} ssl crt-list {{ crt_list }}
{%- if HTTP3 %}
  bind quic4@{{ configuration['local-ipv4'] }}:{{ configuration['https-port'] }} ssl crt-list {{ crt_list }} alpn h3
  bind quic6@{{ configuration['global-ipv6'] }}:{{ configuration['https-port'] }} ssl crt-list {{ crt_list }} alpn h3
{%- endif %}
{{ frontend_common() }}
{%- for slave_instance in frontend_slave_list -%}
{{ frontend_entry(slave_instance, 'https', False) }}
{%- endfor %}
{%- for slave_instance in frontend_slave_list -%}
{{ frontend_entry(slave_instance, 'https', True) }}
{%- endfor %}
  default_backend BACKEND_NOT_FOUND

# Backends
{%- for slave_instance in frontend_slave_list %}
{%-   for (scheme, prefix) in SCHEME_PREFIX_MAPPING.items() %}
{%-     set info_dict = slave_instance.get(prefix, slave_instance.get('backend-http-info')) %}
backend {{ slave_instance['slave_reference'] }}-{{ scheme }}
{%-     if HTTP3 and slave_instance['enable-http3'] %}
  http-after-response set-header alt-svc "h3=\":{{ configuration['http3-port'] }}\"; ma=3600"
  {#- Ask Chromium to use HTTP3 #}
  http-after-response set-header alternate-protocol {{ configuration['http3-port'] }}:quic
{%-     endif %}
{%-     if scheme == 'http' and slave_instance['https-only'] %}
{#-     Support https-only if connected via http #}
  redirect scheme https code 302
{%-     else %}
{%-       if 'hostname' in info_dict and 'port' in info_dict %}
{%-         if slave_instance['type'] == 'redirect' %}
  redirect prefix {{ info_dict['scheme'] }}://{{ info_dict['hostname'] }}:{{ info_dict['port'] }} code 302
{%-         else %}
  server {{ slave_instance['slave_reference'] }}-backend-{{ scheme }} {{ info_dict['hostname'] }}:{{ info_dict['port'] }}
{%-           if slave_instance['disable-via-header'] %}
  http-after-response del-header Via
{%-           else %}
  http-after-response add-header Via "%HV rapid-cdn-frontend-{{ configuration['node-id'] }}-{{ configuration['version-hash'] }}"
{%-           endif %}
{%-           if scheme == 'https' %}
{%-             if slave_instance['strict-transport-security'] > 0 %}
{%-               set strict_transport_security = ['max-age=%i' % (slave_instance['strict-transport-security'],)] %}
{%-               if slave_instance['strict-transport-security-sub-domains'] %}
{%-                 do strict_transport_security.append('; includeSubDomains') %}
{%-               endif %}
{%-               if slave_instance['strict-transport-security-preload'] %}
{%-                 do strict_transport_security.append('; preload') %}
{%-               endif %}
  http-after-response set-header Strict-Transport-Security "{{ ''.join(strict_transport_security) }}"
{%-             endif %}
{%-           endif %}
{%-           for disabled_cookie in slave_instance['disabled-cookie-list'] %}
  http-request replace-header Cookie (.*)(^{{ disabled_cookie | replace('%', '%%') }}=[^;]*;\ |;\ {{ disabled_cookie }}=[^;]*|^{{ disabled_cookie }}=[^;]*$)(.*) \1\3
{%-           endfor %}
{%-           if slave_instance['disable-no-cache-request'] %}
  http-request del-header Cache-Control
  http-request del-header Pragma
{%-           endif %}
{%-           if slave_instance['prefer-gzip-encoding-to-backend'] %}
  http-request set-header Accept-Encoding gzip if { hdr(Accept-Encoding) -m sub gzip }
{%-           endif %}
{%-           if slave_instance['type'] == 'notebook' %}
{#-             In haproxy world type:notebook is simple type:websocket with default parameters #}
{%-             do slave_instance.__setitem__('type', 'websocket') %}
{%-             do slave_instance.__setitem__('websocket-path-list', None) %}
{%-             do slave_instance.__setitem__('websocket-transparent', True) %}
{%-           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 slave_instance['type'] == 'zope' %}
{%-             if slave_instance['default-path'] %}
  http-request redirect location {{ scheme}}://%[hdr(host)]/{{ slave_instance['default-path'] | replace('%', '%%') }} code 301 if { path / }
{%-               set not_path_acl = 'if ! { path / }' %}
{%-             else %}
{%-               set not_path_acl = '' %}
{%-             endif %}
{%-             if slave_instance['path'].strip().strip('/') %}
{%-               set zope_path = slave_instance['path'].strip().strip('/').replace('%', '%%') ~ '/' %}
{%-             else %}
{%-               set zope_path = '' %}
{%-             endif %}
  http-request set-path /VirtualHostBase/{{ scheme }}/%[req.hdr(Host),field(1,:)]:{{ slave_instance['virtualhostroot-%s-port' % (scheme,)] }}/{{ zope_path }}VirtualHostRoot%[path] {{ not_path_acl }}
{%-           endif %}
{%-           if info_dict['path'] %}
  http-request set-path {{ info_dict['path'] }}%[path]
{%-           endif %} {# if info_dict['path'] #}
{%-         endif %} {# if slave_instance['type'] == 'redirect' #}
{%-       endif %} {# if 'hostname' in info_dict and 'port' in info_dict #}
{%-     endif %} {# if scheme == 'http' and slave_instance['https-only'] #}
{%-   endfor %} {# for (scheme, prefix) in SCHEME_PREFIX_MAPPING.items() #}
{%- endfor %} {# for slave_instance in frontend_slave_list #}

backend BACKEND_NOT_FOUND
  {#- a bit hacky but working way to provide default CDN's 404 #}
  {#- inspired by https://sleeplessbeastie.eu/2020/05/11/how-to-serve-single-file-using-haproxy/ #}
  http-request set-log-level silent
  errorfile 503 {{ configuration['not-found-file'] }}

{# END OF FILE #}
